diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/windowing/win10 | |
parent | Initial commit. (diff) | |
download | kodi-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/win10')
-rw-r--r-- | xbmc/windowing/win10/CMakeLists.txt | 12 | ||||
-rw-r--r-- | xbmc/windowing/win10/WinEventsWin10.cpp | 658 | ||||
-rw-r--r-- | xbmc/windowing/win10/WinEventsWin10.h | 83 | ||||
-rw-r--r-- | xbmc/windowing/win10/WinSystemWin10.cpp | 660 | ||||
-rw-r--r-- | xbmc/windowing/win10/WinSystemWin10.h | 156 | ||||
-rw-r--r-- | xbmc/windowing/win10/WinSystemWin10DX.cpp | 205 | ||||
-rw-r--r-- | xbmc/windowing/win10/WinSystemWin10DX.h | 90 |
7 files changed, 1864 insertions, 0 deletions
diff --git a/xbmc/windowing/win10/CMakeLists.txt b/xbmc/windowing/win10/CMakeLists.txt new file mode 100644 index 0000000..7e41c17 --- /dev/null +++ b/xbmc/windowing/win10/CMakeLists.txt @@ -0,0 +1,12 @@ +set(SOURCES WinEventsWin10.cpp + WinSystemWin10.cpp + WinSystemWin10DX.cpp + ../windows/VideoSyncD3D.cpp) + +set(HEADERS WinEventsWin10.h + WinSystemWin10.h + WinSystemWin10DX.h + ../windows/VideoSyncD3D.h + ../windows/WinKeyMap.h) + +core_add_library(windowing_windowsstore) diff --git a/xbmc/windowing/win10/WinEventsWin10.cpp b/xbmc/windowing/win10/WinEventsWin10.cpp new file mode 100644 index 0000000..35da911 --- /dev/null +++ b/xbmc/windowing/win10/WinEventsWin10.cpp @@ -0,0 +1,658 @@ +/* + * 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 "WinEventsWin10.h" + +#include "GUIUserMessages.h" +#include "ServiceBroker.h" +#include "application/AppInboundProtocol.h" +#include "application/Application.h" +#include "guilib/GUIComponent.h" +#include "guilib/GUIWindowManager.h" +#include "input/actions/Action.h" +#include "input/actions/ActionIDs.h" +#include "input/mouse/MouseStat.h" +#include "input/touch/generic/GenericTouchInputHandler.h" +#include "interfaces/AnnouncementManager.h" +#include "messaging/ApplicationMessenger.h" +#include "rendering/dx/DeviceResources.h" +#include "rendering/dx/RenderContext.h" +#include "settings/AdvancedSettings.h" +#include "settings/SettingsComponent.h" +#include "utils/SystemInfo.h" +#include "utils/Variant.h" +#include "utils/log.h" +#include "windowing/windows/WinKeyMap.h" + +#include "platform/win10/input/RemoteControlXbox.h" + +#include <winrt/Windows.Devices.Input.h> + +namespace winrt +{ + using namespace Windows::Foundation; +} +using namespace winrt::Windows::ApplicationModel::Core; +using namespace winrt::Windows::Devices::Input; +using namespace winrt::Windows::Graphics::Display; +using namespace winrt::Windows::Media; +using namespace winrt::Windows::System; +using namespace winrt::Windows::UI::Core; +using namespace winrt::Windows::UI::Input; +using namespace winrt::Windows::UI::ViewManagement; + +using namespace PERIPHERALS; + +static winrt::Point GetScreenPoint(winrt::Point point) +{ + auto dpi = DX::DeviceResources::Get()->GetDpi(); + return winrt::Point(DX::ConvertDipsToPixels(point.X, dpi), DX::ConvertDipsToPixels(point.Y, dpi)); +} + +CWinEventsWin10::CWinEventsWin10() = default; +CWinEventsWin10::~CWinEventsWin10() = default; + +void CWinEventsWin10::InitOSKeymap(void) +{ + KODI::WINDOWING::WINDOWS::DIB_InitOSKeymap(); +} + +void CWinEventsWin10::MessagePush(XBMC_Event *newEvent) +{ + // push input events in the queue they may init modal dialog which init + // deeper message loop and call the deeper MessagePump from there. + if ( newEvent->type == XBMC_KEYDOWN + || newEvent->type == XBMC_KEYUP + || newEvent->type == XBMC_MOUSEMOTION + || newEvent->type == XBMC_MOUSEBUTTONDOWN + || newEvent->type == XBMC_MOUSEBUTTONUP + || newEvent->type == XBMC_TOUCH) + { + m_events.push(*newEvent); + } + else + { + std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort(); + if (appPort) + appPort->OnEvent(*newEvent); + } +} + +bool CWinEventsWin10::MessagePump() +{ + bool ret = false; + std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort(); + + // processes all pending events and exits immediately + CoreWindow::GetForCurrentThread().Dispatcher().ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent); + + XBMC_Event pumpEvent; + while (m_events.try_pop(pumpEvent)) + { + if (appPort) + ret |= appPort->OnEvent(pumpEvent); + + if (pumpEvent.type == XBMC_MOUSEBUTTONUP) + CServiceBroker::GetGUI()->GetWindowManager().SendMessage(GUI_MSG_UNFOCUS_ALL, 0, 0, 0, 0); + } + return ret; +} + +size_t CWinEventsWin10::GetQueueSize() +{ + return m_events.unsafe_size(); +} + +void CWinEventsWin10::InitEventHandlers(const CoreWindow& window) +{ + CWinEventsWin10::InitOSKeymap(); + + //window->SetPointerCapture(); + + // window + window.SizeChanged({ this, &CWinEventsWin10::OnWindowSizeChanged }); + window.ResizeStarted({ this, &CWinEventsWin10::OnWindowResizeStarted }); + window.ResizeCompleted({ this, &CWinEventsWin10::OnWindowResizeCompleted }); + window.Closed({ this, &CWinEventsWin10::OnWindowClosed}); + window.VisibilityChanged(CWinEventsWin10::OnVisibilityChanged); + window.Activated(CWinEventsWin10::OnWindowActivationChanged); + // mouse, touch and pen + window.PointerPressed({ this, &CWinEventsWin10::OnPointerPressed }); + window.PointerMoved({ this, &CWinEventsWin10::OnPointerMoved }); + window.PointerReleased({ this, &CWinEventsWin10::OnPointerReleased }); + window.PointerExited({ this, &CWinEventsWin10::OnPointerExited }); + window.PointerWheelChanged({ this, &CWinEventsWin10::OnPointerWheelChanged }); + // keyboard + window.Dispatcher().AcceleratorKeyActivated({ this, &CWinEventsWin10::OnAcceleratorKeyActivated }); + // display + DisplayInformation currentDisplayInformation = DisplayInformation::GetForCurrentView(); + currentDisplayInformation.DpiChanged(CWinEventsWin10::OnDpiChanged); + currentDisplayInformation.OrientationChanged(CWinEventsWin10::OnOrientationChanged); + DisplayInformation::DisplayContentsInvalidated(CWinEventsWin10::OnDisplayContentsInvalidated); + // system + SystemNavigationManager sysNavManager = SystemNavigationManager::GetForCurrentView(); + sysNavManager.BackRequested(CWinEventsWin10::OnBackRequested); + + // requirement for backgroup playback + m_smtc = SystemMediaTransportControls::GetForCurrentView(); + if (m_smtc) + { + m_smtc.IsPlayEnabled(true); + m_smtc.IsPauseEnabled(true); + m_smtc.IsStopEnabled(true); + m_smtc.IsRecordEnabled(true); + m_smtc.IsNextEnabled(true); + m_smtc.IsPreviousEnabled(true); + m_smtc.IsFastForwardEnabled(true); + m_smtc.IsRewindEnabled(true); + m_smtc.IsChannelUpEnabled(true); + m_smtc.IsChannelDownEnabled(true); + if (CSysInfo::GetWindowsDeviceFamily() != CSysInfo::WindowsDeviceFamily::Xbox) + { + m_smtc.ButtonPressed(CWinEventsWin10::OnSystemMediaButtonPressed); + } + m_smtc.IsEnabled(true);; + CServiceBroker::GetAnnouncementManager()->AddAnnouncer(this); + } + if (CSysInfo::GetWindowsDeviceFamily() == CSysInfo::WindowsDeviceFamily::Xbox) + { + m_remote = std::make_unique<CRemoteControlXbox>(); + m_remote->Initialize(); + } +} + +void CWinEventsWin10::UpdateWindowSize() +{ + auto size = DX::DeviceResources::Get()->GetOutputSize(); + + CLog::Log(LOGDEBUG, __FUNCTION__ ": window resize event {:f} x {:f} (as:{})", size.Width, + size.Height, + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen ? "true" + : "false"); + + auto appView = ApplicationView::GetForCurrentView(); + appView.SetDesiredBoundsMode(ApplicationViewBoundsMode::UseCoreWindow); + + // seems app has lost FS mode it may occurs if an user use core window's button + if (CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen && !appView.IsFullScreenMode()) + CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_fullScreen = false; + + XBMC_Event newEvent = {}; + newEvent.type = XBMC_VIDEORESIZE; + newEvent.resize.w = size.Width; + newEvent.resize.h = size.Height; + if (g_application.GetRenderGUI() && !DX::Windowing()->IsAlteringWindow() && newEvent.resize.w > 0 && newEvent.resize.h > 0) + MessagePush(&newEvent); +} + +void CWinEventsWin10::OnResize(float width, float height) +{ + CLog::Log(LOGDEBUG, __FUNCTION__": window size changed."); + m_logicalWidth = width; + m_logicalHeight = height; + m_bResized = true; + + if (m_sizeChanging) + return; + + HandleWindowSizeChanged(); +} + +// Window event handlers. +void CWinEventsWin10::OnWindowSizeChanged(const CoreWindow&, const WindowSizeChangedEventArgs& args) +{ + OnResize(args.Size().Width, args.Size().Height); +} + +void CWinEventsWin10::OnWindowResizeStarted(const CoreWindow& sender, const winrt::IInspectable&) +{ + CLog::Log(LOGDEBUG, __FUNCTION__": window resize started."); + m_logicalPosX = sender.Bounds().X; + m_logicalPosY = sender.Bounds().Y; + m_sizeChanging = true; +} + +void CWinEventsWin10::OnWindowResizeCompleted(const CoreWindow& sender, const winrt::IInspectable&) +{ + CLog::Log(LOGDEBUG, __FUNCTION__": window resize completed."); + m_sizeChanging = false; + + if (m_logicalPosX != sender.Bounds().X || m_logicalPosY != sender.Bounds().Y) + m_bMoved = true; + + HandleWindowSizeChanged(); +} + +void CWinEventsWin10::HandleWindowSizeChanged() +{ + CLog::Log(LOGDEBUG, __FUNCTION__": window size/move handled."); + if (m_bMoved) + { + // it will get position from CoreWindow + DX::Windowing()->OnMove(0, 0); + } + if (m_bResized) + { + DX::Windowing()->OnResize(m_logicalWidth, m_logicalHeight); + UpdateWindowSize(); + } + m_bResized = false; + m_bMoved = false; +} + +void CWinEventsWin10::OnVisibilityChanged(const CoreWindow& sender, const VisibilityChangedEventArgs& args) +{ + bool active = g_application.GetRenderGUI(); + std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort(); + if (appPort) + appPort->SetRenderGUI(args.Visible()); + + if (g_application.GetRenderGUI() != active) + DX::Windowing()->NotifyAppActiveChange(g_application.GetRenderGUI()); + CLog::Log(LOGDEBUG, __FUNCTION__ ": window is {}", + g_application.GetRenderGUI() ? "shown" : "hidden"); +} + +void CWinEventsWin10::OnWindowActivationChanged(const CoreWindow& sender, const WindowActivatedEventArgs& args) +{ + bool active = g_application.GetRenderGUI(); + if (args.WindowActivationState() == CoreWindowActivationState::Deactivated) + { + std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort(); + if (appPort) + appPort->SetRenderGUI(DX::Windowing()->WindowedMode()); + } + else if (args.WindowActivationState() == CoreWindowActivationState::PointerActivated + || args.WindowActivationState() == CoreWindowActivationState::CodeActivated) + { + std::shared_ptr<CAppInboundProtocol> appPort = CServiceBroker::GetAppPort(); + if (appPort) + appPort->SetRenderGUI(true); + } + if (g_application.GetRenderGUI() != active) + DX::Windowing()->NotifyAppActiveChange(g_application.GetRenderGUI()); + CLog::Log(LOGDEBUG, __FUNCTION__ ": window is {}", + g_application.GetRenderGUI() ? "active" : "inactive"); +} + +void CWinEventsWin10::OnWindowClosed(const CoreWindow& sender, const CoreWindowEventArgs& args) +{ + // send quit command to the application if it's still running + if (!g_application.m_bStop) + { + XBMC_Event newEvent = {}; + newEvent.type = XBMC_QUIT; + MessagePush(&newEvent); + } +} + +void CWinEventsWin10::OnPointerPressed(const CoreWindow&, const PointerEventArgs& args) +{ + XBMC_Event newEvent = {}; + + PointerPoint point = args.CurrentPoint(); + auto position = GetScreenPoint(point.Position()); + + if (point.PointerDevice().PointerDeviceType() == PointerDeviceType::Touch) + { + CGenericTouchInputHandler::GetInstance().HandleTouchInput(TouchInputDown, position.X, position.Y, point.Timestamp(), 0, 10); + return; + } + else + { + newEvent.type = XBMC_MOUSEBUTTONDOWN; + newEvent.button.x = position.X; + newEvent.button.y = position.Y; + if (point.PointerDevice().PointerDeviceType() == PointerDeviceType::Mouse) + { + if (point.Properties().IsLeftButtonPressed()) + newEvent.button.button = XBMC_BUTTON_LEFT; + else if (point.Properties().IsMiddleButtonPressed()) + newEvent.button.button = XBMC_BUTTON_MIDDLE; + else if (point.Properties().IsRightButtonPressed()) + newEvent.button.button = XBMC_BUTTON_RIGHT; + } + else if (point.PointerDevice().PointerDeviceType() == PointerDeviceType::Pen) + { + // pen + // TODO + } + } + MessagePush(&newEvent); +} + +void CWinEventsWin10::OnPointerMoved(const CoreWindow&, const PointerEventArgs& args) +{ + PointerPoint point = args.CurrentPoint(); + auto position = GetScreenPoint(point.Position()); + + if (point.PointerDevice().PointerDeviceType() == PointerDeviceType::Touch) + { + if (point.IsInContact()) + { + CGenericTouchInputHandler::GetInstance().UpdateTouchPointer(0, position.X, position.Y, point.Timestamp(), 10.f); + CGenericTouchInputHandler::GetInstance().HandleTouchInput(TouchInputMove, position.X, position.Y, point.Timestamp(), 0, 10.f); + } + return; + } + + XBMC_Event newEvent = {}; + newEvent.type = XBMC_MOUSEMOTION; + newEvent.motion.x = position.X; + newEvent.motion.y = position.Y; + MessagePush(&newEvent); +} + +void CWinEventsWin10::OnPointerReleased(const CoreWindow&, const PointerEventArgs& args) +{ + PointerPoint point = args.CurrentPoint(); + auto position = GetScreenPoint(point.Position()); + + if (point.PointerDevice().PointerDeviceType() == PointerDeviceType::Touch) + { + CGenericTouchInputHandler::GetInstance().HandleTouchInput(TouchInputUp, position.X, position.Y, point.Timestamp(), 0, 10); + return; + } + + XBMC_Event newEvent = {}; + newEvent.type = XBMC_MOUSEBUTTONUP; + newEvent.button.x = position.X; + newEvent.button.y = position.Y; + + if (point.Properties().PointerUpdateKind() == PointerUpdateKind::LeftButtonReleased) + newEvent.button.button = XBMC_BUTTON_LEFT; + else if (point.Properties().PointerUpdateKind() == PointerUpdateKind::MiddleButtonReleased) + newEvent.button.button = XBMC_BUTTON_MIDDLE; + else if (point.Properties().PointerUpdateKind() == PointerUpdateKind::RightButtonReleased) + newEvent.button.button = XBMC_BUTTON_RIGHT; + + MessagePush(&newEvent); +} + +void CWinEventsWin10::OnPointerExited(const CoreWindow&, const PointerEventArgs& args) +{ + const PointerPoint& point = args.CurrentPoint(); + auto position = GetScreenPoint(point.Position()); + + if (point.PointerDevice().PointerDeviceType() == PointerDeviceType::Touch) + { + CGenericTouchInputHandler::GetInstance().HandleTouchInput(TouchInputAbort, position.X, position.Y, point.Timestamp(), 0, 10); + } +} + +void CWinEventsWin10::OnPointerWheelChanged(const CoreWindow&, const PointerEventArgs& args) +{ + XBMC_Event newEvent = {}; + newEvent.type = XBMC_MOUSEBUTTONDOWN; + newEvent.button.x = args.CurrentPoint().Position().X; + newEvent.button.y = args.CurrentPoint().Position().Y; + newEvent.button.button = args.CurrentPoint().Properties().MouseWheelDelta() > 0 ? XBMC_BUTTON_WHEELUP : XBMC_BUTTON_WHEELDOWN; + MessagePush(&newEvent); + newEvent.type = XBMC_MOUSEBUTTONUP; + MessagePush(&newEvent); +} + +void CWinEventsWin10::Kodi_KeyEvent(unsigned int vkey, unsigned scancode, unsigned keycode, bool isDown) +{ + using State = CoreVirtualKeyStates; + + XBMC_keysym keysym = {}; + keysym.scancode = scancode; + keysym.sym = KODI::WINDOWING::WINDOWS::VK_keymap[vkey]; + keysym.unicode = keycode; + + auto window = CoreWindow::GetForCurrentThread(); + + uint16_t mod = (uint16_t)XBMCKMOD_NONE; + // If left control and right alt are down this usually means that AltGr is down + if ((window.GetKeyState(VirtualKey::LeftControl) & State::Down) == State::Down + && (window.GetKeyState(VirtualKey::RightMenu) & State::Down) == State::Down) + { + mod |= XBMCKMOD_MODE; + mod |= XBMCKMOD_MODE; + } + else + { + if ((window.GetKeyState(VirtualKey::LeftControl) & State::Down) == State::Down) + mod |= XBMCKMOD_LCTRL; + if ((window.GetKeyState(VirtualKey::RightMenu) & State::Down) == State::Down) + mod |= XBMCKMOD_RALT; + } + + // Check the remaining modifiers + if ((window.GetKeyState(VirtualKey::LeftShift) & State::Down) == State::Down) + mod |= XBMCKMOD_LSHIFT; + if ((window.GetKeyState(VirtualKey::RightShift) & State::Down) == State::Down) + mod |= XBMCKMOD_RSHIFT; + if ((window.GetKeyState(VirtualKey::RightControl) & State::Down) == State::Down) + mod |= XBMCKMOD_RCTRL; + if ((window.GetKeyState(VirtualKey::LeftMenu) & State::Down) == State::Down) + mod |= XBMCKMOD_LALT; + if ((window.GetKeyState(VirtualKey::LeftWindows) & State::Down) == State::Down) + mod |= XBMCKMOD_LSUPER; + if ((window.GetKeyState(VirtualKey::RightWindows) & State::Down) == State::Down) + mod |= XBMCKMOD_LSUPER; + + keysym.mod = static_cast<XBMCMod>(mod); + + XBMC_Event newEvent = {}; + newEvent.type = isDown ? XBMC_KEYDOWN : XBMC_KEYUP; + newEvent.key.keysym = keysym; + MessagePush(&newEvent); +} + +void CWinEventsWin10::OnAcceleratorKeyActivated(const CoreDispatcher&, const AcceleratorKeyEventArgs& args) +{ + static auto lockedState = CoreVirtualKeyStates::Locked; + static VirtualKey keyStore = VirtualKey::None; + + // skip if device is remote control + if (m_remote && m_remote->IsRemoteDevice(args.DeviceId().c_str())) + return; + + bool isDown = false; + unsigned keyCode = 0; + unsigned vk = static_cast<unsigned>(args.VirtualKey()); + + auto window = CoreWindow::GetForCurrentThread(); + bool numLockLocked = ((window.GetKeyState(VirtualKey::NumberKeyLock) & lockedState) == lockedState); + + switch (args.EventType()) + { + case CoreAcceleratorKeyEventType::KeyDown: + case CoreAcceleratorKeyEventType::SystemKeyDown: + { + if ( (vk == 0x08) // VK_BACK + || (vk == 0x09) // VK_TAB + || (vk == 0x0C) // VK_CLEAR + || (vk == 0x0D) // VK_RETURN + || (vk == 0x1B) // VK_ESCAPE + || (vk == 0x20) // VK_SPACE + || (vk >= 0x30 && vk <= 0x39) // numeric keys + || (vk >= 0x41 && vk <= 0x5A) // alphabetic keys + || (vk >= 0x60 && vk <= 0x69 && numLockLocked) // keypad numeric (if numlock is on) + || (vk >= 0x6A && vk <= 0x6F) // keypad keys except numeric + || (vk >= 0x92 && vk <= 0x96) // OEM specific + || (vk >= 0xBA && vk <= 0xC0) // OEM specific + || (vk >= 0xDB && vk <= 0xDF) // OEM specific + || (vk >= 0xE1 && vk <= 0xF5 && vk != 0xE5 && vk != 0xE7 && vk != 0xE8) // OEM specific + ) + { + // store this for character events, because VirtualKey is key code on character event. + keyStore = args.VirtualKey(); + return; + } + isDown = true; + break; + } + case CoreAcceleratorKeyEventType::KeyUp: + case CoreAcceleratorKeyEventType::SystemKeyUp: + break; + case CoreAcceleratorKeyEventType::Character: + case CoreAcceleratorKeyEventType::SystemCharacter: + case CoreAcceleratorKeyEventType::UnicodeCharacter: + case CoreAcceleratorKeyEventType::DeadCharacter: + case CoreAcceleratorKeyEventType::SystemDeadCharacter: + { + // VirtualKey is KeyCode + keyCode = static_cast<unsigned>(args.VirtualKey()); + // rewrite vk with stored value + vk = static_cast<unsigned>(keyStore); + // reset stored value + keyStore = VirtualKey::None; + isDown = true; + } + default: + break; + } + + Kodi_KeyEvent(vk, args.KeyStatus().ScanCode, keyCode, isDown); + args.Handled(true); +} + +// DisplayInformation event handlers. +void CWinEventsWin10::OnDpiChanged(const DisplayInformation& sender, const winrt::IInspectable&) +{ + // Note: The value for LogicalDpi retrieved here may not match the effective DPI of the app + // if it is being scaled for high resolution devices. Once the DPI is set on DeviceResources, + // you should always retrieve it using the GetDpi method. + // See DeviceResources.cpp for more details. + //critical_section::scoped_lock lock(m_deviceResources->GetCriticalSection()); + RECT resizeRect = { 0,0,0,0 }; + DX::Windowing()->DPIChanged(sender.LogicalDpi(), resizeRect); + CGenericTouchInputHandler::GetInstance().SetScreenDPI(DX::DisplayMetrics::Dpi100); +} + +void CWinEventsWin10::OnOrientationChanged(const DisplayInformation&, const winrt::IInspectable&) +{ + //critical_section::scoped_lock lock(m_deviceResources->GetCriticalSection()); + //m_deviceResources->SetCurrentOrientation(sender->CurrentOrientation); + + //auto size = DX::DeviceResources::Get()->GetOutputSize(); + //UpdateWindowSize(size.Width, size.Height); +} + +void CWinEventsWin10::OnDisplayContentsInvalidated(const DisplayInformation&, const winrt::IInspectable&) +{ + CLog::Log(LOGDEBUG, __FUNCTION__": onevent."); + DX::DeviceResources::Get()->ValidateDevice(); +} + +void CWinEventsWin10::OnBackRequested(const winrt::IInspectable&, const BackRequestedEventArgs& args) +{ + // handle this only on windows mobile + if (CSysInfo::GetWindowsDeviceFamily() == CSysInfo::WindowsDeviceFamily::Mobile) + { + CServiceBroker::GetAppMessenger()->PostMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, + static_cast<void*>(new CAction(ACTION_NAV_BACK))); + } + args.Handled(true); +} + +void CWinEventsWin10::OnSystemMediaButtonPressed(const SystemMediaTransportControls&, const SystemMediaTransportControlsButtonPressedEventArgs& args) +{ + int action = ACTION_NONE; + switch (args.Button()) + { + case SystemMediaTransportControlsButton::ChannelDown: + action = ACTION_CHANNEL_DOWN; + break; + case SystemMediaTransportControlsButton::ChannelUp: + action = ACTION_CHANNEL_UP; + break; + case SystemMediaTransportControlsButton::FastForward: + action = ACTION_PLAYER_FORWARD; + break; + case SystemMediaTransportControlsButton::Rewind: + action = ACTION_PLAYER_REWIND; + break; + case SystemMediaTransportControlsButton::Next: + action = ACTION_NEXT_ITEM; + break; + case SystemMediaTransportControlsButton::Previous: + action = ACTION_PREV_ITEM; + break; + case SystemMediaTransportControlsButton::Pause: + case SystemMediaTransportControlsButton::Play: + action = ACTION_PLAYER_PLAYPAUSE; + break; + case SystemMediaTransportControlsButton::Stop: + action = ACTION_STOP; + break; + case SystemMediaTransportControlsButton::Record: + action = ACTION_RECORD; + break; + default: + break; + } + if (action != ACTION_NONE) + { + CServiceBroker::GetAppMessenger()->PostMsg(TMSG_GUI_ACTION, WINDOW_INVALID, -1, + static_cast<void*>(new CAction(action))); + } +} + +void CWinEventsWin10::Announce(ANNOUNCEMENT::AnnouncementFlag flag, + const std::string& sender, + const std::string& message, + const CVariant& data) +{ + if (flag & ANNOUNCEMENT::Player) + { + double speed = 1.0; + if (data.isMember("player") && data["player"].isMember("speed")) + speed = data["player"]["speed"].asDouble(1.0); + + bool changed = false; + MediaPlaybackStatus status = MediaPlaybackStatus::Changing; + + if (message == "OnPlay" || message == "OnResume") + { + changed = true; + status = MediaPlaybackStatus::Playing; + } + else if (message == "OnStop") + { + changed = true; + status = MediaPlaybackStatus::Stopped; + } + else if (message == "OnPause") + { + changed = true; + status = MediaPlaybackStatus::Paused; + } + else if (message == "OnSpeedChanged") + { + changed = true; + status = speed != 0.0 ? MediaPlaybackStatus::Playing : MediaPlaybackStatus::Paused; + } + + if (changed) + { + try + { + auto dispatcher = CoreApplication::MainView().Dispatcher(); + if (dispatcher) + { + dispatcher.RunAsync(CoreDispatcherPriority::Normal, DispatchedHandler([status, speed] + { + auto smtc = SystemMediaTransportControls::GetForCurrentView(); + if (!smtc) + return; + + smtc.PlaybackStatus(status); + smtc.PlaybackRate(speed); + })); + } + } + catch (const winrt::hresult_error&) + { + } + } + } +} diff --git a/xbmc/windowing/win10/WinEventsWin10.h b/xbmc/windowing/win10/WinEventsWin10.h new file mode 100644 index 0000000..c4bd345 --- /dev/null +++ b/xbmc/windowing/win10/WinEventsWin10.h @@ -0,0 +1,83 @@ +/* + * 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 "interfaces/IAnnouncer.h" +#include "windowing/WinEvents.h" + +#include <concurrent_queue.h> +#include <winrt/Windows.Media.h> + +class CRemoteControlXbox; + +class CWinEventsWin10 : public IWinEvents + , public ANNOUNCEMENT::IAnnouncer +{ +public: + CWinEventsWin10(); + virtual ~CWinEventsWin10(); + + void MessagePush(XBMC_Event *newEvent); + bool MessagePump() override; + virtual size_t GetQueueSize(); + + // initialization + void InitEventHandlers(const winrt::Windows::UI::Core::CoreWindow&); + static void InitOSKeymap(void); + + // Window event handlers. + void OnWindowSizeChanged(const winrt::Windows::UI::Core::CoreWindow&, const winrt::Windows::UI::Core::WindowSizeChangedEventArgs&); + void OnWindowResizeStarted(const winrt::Windows::UI::Core::CoreWindow&, const winrt::Windows::Foundation::IInspectable&); + void OnWindowResizeCompleted(const winrt::Windows::UI::Core::CoreWindow&, const winrt::Windows::Foundation::IInspectable&); + void OnWindowClosed(const winrt::Windows::UI::Core::CoreWindow&, const winrt::Windows::UI::Core::CoreWindowEventArgs&); + static void OnWindowActivationChanged(const winrt::Windows::UI::Core::CoreWindow&, const winrt::Windows::UI::Core::WindowActivatedEventArgs&); + static void OnVisibilityChanged(const winrt::Windows::UI::Core::CoreWindow&, const winrt::Windows::UI::Core::VisibilityChangedEventArgs&); + // touch mouse and pen + void OnPointerPressed(const winrt::Windows::UI::Core::CoreWindow&, const winrt::Windows::UI::Core::PointerEventArgs&); + void OnPointerMoved(const winrt::Windows::UI::Core::CoreWindow&, const winrt::Windows::UI::Core::PointerEventArgs&); + void OnPointerReleased(const winrt::Windows::UI::Core::CoreWindow&, const winrt::Windows::UI::Core::PointerEventArgs&); + void OnPointerExited(const winrt::Windows::UI::Core::CoreWindow&, const winrt::Windows::UI::Core::PointerEventArgs&); + void OnPointerWheelChanged(const winrt::Windows::UI::Core::CoreWindow&, const winrt::Windows::UI::Core::PointerEventArgs&); + // keyboard + void OnAcceleratorKeyActivated(const winrt::Windows::UI::Core::CoreDispatcher&, const winrt::Windows::UI::Core::AcceleratorKeyEventArgs&); + + // DisplayInformation event handlers. + static void OnDpiChanged(const winrt::Windows::Graphics::Display::DisplayInformation&, const winrt::Windows::Foundation::IInspectable&); + static void OnOrientationChanged(const winrt::Windows::Graphics::Display::DisplayInformation&, const winrt::Windows::Foundation::IInspectable&); + static void OnDisplayContentsInvalidated(const winrt::Windows::Graphics::Display::DisplayInformation&, const winrt::Windows::Foundation::IInspectable&); + // system + static void OnBackRequested(const winrt::Windows::Foundation::IInspectable&, const winrt::Windows::UI::Core::BackRequestedEventArgs&); + // system media handlers + static void OnSystemMediaButtonPressed(const winrt::Windows::Media::SystemMediaTransportControls& + , const winrt::Windows::Media::SystemMediaTransportControlsButtonPressedEventArgs&); + // IAnnouncer overrides + void Announce(ANNOUNCEMENT::AnnouncementFlag flag, + const std::string& sender, + const std::string& message, + const CVariant& data) override; + +private: + friend class CWinSystemWin10; + + void OnResize(float width, float height); + void UpdateWindowSize(); + void Kodi_KeyEvent(unsigned int vkey, unsigned scancode, unsigned keycode, bool isDown); + void HandleWindowSizeChanged(); + + Concurrency::concurrent_queue<XBMC_Event> m_events; + winrt::Windows::Media::SystemMediaTransportControls m_smtc{ nullptr }; + bool m_bResized{ false }; + bool m_bMoved{ false }; + bool m_sizeChanging{ false }; + float m_logicalWidth{ 0 }; + float m_logicalHeight{ 0 }; + float m_logicalPosX{ 0 }; + float m_logicalPosY{ 0 }; + std::unique_ptr<CRemoteControlXbox> m_remote; +}; diff --git a/xbmc/windowing/win10/WinSystemWin10.cpp b/xbmc/windowing/win10/WinSystemWin10.cpp new file mode 100644 index 0000000..6de3f78 --- /dev/null +++ b/xbmc/windowing/win10/WinSystemWin10.cpp @@ -0,0 +1,660 @@ +/* + * 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 "WinSystemWin10.h" + +#include "ServiceBroker.h" +#include "WinEventsWin10.h" +#include "application/Application.h" +#include "cores/AudioEngine/AESinkFactory.h" +#include "cores/AudioEngine/Sinks/AESinkWASAPI.h" +#include "cores/AudioEngine/Sinks/AESinkXAudio.h" +#include "rendering/dx/DirectXHelper.h" +#include "rendering/dx/RenderContext.h" +#include "rendering/dx/ScreenshotSurfaceWindows.h" +#include "settings/DisplaySettings.h" +#include "settings/Settings.h" +#include "settings/SettingsComponent.h" +#include "utils/SystemInfo.h" +#include "utils/log.h" +#include "windowing/GraphicContext.h" +#include "windowing/windows/VideoSyncD3D.h" + +#include "platform/win10/AsyncHelpers.h" +#include "platform/win32/CharsetConverter.h" + +#include <mutex> + +#pragma pack(push,8) + +#include <tpcshrd.h> +#include <ppltasks.h> +#include <winrt/Windows.ApplicationModel.DataTransfer.h> +#include <winrt/Windows.Foundation.Metadata.h> +#include <winrt/Windows.Graphics.Display.h> +#include <winrt/Windows.Graphics.Display.Core.h> + +using namespace winrt::Windows::ApplicationModel::DataTransfer; +using namespace winrt::Windows::Foundation::Metadata; +using namespace winrt::Windows::Graphics::Display; +using namespace winrt::Windows::Graphics::Display::Core; +using namespace winrt::Windows::UI::Core; +using namespace winrt::Windows::UI::ViewManagement; + +using namespace std::chrono_literals; + +CWinSystemWin10::CWinSystemWin10() + : CWinSystemBase() + , 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_inFocus(false) + , m_bMinimized(false) +{ + m_winEvents.reset(new CWinEventsWin10()); + + AE::CAESinkFactory::ClearSinks(); + CAESinkXAudio::Register(); + CAESinkWASAPI::Register(); + CScreenshotSurfaceWindows::Register(); +} + +CWinSystemWin10::~CWinSystemWin10() +{ +}; + +bool CWinSystemWin10::InitWindowSystem() +{ + m_coreWindow = CoreWindow::GetForCurrentThread(); + dynamic_cast<CWinEventsWin10&>(*m_winEvents).InitEventHandlers(m_coreWindow); + + if (!CWinSystemBase::InitWindowSystem()) + return false; + + if (m_displays.empty()) + { + CLog::Log(LOGERROR, "{} - no suitable monitor found, aborting...", __FUNCTION__); + return false; + } + + return true; +} + +bool CWinSystemWin10::DestroyWindowSystem() +{ + m_bWindowCreated = false; + RestoreDesktopResolution(); + return true; +} + +bool CWinSystemWin10::CanDoWindowed() +{ + return CSysInfo::GetWindowsDeviceFamily() == CSysInfo::Desktop; +} + +bool CWinSystemWin10::CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res) +{ + 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_inFocus = true; + m_bWindowCreated = true; + m_state = state; + + m_coreWindow.Activate(); + + AdjustWindow(); + // dispatch all events currently pending in the queue to show window's content + // and hide UWP splash, without this the Kodi's splash will not be shown + m_coreWindow.Dispatcher().ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending); + + return true; +} + +bool CWinSystemWin10::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 CWinSystemWin10::FinishWindowResize(int newWidth, int newHeight) +{ + m_nWidth = newWidth; + m_nHeight = newHeight; + + float dpi = DX::DeviceResources::Get()->GetDpi(); + int dipsWidth = round(DX::ConvertPixelsToDips(m_nWidth, dpi)); + int dipsHeight = round(DX::ConvertPixelsToDips(m_nHeight, dpi)); + + ApplicationView::PreferredLaunchViewSize(winrt::Windows::Foundation::Size(dipsWidth, dipsHeight)); + ApplicationView::PreferredLaunchWindowingMode(ApplicationViewWindowingMode::PreferredLaunchViewSize); +} + +void CWinSystemWin10::AdjustWindow() +{ + CLog::Log(LOGDEBUG, __FUNCTION__": adjusting window if required."); + + auto appView = ApplicationView::GetForCurrentView(); + bool isInFullscreen = appView.IsFullScreenMode(); + + if (m_state == WINDOW_STATE_FULLSCREEN_WINDOW || m_state == WINDOW_STATE_FULLSCREEN) + { + if (!isInFullscreen) + { + if (appView.TryEnterFullScreenMode()) + ApplicationView::PreferredLaunchWindowingMode(ApplicationViewWindowingMode::FullScreen); + } + } + else // m_state == WINDOW_STATE_WINDOWED + { + if (isInFullscreen) + { + appView.ExitFullScreenMode(); + } + + int viewWidth = appView.VisibleBounds().Width; + int viewHeight = appView.VisibleBounds().Height; + + float dpi = DX::DeviceResources::Get()->GetDpi(); + int dipsWidth = round(DX::ConvertPixelsToDips(m_nWidth, dpi)); + int dipsHeight = round(DX::ConvertPixelsToDips(m_nHeight, dpi)); + + if (viewHeight != dipsHeight || viewWidth != dipsWidth) + { + if (!appView.TryResizeView(winrt::Windows::Foundation::Size(dipsWidth, dipsHeight))) + { + CLog::LogF(LOGDEBUG, __FUNCTION__, "resizing ApplicationView failed."); + } + } + + ApplicationView::PreferredLaunchViewSize(winrt::Windows::Foundation::Size(dipsWidth, dipsHeight)); + ApplicationView::PreferredLaunchWindowingMode(ApplicationViewWindowingMode::PreferredLaunchViewSize); + } +} + +bool CWinSystemWin10::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) +{ + CWinSystemWin10::UpdateStates(fullScreen); + WINDOW_STATE state = GetState(fullScreen); + + CLog::Log(LOGDEBUG, "{} ({}) with size {}x{}, refresh {:f}{}", __FUNCTION__, + window_state_names[state], res.iWidth, res.iHeight, res.fRefreshRate, + (res.dwFlags & D3DPRESENTFLAG_INTERLACED) ? "i" : ""); + + bool forceChange = false; // resolution/display is changed but window state isn't 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 || + stereoChange || m_bFirstResChange) + { + forceChange = true; + } + + if (state == m_state && !forceChange) + 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) + { + if (m_coreWindow) + { + m_nLeft = m_coreWindow.Bounds().X; + m_nTop = m_coreWindow.Bounds().Y; + m_ValidWindowedPosition = true; + } + } + + m_IsAlteringWindow = true; + ReleaseBackBuffer(); + + m_bFirstResChange = false; + m_bFullScreen = fullScreen; + m_nWidth = res.iWidth; + m_nHeight = res.iHeight; + m_bBlankOtherDisplay = blankOtherDisplays; + m_fRefreshRate = res.fRefreshRate; + + if (state == WINDOW_STATE_FULLSCREEN) + { + // isn't allowed in UWP + } + else if (m_state == WINDOW_STATE_FULLSCREEN || m_state == WINDOW_STATE_FULLSCREEN_WINDOW) // we're in fullscreen state now + { + 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(); + } + else if (state == WINDOW_STATE_FULLSCREEN_WINDOW) // enter fullscreen window instead + { + ChangeResolution(res, stereoChange); + } + + m_state = state; + AdjustWindow(); + } + else // we're in windowed state now + { + if (state == WINDOW_STATE_FULLSCREEN_WINDOW) + { + ChangeResolution(res, stereoChange); + + m_state = state; + AdjustWindow(); + } + } + + CreateBackBuffer(); + m_IsAlteringWindow = false; + return true; +} + +bool CWinSystemWin10::DPIChanged(WORD dpi, RECT windowRect) const +{ + (void)dpi; + return true; +} + +void CWinSystemWin10::RestoreDesktopResolution() +{ + CLog::Log(LOGDEBUG, __FUNCTION__": restoring default desktop resolution"); + ChangeResolution(CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP)); +} + +const MONITOR_DETAILS* CWinSystemWin10::GetDefaultMonitor() const +{ + if (m_displays.empty()) + return nullptr; + + return &m_displays.front(); +} + +bool CWinSystemWin10::ChangeResolution(const RESOLUTION_INFO& res, bool forceChange /*= false*/) +{ + const MONITOR_DETAILS* details = GetDefaultMonitor(); + + if (!details) + return false; + + if (ApiInformation::IsTypePresent(L"Windows.Graphics.Display.Core.HdmiDisplayInformation")) + { + bool changed = false; + auto hdmiInfo = HdmiDisplayInformation::GetForCurrentView(); + if (hdmiInfo != nullptr) + { + // default mode not in list of supported display modes + if (res.iScreenWidth == details->ScreenWidth && res.iScreenHeight == details->ScreenHeight + && fabs(res.fRefreshRate - details->RefreshRate) <= 0.00001) + { + Wait(hdmiInfo.SetDefaultDisplayModeAsync()); + changed = true; + } + else + { + bool needStereo = CServiceBroker::GetWinSystem()->GetGfxContext().GetStereoMode() == RENDER_STEREO_MODE_HARDWAREBASED; + auto hdmiModes = hdmiInfo.GetSupportedDisplayModes(); + + HdmiDisplayMode selected = nullptr; + for (const auto& mode : hdmiModes) + { + if (res.iScreenWidth == mode.ResolutionWidthInRawPixels() && res.iScreenHeight == mode.ResolutionHeightInRawPixels() + && fabs(res.fRefreshRate - mode.RefreshRate()) <= 0.00001) + { + selected = mode; + if (needStereo == mode.StereoEnabled()) + break; + } + } + + if (selected != nullptr) + { + changed = Wait(hdmiInfo.RequestSetCurrentDisplayModeAsync(selected)); + } + } + } + + // changing display mode doesn't fire CoreWindow::SizeChanged event + if (changed && m_bWindowCreated) + { + // dispatch all events currently pending in the queue to change window's content + m_coreWindow.Dispatcher().ProcessEvents(CoreProcessEventsOption::ProcessOneAndAllPending); + + float dpi = DisplayInformation::GetForCurrentView().LogicalDpi(); + float dipsW = DX::ConvertPixelsToDips(m_nWidth, dpi); + float dipsH = DX::ConvertPixelsToDips(m_nHeight, dpi); + + dynamic_cast<CWinEventsWin10&>(*m_winEvents).OnResize(dipsW, dipsH); + } + return changed; + } + + CLog::LogF(LOGDEBUG, "Not supported."); + return false; +} + +void CWinSystemWin10::UpdateResolutions() +{ + m_displays.clear(); + + CWinSystemBase::UpdateResolutions(); + GetConnectedDisplays(m_displays); + + const MONITOR_DETAILS* details = GetDefaultMonitor(); + if (!details) + return; + + float refreshRate; + int w = details->ScreenWidth; + int h = details->ScreenHeight; + uint32_t dwFlags = details->Interlaced ? D3DPRESENTFLAG_INTERLACED : 0;; + + if (details->RefreshRate == 59 || details->RefreshRate == 29 || details->RefreshRate == 23) + refreshRate = static_cast<float>(details->RefreshRate + 1) / 1.001f; + else + refreshRate = static_cast<float>(details->RefreshRate); + + RESOLUTION_INFO& primary_info = CDisplaySettings::GetInstance().GetResolutionInfo(RES_DESKTOP); + UpdateDesktopResolution(primary_info, "Default", w, h, refreshRate, dwFlags); + CLog::Log(LOGINFO, "Primary mode: {}", primary_info.strMode); + + // erase previous stored modes + CDisplaySettings::GetInstance().ClearCustomResolutions(); + + if (ApiInformation::IsTypePresent(L"Windows.Graphics.Display.Core.HdmiDisplayInformation")) + { + auto hdmiInfo = HdmiDisplayInformation::GetForCurrentView(); + if (hdmiInfo != nullptr) + { + auto hdmiModes = hdmiInfo.GetSupportedDisplayModes(); + for (const auto& mode : hdmiModes) + { + RESOLUTION_INFO res; + res.iWidth = mode.ResolutionWidthInRawPixels(); + res.iHeight = mode.ResolutionHeightInRawPixels(); + res.bFullScreen = true; + res.dwFlags = 0; + res.fRefreshRate = mode.RefreshRate(); + res.fPixelRatio = 1.0f; + res.iScreenWidth = res.iWidth; + res.iScreenHeight = res.iHeight; + res.iSubtitles = res.iHeight; + res.strMode = StringUtils::Format("Default: {}x{} @ {:.2f}Hz", res.iWidth, res.iHeight, + res.fRefreshRate); + GetGfxContext().ResetOverscan(res); + + if (AddResolution(res)) + CLog::Log(LOGINFO, "Additional mode: {} {}", res.strMode, + mode.Is2086MetadataSupported() ? "(HDR)" : ""); + } + } + } + + CDisplaySettings::GetInstance().ApplyCalibrations(); +} + +bool CWinSystemWin10::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 CWinSystemWin10::GetConnectedDisplays(std::vector<MONITOR_DETAILS>& outputs) +{ + auto dispatcher = m_coreWindow.Dispatcher(); + DispatchedHandler handler([&]() + { + MONITOR_DETAILS md = {}; + + auto displayInfo = DisplayInformation::GetForCurrentView(); + bool flipResolution = false; + switch (displayInfo.NativeOrientation()) + { + case DisplayOrientations::Landscape: + switch (displayInfo.CurrentOrientation()) + { + case DisplayOrientations::Portrait: + case DisplayOrientations::PortraitFlipped: + flipResolution = true; + break; + } + break; + case DisplayOrientations::Portrait: + switch (displayInfo.CurrentOrientation()) + { + case DisplayOrientations::Landscape: + case DisplayOrientations::LandscapeFlipped: + flipResolution = true; + break; + } + break; + } + md.ScreenWidth = flipResolution ? displayInfo.ScreenHeightInRawPixels() : displayInfo.ScreenWidthInRawPixels(); + md.ScreenHeight = flipResolution ? displayInfo.ScreenWidthInRawPixels() : displayInfo.ScreenHeightInRawPixels(); + + if (ApiInformation::IsTypePresent(L"Windows.Graphics.Display.Core.HdmiDisplayInformation")) + { + auto hdmiInfo = HdmiDisplayInformation::GetForCurrentView(); + if (hdmiInfo != nullptr) + { + auto currentMode = hdmiInfo.GetCurrentDisplayMode(); + // On Xbox, 4K resolutions only are reported by HdmiDisplayInformation API + // so ScreenHeight & ScreenWidth are updated with info provided here + md.ScreenHeight = currentMode.ResolutionHeightInRawPixels(); + md.ScreenWidth = currentMode.ResolutionWidthInRawPixels(); + md.RefreshRate = currentMode.RefreshRate(); + md.Bpp = currentMode.BitsPerPixel(); + } + else + { + md.RefreshRate = 60.0; + md.Bpp = 24; + } + } + else + { + // note that refresh rate information is not available on Win10 UWP + md.RefreshRate = 60.0; + md.Bpp = 24; + } + md.Interlaced = false; + + outputs.push_back(md); + }); + + if (dispatcher.HasThreadAccess()) + handler(); + else + Wait(dispatcher.RunAsync(CoreDispatcherPriority::High, handler)); +} + +void CWinSystemWin10::ShowOSMouse(bool show) +{ + if (!m_coreWindow) + return; + + DispatchedHandler handler([this, show]() + { + CoreCursor cursor = nullptr; + if (show) + cursor = CoreCursor(CoreCursorType::Arrow, 1); + m_coreWindow.PointerCursor(cursor); + }); + + if (m_coreWindow.Dispatcher().HasThreadAccess()) + handler(); + else + m_coreWindow.Dispatcher().RunAsync(CoreDispatcherPriority::Normal, handler); +} + +bool CWinSystemWin10::Minimize() +{ + CLog::Log(LOGDEBUG, "{} is not implemented", __FUNCTION__); + return true; +} +bool CWinSystemWin10::Restore() +{ + CLog::Log(LOGDEBUG, "{} is not implemented", __FUNCTION__); + return true; +} +bool CWinSystemWin10::Hide() +{ + CLog::Log(LOGDEBUG, "{} is not implemented", __FUNCTION__); + return true; +} +bool CWinSystemWin10::Show(bool raise) +{ + CLog::Log(LOGDEBUG, "{} is not implemented", __FUNCTION__); + return true; +} + +void CWinSystemWin10::Register(IDispResource *resource) +{ + std::unique_lock<CCriticalSection> lock(m_resourceSection); + m_resources.push_back(resource); +} + +void CWinSystemWin10::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 CWinSystemWin10::OnDisplayLost() +{ + CLog::Log(LOGDEBUG, "{} - notify display lost event", __FUNCTION__); + + { + std::unique_lock<CCriticalSection> lock(m_resourceSection); + for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); ++i) + (*i)->OnLostDisplay(); + } +} + +void CWinSystemWin10::OnDisplayReset() +{ + if (!m_delayDispReset) + { + CLog::Log(LOGDEBUG, "{} - notify display reset event", __FUNCTION__); + std::unique_lock<CCriticalSection> lock(m_resourceSection); + for (std::vector<IDispResource *>::iterator i = m_resources.begin(); i != m_resources.end(); ++i) + (*i)->OnResetDisplay(); + } +} + +void CWinSystemWin10::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 CWinSystemWin10::ResolutionChanged() +{ + OnDisplayLost(); + OnDisplayBack(); +} + +std::unique_ptr<CVideoSync> CWinSystemWin10::GetVideoSync(void *clock) +{ + std::unique_ptr<CVideoSync> pVSync(new CVideoSyncD3D(clock)); + return pVSync; +} + +std::string CWinSystemWin10::GetClipboardText() +{ + std::wstring unicode_text; + + auto contentView = Clipboard::GetContent(); + if (contentView.Contains(StandardDataFormats::Text())) + { + auto text = Wait(contentView.GetTextAsync()); + unicode_text.append(text.c_str()); + } + + return KODI::PLATFORM::WINDOWS::FromW(unicode_text); +} + +bool CWinSystemWin10::UseLimitedColor() +{ + return CServiceBroker::GetSettingsComponent()->GetSettings()->GetBool(CSettings::SETTING_VIDEOSCREEN_LIMITEDRANGE); +} + +void CWinSystemWin10::NotifyAppFocusChange(bool bGaining) +{ + m_inFocus = bGaining; +} + +void CWinSystemWin10::UpdateStates(bool fullScreen) +{ + m_fullscreenState = WINDOW_FULLSCREEN_STATE_FULLSCREEN_WINDOW; // currently only this allowed + m_windowState = WINDOW_WINDOW_STATE_WINDOWED; // currently only this allowed +} + +WINDOW_STATE CWinSystemWin10::GetState(bool fullScreen) const +{ + return static_cast<WINDOW_STATE>(fullScreen ? m_fullscreenState : m_windowState); +} + +bool CWinSystemWin10::MessagePump() +{ + return m_winEvents->MessagePump(); +} + +int CWinSystemWin10::GetGuiSdrPeakLuminance() const +{ + const auto settings = CServiceBroker::GetSettingsComponent()->GetSettings(); + + return settings->GetInt(CSettings::SETTING_VIDEOSCREEN_GUISDRPEAKLUMINANCE); +} + +#pragma pack(pop) diff --git a/xbmc/windowing/win10/WinSystemWin10.h b/xbmc/windowing/win10/WinSystemWin10.h new file mode 100644 index 0000000..0f2b41e --- /dev/null +++ b/xbmc/windowing/win10/WinSystemWin10.h @@ -0,0 +1,156 @@ +/* + * 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 <vector> + +#pragma pack(push) +#pragma pack(8) + +/* 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 +{ + // Windows desktop info + int ScreenWidth; + int ScreenHeight; + float RefreshRate; + int Bpp; + bool Interlaced; +}; + +class CWinSystemWin10 : public CWinSystemBase +{ +public: + CWinSystemWin10(); + virtual ~CWinSystemWin10(); + + // 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; + 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 WindowedMode() const { return m_state != WINDOW_STATE_FULLSCREEN; } + bool SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) override; + + // CWinSystemWin10 + bool IsAlteringWindow() const { return m_IsAlteringWindow; } + void SetAlteringWindow(bool altering) { m_IsAlteringWindow = altering; } + bool IsTogglingHDR() const { return false; } + void SetTogglingHDR(bool toggling) {} + virtual bool DPIChanged(WORD dpi, RECT windowRect) const; + bool IsMinimized() const { return m_bMinimized; } + void SetMinimized(bool minimized) { m_bMinimized = minimized; } + int GetGuiSdrPeakLuminance() const; + + bool CanDoWindowed() override; + + // 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 AdjustWindow(); + + virtual void Register(IDispResource *resource); + virtual void Unregister(IDispResource *resource); + + bool ChangeResolution(const RESOLUTION_INFO& res, bool forceChange = false); + const MONITOR_DETAILS* GetDefaultMonitor() const; + void RestoreDesktopResolution(); + 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(); + + std::vector<MONITOR_DETAILS> m_displays; + bool m_ValidWindowedPosition; + bool m_IsAlteringWindow; + + 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 + bool m_inFocus; + bool m_bMinimized; + bool m_bFirstResChange = true; + + winrt::Windows::UI::Core::CoreWindow m_coreWindow = nullptr; +}; + +#pragma pack(pop) + diff --git a/xbmc/windowing/win10/WinSystemWin10DX.cpp b/xbmc/windowing/win10/WinSystemWin10DX.cpp new file mode 100644 index 0000000..c3d2fca --- /dev/null +++ b/xbmc/windowing/win10/WinSystemWin10DX.cpp @@ -0,0 +1,205 @@ +/* + * 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 "WinSystemWin10DX.h" + +#include "input/touch/generic/GenericTouchActionHandler.h" +#include "input/touch/generic/GenericTouchInputHandler.h" +#include "rendering/dx/DirectXHelper.h" +#include "utils/XTimeUtils.h" +#include "utils/log.h" +#include "windowing/WindowSystemFactory.h" + +#include "platform/win32/WIN32Util.h" + +using namespace std::chrono_literals; + +void CWinSystemWin10DX::Register() +{ + KODI::WINDOWING::CWindowSystemFactory::RegisterWindowSystem(CreateWinSystem); +} + +std::unique_ptr<CWinSystemBase> CWinSystemWin10DX::CreateWinSystem() +{ + return std::make_unique<CWinSystemWin10DX>(); +} + +CWinSystemWin10DX::CWinSystemWin10DX() : CRenderSystemDX() +{ +} + +CWinSystemWin10DX::~CWinSystemWin10DX() +{ +} + +void CWinSystemWin10DX::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 CWinSystemWin10DX::CreateNewWindow(const std::string& name, bool fullScreen, RESOLUTION_INFO& res) +{ + const MONITOR_DETAILS* monitor = GetDefaultMonitor(); + if (!monitor) + return false; + + m_deviceResources = DX::DeviceResources::Get(); + m_deviceResources->SetWindow(m_coreWindow); + + if (CWinSystemWin10::CreateNewWindow(name, fullScreen, res) && m_deviceResources->HasValidDevice()) + { + CGenericTouchInputHandler::GetInstance().RegisterHandler(&CGenericTouchActionHandler::GetInstance()); + CGenericTouchInputHandler::GetInstance().SetScreenDPI(m_deviceResources->GetDpi()); + return true; + } + return false; +} + +bool CWinSystemWin10DX::DestroyRenderSystem() +{ + CRenderSystemDX::DestroyRenderSystem(); + + m_deviceResources->Release(); + m_deviceResources.reset(); + return true; +} + +void CWinSystemWin10DX::ShowSplash(const std::string & message) +{ + CRenderSystemBase::ShowSplash(message); + + // this will prevent killing the app by watchdog timeout during loading + if (m_coreWindow != nullptr) + m_coreWindow.Dispatcher().ProcessEvents(winrt::Windows::UI::Core::CoreProcessEventsOption::ProcessAllIfPresent); +} + +void CWinSystemWin10DX::SetDeviceFullScreen(bool fullScreen, RESOLUTION_INFO& res) +{ + m_deviceResources->SetFullScreen(fullScreen, res); +} + +bool CWinSystemWin10DX::ResizeWindow(int newWidth, int newHeight, int newLeft, int newTop) +{ + CWinSystemWin10::ResizeWindow(newWidth, newHeight, newLeft, newTop); + CRenderSystemDX::OnResize(); + + return true; +} + +void CWinSystemWin10DX::OnMove(int x, int y) +{ + m_deviceResources->SetWindowPos(m_coreWindow.Bounds()); +} + +bool CWinSystemWin10DX::DPIChanged(WORD dpi, RECT windowRect) const +{ + m_deviceResources->SetDpi(dpi); + if (!IsAlteringWindow()) + return CWinSystemWin10::DPIChanged(dpi, windowRect); + + return true; +} + +void CWinSystemWin10DX::ReleaseBackBuffer() +{ + m_deviceResources->ReleaseBackBuffer(); +} + +void CWinSystemWin10DX::CreateBackBuffer() +{ + m_deviceResources->CreateBackBuffer(); +} + +void CWinSystemWin10DX::ResizeDeviceBuffers() +{ + m_deviceResources->ResizeBuffers(); +} + +bool CWinSystemWin10DX::IsStereoEnabled() +{ + return m_deviceResources->IsStereoEnabled(); +} + +void CWinSystemWin10DX::OnResize(int width, int height) +{ + if (!m_deviceResources) + return; + + if (!m_IsAlteringWindow) + ReleaseBackBuffer(); + + m_deviceResources->SetLogicalSize(width, height); + + if (!m_IsAlteringWindow) + CreateBackBuffer(); +} + +bool CWinSystemWin10DX::SetFullScreen(bool fullScreen, RESOLUTION_INFO& res, bool blankOtherDisplays) +{ + bool const result = CWinSystemWin10::SetFullScreen(fullScreen, res, blankOtherDisplays); + CRenderSystemDX::OnResize(); + return result; +} + +void CWinSystemWin10DX::UninitHooks() +{ +} + +void CWinSystemWin10DX::InitHooks(IDXGIOutput* pOutput) +{ +} + +bool CWinSystemWin10DX::IsHDRDisplay() +{ + return false; // use tone mapping by default on Xbox +} + +HDR_STATUS CWinSystemWin10DX::GetOSHDRStatus() +{ + return CWIN32Util::GetWindowsHDRStatus(); +} + +HDR_STATUS CWinSystemWin10DX::ToggleHDR() +{ + return m_deviceResources->ToggleHDR(); +} + +bool CWinSystemWin10DX::IsHDROutput() const +{ + return m_deviceResources->IsHDROutput(); +} + +bool CWinSystemWin10DX::IsTransferPQ() const +{ + return m_deviceResources->IsTransferPQ(); +} + +void CWinSystemWin10DX::SetHdrMetaData(DXGI_HDR_METADATA_HDR10& hdr10) const +{ + m_deviceResources->SetHdrMetaData(hdr10); +} + +void CWinSystemWin10DX::SetHdrColorSpace(const DXGI_COLOR_SPACE_TYPE colorSpace) const +{ + m_deviceResources->SetHdrColorSpace(colorSpace); +} + +DEBUG_INFO_RENDER CWinSystemWin10DX::GetDebugInfo() +{ + return m_deviceResources->GetDebugInfo(); +} diff --git a/xbmc/windowing/win10/WinSystemWin10DX.h b/xbmc/windowing/win10/WinSystemWin10DX.h new file mode 100644 index 0000000..84ad660 --- /dev/null +++ b/xbmc/windowing/win10/WinSystemWin10DX.h @@ -0,0 +1,90 @@ +/* + * 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 "WinSystemWin10.h" +#include "rendering/dx/RenderSystemDX.h" + +class CWinSystemWin10DX : public CWinSystemWin10, public CRenderSystemDX +{ +public: + CWinSystemWin10DX(); + ~CWinSystemWin10DX(); + + static void Register(); + static std::unique_ptr<CWinSystemBase> CreateWinSystem(); + + // Implementation of CWinSystemBase via CWinSystemWin10 + 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; + 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); + winrt::Windows::Foundation::Size GetOutputSize() const { return m_deviceResources->GetOutputSize(); } + void TrimDevice() const { m_deviceResources->Trim(); } + + /*! + \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 { CWinSystemWin10::Register(resource); } + void Unregister(IDispResource* resource) override { CWinSystemWin10::Unregister(resource); } + + void ShowSplash(const std::string& message) override; + + // 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; +}; + |