From 3d6661e05cb530d8de9d252557b74ed095b69bb1 Mon Sep 17 00:00:00 2001 From: mpotrykus <42190680+mpotrykus@users.noreply.github.com> Date: Wed, 29 Jul 2026 06:36:36 -0500 Subject: [PATCH 1/2] feat(UI): rework UI --- App.xaml | 15 +- App.xaml.cpp | 72 +- App.xaml.h | 9 +- Assets/LargeTile.scale-100.png | Bin 9386 -> 4285 bytes Assets/LargeTile.scale-125.png | Bin 11739 -> 5461 bytes Assets/LargeTile.scale-150.png | Bin 14390 -> 6653 bytes Assets/LargeTile.scale-200.png | Bin 20164 -> 9280 bytes Assets/LargeTile.scale-400.png | Bin 41834 -> 26861 bytes Assets/Play1.png | Bin 0 -> 13903 bytes Assets/SmallTile.scale-100.png | Bin 2820 -> 1283 bytes Assets/SmallTile.scale-125.png | Bin 3644 -> 1563 bytes Assets/SmallTile.scale-150.png | Bin 4460 -> 1862 bytes Assets/SmallTile.scale-200.png | Bin 6119 -> 2406 bytes Assets/SmallTile.scale-400.png | Bin 11924 -> 5341 bytes Assets/SplashScreen.scale-100.png | Bin 9371 -> 4627 bytes Assets/SplashScreen.scale-125.png | Bin 11163 -> 5912 bytes Assets/SplashScreen.scale-150.png | Bin 14566 -> 7290 bytes Assets/SplashScreen.scale-200.png | Bin 20953 -> 11591 bytes Assets/SplashScreen.scale-400.png | Bin 46804 -> 31601 bytes Assets/Square150x150Logo.scale-100.png | Bin 3738 -> 2010 bytes Assets/Square150x150Logo.scale-125.png | Bin 5300 -> 2458 bytes Assets/Square150x150Logo.scale-150.png | Bin 6423 -> 2975 bytes Assets/Square150x150Logo.scale-200.png | Bin 8735 -> 4182 bytes Assets/Square150x150Logo.scale-400.png | Bin 18825 -> 9845 bytes ...go.altform-lightunplated_targetsize-16.png | Bin 705 -> 525 bytes ...go.altform-lightunplated_targetsize-24.png | Bin 1067 -> 703 bytes ...o.altform-lightunplated_targetsize-256.png | Bin 13111 -> 8755 bytes ...go.altform-lightunplated_targetsize-32.png | Bin 1414 -> 952 bytes ...go.altform-lightunplated_targetsize-48.png | Bin 2271 -> 1328 bytes ...x44Logo.altform-unplated_targetsize-16.png | Bin 705 -> 525 bytes ...44Logo.altform-unplated_targetsize-256.png | Bin 13111 -> 8755 bytes ...x44Logo.altform-unplated_targetsize-32.png | Bin 1414 -> 952 bytes ...x44Logo.altform-unplated_targetsize-48.png | Bin 2271 -> 1328 bytes Assets/Square44x44Logo.scale-100.png | Bin 2158 -> 1053 bytes Assets/Square44x44Logo.scale-125.png | Bin 3122 -> 1250 bytes Assets/Square44x44Logo.scale-150.png | Bin 3820 -> 1577 bytes Assets/Square44x44Logo.scale-200.png | Bin 5259 -> 2082 bytes Assets/Square44x44Logo.scale-400.png | Bin 10851 -> 4376 bytes Assets/Square44x44Logo.targetsize-16.png | Bin 648 -> 433 bytes Assets/Square44x44Logo.targetsize-24.png | Bin 1109 -> 654 bytes ...x44Logo.targetsize-24_altform-unplated.png | Bin 1255 -> 703 bytes Assets/Square44x44Logo.targetsize-256.png | Bin 15829 -> 6291 bytes Assets/Square44x44Logo.targetsize-32.png | Bin 1554 -> 761 bytes Assets/Square44x44Logo.targetsize-48.png | Bin 2589 -> 1146 bytes Assets/StoreLogo.scale-100.png | Bin 2636 -> 1445 bytes Assets/StoreLogo.scale-125.png | Bin 3341 -> 1838 bytes Assets/StoreLogo.scale-150.png | Bin 3471 -> 2092 bytes Assets/StoreLogo.scale-200.png | Bin 5197 -> 2888 bytes Assets/StoreLogo.scale-400.png | Bin 10676 -> 6484 bytes Assets/Wide310x150Logo.scale-100.png | Bin 4161 -> 2182 bytes Assets/Wide310x150Logo.scale-125.png | Bin 5596 -> 2698 bytes Assets/Wide310x150Logo.scale-150.png | Bin 6521 -> 3255 bytes Assets/Wide310x150Logo.scale-200.png | Bin 9371 -> 4627 bytes Assets/Wide310x150Logo.scale-400.png | Bin 20953 -> 11591 bytes Assets/grid.png | Bin 0 -> 3977 bytes Assets/loading2.gif | Bin 0 -> 50591 bytes Assets/logo1.gif | Bin 0 -> 57234 bytes Assets/logo1.png | Bin 0 -> 4679 bytes Assets/logo2.gif | Bin 0 -> 57234 bytes Assets/logo_a.gif | Bin 0 -> 77685 bytes Assets/orbit_load.gif | Bin 0 -> 138409 bytes Assets/orbit_load2.gif | Bin 0 -> 126056 bytes Assets/orbit_load3.gif | Bin 0 -> 136376 bytes Assets/play.png | Bin 0 -> 24155 bytes Common/DeviceResources.cpp | 21 +- Common/DirectXHelper.h | 4 +- Common/ModalDialog.xaml | Bin 2640 -> 0 bytes Common/ModalDialog.xaml.cpp | Bin 37156 -> 0 bytes Common/ModalDialog.xaml.h | Bin 1796 -> 0 bytes Common/TextConsole.cpp | 44 +- Common/TextConsole.h | 3 + Converters/BoolToTextConverter.h | 15 - MoonlightWelcome.xaml | 117 -- Pages/AppPage.xaml | 82 - Pages/AppPage.xaml.cpp | 382 ----- Pages/AppPage.xaml.h | 50 - Pages/HostSelectorPage.xaml | 98 -- Pages/HostSelectorPage.xaml.cpp | 486 ------ Pages/HostSelectorPage.xaml.h | 52 - Pages/HostSettingsPage.xaml | 115 -- Pages/HostSettingsPage.xaml.cpp | 266 ---- Pages/MoonlightSettings.xaml | 64 - Pages/MoonlightSettings.xaml.cpp | 115 -- Pages/MoonlightSettings.xaml.h | 38 - Pages/StreamPage.xaml | 101 -- State/ApplicationState.cpp | 158 +- State/ApplicationState.h | 38 +- State/GamepadState.h | 79 +- State/MDNSHandler.cpp | 8 + State/MDNSHandler.h | 2 +- State/MoonlightApp.cpp | 20 +- State/MoonlightApp.h | 90 ++ State/MoonlightClient.cpp | 243 ++- State/MoonlightClient.h | 119 +- State/MoonlightHost.cpp | 52 + State/MoonlightHost.h | 51 + State/Stats.cpp | 13 +- State/StreamConfiguration.h | 1 + Streaming/AudioPlayer.cpp | 27 +- Streaming/FFmpegDecoder.cpp | 32 +- Streaming/FrameQueue.cpp | 5 +- Streaming/LogRenderer.cpp | 12 +- Streaming/Pacer.cpp | 11 +- Streaming/VideoRenderer.cpp | 17 +- Streaming/YUVRGBPixelShader.hlsl | 43 + Streaming/YUVRGBVertexShader.hlsl | 19 + Streaming/moonlight_xbox_dxMain.cpp | 179 ++- Streaming/moonlight_xbox_dxMain.h | 6 +- UI/Backgrounds/BackgroundRegistry.h | 22 + UI/Backgrounds/BackgroundSettingsHelpers.h | 29 + UI/Backgrounds/Blobs/BlobsBackground.xaml | 53 + UI/Backgrounds/Blobs/BlobsBackground.xaml.cpp | 255 ++++ UI/Backgrounds/Blobs/BlobsBackground.xaml.h | 57 + .../Blobs/BlobsSettingsControl.xaml | 68 + .../Blobs/BlobsSettingsControl.xaml.cpp | 166 ++ .../Blobs/BlobsSettingsControl.xaml.h | 25 + UI/Backgrounds/DynamicBackgroundHost.xaml | 20 + UI/Backgrounds/DynamicBackgroundHost.xaml.cpp | 205 +++ UI/Backgrounds/DynamicBackgroundHost.xaml.h | 29 + .../GlobeGrid/GlobeGridBackground.xaml | 49 + .../GlobeGrid/GlobeGridBackground.xaml.cpp | 218 +++ .../GlobeGrid/GlobeGridBackground.xaml.h | 42 + UI/Backgrounds/Orbs/OrbsBackground.xaml | 48 + UI/Backgrounds/Orbs/OrbsBackground.xaml.cpp | 373 +++++ UI/Backgrounds/Orbs/OrbsBackground.xaml.h | 47 + UI/Backgrounds/Orbs/OrbsSettingsControl.xaml | 52 + .../Orbs/OrbsSettingsControl.xaml.cpp | 156 ++ .../Orbs/OrbsSettingsControl.xaml.h | 23 + .../Particles/ParticleBackground.xaml | 12 + .../Particles/ParticleBackground.xaml.cpp | 312 ++++ .../Particles/ParticleBackground.xaml.h | 49 + .../Particles/ParticleSettingsControl.xaml | 52 + .../ParticleSettingsControl.xaml.cpp | 146 ++ .../Particles/ParticleSettingsControl.xaml.h | 23 + UI/Backgrounds/README.md | 519 +++++++ UI/Backgrounds/Spheres/SpheresBackground.xaml | 19 + .../Spheres/SpheresBackground.xaml.cpp | 306 ++++ .../Spheres/SpheresBackground.xaml.h | 46 + .../Spheres/SpheresSettingsControl.xaml | 64 + .../Spheres/SpheresSettingsControl.xaml.cpp | 192 +++ .../Spheres/SpheresSettingsControl.xaml.h | 24 + UI/Backgrounds/Streaks/StreaksBackground.xaml | 52 + .../Streaks/StreaksBackground.xaml.cpp | 280 ++++ .../Streaks/StreaksBackground.xaml.h | 41 + .../Streaks/StreaksSettingsControl.xaml | 59 + .../Streaks/StreaksSettingsControl.xaml.cpp | 151 ++ .../Streaks/StreaksSettingsControl.xaml.h | 26 + .../SwipeReveal/SwipeRevealBackground.xaml | 94 ++ .../SwipeRevealBackground.xaml.cpp | 444 ++++++ .../SwipeReveal/SwipeRevealBackground.xaml.h | 64 + UI/Controls/AspectRatioBox.xaml | 13 + UI/Controls/AspectRatioBox.xaml.cpp | 85 ++ UI/Controls/AspectRatioBox.xaml.h | 29 + UI/Controls/LunarPhaseControl.xaml | 132 ++ UI/Controls/LunarPhaseControl.xaml.cpp | 424 ++++++ UI/Controls/LunarPhaseControl.xaml.h | 92 ++ UI/Controls/SlidingMenu.xaml | 134 ++ UI/Controls/SlidingMenu.xaml.cpp | 134 ++ UI/Controls/SlidingMenu.xaml.h | 31 + UI/Controls/SwatchPicker.xaml | 73 + UI/Controls/SwatchPicker.xaml.cpp | 274 ++++ UI/Controls/SwatchPicker.xaml.h | 111 ++ UI/Controls/TabsLayout.xaml | 81 + UI/Controls/TabsLayout.xaml.cpp | 515 +++++++ UI/Controls/TabsLayout.xaml.h | 69 + .../Converters}/BoolToTextConverter.cpp | 0 UI/Converters/BoolToTextConverter.h | 24 + UI/Converters/BoolToVisibilityConverter.cpp | 44 + UI/Converters/BoolToVisibilityConverter.h | 21 + UI/Modals/AddHostDialog.xaml | 266 ++++ UI/Modals/AddHostDialog.xaml.cpp | 123 ++ UI/Modals/AddHostDialog.xaml.h | 26 + UI/Modals/AlertDialog.xaml | 148 ++ UI/Modals/AlertDialog.xaml.cpp | 37 + UI/Modals/AlertDialog.xaml.h | 15 + UI/Modals/AppActionsDialog.xaml | 127 ++ UI/Modals/AppActionsDialog.xaml.cpp | 94 ++ UI/Modals/AppActionsDialog.xaml.h | 36 + UI/Modals/ConfirmDialog.xaml | 163 ++ UI/Modals/ConfirmDialog.xaml.cpp | 61 + UI/Modals/ConfirmDialog.xaml.h | 23 + UI/Modals/HostActionsDialog.xaml | 115 ++ UI/Modals/HostActionsDialog.xaml.cpp | 70 + UI/Modals/HostActionsDialog.xaml.h | 30 + UI/Modals/PairDialog.xaml | 170 +++ UI/Modals/PairDialog.xaml.cpp | 27 + UI/Modals/PairDialog.xaml.h | 14 + UI/Modals/StreamErrorDialog.xaml | 164 ++ UI/Modals/StreamErrorDialog.xaml.cpp | 55 + UI/Modals/StreamErrorDialog.xaml.h | 22 + UI/Modals/TestConnectionResultDialog.xaml | 164 ++ UI/Modals/TestConnectionResultDialog.xaml.cpp | 68 + UI/Modals/TestConnectionResultDialog.xaml.h | 14 + UI/Models/MenuItem.cpp | 36 + UI/Models/MenuItem.h | 21 + UI/Models/UIPersonalization.h | 68 + UI/Models/ViewModels/AppPageViewModel.cpp | 100 ++ UI/Models/ViewModels/AppPageViewModel.h | 32 + UI/Pages/AppPage/AppPage.Resources.Grid.xaml | 106 ++ UI/Pages/AppPage/AppPage.Resources.List.xaml | 164 ++ UI/Pages/AppPage/AppPage.Resources.xaml | 102 ++ UI/Pages/AppPage/AppPage.Selection.cpp | 755 ++++++++++ UI/Pages/AppPage/AppPage.xaml | Bin 0 -> 25728 bytes UI/Pages/AppPage/AppPage.xaml.cpp | 1330 +++++++++++++++++ UI/Pages/AppPage/AppPage.xaml.h | 165 ++ UI/Pages/HostSelectorPage.xaml | 196 +++ UI/Pages/HostSelectorPage.xaml.cpp | 1040 +++++++++++++ UI/Pages/HostSelectorPage.xaml.h | 73 + UI/Pages/HostSettingsPage.xaml | 440 ++++++ UI/Pages/HostSettingsPage.xaml.cpp | 465 ++++++ {Pages => UI/Pages}/HostSettingsPage.xaml.h | 43 +- UI/Pages/MoonlightSettings.xaml | 327 ++++ UI/Pages/MoonlightSettings.xaml.cpp | 296 ++++ UI/Pages/MoonlightSettings.xaml.h | 55 + UI/Pages/MoonlightWelcome.xaml | 178 +++ .../Pages/MoonlightWelcome.xaml.cpp | 14 +- .../Pages/MoonlightWelcome.xaml.h | 11 +- UI/Pages/StreamPage.xaml | 389 +++++ {Pages => UI/Pages}/StreamPage.xaml.cpp | 238 ++- {Pages => UI/Pages}/StreamPage.xaml.h | 62 +- UI/Styles/Colors.xaml | 48 + UI/Styles/Controls.xaml | 914 +++++++++++ UI/Utilities/EffectsLibrary.cpp | 628 ++++++++ UI/Utilities/EffectsLibrary.h | 65 + UI/Utilities/ImageHelpers.cpp | 646 ++++++++ UI/Utilities/ImageHelpers.h | 29 + UI/Utilities/ToastService.cpp | 127 ++ UI/Utilities/ToastService.h | 9 + UI/Utilities/XamlHelpers.cpp | 253 ++++ UI/Utilities/XamlHelpers.h | 72 + Utils.cpp | 284 +++- Utils.hpp | 41 +- Utils/FloatBuffer.cpp | 5 +- libgamestream/client.c | 37 +- libgamestream/http.c | 8 + moonlight-xbox-dx.vcxproj | 307 +++- moonlight-xbox-dx.vcxproj.filters | 970 +++++++++--- pch.h | 9 +- 238 files changed, 21018 insertions(+), 2744 deletions(-) create mode 100644 Assets/Play1.png create mode 100644 Assets/grid.png create mode 100644 Assets/loading2.gif create mode 100644 Assets/logo1.gif create mode 100644 Assets/logo1.png create mode 100644 Assets/logo2.gif create mode 100644 Assets/logo_a.gif create mode 100644 Assets/orbit_load.gif create mode 100644 Assets/orbit_load2.gif create mode 100644 Assets/orbit_load3.gif create mode 100644 Assets/play.png delete mode 100644 Common/ModalDialog.xaml delete mode 100644 Common/ModalDialog.xaml.cpp delete mode 100644 Common/ModalDialog.xaml.h delete mode 100644 Converters/BoolToTextConverter.h delete mode 100644 MoonlightWelcome.xaml delete mode 100644 Pages/AppPage.xaml delete mode 100644 Pages/AppPage.xaml.cpp delete mode 100644 Pages/AppPage.xaml.h delete mode 100644 Pages/HostSelectorPage.xaml delete mode 100644 Pages/HostSelectorPage.xaml.cpp delete mode 100644 Pages/HostSelectorPage.xaml.h delete mode 100644 Pages/HostSettingsPage.xaml delete mode 100644 Pages/HostSettingsPage.xaml.cpp delete mode 100644 Pages/MoonlightSettings.xaml delete mode 100644 Pages/MoonlightSettings.xaml.cpp delete mode 100644 Pages/MoonlightSettings.xaml.h delete mode 100644 Pages/StreamPage.xaml create mode 100644 Streaming/YUVRGBPixelShader.hlsl create mode 100644 Streaming/YUVRGBVertexShader.hlsl create mode 100644 UI/Backgrounds/BackgroundRegistry.h create mode 100644 UI/Backgrounds/BackgroundSettingsHelpers.h create mode 100644 UI/Backgrounds/Blobs/BlobsBackground.xaml create mode 100644 UI/Backgrounds/Blobs/BlobsBackground.xaml.cpp create mode 100644 UI/Backgrounds/Blobs/BlobsBackground.xaml.h create mode 100644 UI/Backgrounds/Blobs/BlobsSettingsControl.xaml create mode 100644 UI/Backgrounds/Blobs/BlobsSettingsControl.xaml.cpp create mode 100644 UI/Backgrounds/Blobs/BlobsSettingsControl.xaml.h create mode 100644 UI/Backgrounds/DynamicBackgroundHost.xaml create mode 100644 UI/Backgrounds/DynamicBackgroundHost.xaml.cpp create mode 100644 UI/Backgrounds/DynamicBackgroundHost.xaml.h create mode 100644 UI/Backgrounds/GlobeGrid/GlobeGridBackground.xaml create mode 100644 UI/Backgrounds/GlobeGrid/GlobeGridBackground.xaml.cpp create mode 100644 UI/Backgrounds/GlobeGrid/GlobeGridBackground.xaml.h create mode 100644 UI/Backgrounds/Orbs/OrbsBackground.xaml create mode 100644 UI/Backgrounds/Orbs/OrbsBackground.xaml.cpp create mode 100644 UI/Backgrounds/Orbs/OrbsBackground.xaml.h create mode 100644 UI/Backgrounds/Orbs/OrbsSettingsControl.xaml create mode 100644 UI/Backgrounds/Orbs/OrbsSettingsControl.xaml.cpp create mode 100644 UI/Backgrounds/Orbs/OrbsSettingsControl.xaml.h create mode 100644 UI/Backgrounds/Particles/ParticleBackground.xaml create mode 100644 UI/Backgrounds/Particles/ParticleBackground.xaml.cpp create mode 100644 UI/Backgrounds/Particles/ParticleBackground.xaml.h create mode 100644 UI/Backgrounds/Particles/ParticleSettingsControl.xaml create mode 100644 UI/Backgrounds/Particles/ParticleSettingsControl.xaml.cpp create mode 100644 UI/Backgrounds/Particles/ParticleSettingsControl.xaml.h create mode 100644 UI/Backgrounds/README.md create mode 100644 UI/Backgrounds/Spheres/SpheresBackground.xaml create mode 100644 UI/Backgrounds/Spheres/SpheresBackground.xaml.cpp create mode 100644 UI/Backgrounds/Spheres/SpheresBackground.xaml.h create mode 100644 UI/Backgrounds/Spheres/SpheresSettingsControl.xaml create mode 100644 UI/Backgrounds/Spheres/SpheresSettingsControl.xaml.cpp create mode 100644 UI/Backgrounds/Spheres/SpheresSettingsControl.xaml.h create mode 100644 UI/Backgrounds/Streaks/StreaksBackground.xaml create mode 100644 UI/Backgrounds/Streaks/StreaksBackground.xaml.cpp create mode 100644 UI/Backgrounds/Streaks/StreaksBackground.xaml.h create mode 100644 UI/Backgrounds/Streaks/StreaksSettingsControl.xaml create mode 100644 UI/Backgrounds/Streaks/StreaksSettingsControl.xaml.cpp create mode 100644 UI/Backgrounds/Streaks/StreaksSettingsControl.xaml.h create mode 100644 UI/Backgrounds/SwipeReveal/SwipeRevealBackground.xaml create mode 100644 UI/Backgrounds/SwipeReveal/SwipeRevealBackground.xaml.cpp create mode 100644 UI/Backgrounds/SwipeReveal/SwipeRevealBackground.xaml.h create mode 100644 UI/Controls/AspectRatioBox.xaml create mode 100644 UI/Controls/AspectRatioBox.xaml.cpp create mode 100644 UI/Controls/AspectRatioBox.xaml.h create mode 100644 UI/Controls/LunarPhaseControl.xaml create mode 100644 UI/Controls/LunarPhaseControl.xaml.cpp create mode 100644 UI/Controls/LunarPhaseControl.xaml.h create mode 100644 UI/Controls/SlidingMenu.xaml create mode 100644 UI/Controls/SlidingMenu.xaml.cpp create mode 100644 UI/Controls/SlidingMenu.xaml.h create mode 100644 UI/Controls/SwatchPicker.xaml create mode 100644 UI/Controls/SwatchPicker.xaml.cpp create mode 100644 UI/Controls/SwatchPicker.xaml.h create mode 100644 UI/Controls/TabsLayout.xaml create mode 100644 UI/Controls/TabsLayout.xaml.cpp create mode 100644 UI/Controls/TabsLayout.xaml.h rename {Converters => UI/Converters}/BoolToTextConverter.cpp (100%) create mode 100644 UI/Converters/BoolToTextConverter.h create mode 100644 UI/Converters/BoolToVisibilityConverter.cpp create mode 100644 UI/Converters/BoolToVisibilityConverter.h create mode 100644 UI/Modals/AddHostDialog.xaml create mode 100644 UI/Modals/AddHostDialog.xaml.cpp create mode 100644 UI/Modals/AddHostDialog.xaml.h create mode 100644 UI/Modals/AlertDialog.xaml create mode 100644 UI/Modals/AlertDialog.xaml.cpp create mode 100644 UI/Modals/AlertDialog.xaml.h create mode 100644 UI/Modals/AppActionsDialog.xaml create mode 100644 UI/Modals/AppActionsDialog.xaml.cpp create mode 100644 UI/Modals/AppActionsDialog.xaml.h create mode 100644 UI/Modals/ConfirmDialog.xaml create mode 100644 UI/Modals/ConfirmDialog.xaml.cpp create mode 100644 UI/Modals/ConfirmDialog.xaml.h create mode 100644 UI/Modals/HostActionsDialog.xaml create mode 100644 UI/Modals/HostActionsDialog.xaml.cpp create mode 100644 UI/Modals/HostActionsDialog.xaml.h create mode 100644 UI/Modals/PairDialog.xaml create mode 100644 UI/Modals/PairDialog.xaml.cpp create mode 100644 UI/Modals/PairDialog.xaml.h create mode 100644 UI/Modals/StreamErrorDialog.xaml create mode 100644 UI/Modals/StreamErrorDialog.xaml.cpp create mode 100644 UI/Modals/StreamErrorDialog.xaml.h create mode 100644 UI/Modals/TestConnectionResultDialog.xaml create mode 100644 UI/Modals/TestConnectionResultDialog.xaml.cpp create mode 100644 UI/Modals/TestConnectionResultDialog.xaml.h create mode 100644 UI/Models/MenuItem.cpp create mode 100644 UI/Models/MenuItem.h create mode 100644 UI/Models/UIPersonalization.h create mode 100644 UI/Models/ViewModels/AppPageViewModel.cpp create mode 100644 UI/Models/ViewModels/AppPageViewModel.h create mode 100644 UI/Pages/AppPage/AppPage.Resources.Grid.xaml create mode 100644 UI/Pages/AppPage/AppPage.Resources.List.xaml create mode 100644 UI/Pages/AppPage/AppPage.Resources.xaml create mode 100644 UI/Pages/AppPage/AppPage.Selection.cpp create mode 100644 UI/Pages/AppPage/AppPage.xaml create mode 100644 UI/Pages/AppPage/AppPage.xaml.cpp create mode 100644 UI/Pages/AppPage/AppPage.xaml.h create mode 100644 UI/Pages/HostSelectorPage.xaml create mode 100644 UI/Pages/HostSelectorPage.xaml.cpp create mode 100644 UI/Pages/HostSelectorPage.xaml.h create mode 100644 UI/Pages/HostSettingsPage.xaml create mode 100644 UI/Pages/HostSettingsPage.xaml.cpp rename {Pages => UI/Pages}/HostSettingsPage.xaml.h (69%) create mode 100644 UI/Pages/MoonlightSettings.xaml create mode 100644 UI/Pages/MoonlightSettings.xaml.cpp create mode 100644 UI/Pages/MoonlightSettings.xaml.h create mode 100644 UI/Pages/MoonlightWelcome.xaml rename MoonlightWelcome.xaml.cpp => UI/Pages/MoonlightWelcome.xaml.cpp (88%) rename MoonlightWelcome.xaml.h => UI/Pages/MoonlightWelcome.xaml.h (77%) create mode 100644 UI/Pages/StreamPage.xaml rename {Pages => UI/Pages}/StreamPage.xaml.cpp (58%) rename {Pages => UI/Pages}/StreamPage.xaml.h (75%) create mode 100644 UI/Styles/Colors.xaml create mode 100644 UI/Styles/Controls.xaml create mode 100644 UI/Utilities/EffectsLibrary.cpp create mode 100644 UI/Utilities/EffectsLibrary.h create mode 100644 UI/Utilities/ImageHelpers.cpp create mode 100644 UI/Utilities/ImageHelpers.h create mode 100644 UI/Utilities/ToastService.cpp create mode 100644 UI/Utilities/ToastService.h create mode 100644 UI/Utilities/XamlHelpers.cpp create mode 100644 UI/Utilities/XamlHelpers.h diff --git a/App.xaml b/App.xaml index 15a4efed..2df906df 100644 --- a/App.xaml +++ b/App.xaml @@ -2,8 +2,19 @@ x:Class="moonlight_xbox_dx.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" - xmlns:local="using:moonlight_xbox_dx"> + xmlns:local="using:moonlight_xbox_dx" + xmlns:controls="using:Microsoft.UI.Xaml.Controls"> + - + + + + + + + + + + diff --git a/App.xaml.cpp b/App.xaml.cpp index e25e67e8..a7ee16c0 100644 --- a/App.xaml.cpp +++ b/App.xaml.cpp @@ -1,11 +1,15 @@ -// +// // App.xaml.cpp // Implementation of the App class. // #include "pch.h" +#define MLOG_TAG_OVERRIDE "App" #include -#include "MoonlightWelcome.xaml.h" +#include "UI\Pages\MoonlightWelcome.xaml.h" +#include "UI\Pages\MoonlightSettings.xaml.h" +#include "UI\Utilities\XamlHelpers.h" +#include "UI\Utilities\ToastService.h" using namespace moonlight_xbox_dx; @@ -23,6 +27,10 @@ using namespace Windows::UI::Xaml::Input; using namespace Windows::UI::Xaml::Interop; using namespace Windows::UI::Xaml::Media; using namespace Windows::UI::Xaml::Navigation; +using namespace Windows::UI::Xaml::Media::Animation; + +static Windows::UI::Xaml::Controls::Frame^ s_rootFrame = nullptr; + /// /// Initializes the singleton application object. This is the first line of authored code /// executed, and as such is the logical equivalent of main() or WinMain(). @@ -30,10 +38,36 @@ using namespace Windows::UI::Xaml::Navigation; App::App() { InitializeComponent(); + Utils::InitFileLogging(); + Utils::InstallCrashHandlers(); RequiresPointerMode = Windows::UI::Xaml::ApplicationRequiresPointerMode::WhenRequested; Suspending += ref new SuspendingEventHandler(this, &App::OnSuspending); Resuming += ref new EventHandler(this, &App::OnResuming); displayRequest = ref new Windows::System::Display::DisplayRequest(); + + this->UnhandledException += ref new Windows::UI::Xaml::UnhandledExceptionEventHandler( + [](Platform::Object^, Windows::UI::Xaml::UnhandledExceptionEventArgs^ e) { + try { + if (e != nullptr && e->Message != nullptr) { + std::wstring msg = e->Message->Data(); + MLOGF(Utils::LogLevel::Error, "Unhandled XAML exception: %S", msg.c_str()); + } + } catch (...) {} + try { if (e != nullptr) e->Handled = true; } catch (...) {} + }); + + GlobalMenuItems = ref new Platform::Collections::Vector(); + GlobalMenuItems->Append(ref new moonlight_xbox_dx::MenuItem( + ref new Platform::String(L"App Settings"), + ref new Platform::String(L""), + ref new Windows::Foundation::EventHandler([](Platform::Object^ s, Platform::Object^ e) { + try { + if (s_rootFrame != nullptr) { + s_rootFrame->Navigate(Windows::UI::Xaml::Interop::TypeName(MoonlightSettings::typeid), nullptr, ref new Windows::UI::Xaml::Media::Animation::EntranceNavigationTransitionInfo()); + } + } catch(...) {} + }) + )); } /// @@ -50,34 +84,27 @@ void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEvent // DebugSettings->EnableFrameRateCounter = true; // } // #endif - moonlight_xbox_dx::Utils::Log("Hello from Moonlight!\n"); - auto rootFrame = dynamic_cast(Window::Current->Content); + MLOG(moonlight_xbox_dx::Utils::LogLevel::Info, "Hello from Moonlight!\n"); - // Do not repeat app initialization when the Window already has content, - // just ensure that the window is active - if (rootFrame == nullptr) + if (s_rootFrame == nullptr) { - // Create a Frame to act as the navigation context and associate it with - // a SuspensionManager key - rootFrame = ref new Frame(); - - rootFrame->NavigationFailed += ref new Windows::UI::Xaml::Navigation::NavigationFailedEventHandler(this, &App::OnNavigationFailed); + s_rootFrame = ref new Frame(); + s_rootFrame->NavigationFailed += ref new NavigationFailedEventHandler(this, &App::OnNavigationFailed); - // Place the frame in the current Window - Window::Current->Content = rootFrame; + auto rootGrid = ref new Grid(); + rootGrid->Children->Append(s_rootFrame); + InitializeToastService(rootGrid); + Window::Current->Content = rootGrid; } - if (rootFrame->Content == nullptr) + if (s_rootFrame->Content == nullptr) { - // When the navigation stack isn't restored navigate to the first page, - // configuring the new page by passing required information as a navigation - // parameter - rootFrame->Navigate(TypeName(HostSelectorPage::typeid), e->Arguments); + s_rootFrame->Navigate(TypeName(HostSelectorPage::typeid), e->Arguments, ref new Windows::UI::Xaml::Media::Animation::SuppressNavigationTransitionInfo()); } if (m_menuPage == nullptr) { - m_menuPage = dynamic_cast(rootFrame->Content); + m_menuPage = dynamic_cast(s_rootFrame->Content); } // Ensure the current window is active Window::Current->Activate(); @@ -127,6 +154,9 @@ void App::OnNavigationFailed(Platform::Object ^sender, Windows::UI::Xaml::Naviga dialog->Content = e->Exception.ToString(); dialog->CloseButtonText = L"OK"; dialog->ShowAsync(); - //throw ref new FailureException("Failed to load Page " + e->SourcePageType.Name); } +Windows::UI::Xaml::Controls::Frame^ App::GetRootFrame() +{ + return s_rootFrame; +} diff --git a/App.xaml.h b/App.xaml.h index 0e7486c6..7c3e66b5 100644 --- a/App.xaml.h +++ b/App.xaml.h @@ -1,4 +1,4 @@ -// +// // App.xaml.h // Declaration of the App class. // @@ -6,7 +6,8 @@ #pragma once #include "App.g.h" -#include +#include "UI\Models\MenuItem.h" +#include "UI\Pages\HostSelectorPage.xaml.h" namespace moonlight_xbox_dx { @@ -17,6 +18,10 @@ namespace moonlight_xbox_dx { public: App(); + + static Windows::UI::Xaml::Controls::Frame^ GetRootFrame(); + + property Windows::Foundation::Collections::IObservableVector^ GlobalMenuItems; virtual void OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ e) override; void OnStateLoaded(); private: diff --git a/Assets/LargeTile.scale-100.png b/Assets/LargeTile.scale-100.png index bc8edc72974c572628b6b309bbcc8327274903b0..ba34a92a3a2d5f8824ec102a21b41609a5c58fc4 100644 GIT binary patch literal 4285 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 NWDj$)skib;`Zt2n$|L{) literal 9386 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�#>^Mc`#HdS&1@IJKYUFS{|Yi;0vliJqVd zc^Kf6Zl5iWR%g1oI254un81gyiy$|VWjO{kii02^<(?dq4slYc+nVe*fe&fk4H(1Vb;k(fkfnO=$OXVK$=mKKD=8`U5{{CG%)CVj+92DXkBLlN?dhKNSb&-l13 zf*Zlvi9Tbvcx10+zbJz57X>U*$TT^r0|0<4aMx<^SBs#e z7du_Il9%4)H8qpp(tB?MyT)-S9ZGbww5a1}4TBBHDM8C>wXG4k!irHwH<(_Gw@m5) ze}&qnQR0Pv?8OS%!2bbvXcvP3 diff --git a/Assets/LargeTile.scale-125.png b/Assets/LargeTile.scale-125.png index fd1fe6532488db4560fd9eddad8f49952838f247..bf408f73cd7f2067236f5c50fb43c87ec4730e48 100644 GIT binary patch literal 5461 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

nGF()cL3Ih$COlOWP| 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 diff --git a/Assets/LargeTile.scale-150.png b/Assets/LargeTile.scale-150.png index 09b5f9c9a2b5dd323a8d2fa08fcd4e97724feba2..d7e1dfd9e75472c2704200e037ec36894781d367 100644 GIT binary patch literal 6653 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%0DoWKDzia^Ta)!(g`2mJFjD^R(`L zl5P}bCbTN<8yz*8 zna8sjQUhaMXNoA_MFoUDVrScVdn@Wa?AXM@Q)UagDJ&o^ynl-C){vwplb z1pvmw$cNwz15hKMd!{J*G&nSu8sGQ*l26A7_SoST8xM<^TV`ykJaDY8t#xT`Y1vxx z)59Q?HQG%78>PHQyBx_OB2rjjA47u0+EHzH3Jwkc((~cb-zc{0J(zTgeHgAs7eta9 z{C>3^4?G=Vk~VnqmJB*(5(_$n(0@9-(DuLhaxvl1!bS6BqGTln~vEyv8vpn#v%At8njEUc`p=e zmt?3ADk%1Xy%t0~HC|2HD62BTT!nJOhus_^9rF(0OpCzUe_JIo(@WG!GPYctnU6Ru zeE&AfTwOq_>|f>bChA9rPuy&k4@(D|g+LNDNC+k`DMeQ&M`eywBvg^etk`#VT5oMn zVf?0R{$zl>tw$2+w5M-{%hF&c=1UoBDkA4hd{i#MXLMC0qIZgY>;%S|u?Gl8? zbl}J#p2~R4pN1 z=~2G#cZKn8u|WwvbT5%xl8Uv|b9pWelyKnRKv?p(-t{<M0pbnMb^bAZ1tPok< z_h&0^Y+rUrf7zX{DfOpNMHQr4wVR8zz0q1q);kO){Th=0k7v* z2qB!%miT%>#`wEn@|jtQeM-5nkvgjfZX(c|4+VXX%^2~j`Nr~ zz@bhU5b_s-jvzN~wp#j=H5F-IGyd?M@MGhXtGWPvg^58&io`wI)HE5zx7_Nvjj~pW z3pY!P`pNdpVPN1(2F!sIWxal{^*aL?LIKLLa2Y=JS^QK_lxiNr|2EjfQ7o`~cDuT# z0zb-kK82%w;r8IufU7eV6kUR|?eE;QdqC$y7lR!S`Dpqdo4b(ej_m&4DGGZ)VQg9g9fMt5YpItbn34vZ??CGv4{7Wln{VX+S;k(!N-sM z%d7J(Sm&>rD(HFy=E!2Sd-|%hs~>!>ywkZ=jzCQvFbgTv2xj$)Lq+@&gghnp?<@38 z$DhV{K7{K2OxziB%!jy+zR5FIM%UY=8t#@5`PBDJh^U7|)Y?=1L1thVv$SXe7ckuX z;gA?xDfHH1P(}))OfSy;!R2VUm}fNVRwc_BDCC5eU|<8DKwZ2Tw0IdD;K|;9{Jj)Mqa-+i%@Ys$HOo zR6q)?WW|flR{*TLzWA7p0}P{#R#OGY)DyZ<(kE|>%0DfGq)$Bqu4Zm7hmO5gN$L@H zj{md(wdZ5!{7=gQ(0+E%e_Ccq>gINben=(2&je79VzzmzeCleRfaL2oiScVk@DA-b z0dqL0V8*UQ=nnlU~Q~{#t14BLFX!%=}%#094?FS+O2(n{G1xWCY_f^3yIxm z1CE_A9dOSX58D_g1t!%K(!Gh}JJif}YX?mtNbyT2RSGpu9=y~z$rv14Y|fwTXheXu zDM3Xe&=ElY9u8PlRVC}_>^xD}Z@O9Q$+(Twh~MPoqU+Rbf8 zD^l_SMgDjEuc{JGhN=&{mdHf~F_2*eQe^Q;lv~=mJ&Md(0|y-370Xf*did|eRu|zNypuGTbgn`SsRgz zFg}SlG`8=KbehBG=uUBg>A=62`;!0|xD_gbMk!J3fb*FY4W1yB zZwgNS2w>N4Cf>!&gPRbTY~#xvY_>TQGe(xJj0z6;&qo1yK#rgIopmqZI_mv37zR_x zZfFu&-*@N3qNw+i3;Ugne$M^H)GB;ux4NDUFSSD1#ImK6&mRvH0P~(Q%e!9?;5{hy z+ve-0&Ps{~IJDK6qWW}W@M1A__tJrIP{5KjUP{G!YFciSlD_(8t#|rvi#>+EChVoA z4dyLWJF@g$P{ha-y^rymirKyyyTq}B+#X_(F;QCd=fNy%_=U}zk}=0fS1W2u^}C+y z7ZvEM`K74Vb6zn|G8&f9L3Oq?5#<=a&)3`idk_8Zef@- literal 14390 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 literal 20164 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 diff --git a/Assets/LargeTile.scale-400.png b/Assets/LargeTile.scale-400.png index 70fd7c473858bddb7a510123572bf003dd229284..66d08f81a2b6b0b0d0c95cba6df23679d5c87932 100644 GIT binary patch literal 26861 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∾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 I>pDmOAH`uT&;S4c literal 0 HcmV?d00001 diff --git a/Assets/SmallTile.scale-100.png b/Assets/SmallTile.scale-100.png index 420a81f55d02512c679ff915baa0185292834985..d407d2e87262311136816d6202ac2d04be5ba92e 100644 GIT binary patch delta 1243 zcma)+>08nV0D!Sfk`-QPiWtnLLS^bahN)>L0$D>%a+P_IDPBeSJV6HgF`-nv$nwZM zmS*bK$kLdofo81h*vun{qbynf!S;T7zr0^xt{n%$#{#AgiDCO=7&S}P zC-cc<*t6%4;4yN(_r~fMg?z5XYyC}BQ3I56>L^-lu%xsk+f!~?@RUay8jrxtKcZOq z);sRZUTZ11J`8Tb?MHvOe4RBNt1OZCHYejq%twP! zu_o>Izi$U>bP6x<8#(Qjs|Zt-x#Bz%8^lgY0R_$SaO1&ggBnOx6Jn}*S=uQ?NH9l( zUQF}lo|)v+^|xhk)L2U5*^!k1QqSj3i~9n2Z%jwEMwtC6omz*xmIBPT%k=b9BS_GtJC zp&VIJmHd41i~;{np%4Ot6&?qXZ&kw?bGBQd36+v_LvEzqQ3jWZrttW0cAa<|olJu{ zAaG{*C?D+L{y&ti=`8!m)9wZufms(xS;~|$|M4Q#OK5ovEYs$EaS!E{KbY8sTMSXF zeH?A<6I{!XwBl5z(+ILBLc!d-3fH%`-Loai8rQYMn$u8#V`rI~P_F%%x{K>I!z_F# zf};r)$T87m>1ejeP;hjA&Tv7F;41kLw%Z0=^S;E&6YrGel66k{SKToS^>64Ot@+(Y zzLepQ9SIETYqr^y4V421cKv}h41SF|rl0Ok13bnnjz#rYSguJP7;$%VDeFuFhdb)> z<{+W~kV?MI$ z<~z5dVIgEtG38@v1TYj4$iA~-;p7F<1BrL2!)M-lhjZU$u@F9bswNsV01EWqk>YFn zA>pED6_3e=H-%0|4rgd{TM4tPzFh@I039B97}rA><~~CD&)a3Xu?5eHZpK>ChkP?V z=9w~Qkv_DSgV@F@nf%4A^?1yaLen61*Jhnr_bvtU>f-E8Qz-kumSZVP$%BgR0i;TZ zL-q*Z(uF<));+NyhFcS@rW&o3X#&u3pr4Jatx)@y>$j~ pLB$<{zt0zth4QBVJ%lgESb&1B+3ItfDiT3oAmY9c6NDz_{tMUoUwi-n delta 2792 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 diff --git a/Assets/SmallTile.scale-150.png b/Assets/SmallTile.scale-150.png index 7a6715f8057ef2cf2ea85bfed09050d71130f177..576a34a2fda8b269b2a03d156a8f01166b076990 100644 GIT binary patch literal 1862 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 literal 6119 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 diff --git a/Assets/SmallTile.scale-400.png b/Assets/SmallTile.scale-400.png index f5b4d646cf304d364068eecda8e7c4891479ab4b..f2296c8fe5dc14d2e60a5da0244336abc3f7d8fd 100644 GIT binary patch literal 5341 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 literal 11924 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 diff --git a/Assets/SplashScreen.scale-100.png b/Assets/SplashScreen.scale-100.png index 32486ee1f57dec1fab4f8f1fd2d589c0ba411c3c..96e61b7ff21cb410c1d9b7c7d40f183ff6b9aed8 100644 GIT binary patch literal 4627 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 PJfIWD{XDANP-*`Kc2Q<) literal 9371 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 diff --git a/Assets/SplashScreen.scale-125.png b/Assets/SplashScreen.scale-125.png index 3ce084db33eb34b208ea83bb39853a31e03e1790..3ed011e75aaf5f11f6e34383100c3246854ffef8 100644 GIT binary patch literal 5912 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 R8h9y&tjz7rs!b7h{~I6yntK2M literal 11163 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 diff --git a/Assets/SplashScreen.scale-150.png b/Assets/SplashScreen.scale-150.png index 8715c3883b99675967934cf69aaee80430612934..bc0547df72fe6a37a62dba145d144b38ac98bed6 100644 GIT binary patch literal 7290 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 diff --git a/Assets/SplashScreen.scale-200.png b/Assets/SplashScreen.scale-200.png index e6eaf2b8821940c4ce3710699d1f1e9a9478a754..d3fc9197d3fa8dddf5822d58a7608f478d0e5d84 100644 GIT binary patch literal 11591 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%0A~zw+)uk>Ub+ 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 diff --git a/Assets/SplashScreen.scale-400.png b/Assets/SplashScreen.scale-400.png index 62fa07630db2f73ee307143a6c2e69335b7f2d7e..01786401ba4dd26064c4de534451752761d3d0b9 100644 GIT binary patch literal 31601 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 literal 46804 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 diff --git a/Assets/Square150x150Logo.scale-100.png b/Assets/Square150x150Logo.scale-100.png index 7f3daae08b2bda8c5736b378ebbb9fd8099e99d2..faa8dca4c5910e952ab272d293c560122a5229c5 100644 GIT binary patch literal 2010 zcmd6o_dgqm8^^=N?jXcyt0cAtrPQ5KL{xCph*h&8_FgG5zD-e~wxn946eYEq*BR9_ zszQ`=MXSUpg4PP9#5HN^+>hV;3+{){^Zw!U{Q5j^g1w#PY2kCi007{$6%yfiLef7G zVwG2~e>(A| z_wd4xx17p5Q`o>ye{>wvE3O>N8I_+P_D}Zra=LMBz{h1UMDWyZf&iiv2{SW83kid4 z>{PpVKhJ!?z!0W=d**)kP!$*3a*IEY4o_l(i! z8S!D9Us`E{Rc&aq>`M+X%Xw6@Oa3=(knXu7KkgM#|FqYvz?$2DxexOmu3c_%xwcDHJ%H+Jjb02K)CTmrD;%BB)^K-1oRQMB` zY3Ug9prkgb!Ko7(jtyp3S0v6%^95n~^SqD!H$o@Wh;B>sx@3dg(8vpndZRh!)jE)f z)sHil1@Wr8e>B>*bxJac*6KB3Xm<*r{;3nisBGb4?QE9Q5@rqvqV(pDPu#NVXdgSgV`bnp@)1lgQY-r$Zw?t4qd*{VNJskqX5$2mwo z@(nX>`r)o`{p^ksUR=b=Q1%U1TIhGdx)k@_f&Cqqc;;}%;gz|<#?9X6dMj$g_OCiu zP2>(u2DUbo6GyqNz4?@5cz=RQSBJ`+uZH9< z>qi_PM3qwTVym!eu558&ODKF;Pk61*D*P5#^ZjD}B)^wWI_j$vJ3%9RtPo|(alaq` z){mq0{TfDF&>Hr|zES_m2u6y8t@q08XZOidlixoURIB{;*;XFPyf%c)bFUPnFc8Ym^HXf);0^_VVq0h73w3Nx)^qa#qFHu)WQT^T~q zgW6zJrf_F~OtNR(n_c=swAI?50~I8Tg5RZl4xm4s2h!(jmqKpQ0$OQ)v1wS0VP6?mq*8H+-JmNwi_7S#kaoF31jomuaf*$6R$FGi z5XaEVx>2~!7ePYU%(wQ%yLIzYfE=#kQy&4N)!85{m)h=(z>AHV#$0S^6BV&qaM&vj z@ioxi^vPPoWSNLRI-gUT!&Y!O_9_-g0LLR+kAI6M7Lj*)@bV1SogKrk{ic}@7?Bq76p4y*MDC1xA5AgwPUrgQQq z2-i+i2pZ1m#i8P>kAPjQPZI&H_Ue>Z7$hq(@oEs$tVM{}pR-z5jY32ES+wGg&)+P8 z)Wq5k#)J%#5X-l1-0QT13&5AY)DHqDvIbaL*dbcrzWBcZ6Q`9B literal 3738 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 Y&%xux{P{fX*leD#G=rJe8++pZ10iVjT>t<8 diff --git a/Assets/Square150x150Logo.scale-125.png b/Assets/Square150x150Logo.scale-125.png index 80c4772c1e79026344ae19111820d5eac2ea3aa5..cd38ec4e3ad78aa72c7d1441622551045b2d0998 100644 GIT binary patch literal 2458 zcmdT``#Tei7x#8?drN(Xlya#}jO49VZn>-*Gn@Nen?ie7Xfzwf-eUQhyQr8YVIe{h zV)is z%JXF1^9RQhuryB(O`GRM{|>6}y6Fx*4er;t4ZH5vex6XH8HY%;w$=Z#3!khE1VgX) zg!fT15j=V7r#pD3%eFIlJG!$!qgLoceDk?zac*`!VYqQ_a<>Ivm>tI79FEG1GMeKH zAr0YDN&Pibd!$k4lQ1AV)HyiWz35$8tcmaw4l zVn>qeLI9mi@2COnOtoYlzdxUC{Z{<44tcW)Qq@j&+HlQl(CU?QqC=J^R^vj~atn6? z0(btL_ppBLt4wohIW<5gJo;CRwsQ#U4^H|L*WWgK8oIg<)DJGmVD3AvGJe-l8`rT% zHt$0+RT-guCf19Fe1^~@N#~ddUw~VVsCrZcm^}XTni!JVQ$gbwNJ=LGx9No=Uft+H zKXr!{JYwS0wS!H205d-_V#qLZxZ>pFX|%n<2zFE$BqM^K5V_i{!(imkMx95k!u<^7 zBpVtnPh0weOI}Kv?t`xG0(^f+9UmmQ1JGr}2zQ!MH{qIobjoxG2CY1)$L3UF>x^o4 z$E8|l`0K_THrOP&dBPwzC-3f{SdXAwl=?Rh)oZ9a1$oVoDVb zO0x;u7AH{WeZ?|LwNw38ibWP)_!qhBg^R1`NUR6o^8(pz5+yOv;)9+b%2t zG(ea@&Ul>?1v}-s--!JAAnD?^GbtUs@t(f@I-Yc>v?8&3Ei1`;LTiN$&uj*@OvPf_l+Q9tIt|MxD)1#PIV^BoIK|U}sSPBy*afFJY?AyEcsL;i zoin6Kh=j;@v<(A?kxBE6V@?c**(C%s55=7dOtz55xroNz+Rg03$k#-IL4+; zZ{Q!0RUt_PL6+T7oJx~d5iZkwv&Zz+##c9&Otbk^&$9vGS$0KwrOHd8X=9pgJL^Er zMjjPo8j<(L?b2FMS46YT-VpFK?fm2uOe#i6B}Iqn>Z9MdJ=N&h6GsE_P&mO;z}So& z0_{w*axt$6y$xjW@oyG4JTQ^WoSARS4bF`LmRo5fZ`rS^CQMbT4w-9Cq3stg-4M*> zbZAj@ZaWN?BgVV0O;cnH4yu<~5-WF!-pj|y==}|pQSx$f6=3>EiKDb zw;vy|2PNFrpkwPp6@KuJr44M#ae#p={@Thef~eW#oJL7MPmj8cAA4x&9^vEi{j%bE ze%_jyuDO%fHT`B!B97Fj0Qv;MgEHEhT}(gG#tTgV0eZ9S_^t@?>twAB!`AvFI==?m zlOuNVn1&f!pfSjx=Ig6m(*@M3H;7LF{U;}~=LmBDhjlYg>?M6k2@OtZ3sTC-|9B%ZDB7m@BElH|&&+wylvo53L0Phv``ktfXoY1}D~HBcx`gS?K%`;Imzs;7m57JR~vwut)G?QU?6in%8^UX z$l#J%QN;GeYQK708hVy+vE%7EJN3#F?(Q2WVXMd+^kd5lZ|7Sb^0dEgUbgT^07hdD zO&J^(D2wWonDgmykl+QV1ng<*lR z8gd`J;F)5Zw91uHS7wZ6(e*3}!YQG9(l+FC@oe&qWVIS5ckNbHwkGMfghe7|{W})$ zHH~Q{q1ftb*E+B*Mw0}SPt9&;B5Kc3(zj#5Jx7UVvgrHlHuDPuQE_1twjn;4gXRx$ zNMGr6Q|3K2iDCk7-=iiYI);HZEC(EDCzBC7A5r$mTgH%nB{eNE?tnn<^94HG%kr1{ z9!E@mir@O5)T$y_MWbH%3b)4rnv_Fk3Ax+CMnE@V|J!=1z%3Z>S^zd lhW>vZ{*9FX2tXA`afVPdjo~66$+IN|yNbN>>XOg>{{n0+p}_zE literal 5300 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 a(0whIdu%~sX_qx5#nZ=zkE$LzMEwW7f;0XA diff --git a/Assets/Square150x150Logo.scale-150.png b/Assets/Square150x150Logo.scale-150.png index 5e997c795fd39443d1c60130d7c24c31ec2a2fea..f29c87ba75138f4298bb66d691760044efb21716 100644 GIT binary patch literal 2975 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 literal 6423 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 diff --git a/Assets/Square150x150Logo.scale-400.png b/Assets/Square150x150Logo.scale-400.png index 3bc8b95783f5ea92b6cab2238f4167f7f501dfbe..2186ea13009261ee01ef4d417f0c5b39013f3714 100644 GIT binary patch literal 9845 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 literal 18825 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 diff --git a/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png b/Assets/Square44x44Logo.altform-lightunplated_targetsize-16.png index 2f3379d66a0d299579c0238ca1814b39d2045375..c394f499e7b6e438af010460eb3bc02e8cf72880 100644 GIT binary patch delta 479 zcmV<50U-Xt1&sudIDY}ZNklXVzpYQ#eXT4N*x;C1@8nG;akAj znhg*I5xBr!!h)76sHV^4HuM^k@w#PP5&`T5kr%s%YtwWNxHLG5CPz&_{vQ6C$!m+i zhrmt3OPm5nz;>;{A@PSQb`f7no|=%!%TFNVcgl#^F#Z#wRJod^J*Z_4k-OCz&&dwf zDRPo%z_(#dmVY`A8XMXJ&;Y-o-c7on)GMgbAPFQZmiXV|dXM~tUC^Oo8UKQ*61Bu= zr+|6vOHGuO2{v);LhxOAAo|CFY0QJ(q4b~9;0#+Pkga<-JA%0H;HTP9E+=yXE49~Mw-a49$3S+;lk1&gR`Jc8USK&%?NVZ z=sCq*q4D8A!Y%zSvFaNm+6}a8yj?9#Zr?uccsWtsU9_73_H>begHUg V%AcnH4Wj@6002ovPDHLkV1mKS<3#`f delta 661 zcmV;G0&4w@1i=N6IDZ0jNkl=Xu}fIqxT9N$fw=?T|#;0YR^)K(h#_govY}NPpgGx;!)-4QER8W%Jxsc|G;Cf7j}LK;y$t*w1~lF5GEAn-#crK74m7!4 z*wWa5YvDV{W`A>S%hr55jU+-c*GvVi-)gNuz~{yE%&Y;h*(%DsQPp3V%1op4-~rqn zj6qTIXBqa1%yjvgzRJdidU&?C7>SRbK12K7-DP0&9Vecd{fv0xm0bk!%ap0I93(}>I8GHP|y}M^MByr`wt%*XTNtJK7E;6_?B1m z#?G;<14;$nf`I>sVlv0FzaXaBd85^0G0ZE~%q#QAFk?$4jE<$u2}v^on&ylX>5pjH zy1C3Svu)E8lVnjUP0KowVxmCKycv0G=eobo&zDtOYV67t%aMFN0z!}{REo)bwtbyl zeR)+4-G7S>;Y81|KW4xrac-!=mFok@DGFSxWLt=jr$qn#I{yY1`Y9%Q`3}MT00000NkvXXu0mjfQ~XAM diff --git a/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png b/Assets/Square44x44Logo.altform-lightunplated_targetsize-24.png index 0fbedddf80a5c65bc18d24c8ab0d14f6d3406afe..394710310e161fb900ea52a7e42c0f44e73f4106 100644 GIT binary patch delta 659 zcmV;E0&M-O2)_l8IDZ0hNkl^kpWK8F1s(YIO>eLw`S@lf)lcIFSK-6n)6D zFAT%fciO0JBi@MZ7KO0Dz90Ry8^K{Ma^;Uf;QP)~o5k)-z)?TbO^)OjbaT)Ew&W+; zMeP?q5NX%)S&ozNQArjd$>S?4NxTy{c>=zaI;cH`S;hA|J;7suItcXRoKcc_C7EN} zXaF}5Thq>6@P8s$pO*nhuCZV`!B z&oOpFNq(fUnmK^I8B;Oe07x=7jQR>(84Lgb002ovPDHLkV1mANHXZ-~ delta 1025 zcmV+c1pfQK1*-^%O%tj1hsH#))HV$PLI6Q3 zf*9J=hql75w!q3m6aS(wz8HM)!GH9@AS|_OyNhM3Y!IzNDL)TLuqmX4LMa%a6zPM( z@MFMsXFO-td%NB3-hJ{UH+SaTx!-r@n{&^(7F?{Mp>cN1V}Gp`(5r=#Pe7`C^FYb# zTB>sz;+F|6qR~)bG#!RDowMHi%6vfkG~km!9v}e8Ph@<7M6Q5*jJ(w|1m3G(?+>di z74B?nIi8WA&bx7g()vv)QSP?81j{4BR}sUidhe!BcZYUvPfJkm^}kNRZWUtES6)_% z#fuhTZs{BhoPR!Rd^{dHKa69?f5X6;xKBzshi+dHM)vMd;PVs$jvPX5v1KSOD#Wu@ z%kj*LWys2k`_2swn|r|{`Q}>w)DmP*(c5t2Ru$dgTff78IbhElebQ*75Oe8^JWvNn~h$(Zr0Y;Jdfz!uJ}$Q9yFN18y2Uc7OL5nhGki@Xdz09i}A&m z+g(W*JaNsP+kazcFl0;BT6n3EnDV$Y(r}~kTot-K+8G}cE=rZHl>NOa?aM4`JcH_ohlM^NLEvcaG z3h;lJMfhV9;$fc5tE@o&tcOw-vF0obN6vPt|9>==EnPgVM13dwoiSruLh2DxtCAi4 z1A{39i3e&+!r0oR6S$@D87G4bMUN%NC-!Jh z)`y(8T)6gkA2uXzshEz0-UZ#4!&6HfS13gD@-^?8B-S>0GQjthf zvw!wE^X}vU$ZgSZVARy2e*`|50oBCCj%&&Hd-h>tlP_Z(vPoX{zKE9Ac3i#ohmC$q zdt&eN$PjSLYw-Es(h48bPVVQ0t!=wf%5}2EVhRTj9W{|V;0iajly66ZEn^M=TSt3f z2Q`Zv8gAt|?}Q5%*A}i(CB;R!d-tAM%X+DJ>B@*{HiJ_R75w?W8zC%vI;Ep=Cu&&l zzd#Lrm)#o}AO&PvQ@50s8#+UQ?-IQ!J-H(s_>r1^6*qmF`Y572S6q!h>7u5OaA$iU vbJLISWJfREdZ|}@mhra*^xG0n|J(8}rTf52v>Y;C00000NkvXXu0mjfK@j~S diff --git a/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png b/Assets/Square44x44Logo.altform-lightunplated_targetsize-256.png index b91aa0068a5076a73171082aa2126742a7435ece..510fcca96fd25a0d83d1bc1d3c1bd1f6a32d53af 100644 GIT binary patch literal 8755 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 Xy1#1#ZZH%70s(Y1km?^0c2WNa5CMQN literal 13111 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 diff --git a/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png b/Assets/Square44x44Logo.altform-lightunplated_targetsize-32.png index 2462dcc0a0716e690401fee5ad7eac7437dad21f..244b8f9b5a6082e58fbe3ac666e9ca8930221773 100644 GIT binary patch delta 910 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 N002ovPDHLkV1j-ZQmX&} diff --git a/Assets/Square44x44Logo.altform-unplated_targetsize-16.png b/Assets/Square44x44Logo.altform-unplated_targetsize-16.png index 2f3379d66a0d299579c0238ca1814b39d2045375..c394f499e7b6e438af010460eb3bc02e8cf72880 100644 GIT binary patch delta 479 zcmV<50U-Xt1&sudIDY}ZNklXVzpYQ#eXT4N*x;C1@8nG;akAj znhg*I5xBr!!h)76sHV^4HuM^k@w#PP5&`T5kr%s%YtwWNxHLG5CPz&_{vQ6C$!m+i zhrmt3OPm5nz;>;{A@PSQb`f7no|=%!%TFNVcgl#^F#Z#wRJod^J*Z_4k-OCz&&dwf zDRPo%z_(#dmVY`A8XMXJ&;Y-o-c7on)GMgbAPFQZmiXV|dXM~tUC^Oo8UKQ*61Bu= zr+|6vOHGuO2{v);LhxOAAo|CFY0QJ(q4b~9;0#+Pkga<-JA%0H;HTP9E+=yXE49~Mw-a49$3S+;lk1&gR`Jc8USK&%?NVZ z=sCq*q4D8A!Y%zSvFaNm+6}a8yj?9#Zr?uccsWtsU9_73_H>begHUg V%AcnH4Wj@6002ovPDHLkV1mKS<3#`f delta 661 zcmV;G0&4w@1i=N6IDZ0jNkl=Xu}fIqxT9N$fw=?T|#;0YR^)K(h#_govY}NPpgGx;!)-4QER8W%Jxsc|G;Cf7j}LK;y$t*w1~lF5GEAn-#crK74m7!4 z*wWa5YvDV{W`A>S%hr55jU+-c*GvVi-)gNuz~{yE%&Y;h*(%DsQPp3V%1op4-~rqn zj6qTIXBqa1%yjvgzRJdidU&?C7>SRbK12K7-DP0&9Vecd{fv0xm0bk!%ap0I93(}>I8GHP|y}M^MByr`wt%*XTNtJK7E;6_?B1m z#?G;<14;$nf`I>sVlv0FzaXaBd85^0G0ZE~%q#QAFk?$4jE<$u2}v^on&ylX>5pjH zy1C3Svu)E8lVnjUP0KowVxmCKycv0G=eobo&zDtOYV67t%aMFN0z!}{REo)bwtbyl zeR)+4-G7S>;Y81|KW4xrac-!=mFok@DGFSxWLt=jr$qn#I{yY1`Y9%Q`3}MT00000NkvXXu0mjfQ~XAM diff --git a/Assets/Square44x44Logo.altform-unplated_targetsize-256.png b/Assets/Square44x44Logo.altform-unplated_targetsize-256.png index b91aa0068a5076a73171082aa2126742a7435ece..510fcca96fd25a0d83d1bc1d3c1bd1f6a32d53af 100644 GIT binary patch literal 8755 zcmds7_di?j`#wo+wW&>PTBAiHC?Yl$v$bl~eiNfs?U=PUtqP$iT58m)t+lGPRgKnG zGq#ipzWMwc-yhC-&g=E;`<(lJ?(4cvf+13ihKhp<000^tZFOS+01+QS00kMbc=D{m znOIPIYg_mN0PUCmU7!K43P%9oTh~!nHGPi36;QTtYvc@g@#v&K3NWS6gWOac6egLX z3?F-Zh^Zu{gKk5hQ`qiwAe5fX9%b?mrp)0*z*E!T$8 zYt3({s;%F8d+W*CWFx+XLcxu?KQ%+!0brVY(r80T`9KGflPxk@9% z`EG-LIn0Lf>0PC-$IRTUXtM6{{w!SV@udk_`8>ii$Y)cG9dk#&-`F<)Z4fv$B5sXh zn0#tB++#tbW7yyrqtQcJL_Sq~_2^oO#Kr1P(($vxVV_L_y@FB3sF+qxohGlx8zm$E zGQ(nn6;plF#$S%A-n>fBp($Pl8Mq!uG9@Clkvo;9;qu%u>=Wm6*tB_+C@Gry+7b8VIRLDui0}6FP>G`d<$*HHRTlzek6fiGIpih&%qzP*I(XrGZfu^IcA`NdY%!n3 zGERZEA(*Z2g0NWvg8erbTBvL@B)`08h?=iL>E?f?254Fpw#=b5CrOK2l&2YA4)Zz~ zqKx9Z3#OrEI>n@DO#*mJ6Z&)XcGRdC*;!KZW;^LMdq3WK8eghmPYRs!sILx}A5J$k z^-ns>P zH1N~ICCr$um=u@82qPKK&=Bq8n;J!g)Pp-mx^HkQJ0dL z$rft!-*faLp$9^FFAqK36(@?)!D!>rWyvLT4o@h*;h?m_ESgS2K=BsV(hjA^!Hi!? zCsQLyEe4effEScDWs?IgxNWDBY(=X8iuwoHACSIkU)#tE`qTH;4mE6_R*iunV`yK7 z$R9K9&@Iy21y4WAT`vwISr+S>giusc0GLKTNfW2R$S~7A$0O3UBv2pv>LG{Xx4B&L zcmzPg;U^pjZtxli^v_`%u%HZ8%av9X%1yG3*91sDF^#c-SOFKIma|1ZPU|)D@E`HI zKz7aKjPC>EF|lZsK}2zE_?&IhCJ9>eifBVM*OORf-*l6c@Th?R~tAUZGZ}r7&8Fp(}MGzD`i>;+V(NY&vhL4HdV-<5hubzvOZhR zPu!Nq3eA4_x1W@~^;&i$s{*h)DR%s#{pFM4v}Bj$xLJE3TYi@4V24hj%$;7(A@BA9 z$~C6B^$uguzw^Vo2tiJLg?C3F*_!?8d>OG-lum&Z1E{-2>8m6kE@~XR`>*qsBRIv6 z9vc>glM(A()xjw0$s+A4@U+h{aOBvB&SgGZ!r6~h$Cp_F75MgqpTgQm3%Z<&M?=9{ zj2kEm;I6CO#IIn4Yop!e8G)b5ha&cQ{f}c88t66SJb4JKz4DlbVpPZ3VVVA*plpyY z!R=e2vXpIYb`bwXM=zd!0&687F$qR9WCn=*p5FAf6sy?P;-h`L9KKjZ^LR59%qVhq z)a6#a+Mk1xtARL$LSEjvR~`)b{n^3rP#bjzrXi_78Yr>9F?x15walff`nLK~+VGq` zDO3fG(=v+7zwEKYyP7SE71M6@1+H=0@;rw69R}LRefJcitISt|PlCG>$Qy&Hzh7OR zZgk&cUJ0z``UWnr*(KrD*tCveH@fE)kzVqzHo|?v6#DnSb=cW2%cEnf8mwZzyru~l z(%j|9q(@oRq{c_4-B94iZ5+SY5Zqohz5v?d$87c>@(lMj=<2Q5ZMSMV>%ABUR$)8d z4}zYz_{vjmzcrTi#k|M5S=sJpj zC9J$K;rLx=4@ytU-sR=^T_aMFT_7~3`ETyVrM!3A2W@dBeom`-6WJ5DYl>~?RQYB8 z*72KB!7z^?+JU;7Wv;M;FPxmS&3vG-SZK{!46m1CSMYL+;*em$k+;McERtLnx%8M< z7sI9CA2?_oc2pD!Qa;>j7~zWyoSKaESeO|M_ooKP_toYEs*}2I4lE*hZf4RtUsTGD z=8&0}-2{v)Qul*7hsp9A} zB3Sv#0P+WWBVsZZYW0aJQ>b--Ec8$>q9c@HH>2--jatjW_p!spmtcIQ9^mKnJ4}A_ zb#O9IH2Fd^}vWE_Gefm!!2 zq)V>;(hL6Qod$-hmgX<-a8*^td615LqS>&4(Eap|(W+ODc<+ko-eg-Pku|LP;8px# z(~+nA#lolo_iondCwUjnSvgo2#YRo=ztA*rH>j-Ux}C(sohnTDt~C#w?3V`2<|lW& z1k$cRGq9*|LEJz5n0$7$hBe~pB|+jYs4L`kaA-#xo&Ozx%TAxlx3l7t5GH~=feG?e z!L#fB{kN2YrcCo%JEM`0H1+V^9F=T2G;$_?BC~_`3sqX5ChQr3RB($32URl5dHx>b znIjZ5pB12;c)43DNaKDhh3S>9(6^J3G|Tx((V1(eFyY#w1(13uKD?f{%)|1(&Gy8! zy4e!IkEByw4s{1)C$q!SmfR8*tOUx>o&E=JCVs)yj^AK#$+xtVFi=7mPjblvdz3+m z>pnb4)JMr+fY${(U@RxDA-dC7Hga$8{tdeZY=n6Qs|w!hT^+B5-{}t;y!OLHzj--H zV7Gm&LAg)QGk<;eoz*TdXzaN-tQtVLKTjYtH|U4eYtF)wZV zkfBD7Yn5TVi3hKK@Z#9_-lFyz9jmXut+q-)jw`PgC;u)=%X}aeofPqxjQV9wi265) zpZSzR;nO(k_ZQch%LNgdx71uEaTGmdh#V0YM5>r&!6sv~H-Mr0nJ4wt{$B<7;Q{DI zZk^&AA)aE5uu?gcAiaM7Osz$u*_(I3Qu4EJ=@)sPCmY=lCJyLB$Zs!$KXuQMl`H#c z?+Ye{UkO*Try1?gE{Rh$qjdWP6ZPN zBN8!qLuKpJy?C4f6}zCHG~U+sd_@=BO+M$h5(^p6i~aM>`xia$O@(uHt<&GHe^hkJ zj&1dt)pflGFb|=^GVhJyA{;aULp~z5o6oQ25uImu(&d$=-zDGN{l~QAApO;@UsEU* zDIeZOMolWW-jYQp*lxDLEB?7pDtWmz{qDazLsC`rKtWSQWuC^dc3&u1O$}r7y^get zh-oavHZBP%-S@|}1n!y{29Y0-DyJt!s_5`p=P^HjTw#)`2-RNWn$?$Qo5&}_{;U%n z9aPBy*S7T2s+!zeKVwT{@(xA_5IlA5xA&gFKgH=BaOLw-qWjCRPHyI}U0Ig}JD4au ziEHO{{3KcM(O$*f%GkNj)P7wN9nKt_o^dY^ejc#B`6P01l?3x>%*F2b81_zzRPs&x z=wLgC6X^N;6ef8LdxKid`t>gza@@w~f7_nEqU9u{64>h2=7)R_d4^+hh*2%#Ua#0d zfnyJyP!0jmsyroz8cdbd|dXnh#sSyOOWmgPRFpWQr+Ob`{&+?k8N zdvv{~02%AHXywu$Ha|->tyBGPvs~d8&$!pSn&nsy6gY7T*ypAv+fQDH#tl&RYVB-Sbrei%x>~j8k;U=5Eqbolm@cxCe~XLi;&z{Fq%IaGz%G zrf~k=BH_;Snm{l$>=u{>M9HZ&Y#F`r@f1o*GW zB-PgJ_Sh5rJ%pl-e$%H$Zh0pqR`lL^_Z(0jbn7*a};_qY_q2;lq!73f8Nnz;|EFm+o%_al_UA5nIaADR+oZ-~C%yWYhVu z`y8l#=qH)zrVY-5G_Rd~kL$>)4j_tTniXWh&_x`)`r=21&r6c=5;ic_G=B zladLoZ*p2Gz2Nke`lc^#g-uOOW`{d3{3HpuDqLM$@bj0u787z6Y3m=KR&SD~$KI$E zKfg9T$I@}R+Lzup&8(6*6PeK7UQ==p!Auv?7`)N{IVMzpvHG*kV%`z7-j(kGJ@xTl z-?`&|3#CJgQ=V$bHjH^)JO7DU#KnSR!gas#TG-gz9D!UF3a?J zoCQ*S%`*e;M-kM$?EfyVPA*pAHHTqnOGQ6&mIw4|bY6a4RmFT#V_0RRdPQv-%Gs6> z>_2f%nQtm3_4MMA8y9RG4oMD0??wuyZNY-zJg+heTu@pQ&jpKC)sv~w|Nm}G_i;Gf zMLOy~lvd`+_KzLqiOgVSD-)pNRgYu7knMyfvD>xNTdKY>*y4ar*HNC7_7Y323+qAycf%Da<>M8M+WZJrL#hu56S*N{DS;NkI;*s>p#sX0Ixk=XhuQc_o1-|h;uDnWS9qB zBW!q%r~JM*p(ZQr+Z?U?!Hz*jfnFF_f>&C1GjGPvm(+mNE&`fGM^h^+ZD7diVkFg3 zQSq?~r1Lzd-=?g zzz_BjT!}5A&mR0H{JrwoGvzlm)Noes>^?`8#-=Yiw7mq-9+Y_ZXQX|-8!vl9(kiZ<_5+P^@gN;1_BP8 zyb?)&%Zfsz2amyvnidM?55q1@&VCN2mhN^DIgUEj7_4HXKx2}(fO|-Nj5Jma-%bH< zBk42*y|?1bVafjMU*8i-eb&b-zj8zRvLJxL(+*|u*l_xquVXS_W%Q`4S8w!glNgh$ zYo7Qa1;-WZbQA0x9tvWBffrj+cKG6K>g3iyMIP}$sP?jLy!`|T)g}L|&xD8vDk)2< zSL6h_dXOVV zn#S}z|40X7SPmE-Xo;PjdyL54za!*2wlUa$(YJpRlh_Rl{X-_1DN%*fNnrUX_$Urs zV{fOu?W%HZzP}N;t zlo*Tok#?#TJ&2hr+F38LZ|*rYfh_1U=9B$A%UnqP6}uA~GPry0R=N3LDgW6|`U#2F z?~h%7MgD#-p3yWE0}0*aqkKgv!G>u4R~wX^wh=gi2&GoxtSq5W+u?s=5_Q$uX|4}^ z*pOU;L(@ikt4N4&-beY}s17GLTo$>(M1dfwJ$3RZc!cY)G>IZTu2uUI;v}6GLiF(! zc&NUHoh~GBh#&ZA7E+Q6JV7jz)tX#0r~Jo`+jz+y8ja~SF>~5${dbU`y@8e3)!`fp zadP`{E+Sdd-u1DzoW+zRZZ-Pj5tm5O@8yl@;I4Oo>vehP$3Kq&(V0cvNc^!$vot7H ze58eR|^!hLf-&JGu5rK79Is4Stda;E2Z_d5*deg>8Hwix52OJ`hV@eNK zlmShy-Nt82uK>9_wK0e*OcV+eWh7C2Nl+z1WcyNOr`>a&Mr8q3DyJt&qv6TYoiB@n z5(Jzeoa%Y_Dtspe#HpiTRB5z}pQOgw-QL~QkKvCb63BP-cMo#4cz1uU`7K~rTP`+} zwc8F-tjW#INMU>y!+R<=?${g{cxVPup>uSrmziw5?O%G6$eV-0UNyy1_4HU@ygNq5 zQhfhr(OVS1&-9z-bl=Sy$P+)QNq9X4R;YD}Ir&N?L_}v6(quo5B-u(Pc7}$F%%R2Pu6co(^;5?|m7IBbFJ#3Vq<2;Xv>YxPxM(23wdGR#o}}o4mlf}5^rt`cXH>S; zIf#q3@O8j6Be*S^3tWbSYG*!;x3ns+w#sI$hzPDqSFkPLvQFMKo*nHWC z(h=>F*>GXEk_q)y65$F84^O8TF%mDg&6nR5mi0I`6}gLkcdO3-0h=3DQRqD+z@i8r z3eQ>}S~W9#MC!hqmB3(hlHJ9*f3REE853gLc-yp%B08DL?ie}|>{ghh zkohX(1xIB`>r3*VTVEmsRAFvp4_u$`-{Z3(91R-;-1#bX6=S4otU@eGCB z@?R5+JeI7))L8lP#==8!B;2K^_0?)b&3G8+EdcqfNPJ}j}^7Q7_4`K*7;>%ZF1 zEEv8Q&gz9#v9JLO>a|E65uv^e-!GQ8Lg)NM|A z+^*-MTksv&sryr8U+GQ$;!~`f4yMfsMDI8SnU$rgeVx4Y?yydt zEli(r<0Jgy+zENsQH}C>CxBAp9Ok2;lR(0k{=L!@*6H*ZDd)Lt&yh4E8Z4^|~xv{>f+@kjKjrjlQRAvS)w>$Ou?eC?y zN~;B-Y9cH_({qInR>5O5uym6D9-cz52gF@J6M;Ge!3UPIn0HXk1{Yltl86T?aRj3(AMiLO z8h=d8395>cDAZX*=yQNU%doPM_14e_T^|`0u-%9-Vfrb{m}>eI!1Uz!BHgC^_IlY@ zheRf#uyFhB9=`k%*-XT)ka8}sxX;w+1$>A~mx-Lh`dKgiOJT2U1T|V}94D(csFx*& zuqXZBi`CxWBc?$1Z$?ShdX|bcQkXFeE!OkwjfMFq+kMFPc&RM|kcID(k$XsHsW%vSIcYa<&biD? zeJG|5f4rnLIF~C()Q|?V4IC4y+xB4AGsfjCZC~cnOGW>uEHMp|na?jXGZEZ@K#)lN z7C2qY6(b~@*~pHp3;H&P78FYcyxjv7X2*&86H=R56M)B^Keo=B@xg2}>j0hB+k{J7 zcmJmYKCOmqAXj(y3p5rKR)zCU)E{5bqY?my3WzE$wk!ks5!x%pbCxSEYW#`^C^iIl zTX&~@qGlU-OO4JVi(usa2<6wI5(0*V6ply#O~=9dKv>I%0SQD-$^-_27kd#C6E=%m z91P|IM1hAWG;Lj|kS(?j%;o-1grb-*jPEw&*n6yQr%yz*cY7joGdlD*EPIKHQYo?4 zPIieN>XJGAv1x#kI@CpM_G@X#VHs;RPABOz6}r3m+M37#xEvc#G!gbA5y)om)+;hZjmz#miYI2#Q2BF`>odjKptJ9RRj99tpNT<%<56x* zt|m`B1;!bbmL=sl_q^VARm9A4K2wM&NkaJH(1iZ*_y!ro0gHP+m#=3pD^HJ{WHYok zs)FdktXHR*zFG$nj^SGLVo!+q_e+5Fda)W+$FB<#Em5I%9u)uV{jO2zzWMMR`i*MH zC|_IR34r6l^+^#u0L*{D!+-Dyv(lwkkYN*jbW}v2uM7zpm`lAiIJ_jjeT1sl(k-wK zk($^`O3@!8CHfLtW>5p)B2KE&`JswJP4YcebqbruQzzDq!X$~876mFGn+#Ef_wf&D z7IOr{{z+SW|JLAN?0n+J!iuIcrC8|kQ8tiFO4=qhb^;Xq{$K zhK(!=&C~*U1+0^4n%_(CI=iWf5_Kq$i`&m(+YJ1dV!u9n~wxI2Nz4gcJIGl~-@R3da}mr4LWr2CQpWpvh6 zQ+suD<;T`g^;@qo*4voR1j&g{>vHs2D$Y+!8L|x|m&h5SuB%s9r4G-VC!53qu6T>i Xy1#1#ZZH%70s(Y1km?^0c2WNa5CMQN literal 13111 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 diff --git a/Assets/Square44x44Logo.altform-unplated_targetsize-32.png b/Assets/Square44x44Logo.altform-unplated_targetsize-32.png index 2462dcc0a0716e690401fee5ad7eac7437dad21f..244b8f9b5a6082e58fbe3ac666e9ca8930221773 100644 GIT binary patch delta 910 zcmV;919AL@3%Cc6IDZ3bNklKtb z8}Q_V$E$wpU)9xBuf13Z`MzHTC*$$>D&$;LPvI4bMB<0$SAPayXIKkAhp)hW_`P%G z_guzYF1L}SZ4|GOXQYT4bkmV|rMVap?m__kR>uWbe^0N&jNR$&uK_eTL@0Jff77b62uAPxYc z3=rKDD1O}KGk*?%LcNF#0Ua8M9&E8N5OHn&ZNR!vD{=+GY=M*8h@44Zj@J0ZwgW?5z|3LCBw{v z)F1#B*n$;^669t$@9=REvy;_5NnC@&ZJ_|ThT|d}OOVBy!!Rtz>tuK+3IIr6>wI#4 zK$gKp0o!+Y1it05#P!Uw6vt8&I)EX7rC~o}aap}AIqTpO1z<|R_6_}r?3-Voq1r%$ zQHvA+!hctJt+|~i;lH5&06aEeC33dI=PP^URn)|6vY6_iBzNA?4M|_|P*&hJn`NFO zQ5ul5^cJiD0Md-yCwGy<=Yas28Ir4uB$0{>5tE_74u*MMRe)*)z&&`W1z`&_-xIaC z0wAJW?i9yWen$YDGXYS=dvrYstI?+|fU79>C4Z<3=_mR<4E+S|lG~|(;-?YIkhRz| znyrGkC&-)F)`o$=_YGn|0Aw+4I@f{_{x)2b} z=!VCR;v*8o@Dt;!mf}fO+}M_jPHfUKiLbiKO(5jQrj+qn(Rl&mQA5(O``k(jgmhL( z4S&St;Is84l)eu>_BX`?Aez)yumK)5`M3aVA6xc`kmMlOqnM?YbKM1zI-n35u^C){ zMx895?DSSY{TKYv(BStHv6EaUA_Gt$4od3`%jdTeu7mGV>!ZQ_Gkya| kxqu$S=iy7L*|E}3`V>l{fJ^=wGLI)pdq7frrlE|`{ zF*BZWM@`VuA5EDvP1BDCo2Gt{&Ge&52SgEs5lT@Jh=;^5WUCf;6?6e#zTAJ!HW6g)6uSN1g^dDEr0h zu+^r>_fj%43%P91E-4}gbAw61BXh)p^3=-ne336 z8Mpd8t(CUgiwW^gMvaQ_M6Q8so~WqSw zLonh#$^TEtxDamPnhew$=**up+bs?`fxbDpon}tB+1&%Lz50sJPtg5a51cslo&HSR zG6!}`jP!$pcf1M^7o6sDA~&)K$+igMrhiXxLu{mTo1&U8aw5_M7(FLkVgliY%s{C_ zXKdA4n$NdFMp}~FS>S7{`4Zu9DDY$Rd8anwmKAB7RkAYuEQ;9i)*H~=+$uyuTzs5&as~3~UKLPQ zkk^9QYZ2aNYlCV}H$r#M?_WpMRcm&@c?QE72*i!OlhkVD>Mj4u$gB6_hL zKS$V(*?M?33UYg8b)6eIqJ+4#2Y+1vo@2#%y>0>sl&xY~z5`c9B?y`E^{$)hRe?#Sn3mh^cD28M;Fhnn9HI0$ z9P&1=X*uCY;r@2?#Oo4fuRw)~0NSEeHTCKx;^=Xg74D7_1!wL`?oj}NJXnFHnFTAF zUod11mu6#uat}Q@`qGS`zF>vjSe)*q;*18WFTcBKbQN!D$eP*w1%;zVqCd z>+9~l&w1ZF^^ZO9>^bi}&v~Bj_nvd_Ip>~b%ZilV-rj0-C4a@4=y>!h`g(bJc~@M! zTrMa7-RLOvceE4ThaRBqpT0PDEgs!FS z8DiH_C(T*$myE{` z8p8dXP(K}QBY)?!kT{tX%-RD_a)*+@rzYeY#gI~uq(-2bfcpvja%s?PqFk6Zi?4|Q zUHLabYtVK`JXaFB1R=)(a@3&zSp3Zou>v|0t(2cG9%8gO@XOJp=Oiy)uTcVD;o(|9 zqQhyECLx6BjCRr`eXkcY}l{c*z_&POv6$Bw8H_ z({k4;#GQ*a1#d#5OcsIDsChyn!DtdKqL?;;*MB_0OS(m1F^!jCA3#T<<6;{0kmM3- zdSE@}L$nJl!^>_>c3Mss4yC`)v#zY|XH%A;aGAo+0RZGB;JAkQ!nd9Gp7?%~K?3($ z1_|7286=RlFb@nxGjTpz#Ri*xtwvu#*HIp3%%KV71K!&|mWcH>VR`Zr<{hwTX%jmm z<9~yD?i|HB5%>}|BR%uAqp1==w4bQ<@E@;za@OZVs)R;e?i*>^@)5D;dG4_@>Ve;A z^pM3H-(T?6o%*9lkdYGFp89+!IH8AF!=W0Eo4bZV??n%NwjJReNEx*IIp__ zJ`gJj>a{g(R1bX5kK;H`iEvB=uMG~S27mn|>65t588EfP`wDF@fkX?rPW3&+$j4l$ zFA}`Aflcm%nC{A!I0CB2cO$;*X}by#-xNt=oBs4oWCYnmFnt?(Aq2jg-wO@k8$&xO z%Um+d0n`hmu^19+N1Kj30Vi)#aRkRlvCY`Eac`T54fuZs0(l=#5)G8+x*PjeP=B9K zPJq({nx>hM@(*zciuigEDOM7gDtsy^pF(fmLBNsFw(eR2_@!xq4*?8FtOP@Ilpl57 zh1a}5qxurv=a^MrE(QdAAfcY9fyB3#Hr8WS9ZWY2B=|rai3W03>bXqv zw*t~<@DU|qGmAUO&`D7`1PG{O)_?bOep@m+gOKY=)GS>nRsz4eGk_hG=a(2SX|tlC zx)r@>!Db%QjK|}cYMqQ=H-%^IkXTM=ZWLZ{OOO`yQ}TRg#U$~5D0QrZsfUOTytNI~ z$>w9I_7$HQLev3R3&+vug);Z=Ho>8tulx_qsoXpiEe&k|0000E0Mqs__4OuPC`gh9x@4u?C?lJoPtUX zHmMC5Oe9DQ#x{6&ZqK>qn%%pzyLTUr)K6OP^*wWEzHjE-nSV29R)qwhq@*NyP$WIW zfh`A3vuliHG7xq&P0Z`)OGT^!N8e-?eYxY{z-{vi&sN z9vqAyf-lv4BBRi?&EDkR{YKORh&%)Sd+d#g;Tj^+tMY#YbFyYZQc_ZYjo*7w9-eI1 zKlA2h!+(lrmVwVFP{=tr+;S8?Kh++(a?WQ6q3tYmZB?4wRe!TAfZT?9J{WBiYcjANz@ZN#L(9?4%xD3A7KEeKqtNs%I z6+A#d#?;=QK?w5W#ytSTLql-kKivrzV1VkTHmI$Ef!jfLbf?D$eYT(DWu$X1O~tZBx;ft@}N7 zRhRIcC(3)0?y@r44t*NvV}vDu#spAc24483 zY>}l|X2tTnvHy(*5+y=Lo{+y{Idq)sgn#2FC9{)&myq+H_t)0;>jF}Qtx%IpnTn$5 zQzd=-BT*tkkvH0=5pVzJ85kZmMcv4iIOElBT>uVA9!O@yrMXXroaf^e5sth8nqKps zehQ9!^huCS=S%`Nm6c_PVUb7klo^W`&JRl>RuNXn-xu*isQ}PK9&uNFcX%FhpMPPt zXV@QvIoY!#l8IJCG~@|cvuDDThbP13-YbCy3^ROpAWy|LWTEWAIoVnIs%r&SMMO)U z@IYD`WM|C`76AI@Z=p(iSdfGSS+jx$B`hL|h=sg?Su>`?heun34agk@FuUvprf@h~ zm_-97LslgFNaVJ##l zCx=!Vqli=Or=|Bf)7qMl`{;f^@J}%!4HD3H{3I;GkeX5-p~zEug?|W8X3i?Cil7W$ z>!&B=m1s&Eedz*JjeACfs2ojs-^bm%BYM(l(SrE_yE23P^Q-f9pO1#TRS~pyv>rPl zO+g9)LrEHI8WTW=3l3NL@6e52H5aCg*{qqFx|;-$$EnhR4W_1~L`~j`hy#aP;Mj@J zrOz>@RW>$^9*B$uJAZVzK>2yR3`+X^<6w)PbfVM2n{K&W8^~0XJ22`%-i&TlLFmB>z5u0(HxH~ceAAa11VZILr z20~@8S)-?}x+6#cp|5Ak>-I-_w@@0-{1{a@ZR!*~>GZz>)qnl3vmMZL@v_v8zJDf7 z9r=s@y#N8Nt*`lBEZ+7DE_{EW{JgmWs`&L?Z|aK+vtcglMy;xppCQHP7PG;sn>goQXZ5whzI&qiL*_p7j_il3S)d{bA8oO zvqidNp<^978GpeCF&blOvNDu0qtCLXPr*tY4Lab61{7;iy5!^5W04CpwzH|OszGK7 zx#gl`9Xc5ipuJ)aGyFjd4Smwn$H9X6j|Cht2{Gypy;u6ij+oA2jCBfE_*=IIA`{1+ zA0=P?$5180?iuM=hfYS&|0-lJwm*oFA;yfCxBuD#*?&Ng&y(AP>UjeZH@$SMQ&d!X zQsW9v(tK{zOkx1dHo&#Q=K5-R80rU0yLOMKrpM*lnvbvlX3Eb~iaUOqGKhAX=M|r? z(bKS3K9`zcIql$fS5Zt`Nrwxg`FXJ^Q6nNW(S-|WBdxIhixkOYGQtF^Ixt1-OHI%`)>dM N002ovPDHLkV1j-ZQmX&} diff --git a/Assets/Square44x44Logo.scale-100.png b/Assets/Square44x44Logo.scale-100.png index a81802a376a07785eca397c54d9795f5852fa5a6..dea410dd8db86b11a9ef88c1bff175bbad67a951 100644 GIT binary patch delta 1011 zcmV&PiK<1LYKxUeB^I<^@qQ!{Y$R3|1VOw*#4D9)Nkp4=#`kkZZtt9X?wrf0 zR{!MBf9`+&$2Vv0oqNvYgoc^q3^jnQi~?s{MuD>}fq|3B*uN652uBo4*E}NF>$+Q4egHTw}$Q;hThg?E;?8`~r1Ziq6MZLyac< zZ7Mf=z%;6*$xgQ)2wB;L+fQsn<15Crk~4taW79v#3O}<61}bS-#h13tt%14s&a%oI zfY@uB>l8l6T7PPtx6KQ557THL(1k#^1j$#hM}p+~0t42DiPNCb>F2tYxsV@p*mXa6 z!BVT?XW2-7U|@>G=oeGtVv23{f=2UzNqfNXB~r`=*bZW%YW$X%1J=M4pNux7t_7T8 zVT#2x&|?^`Af{VW>IrqOVNZI%c9OT+;c6uQIKCcRV1IcjjCcCD-b90>W~G}k&AK|w zeZ?%+&8d}j!b*=4dRH^luCnQtsMI;tN{8IgwxU^Cr&NUk0MGH7P|*3vk;Kn>p?ek=tw*HQ!+i(Ok#e7qlO}VGYbz zUdbvJP=C09OE+*YAEXOSTEfb=(@o6^G+zOabIg6l->>d_J75JZiJr+pMSajfwf~>} z@KDLf0p@`#!Avvc_Hd8Cux-dg6;dY@sh##JM`T}ppW0ozeA5cw%Nl{HgKPuKA-(`r!_EzI%%{+c260= zp1>@^mNOrB%@vdP2z#BmResRKzQUXf0^CipI)?44P>cqzcET7lk%?Y$=aBXRP(DjS}M=o$2a=~VH@fko7R5ba7$adPHc9<+~JM$Uft hUzJhdY|F3%{th0i)R5TAGR^=1002ovPDHLkV1iwy^QHg* delta 2125 zcmV-T2(tH`2<{M&IDZHrNkle3k7RUc@PcREfK&NDh1IU;~))=BuP~tME zMS#Q*2oq*N6ly+M<)`^F^I>YfSyMGtQ#dSIV0R25K|+Zeph78IP?SIdNf=B54k#fL zGLrP`x6C=WyF(JXJ82;})^MoqN8Y>do?oAP?pdyipP8sXHGe>?atC;% zUAZzHj-2g6dCGM#MFQ@D9m)A4 zY(2NA^u%$Q}0JoVge2xt`ooy z&&eU+Zk%au!LcvDM*H~-Fik)GNKL$_Y3c$?veHYIuU=DCvH9RF11>K6T?RSf217S8 z{C<;+Y{Mkj2c}NJyhk6woQGy1EiJWg{1?Yh5R{9l_fHmo=ggjg14Af2 zIDH11s(%XQG-syI^lc;^#ns+78*dtT#fsmj`q1+_SvHelEz5$eD40L*F{~&p8UPv+ zKl!t!Y6I@2rljDRC!auW&P-Hn+kwvx9uX@fAZTvOa$YB0i#?vdU%4q@)9G2K>Bd~P zs!bb;EjNag=ggWB7M8`LO&DhwVV|Xp9*x;ErhlQKu?ZJ1T@rKh#?@3~uHWCgmj3w5 zj|MJTu`0`PrFFcq9EbeYH1PykcINbKy!YWAvW!gbF#Cr0Bo1g1ogk}_S>#<3gt{T3!q`X!c?EXLhq$G{*9R@KxB zJb(7#;Yi)F<9O@s?O43qzBrGQ-&gk>2^bMc&;-t>q$M5-a3r2B90 zc)b^60#=~s$*M9XkQ6N3mp%dcdAS3|Fn=UlhAmB(u=WafGE|6SQ)sQUv$(Vs5xf5N-6{(gpSaw!M#G!0iUeWDjX-Wprb$>&= zbNs7%c=~+yM%a*KWjui0AM7PvREN+HsC&S1$2Cz4xK_- zU^j=u1>(r6Y*`+{6?BaX+p2Ac3sOrjpBu_+& zX!7-mQ^E}$m~ku>q6gcd*?Q3V$_MySs6@ zqa!M?h_jW|C|R-)=P&#V33rXc=+Vj8w8e|xlolg5N^<%B@@1ien*fm~Wn7LK7_QVN zQM%;Vc&(=gry5R+`XQY3Ww65M@K}Bxws^L|N6~`+&mobY@sdA8g-;?G+S(`_i)U>oXe?lqJQ+|W^v{+sl41r zp>6ie=}1dW5tikouw>zLeYHU}VQ3Cpo}hcMr8};4ia9yf>k9iXt%2csS(&Ka{r-SLW;nuQxu>q$QA@3oDfR`=8R+*E?qV*G3wxu@ z5fzy0(vsz?)=QH7XMgT~Y1Odr(*rn3ik2*TKB61AQR1nvG+B6CbuBJ^_Z@c|qO&F) zWL$TBl{a!NJR~rSF2}F;Sv^mysy>%dsFe{**?77MGpA)EmE7ZoNy!&^nZrrvzq@dz zsRbPrCV68DU@BexmhXT;x`yHz5}11|Y7o~_iCN7JBj4k;o_{+9?t;TLq1?|6&WKk! zJFhpP6vntnVpvVnK}~1_jbH2WM(^i_1jZ`;y->tmL?BwpR?@F%KJS=Bo0zIwTCvAuCrwo;SMy{`>u^wYbrP2`hygfYVSW9 z3(L!wK^#>%Yxo3*r;ue!EHOiLbqzdu;te%wRD9^D3SuAF*0owL)-PSTa)ay0*_Q46 zZy&dYWZRZ4t#skf?wg%!IP(8@0)M;Z4)93J&kp#%)T*|Y{sA-Q00000NkvXXu0mjf DiKrVf diff --git a/Assets/Square44x44Logo.scale-125.png b/Assets/Square44x44Logo.scale-125.png index a9f612f253678d5259baa5d094c3660db372d6ae..7702f1a7cdc23886bf2e0a7222c892bfeda69183 100644 GIT binary patch delta 1210 zcmV;r1V#I@7~%<#IDZ6`NklTC_z)Eq)XPi;LR0 z(BSt*TnK8U7%SpuH=5KcMN1O=LXARDTxd0jiiz2{5x*9qRnRW7aZ%{TZ%|uJ`a3W4 z!t2ewbLQQ-YM;D69A@r4XZ~~knS1BW%uRXl0i*nVC-62=5`R~$D2Xdpl*GB9v$L~; zVgXi#jmP?7o65?{nk{XrDYi=$yMwi1Cu#E_jhwv2b#-<1$2L%1mC0m23Hg>%G+Ual zp-qLUe=A0tt%z=-z73I&d~)*=M^YazA z*CH$%qENhvbAQ158T`Le{Vq{*f&XK99Gm9JVZ(1ejlIP2P8{H`JCtgz4^I@}mC5Lk z4~O@J!sAmXGbLq>FINr-+la7TzC5Y?6p+Yj1O{mcOiP%+NgRoPL$RCWZ&5Cz>3<0RIaH6u@oVC$Y))+ilOp_%Suii~5I0FnBsYR2w1km(uElFG zyNe4B!USL2oSL})E(sZdYl80Wi!Zi13KR5Nc;`bCw}_*g%bLZxMn#Chzrn0 z?nfGHd$KGder%IsfNW_T;$)w>o-(nxzp&w2FLK1kBXZawArmkx3L~)HBJf}#eBR5i0uFPWkP{g9h!z6h?{hMXwquihc)~D$C=pocBY!ZB zkB_Me%Y*PXU`vw{Bo-%!IM^3S+dE>UPvgYm7%_R@mYI(vqhrFHKZJwQ1lWH*B=z}xQqFum&%vb7Zf`f#Xa)h2h=OEi9z?MG%C#- zX)~De74|!JPL@>IKfZYhQrn3$iZjnBjv8}qbC%+4S~-OAzdUA03ng*Iijufu#Ro0! YU!IaPmP@6G1^@s607*qoM6N<$f{+nHxc~qF delta 3097 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 O0000IBS#drrllM?W^NWQ>5yF89Lv=d zO;oPP@fw*cuY@eE=wZmPTn{cP)>jJt(g|6n{4h-7USBRR8MjaIq(?>y?r)?C zB%LFueT1km%cX?5neaxe9nnG+_>X+9|NH+q8W8R0$1b{TFe0&wxbl#jg2q+&KI}5MK7;QX7CNe0NMCBZ)NZ;o-5DVYDjbq7T};v7eKLnreZ{ zjdb~3l!%cvEWw&J{#4(&BZ&uXgJcLo4hFZC5T|5S^y-G_+SL@)kJK#RsrZL0uAfc0 zdJl?i+66ZQj3>5p7g}y&Z6t~E?iv}rVpm9*qCk3tb8(2%&y2vztdtyvBwoWZ3u{cM zY@8R^5P*=hHD|F=%ZWyTvJN6Iie^b2%IrfnTk(DM?z2;ETf3Uqdx|x4YJFXcQl9tze2ljiE7EHa9$3pWSbT z=A37@5PxO(gOYYr4Kr@xk)7%(Z(T_xVXVKUdwd(&%t6k3P=X0Ru9X2VEI~x`b30$p z0*GB9*3@4{3QX&X6m^2>>Db%3+AO3&wx=7NiiZ$6ag|sq!s{=mGV@ukp_G`5YIM+{ z4+@`YpsP)eR*<~gBnmGz*ioK8^KEj zO=6ZljfzaM3r04Tzw_oc&gfa1NM#O>v$SZkub1YK{O<;7gyB5Z{tqzRwGzg_C`Y6% zGPNG&F8JJJ$>FP-*moUn=Fiw@qkeeI;Rz3sqWIDMG*ZB?*b)Zx%gW{VZC1(44>sfO z^gNb!md*rw=O$L66>Vb*jM2_`QB%>$?CkDveARC-uiGXLz!@LD?p4_641k2T0)5U$ z?1-R)8+Gyt72Y+uh_+;k?-oWS6pbIPCs|mt7u{Q3TGcDPyfiC+U;Z}GGqk4A?Jf{# z9Yl#pT1OhwB1`%nyR*BsT0(gFqM|3lGo8$(XB1T}jPQd z-oC5359Rx1zl|7;17|Kw?#mRH;0}=e2A(4LS#@#3qOhozM@@xu`gwim+s`z4{Yg5g&~92ezI!v^voE$9Ut-ywt6H;0 zI2)AR_oQ&HQ>J+}N!^B%-<+&s@(7>6k`8VP#_`G%j_ETF5xWV&&1DG|UfT}SBmQI` z!#pqXQ|%-FH{3r>qb(E5oubQYZDz~G?>KaXiQtMl6^?-K)A5@>!hGw#U+D@Kau-;b zBkal0!YvaE0jT}#^7iO!&9&l3{I{bsN_Hb@k**@=har6IqUrb{J+3)o9$7y;EJtp5 zq>%4FbVil&shtyseDFEde=_H;RCA!?HKpnsT6w@T=IQHT?@{_ngGkQQQJwy%^?W>v z$fw-PG$Y-wKZ^#pr*FYWtGZ8~cSL+8WWSm}o^s%IkoQ-)L*H`|zs*T+oNfwQy^C^p zdmQ@ggqLN}%#(j&FE6=flK8ItTsoXrVXpEL>0A59?b<3Oy$kll@ZFu6*S$3ZK-12K z5=>lzC2EBFn$9XsJM1VRQq^?mX(iJ#TI7k3cSWb_&fOgU7@d%pbh-)j@q~`S9O1DR z5463~YO|0Ze(zzi7$5agc#C6&JF=y;pH4%WXuY^IhuQLRrXC(J`2_vae@&f-P!DvP zwg6Hq0N(!bn-VZjiSV4J)v)$csmo=TjB{#*3vcU2!MVF~drj*=xeQK5oaBgRop7P$ zov>jHP&~rk7b~Tw+%o(@26fMZKwizVU5HwG{tcgS_)*(wRR(dPbV00I*JPGcUdV}= zzns1{Ea-hTN{>zSmY`n+Z0#XgWa2;?-@?jhHhCWLKU*MRPGr`nd5 zpZ9>#@Wi5%u%|Mm3(jf{0hKcJc@_N>OBpS=m*&BUHWoiqNc@pdRb{NkO_w+W zfRdFyjnyRi02v35l3T5*6TO56fijD?Zn!B{T2uORgLW4-$^wG1!aU5|FBR?P8P65? zvlbf~_obdHKDqvl5Q-4pr>e#ImtHjkqwbTh2JB&K8s6-O^dkKT+DxTh16j5>kFF}T zrA@rWYVBagKt&cI9t9GD1-*)ICku$i1qy$~NcXz!(p+k$-|$R0)g*A)vUYba7Q?f-t} b|Jvzq0M_o^>aj)Q!ht{7!%%khP=DgTkmujA delta 5250 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 diff --git a/Assets/Square44x44Logo.scale-400.png b/Assets/Square44x44Logo.scale-400.png index 4f4873e52848717ba69d9cb5cbbb24678c31ac01..3508ff57a1b33c4c213d70bbd30a9f58abfa68ed 100644 GIT binary patch literal 4376 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 H;+6D2X*6I0 literal 10851 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 diff --git a/Assets/Square44x44Logo.targetsize-16.png b/Assets/Square44x44Logo.targetsize-16.png index b39306e3944936d32a593b16c41b96777ca5dee3..e2b73cd8bb21c446057e0ab5247bdf1628d65f73 100644 GIT binary patch delta 386 zcmV-|0e$|61+fE=IDY|SNklCWrSsR7ZDveoSAdaeay`a{PAS| zy+9BIMQ9IXwbo7;h8lRh-@zK%G$Ekw;}|m=<9# znoWEbI)elF0ej5YJLr*^$54Siy3NDaCfSj#qphPI@b1RGz$p_bU_8dS!Fv^qnb;{> zmDn!)%KJ8HiFK%sp-h(@iao+#%Ruu*gP-voCN42@Zb-g>vk~nMtqr$rbF2UzuAq{o gEhIEbo#0pJ6Eh69br$~^egFUf07*qoM6N<$f^(U&00000 delta 603 zcmV-h0;K)11BeBXIDY~;4-Q2y--EHo4?#&mC zOZ39NhXW7ia6aexKIb_swkY<$0|o}Jn+wIPx3H1#C4?LSVSmbmFtZ$Q9N)<^F9Lyp z{8zx>;MICbN#Ef(&V9V~n6Yk0EkFQCmYuQKoJW)jUa$A^jZo;(f)3E%f6XEnbGNP5 zJwErjGdS6P0zbv9wrAbF3-709*zo9tE4}=KT%rmFgR515!iMBC3&Mq-t`6+C?}Nc$ z0K+ge0d)X2+kalTx;qgHkM(AAl9w($tO6*-Qde7B6I4K{R7Ny54-;HGBBHYP^Lpy1Hv$b z(E>GAlV*O$nt^IjYeWwqNUr7cN9}eSib@f*Sm>ZPtAEh^`Z^jL4!|s!QBV|`Y$d4! zaNCTL#MedN>&RO))Z3|FCw+yCcK^YqMl2*2@hz1?Je~w=G)?LNwS3)(l*^tS9iKe! zareO4?9gne8Pu>W(km-?`6_~pD2{Dsg%KSf7`(gM*LUSsdO6KLe)_!obM#A%MG#cO zkk|5|%MZ18uGRl>T*2)t`@Zr$V p{oJ2tbbn#9*Pm?8e`?FW@fUj)<)5Eewc`K)002ovPDHLkV1nsFDG2}o diff --git a/Assets/Square44x44Logo.targetsize-24.png b/Assets/Square44x44Logo.targetsize-24.png index 5809c40399c1e94d0d438449ba8f5bc1f0133971..bf165d643c4c9bacbd242b0c64a72705f80aa22d 100644 GIT binary patch delta 609 zcmV-n0-pWV2#y7iIDY~_Nkl!lfphAA_$^jKy=l;`Hed(lXcHUA#34*Vdl*I z=Rb31&$*lJWq4)Z-vU7ph`h$^K`M~0XkmR?>G`4h@Wnei< zK2EGl#zUT*=mldOa~1&92KXC^>)0})KiD0JjOxNb5AaPH@IqMfJ!b(rU3HN;3Y?;*!!URIlJ$xf7?)3%!`kV! z1^xl*gZJ+h-ewXbX$!DMA@h?Pxvr?;unxh$Lp?P)F@Kb^z)vsn`~@giuS2@0(>(9; zC-RSzoy(7p9D}Pfk5SgFB}^^Q?U-L{S^3tdK{K}qXPe9WxT*&jVu^&@G_d}00000NkvXXu0mjfgsLMp delta 1068 zcmV+{1k?MD1=R?UIDZ5ONklO&Of2V1NcQAz84Ik%i^E)DjSv$y%^Amb>Tl zp1Vd)!_vo0KkB`+_uf6{+~;}cdEWON9shFZ|8Ii2y1IuK?tk&^+`=lQklP?-k~zUJ z!#L|R4Ax>}w%*gH-(C3I1XYKQJac!Ey$LRyQZSnl4udmp&Iuvx8uUWOq-#kA+xOSH z-Z}SCeeYi;*#A;p1!Fi#nWpMoN8S@TD10g(^YaS?E)gI12>P!M!Z$Kz4Tr<~G${(J z4;^{6>Enj>KYt}SSX=k3VA^pJmZ>R8iP-0S9^1A)jhL7i_=ZOzq{NoIT$GkL(D!ve zK56N|&8g{RP3Om}YhI~oa-X^!mB8tAJrtJ4QBBvg)~`#z=DZwaXJ?w=Di%W_vO${f zYHVXxCXwa9FbvH7a3@n3#!;8k^>Te{{k;_l);x+5DS!ALN)s>ctwct88amH^iQ*ka zNZXKNu3>bkxbfXhbY1eGY^MY1X{k8b@FAE6=N-bJ>5df%NY6bC+?toW367$z&~+U< z9M9omcMrfI6&PWI3@tFhRjBMN z79HJ`ZGT)`EEI!xei*w-i;kSqTa8DAJUm634}bYaDD!d?C=ggcLVO&OlkCfDCcnL= z8W5bbxzz}~x~3Oh=^wzZoyFvXW>!nY3qIUcl9XLFJa3Ym1p( z?21a@cDsWIYF)>uAqsBI`qOoN1X&sBSi3gC>|Be*vfRm`&=S5O-G_Xmn3=r|oohF! zAda~^-NEP{P@JV*vFhOAH@L=6%*^^XynlD<16;h+gU2^!o1iKtCa+<9ViF@`6XxEe zF*d~vyngmfgXfQUEAFPvXWnnCu5ndSS-cezqI}3VW*zVjN6JviM4+4ul|qDQllww& z`F4}LzHcS0sOJHFaHXp1wF69FbTA7lHSpgL9M0&#k$r~XEo=0!r?Ii|ZZw=#PbIYG m<`X~D^kpWK8F1s(YIO>eLw`S@lf)lcIFSK-6n)6D zFAT%fciO0JBi@MZ7KO0Dz90Ry8^K{Ma^;Uf;QP)~o5k)-z)?TbO^)OjbaT)Ew&W+; zMeP?q5NX%)S&ozNQArjd$>S?4NxTy{c>=zaI;cH`S;hA|J;7suItcXRoKcc_C7EN} zXaF}5Thq>6@P8s$pO*nhuCZV`!B z&oOpFNq(fUnmK^I8B;Oe07x=7jQR>(84Lgb002ovPDHLkV1iqUHd_Dy delta 1215 zcmV;w1VH=01?LHnIDZ4(Nkl%O%tj1hsH#))HV$PLI6Q3 zf*9J=hql75w!q3m6aS(wz8HM)!GH9@AS|_OyNhM3Y!IzNDL)TLuqmX4LMa%a6zPM( z@MFMsXFO-td%NB3-hJ{UH+SaTx!-r@n{&^(7F?{Mp>cN1V}Gp`(5r=#Pe7`C^FYb# zTB>sz;+F|6qR~)bG#!RDowMHi%6vfkG~km!9v}e8Ph@<7M6Q5*jJ(w|1m3G(?+>di z74B?nIi8WA&bx7g()vv)QSP?81j{4BR}sUidhe!BcZYUvPfJkm^}kNRZWUtES6)_% z#fuhTZs{BhoPR!Rd^{dHKa69?f5X6;xKBzshi+dHM)vMd;PVs$jvPX5v1KSOD#Wu@ z%kj*LWys2k`_2swn|r|{`Q}>w)DmP*(c5t2Ru$dgTff78IbhElebQ*75Oe8^JWvNn~h$(Zr0Y;Jdfz!uJ}$Q9yFN18y2Uc7OL5nhGki@Xdz09i}A&m z+g(W*JaNsP+kazcFl0;BT6n3EnDV$Y(r}~kTot-K+8G}cE=rZHl>NOa?aM4`JcH_ohlM^NLEvcaG z3h;lJMfhV9;$fc5tE@o&tcOw-vF0obN6vPt|9>==EnPgVM13dwoiSruLh2DxtCAi4 z1A{39i3e&+!r0oR6S$@D87G4bMUN%NC-!Jh z)`y(8T)6gkA2uXzshEz0-UZ#4!&6HfS13gD@-^?8B-S>0GQjthf zvw!wE^X}vU$ZgSZVARy2e*`|50oBCCj%&&Hd-h>tlP_Z(vPoX{zKE9Ac3i#ohmC$q zdt&eN$PjSLYw-Es(h48bPVVQ0t!=wf%5}2EVhRTj9W{|V;0iajly66ZEn^M=TSt3f z2Q`Zv8gAt|?}Q5%*A}i(CB;R!d-tAM%YUhO>B@*{HiJ_R75w?W8zC%vI;Ep=Cu&&l zzd#Lrm)#o}AO&PvQ@50s8#+UQ?-IQ!J-H(s_>r1^6*qmF`Y572S6q!h>7u5OaA$iU zbJLISWJfREdZ|}@mhra*^xG0n|J(8}rTf52v>Y;C00000NkvXXu0mjfpn5{leSeQF zl1a4$W_Sp;pv9Gnh_wW-;e?VYAxj7vPE0<)WeHJ+qvr!+ED3>%tp(y>Vv;ipk*Ne` z$tECX1Y%!e^9rd&1<3IKv?z;ApuqxixgRO{goy;hVPQ^EK4Bu!aFR146B&k+WC;_Q zh7)ZG69tA7WeF37h7+^|*#b^74LHYZ335&r1!5NpYBOX@O0ZeL2E;R{Sl_FlTfjw& dRs}$S0RZ~1OALPqfhYg~002ovPDHLkV1l}3M8W_7 diff --git a/Assets/Square44x44Logo.targetsize-256.png b/Assets/Square44x44Logo.targetsize-256.png index 0a39410265cc8fefcfd68c92ecc1e59ba658b87d..7a1940c28a206c2123f26460520afb4212a88362 100644 GIT binary patch literal 6291 zcmc&(`8$+v`+jDIF(b;7eP=8QNeW||7=!HPLuHq(q$!Lw%w(;xO_B;(QdIV}?2!zz zFPUQO4H`=bQ4KEk_=V_;lxEV}R zQ1Iz_L7RaXf{+l44f}bb;x+)6a&LRX!I7L>$?GO5&0z{#Oa$9iz#b`Gd5TavpPG;7 z&IaFMiH}I-zJlSGLXB@w_w@Y5Cv;ZFc7k?-e?LDgTOL^oo@$yr)ayCC8xiJEV+A8A zy$w6UWw0v+;gWXq01Oa`9H@jC5(q|8RRg4WFi=T!8ySc(5fs96!DarRoU(*OMMcfF zgWWFs(D_0JD9ptcERbmEVNmEuc;Y8Eum#{wI>$Y42)X%qOcex-n zQClI}Te(6fX{y;Mti(ERR@VcNMa?-UsS`RZOywVrNtRqc^c`B#$K#a70yv2xqytC% zNHRP->ZtG+@`iSE%O!G2)=_EQ&iYqB2hGsXy5o}nj4&gC30J=Sby?IZ62`el3@y(1 zRowNvxp%f&XDowlQOFc|)|V!6w54vNQvKV(C)oE5O5oUj_~+>ko$3eaB+zkyGuU2u z#Z8^6ySv%WdUF<4dq*~VHj4dnu-Et(2w5*}C+tLvX>qtHBXi!1<;XbAc#~?`O$Pj> z6L-IrYoaK~}Pmj}V)4AbWd1bXIT=e=P+e z6Y{uoI)3JTc>Ez#*01mceMezPpnII(vjI-gpiuJ)0*-SBrf}pQS=@C<%{FS!nl9-$ zf}HtHKh%Hx-lDed*3^T9X@y$s;8?#*M+B<=J0U}*xXeLr(sD#1$Y1!xgd%KlGwf^ZzJ`&+z?T&A$Pl_ z?)haBNS$W9yf-R~1_xD)SW>UL8$kyE;l6`{k|tb2>6Lf|SHFC`eN*hm1dd!#;DNH> zdA<1(!$3CNJ3bu4D&nNvoeotGfKxEftS1bNY|*_rkHd;pNJD%8PFjY?sTs(NrkaG( zT`^n;sV?E~a;+*S7sh{D^6&f{ldhk=&f)t0Kl8LiJt5SnR<8r&XYo}Y7 z;ZWoIqQr487L{bw@O_%GP>qIfv}xj9m`n>Yij52)@t5+l@7|c5a8XTZ2o|G+(9Lr zSSynE7KKIlC0^cQbLiD3^0@gcNaZ(N>~lUm{AP@37h6N-g9EUE2)`R(dSfiuwjT9{fz8|VzXWf$OxNd8HSw zqCrtQKd^>~+hXaJ-e^+mJQuupsYD(sEymEUA5>_>Q1eIDzQ27V_{JSzyHuV--`e@W z;{JdoZdJ@gkEdw%bkxaldF|sN_x9`6PB|HIH-W5EwI4c7tb|EF=Y28>A)$=dY!U% zKkoz>qgthBg55Rn%C8@phL)Zg(js(Yz&?>jSAvrF5`&_6m&h(3+l?%}CwHpKb_B{- z9TUa?I*&_bs8O-@6TI$_?_Cd4>)q;-l@j?QixF%qhu>Crcm)|#(Z7;Cqx#+NOi7RB zu-OO$v0iR3o&2?475T-q6MnR1wVE0E6*}GMJ6bIQ|F|XECNS7kJ?h3W^|UMUx#Qy^ zyW^T0Up8bjJKbB>ltQ${xZi>j<+KUiCjzylcrNp}AqE01@rQ>5dMVG#v#gWn-iK)r z8_xZ<6l7dm-iuAzs~HcFx+zW!6zGXI!k#+G3?R!uaUn!=!^E`&>Qa?y6?y4R`Pg^^ zPc3TmQ~M!lk$fnAF2zXbv+GZ4Wi3TzPzd$zr~B-HaccYwcXKR?zrMEH^1E*Qn2Kz-3x8LRUX; z2UpS1ntm-RHbQud1Cm^rpMI?t2qJbY^-BFG2Q%&5o$Hl_O*oFi;%f((nD_XW^{FxkhM+F#JlFYWT~R!enp<5bIcQ}hCV3|DeAGb9qXe?->^YgJGl!p z;!pGY<-F3}A9G0Hjs?Zg_WV{$UWh+nd1G84bJ!3QA9wRpK1XicsOhC9BXt833YQQv zU$y}FS}i($S4OZy${P-TBO41Fm5wu0=p9*pyAL~VTDeI5^)8*!;UOM75zmN z{m-isXidKgP~s`8$t*ka&`wuCB=pGQV=nl3R;y+&{J5DQBRp?YWc%^V{4o%9gFlt+ zEU8uR0n0{wrP%djL#~!zats&KpL= z-{k&tyP)o|^SyJGk1F^1ms}4fGB$A7y6iT+y@zxr+q{5P#;CKJ5=4pcEMt&gXO408 z&$M12SdQXVdl`D5OLj_O-jeu5&r{fBzV1LN&*mo{{hm)A&jdFJyo8HwQ=^sE^xg^T zYNR=zyl}}b|1<#M4SDc+=$_qFhmszKRsF7j6UmwoW$?ow#jnsjBx}3OB)s~MNcgu) zF6WPB8ucp|$P+IIUqO2US8^d=PrjbCdXexhFWZq;BITv%3m7D<{*Ougf7cA=kD#im zs_Qmb&g=o^r?0MybB?b){p_Y~uW@0T-giVHA2zkwFs$Q1@U;6!|Dk~9hjwzpkX&~S z#5ZC!=aRLzme>b9bNNgnx6We8%P)e=62TPtq=WbO4Wy3P4cj>vM)Y^xE@<0^+E?;Z zrM|o*ow>I+S#1P?*h&z^T`@oetL(Jlq{uMY_Qopi>S0eeu`Gm1t<3pSk(Kwplg>sx zyRzS!n5a%T-2Y(qg=-ax@}@kw>3l5kAlOa+T)zJ)_n(_g%(8wZk9o{B;^t#(a9_z3 zCGpvkj!B?3YdwG}?eBFK3)20B{z``a9Rw%($xC-=>liUJtT6P50)BxB?!@3m)_Kmj zZEm&TOT-TVruN{%!ft9E-uYhN2Cu<)8S=@{pdxPiaD~UF&ROP*O`2C#w)qFl27cxs z9Ge@l9Vcp>-F^RpAJvb4(PA0|>cq*1`YxT1G+~Ix3l+~OBaEr*{7hby(Yd(S8BSa@ zTHNh-2mX*sJwwH?x>^PFO4=sRX|F2|U-f^lkv7f3)@j`5#ODsqAoZ?LP(=Tp~j*9OoZ_cinV zC7_7QPE5~R^g1??#cCSl^VXQ#I}!Z(my;%WbpG&#SW*-Z=c*T0+1>MoG~qp80!ymq zEC|buISvIf(&%o&gTg35Mq$xrCvlWhPE}U)DEiyu>DSGC!HFuH{5-KBYq#&?gH(gJ zq!y&L@fUkZ9?c_uW&b(U+2=efH%*4KUf#t&yB3K%c)%r1P@u82xz6e_G|)=W@+|g- zXB}nbjqmH8OBr~sSVjBmJIPdXe`om=e<|j)5m-OvwXTsH0FB*H;M-Ou+6bb0Pe-Yz z%T~JK1ctK!q_a`yP9tuFCke#DA6o?~6~Ot-uZgu~p_E8~8dSo6tenS22;d9kW8J!D z#VO1PPq{1&s`cA+#D@8`h%-%FY5Whjcg90*%0z|Ox7qvZg^BuAHEmTxo*k3?R{Z@% z&c}6*y=Nw_rJb8B=~=?hmUEb@2YlYTVf%mcHeaU4Rd)Y^OxCU&4%*LoM1oM);0hZwmE^e68fvTfXz_>8tgT=k?=y zKbsbO^j@N`L%f?J$N1>4kZEFmIt2aHW{r)HbeVru{cj;d2XVw^!{;O*T zODll~Z?63zH#hUzAXH^_Y!N#q_+MYm&X>7QYRosSHYN-K9EvncMOqO5PFMIOlc92^ zFhtzs9}$Pr_=bm+_9QG5SnTe5SWf>2%Md(^H=o>o9sUUI3lN5jo>YYDDItbPQ@RM1 zOc;&jK>~{x>GAh>%@g;(0`w7^keuKDgckR!bRaSXZD{>E8#_EC40xOTv!=mG;s-;Q zGFt4L8;@JB9)lL={KB(G1!R3Zf)haY`1^oy|2)MCcWQBxKz7s9qwm*L}LMHy1#6?`zfx9VUHhP4CVAYrzq74$QH~25-kWfhnGn? zob182{}Xl2)d<%cC8Nkha=Je}z48=s?(05h`khGgwdG;*R2EEzu#bGgyUP|CZtT|P z|4I$Y_2feAP1D%B`J)01s=4Cqyc)U&l5{-|8Rct$P~|e5EW?SDTV~ zJH|-5AcQ|J^$o&j`O<*?d$~P+|BunoiPICI=VCAieF-OcRO!|{S{dcLbp=u~<;mCp z{(gJ6Pc7oP zzdTi+P{5QpOQOMVKi(E_D`RRYPj5eG*3#Z>PBA^qvXsanX&m_3MX90}z6DmpXTjW< zXV-@qEy8*8X#`fe3C|(P#pC^=)&J`jEkzKNXW=1H`)Kn4U(0o zDT7obx>M9b>>#V1P}msz@c~D!mcTC@Lk~RjU^IJlhDtT~R~^A5J9X);H;3D31(nw) zE>x^nNI`r)+ld+{Z}Usl%Z$uyBZN?59sthu<9I3^g%y*QfU>b=g!(4%G3N!(+7%8K zuay`k%gWS5&b%v4DnFEekWVlp7S`L4;fdRPGcKof@BonopR|7tS5jRXSW z+U1EZ2jb#`KgB9dM*l`=eCD3gE0Eu;GTI})KD^RO!2c}Moh7_O(56*Iz2L`b!f9$4 z`j53Y|8`2@QHft;JZIGK{Ffn>UmgzUK33oPkw7kC(F;wqiA zEQ;05*Bj}t@vygcF>9=`WfekY##s9!G3!zD@op^IVZJ;kVV%E;MQPZFjSIHyLMV*t zy&j+2_Nj84G+W^be(JM}abZs`)aJe^qitiL1E9d)3m*Ei!k0}TSs$k|kmSYSaW8f2 z)!?lX*v_fVv5THe2%zR1E(8f-Tz4`hFv2N9iHczj>IZdQRE02YA;BwSac%Si%A-xu zA#G2;I(rgm9zS{GLVID2w6rp1YNVb=v!a&fNotf-l_zC#cLI)Z9v^U#`;{G~NtIVIFRmFrG~Y z-&X1}pZhaYrM=|Z|LA4arO&yl1c@sBB$Y>DM)u1c(HxTCHjgh^ZW1{Qd9ugu*>Fxt na7+E~ru?V41lSTCA!qW_1^yhrBh+e literal 15829 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 diff --git a/Assets/Square44x44Logo.targetsize-32.png b/Assets/Square44x44Logo.targetsize-32.png index c0935b0665c5ae11bac5d11e4d2535b6b10f1965..411318f4d941d466efa4e9e4493530d4577caf04 100644 GIT binary patch delta 717 zcmV;;0y6!Q4EY6+IDZ1GNklK3g?E{-7u1o;8@-3^$ z(d;3sS?jA8`-~p|`_5OW3Xt2rnFg;!#55xE$Y*R3;%*{uOE4Rsu^crUFKEocb0074bUFI6#s3PJhmpc^!7x5X5 zTzVa3+{64m-yJC014TrcPy9rIe z5YMZfxWVp;ZwVn6+eD8C(jyjp6`%DAV3h;EL8KZzE`Nm9HqTkmbOLx0vvGQOM87t- zghw;)6#(!qVXKo~6D^*AX9LM^5W3;~VWkuDFX(Zop#Ut0@O5sIX6Hl5#m4AyQpxXe z6Pkc2yw@ULAmq(17|KrVIIA_^eAzz0_`T9mvYb@tY8@iHJ`c&$0q+LwyIK z z&mq2!Klb$w5?~3b5GoDk&xQ8-BLV-)Rt5Y7ZQVOiAV(MM00000NkvXXu0mjf2bxOg delta 1516 zcmV8sArF5e!NEC#i6l8O0 z94kVNC>0Z9qJID;CHS+Nm;jMAQVKLBpio*$X;2VDSlU_IKnq;~r9h?7>1^-4>$!J2 zrF19*iZLdh$z{2)_;et)J4%$J!K4+&2*Fd0nh z0;E<+mTP4wCwA}HTK`x9ytS+-O{ZrcQIbNPK|dOb3XMq!eEGi;5GQw;&fiX?^NRkY{pFi`#V9<{= zn=PLP0{(gS_kUKcE8nqs7X-{l1dyMfFYBzMR?EOQvQB66cs#J#tjNhqM{af&Ql?CX zv!Ri1ADJ0xINx*;XU{osq4^SedwXFrn^PrC-DWQ=dP?==f46VnKJAeJ=wo6F2=_Wj zvWf35V`?gvEX+d=B{%3pueEht=JaDy+(hxsdwCA(oqr9es5%0ttAW@snsm^D5p0yaH2i+*L?>ghoT0VF13 zH6>U~F`Z>4TO(>B0syC;L0V18lRch3WM!mdG41^HSqH3E87S4gNB$=GsFNVE=Y~Fy z1b-kSmg^g*+2M4fy`vMHkwFxRo%7^B9X}}m%$+?G*REYhTYD!4H3ei@qHefNOPZ($ z>X_K*0Q!@J$e<6yJ*~$7gl{YP?mti|7=I-YH=_a|pjYWu6WZQF#WfTFm)y$gqj+uc z0(5oV5IY?E%ozM|@GurGctzC6Jz!hfDOP}vgDy>`UVRt_PSV-E+1D4q**YiY&Y3mP z2~EuxMXN2MM3WQZsXi_X0K8cF8h=K_QMZIZD0FL{sjH^~^idnekaFT=Q~*qqj(@65 z?V{UnjKk$dox_dir)8t*A{Ut>)aUcXMHF(r`yST?ery%w!;gjQM!mxYhpSO^0g2Id znj4Nr1@L>)1jo3RE9C~A{u3_6hv;JAna)u!heYQ7D#$G?R>yw+1wB1?P*q(6kH^cU z7)nst%VS3caD44r)xL7&hMf*^n%Qqk=tyK&{pZ?M{k z`k>NG1I>CKJ1PKf<=eNr?aNmc6K&rbjK{^va&Hm>;% zjTdmBq8j`iPQ9uIyEREFF8h9q>wdY0od;BJ_a4bIVkC*uS1}fg$xVUV+J8Ecnvygy zklem=NBDBF3J)awx0j|SE>?712dGNdTkHm3x}`#0LsgvWrIGB^ z={j-6a;Xk0Z&vGOGx#VWLfOI8GAk&g1kZDPqKz|~USb`}*XQG4A>YSXM z! z#Hx4^UjiiAgDzxPPNK_hAJ=ys60HGAL|wR2d`wk5n9ISdVonqO-PQyET8ya#OcGv@ zYXI9kB-&+5b*)4{17shkczJDU zi@YllR)4@KF%>UDuDLp&iNHt`sJu>?I0cNg-3E!B1kOpnc~1YN6cd3`n9s*bp`#T3 zF7z?_-md_IbdsY6I}{z_*p$fM(hFyNQ3s$U;B<#p9b!-+$J@xS_S#yq2bxSvmO!SZ zR}f$)lKv@2Yl{qG$r_gZ`J%KKMFV2u-tIBCw zfl5^?;VGaqqB!RU6{oSwC(j_Cd0Q~y2oUrX;5>uB1GB){%dJE@Ql4%BSeJZGc29H; z(+XTg^HdHJO6yQW0rvt`Y7pl<;dinNe;z=Nqiei3o#N#%((d(9h_urk_+G03@k@U- z0)PJl4+-PRj(hc?2@uo*;2r75UB%05Q=}>W=Shk=LcB}^djKtEZW$QenmE_VuGRfI z$?JvhmJI>$5}O&eIY7@a#hT*X{k*nwM20a>j+G6>M8RtCkdT##c~+vrfM@<4kV8CR z>(K%(x69`gMe*K~)Heg<@6(#Z5S!Gj#D5%emU9{B`nELzCs7@yPUZ7ENd>^xt%S7c z8-ie@^Kn;sh7Bval?W=fA9t6gK7ITNaaQ63B+iA64NZV|Q*>|GxB#*f{rd!@z2Ipb zVYpv6#T2f($pewE)1F8mrw#C%xi8la6EwWLmNWGA%y< XDR>7~x+jif00000NkvXXu0mjfnr98; delta 2559 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 Vo6hFrJ0$=B002ovPDHLkV1h=L<$(YI diff --git a/Assets/StoreLogo.scale-100.png b/Assets/StoreLogo.scale-100.png index c212fbcf03c33bae03201ddfa42e86fb74758574..62dfd12d23ccb3e2d429f79192be65acb09860c0 100644 GIT binary patch delta 1406 zcmV-^1%dj^6r~H0IDZ9KNkl~*IsM=&pCJRwfDK@C=eqfBbTBUB7fH*mm!mor`p=u)*1Rf zj9P`1kZ+M)$ZPcdG&G;GZwHdN615h&sZ=VB>R+i;?xX!_5;~aE{~Xl)^lfurUFBpg z{Xam~)Bj!Hyv#%hFd2D_1n$nVp=}tO6AE0Nn;=n-`LxZ4?2G6=JrOo9VA z?#Q#lW<;vUU4KzL;w;72g~aXR?)+oP(RxSDD7nH ztsi#}$NSP8>dw&K9YUt$esBRK6z3(nUx585oOkM?HV^?DMZyKU1mm4Zrze?`lT2V= zNpkTZF-X6YTXtK3-(3 zBogR~9Ff`9ATb$Nm3|kJXddvZb%_rNVU9jTWPh?xFh^!Hhx2^;6+xpivelD>P>w(P zaIg~Hj!cParE?ZEoBoQt5|V5Iw2s+IE{metTy!FGc8c8*$`N;F05owz1U1mTW&WLjpI!s5r4OkjF7cw;8p#$ibM=6nKYP*;9S3*mXU~&Aai5Z z(3}Pn5geb6Y;QG*7>>+#$jpJveoG(g9l0Fm=Z_&bZzvvQW=6H5wh-1kqCdL~jpv^R zi20u0?_2FnL^M*kLT&@6D+rMFe-ru3oe*7nUR|eQc7x^#WZ05l9M}X`Fn6vd(DR61 z>fFM2Q|ruRlUw4u`Kn>!#HSWj+EdoQi}qOais8NapmGMalpo?Oy{6`T9(uM*ZXHP+ zF6ghyRtqzgL%QN$BD?wi&7wIh55c=AB)m7ORlJ)5`u0a|V!Z M07*qoM6N<$f-lmw*Z=?k delta 2607 zcmV+~3efeX3(ORdIDZNLNkly$5ClbyDMdlV$NB^{n~hmPA&Ext z0SI$@&uP5K%=FCM8D>9fW-fjE-ag+weIDKC8o^IUN_sDZnSVN0GGG&d!4M7*!&Ha? zMI9nKs7fAAkiJ*2S0%I}SIYwVf z?-%`qVcW~vNhAzB@JVxWdS(@8yvnFR)t>yk%f8Cu6OojR4HGywq%qc&23#2-gjKx1 z4_-0^`J^DJlz(m&nv+vE7BNM!>?_PU>+QX~B9fGm`66(h#yL-A495E;y$c9Z2DXKR zx->a`qeW4eWp6?5cY3eaO=NZY##xFAMWC>uPbbd*1*o0T`1Jv|!8=JA8#h?Zd582| zr;A8Rdgh-|6Wj2y4eg(pUI52ZI4eXO$__F-`6xd>zkkzRceg}RQ#VD~6x%j@Lil02 zNQZ@m!mF>uLS$qFM1+UK)rRY${gv1;(Aw4p_wTpD^&9_&yZ3(ed@}}7sQbTn*o?z) zT4!6$xi`Hgl92FMgUxX3Q+zbZp2mxeh=BO%aS%6o5{w!BG8hccZ0F+TtMK^oV_3O- zvE4P-kblhSGv}b@)ET(n+UkLj@<)a9iRj`NyK>hqi41+dal0oHBVvX_{IocjF?}jT z4<6)R7zd0U{gU`BTQnc4YwMsI3ElkhzpgJ}EI~1K{$p8I*4FZzoOZ|4QX;}8XgayU zqWN&5}~ZT5`RkfA9UTBv3G;pc@rM}&GD2Ik(7+A zHK3|4ZDQcSNLapP0W8HQ=}*uDNljS=gMJeQNxG-l|-)1{u zd*Xz;8C`4)NDv$p1d}Je>NSaAFSIsq&MdgqbO#^pdfmCW?{7S4d(Z#(yNbmm>)c^KeESIeH8O<}IglIDVoavSwY@DBg`k zBpLb4_^A*O5a3c6nogt%B0;{NR73&=1qQk&@l+E;V$o9)VWFXJubDCZbtw@A)I@Sr zM)b*vuHr`Q*wJopP9P1CCWu5iY6iqKLoz|qnF`(A-904A4#2{BZ}gA|bAL~QFb-u2 zFdCr?ueSqJBGFtEjrcWAw!9#_h#ljZh(?A4A`;~Xj|dIX|G~lbWV*VDM43oK7R;M1 zY(ftS!b`s$DIV;lEA^7I>Ugvf7A?ClX815W@8JnFo!(AMLHEv~qVkCFYFg0LBD-Z@ ziHH-^roINh&q%e)lJ#KJ$bT24MDV#q8*$+u%_J%r7#Zo+AR08`N#MSqh^>*>{N5(7 zNf5%r!=&w;5fK%lndF`k5#e4Bq@gbvO<;1L{UIS_3YW`-OYmr;qVU18HTaeK2DotP zicbS-Ac1W*wfFz~_uF9mhadTTztB**dj^_Xm=+sku9wNed_O>>6n`Hzp~00%IJGeI zQ&WG#gb8EsBI`tEY9e z8}v17nogt%BB40|U&*w#wz)dhVnhxt(u-t^8Xa)-PMS?PdNW_h&@PM&qzt3Vpv53L zd8_d@w70jrjLNKRRUg0(a_BCRAOQ!1NDQP=XcvZ1E@4Q%#PoFKw zdx;bkeSSANb>mKEfGv(4$>@)ts1ciI{wAS^Ol{pcxODld3l{2UXd2)55+P8f2u)`x zP<_Gyl}El6JAZ(*dFF2t?#R^EorO|dfJve9M%7@jX#e&Sp>h{hiqMsP;CM|(N2gfI z)?d3J(gc5#kdbL`@4#5r1i#$9C-2Ol$Xbw7MO(j(f2=cQ?kBVN@xS-G}yKKg-)K zb^w$Y{+0wz+W$kknG zy+I$^JexH$9^$6_PIxyxpkR9%UF-3aHLio59n=Pc&A4TsIrpw^A_S_UQh8Y6d@EIo z+zmm3-28E`aLu&tm^5L$SYJ~?G(0R+d}{ECQ-2|pQt@k;H&G4I3lyMj?4Wq(s)O4~ zt<{|OJ=N@j9;>$*XMdq0i>DELNI5@Y3T$2j4V!F&rc=qTfBxdGtH+Y_)57ESHEEgu zLf2fNhm2pK!mpgMKb2T=3-xr@{pQ17OK!#LwV5*w3QxloS(?JwNZ+M&1B}@Cuq+&y zrGL0E@1owzym}Xq>Sqna| z#h=>M85_(B7!zn~Utyv&;nnt}SQO!ULZMWg8LIXc=ExTvI?%88wMxvp>hOm?*~#nI zN3(9W43B4EZaqZ2aHc9ldf#7B;SRPn(ytnzaAqxtZOY=jA^$J3Bi&n@Yrp&d$zhB!4#{<;c~@2;?hdSu&aY zQDNr_lGl+^IW8(HI^NaQb&%`_NDH?9i6h>WuK@xbPH=Y<+zQfZS&Gc$zxAZ6ZEa#_ zBgvG|sS?gjCVdE7-{A8N@-I>er+i}fB2N%YNUD)Cg3DF(N?V(d8-`4Yvd8&rNNOmv z2LH7jZ$Y{=aeoJJB9GItBro7}k)_HRh500MPQF6;zK~=Sm9Ug}7Sjt(D13B7O&&+$ zv>E5uYIQYkYzq>{3@Ttham}OxS~X5wT7FJ}iK`*;PysW@zN^u5A%6fzf8p>v2i<}| z;vxB5tEVPU*ZPhwb{7tJQ32j}3ttdN2g#kp^^;cDDSvnz?WO{jlilf}9~TVqCB(DG zMc=vQaQGU|Wipa>wc!JN%b-~0N?hR8ZRz$k^nfJkV?K7Y@NMw`-q$PvRGU^v``WUB z@g?{5-M+T^kf$XtC%G5-76&ic7%@G7q^R0Apj-&GxD|2A7n7_Yy@+&s=Ad{Mc?ih; z`v4@yn1AD&$yeD-A2FQ9iIhv*QPIH=w{CG_ABWC6#6DG~%JS^Nx#{Si49fJ9=g%ba zazI=_ej(@TkwrEY5JwNdS5YFPQM6rMh?6vhCu8Iz;&61ICne$ z)ggTHZ3?T3Zk_H>w8GfY2 zt$*$`0z-U1dJ`;FRwfMj$(pCM&=_FV0}2fX;(~HnDwA%1tJYzt2UOV%f`tME#wU8Z zx`w#KFL$;DLcYSqai|AaT>^1KCN8pw;jy+bOUSEgEsUT7-D@1#V}%5OHDpzQa-0p*Wz{HBnX^)Kst>i+>| znfe)DI|IH3H;@=2KUgtDez0PQ{9wfp`N4|t0%qO~0TE~)h-Z4)4nrbS@{vl_lYhi} zN|v)z7@2F<%*Hv!@h3LcGv%yv)K`O8*CO|k9Z&4mE5ioA%Tp*ZKOil?-5Bu+Mm8Ju zC;8RHd#R)PXmm$o<7pv3Du#Hr#9%{%Cy=e4HcEy39|`?+(+g^_nlAsgXCNxzZySDP z{Vwfc=D3hQid^iXO#n3nQtJ_bGk;=uHTI>DZ$xfzP)@`!ad(U1#zTE^EHBqr1 z+#=qcool0;t-Mr%*jOdhmC$z zd9kOdfT%G58=KHS92{pp)qmYXfd-T+NM${KrR@a9yfoaWeQ9V5Qvr?mnPaoV+Kd6e zl9j#wB}jd^)di^xg*F__LS+AZtIa?o2(@@YHzbEHfg3F^fz6baFE+-IKbHA_pXYh= zfb+F&K(;#~;)2;>-KZD@+eq57{pb6oMczFyg@EY+O}0HC3eM#FyMM(qq3r?L;QE<~ z(~gL^5gG$#_seG{IFo%mbpuU2fVm62g;dJ+GL5ba`K351*Xk7#<~y5P$f|Z>yL9^! z2c78Jk#C2M$ z>-vq2Y(Sb%CCn#%(tqN1rzJluUjayeg5Pz-a!_fz7haPb9Pgz9J|Yn50c((oh%c_1 zl%@|Mm08Zqn!9{ECm#;9C~fvZ9G{uU7tOLAT)wy}Be?6y%Ljh)&8rMTA5$T!rESV@ t#E$IymmVeiaOFwtGo;z&ejbJY0BV_b4yBNCI{*Lx07*qoL!ViFihq7_FwR_aV!$7#`Nol)$xosO*p#t0!G;YR}gKy^kyLXHP@+L+`4!Dn*75(DOh@iC2S{I{*tyE>GWQ!F~;AH}Oz9QQ-!Zj+y%y=a2YSq2f$ zFiH0egoO}2%}5WXXXO-Wj2Eq4wX&=`Ek;d#UUqI0=aivyeZC-^+;gNm1077y>YUG- zmz`H6I(_ZBRryuXv=kNj^sGe>@-V$7?DEFTWA{o!OMm8^FD0EG7xFy**6NjSdu_oJ z`2`CW^ls4uOT^bDf^$Ew59<*K1?Z#|1T%A%^`@3(UM5A?yk+a&T>h1t`&^Mv&&*399u^vB^Slby zOOQ+&Hh+{-Zka%XZoHAMzrH{9>(__&e6pVk3fEC6Z8rV#kK^+plk5c_a*pm-o%O|6aOy@mtz|;1KOUSbs(b4;}IFWR@oWx$U9M+{v2@^J`RB zD#^ptSvdT%F`K)?)G3o`+SH%XEfdGPy~Psfu)FR|mB)cYhiUhoPifEI{qAlS^FTnS zzYE{vH!7dNmOLgmjB&T74y|RRr_qFQW84Yo0q95^ccf0G)kVcreM+fN2~DFp#HlVb zGks{k$BRyF_wCz(FTuy|#ZtoXx!AtNfX&(%AAa6ux>SYa+n>ogV zX|rTJ1E&NC&Y4Y#3GO?k9LC<5wGbo$)kggp0O#;J$mLyV@Qwb!Y&c zh9Lmp_t3;4o(bBa`jnw@5QoO+&gn=TfL!<8d$)sIti^+;_;Q&%QctXzr+=yhfajJv zb&BP=#DpP|um~}wH-=AGfFB)TnEDn${D7Qi@SG7z!z{0za%+mTz+E5jwQWxXW~t!2 zb@}pUNnYmMh-_#0n5(arE~{!ugQ=SxaIox%96k6wEHS|yOAEl0Gc1k@C67u{2SWy5gS?pNrZdIzHgWt|*FuA6Ffs`qEc=Ym6Vd{PCb(|nWOa=+br2r| zjf=_64e|E!s>q8OK`J|7%Vb~{#Zl3UpNz<`M01~P>!??=FbHb591P;eb z(QfLc`L{6vjhn;)(-pU7q{5C5 zKJvZI0sZ^gk_T@rb;pIjYw0sKU%EtT4?ZA*2Gx4t4H)C5skg^Hci-V_LL?jx(|bEU zv^@{r0mfOq7=Qeh ziWFR9<9T1mcb?TT#$oEvIEb68fL|TZs7h-Q5;s&I5X)S>vXS%ZM%=(^IWs3Wt7XuS z4seniyaPh%|5aU7SFb$nh!ZD;=eD^-62Dn!8@@V-wSUjy;rHUl7D=DJ?C|IG&*cS< zuCNoF*XK?(V2s<4o%B^02#4>Op=hmnSp7G$2|loAc_tEahVYki&794keJ~5`HLmq$@utVYoW_np>V8$fNbhg*9)a(EdmkANk5i%#3;4UL%4jVf# zPCLO{W^z#HhH@{vi_6Q8%EY8oD$B^SZt0M7i+?5_2V_1#ro>nzB}T52qOg|o3Uz&w z#u=1tWN%U6*Ge&^WRt{fZfTMGNts(k_M z;VY7tKq>g*izJWz>rhtCE-};EW^mCc$fw@kHGK_J$1>$7aT73ga}I#@0im>6GF{#o z$baka*}G5Ko^We-nfvu^n-;%WuvP22o(`X6r@ZV zFNtF~jJMc3>j|I(LXk;u|ADf$y}>WaD`?BM_Z{xm%S8-R)c!9W$Yc99Ju7#u`1(gx zGcf;yV*Ft}g|EK&u}3`n;?Wi-z|`{|$)eiYIynT+H8jfodDZr`0d4KtRrwuib$`1Q zCx}ih60YAYV?*B>Cip*} zapX`%+_FfTfae@gv7tePjPST@$so0`8*|H!B#XG^w zoaZ&hmZ}YRNk zTCDa;Y!i<|NvEoD2!M(R6b8%-KGc?y?g=32v98$7tZqQehs&?x&9B;>wT^DXN!4 z3l}bCtykS<)YdyZPzZ*?032RwZ>_Ui_#Td{lJoVeUjI0ll~adWR+Myl z5e5V>Bsz~%)Y~JkzTxeyGJh`c19FTDQ!gsWuXJ<0>z55sy^JDW)Ut*-dqx)ccLR9A z;2W`8_yheS=WH1!H`N&L3{S6_M53V=P|HfX_L`e{pvNJ`x6ZUDiwpBDmopWjdcg>O z0BTwBc9nSge6KGe_yfHhE-ULrMOF@s8$|tTS)_<}>HO?PTa3y^^KQMmo`znH=nO$h zXJ6eby8TjNq?WZzOo*>D;!lV}nG=W}dNn6jERG+}8|qJIDZG;NklKZ+!g}UfmKJ4YW`z}Hp_oaK3#Mg@WGUoQ+G<5vE@9*zB zt!;+QV(~gGDcN9~v31ycaIVAt3snXi>Cl7}q$PGei??FE2>g_cL(anP;FwQI7c2O0 zhjV1c=6=Q|v;R9z(n6MBPNh=26h+91U}BjwJ`x*GVLHWd8JJLr1+HmqCwX5;@&s{A zM6@}qPsR2r@_%W7AYu_zCBaEhmpQl{Rc3d7oP&s^HOW21HJJDwqn5p-%qxSIIIlpkvC2H_pcacWCdrf7{XPK{3Wzoa!S*7U=ET;)r)IT4 zu(1T;K~ZoFL$Ij`c0}XsBi6r|JkcsWI0}vRoLrSq0)MqwWXegZnLzF8!E#$IPoY4Z zdr2nyTnSz`c&hXF2-crKRujb8K3w^P5vo7&e(S^I{WK(ijR+{~SY zGG%_8#VRaC?2CwfguEWv$7K_1o(Lt&q%BD9#kn;BjB*K}(1K~7dLk6byTsYU+FZr< zze(@1k3B&w3U?z;*Jct*`w{zU31qZOAbAq3S%2mRZH*$fGx1%OAygftagzOS!?`xv zmbM&QkvyG?80S$^@PLA8A1*&(9itWP&0K^c5utiEa@cf^>luAZ+5ta;<9Ur`5LmFk zTsSPelc;YMjcSxusFh6!CGmD6kK0(BE$eCHET1ZwO|`iQ)Pj-m0Zbki$FOHy{4G2O zM}JpRX|8@2-pww~F>92KiROV<9o4KGSUD|jhFZb!TSBtL#U~TylBm-7Duj}lJ0O;{ ztSn7SBOZ2(YsN;fr0Y|%SP-Fzc{Pg-Zssu{S6!W0^1JF1O#s@bWT9bR541uFdQ;AG z&g+kZEpQWY%jQ!ftSxKhNr+&`?+6O5YB23rwA zD8tuH#A+RaOkf17B^`$y3&raS3YNrP2_=+4XAw*Lvok9Qa5RC+@1{K=d0jyMy@_K$ zVA^dW)hIe+jS z#y-c65}@2C{s|GJ7L;j+w8v2(4YECi(>x)qBTz>P}!1pM& z6TWS%j+L6V7&7m6cnj0XU=iuwfSj(7#=kMXBydQ-3K^i)g);3EvAQ?m;{-^5Nz`}y zE+)1T$9^*;B@_mVSfPiT4NIj`2Lp3Df&72v(iz91_jnx(hefR2*y$nIIDebN?JhdS z1aT}xux0_fJ8c!Qq=mGzFB3vl5#W6Pn8LyI-jTme^8(Oq60xKU&k!FLQ=>{ss0b35 z^vUq8VfZf-u|A@(@;=dALt=yqapv*{-yVpR@eh1A$lsmq5UpX`)e4f57b=3MVDfr( zAbNv;BjG5PMksuw*UnWe%73`OuUvd>P-q!3+g3()xcF%VldF!T?)XTo*VriCBs;_; za2Cq+W?4=iz07()INo>RFn)dESZkI=tfBD4?sBOR>;rthk%j2L;UNf~V)6N=Jheh4 z8uqxm33C?9_|*Zv3h7P+e?>k7gIk^q$2%q4AJTK!IZ8U6HsTygD1Qkw<-(K8Pk8ev z*qiKM6RjK@0aI|6nKlOD zyq|QM>j+Ryuiy~yyniIu<2!;?iG5+VCvDcoKMs^?sd|AcmQWEq1e5;s;~G0AXiI6a zg^HjRtSJN&i@;<;o*{K(SLrDr1!3ZYrVST7!hqXMuHEU|A3l+f#CV%s4-L0K9L@gF)Nv5PL zi2(u*kjXkGVbvPvll~`b2E3+ZQj5j4|2ws7W+LPJsg%Lx_hLd>s|3Ci_dJMTEeK+~ z4^O^fI{i;3-WfhTAtvsbU>e~jIsVyhFp5476iqv?V^d^CCQW`eLQUKwGaSjwpUkNz zv$b`N;iekfBY*u5ntGa<=ATaz^<$`)Y4XX2&~wquCeRnKL5cdIveE?HGRNWC(GN?p z5!rT-?{A6v={y(vwDIRc@TZ~aJ+oYlnkIv><>!K2o)=@C*uP6gvs0KaHT?LkfOi;+ pLuC~^fPD?`7ub5!tdtl054C&$X+)x>Q2+n{07*qoLIDZX6Nkl;?$0gy18IhDVh^K~S)0L{_vT zw5HgWktDELu&}{SXXtbUowk2i$3LufYIItiu_mn1B%448f)ZOFGez75BZA072oDEH z0*MOb0fa!Z_w@YE<}M5D?mhRF*l%X%X7Ap6_MFe2bMEi_&VTtGCk%joU08IB6Cj5Y zWfB0L$P}2w2)jXi$VfV+kgO?T#S&}gb|m*z=c3KDDgSr2g3itZ^DQ0=VW}}Nj%<1Ncbuz2f47&8+1?_2r(9f z?)G49ZEcU;<$tgZmdo|5BYj+B4lw!{QOJDGWrE;9R7A$r2XZd2X>&0xWpK)|Jdbx1 zQRw?EO_`qr0?&oam*9HY0&Ah$yO`3GZeWi2;)Gc`8lpthyeEOOCo{56ew624UX?n` z;V&&M?X~iIEQOW7#QO;Ef84yec&toUmnh65KRS zt$5!yoMU@}ve2@;!WDmOZOnYmOn+t=*22Q&*}Sh<&HsIZ+T16Z7fm9BJ(Hf*JZ8~S zZ}Hl4-+w+MsThKl?_T~OBe079YL6PZf1pGtdAv8oG8cNh#kG~b5B1)!8x}t46bh>; zkh}HXd##d^k{~r@B#cfQ1*xei{Qnd-vpB;X2YcH+hJn% z1o1vvua}g;7rVZKnRiczWlI-d^;t{ncW~(N5okDk6dDd47VnLS5PAnNcqy;2D5Eni zx%k6ZU%f1IpERs6C{i3Tv31Ddpx~O71GDeXfyt95iuXo@VDa_f1NVy>cA()9Yz=IO z?SJ(<^w&57qj^dGn$mUwGxNO6EmE+s=`miTh)z(H_x!VG-OEAU3)7}f(RHtu5ct$7 zKNhb!u2~S=Ru93?c0g~hK0A~tbTxlco?7oOEz`PI6IMZ?cTR`^2c@s+-97zI0cuvx zOg*<5k_v4G|IXm{&tb>TFQxAd+l6d(fq#2N%et!44-#FQ5EhyT3Sqo6r_!A{{^FiR zd64__hh=ZEO!v;5AzmN#Nr6iFZ+4SZ?>q5xwN%YCYz` zn5&&=NzPZufGl3BYQ( z4~i+F<7E=Znsn@VBb+;b9$xtSKY!rQPyd12P!pswW~UmsJ%;(|?!E+yqQF$1&wcx~ zYZ$j9(nE~7`M=t*gqsDu2NIPE`zUOB=Q*g_jho@*sa7cQmBYdXkMgJBJ#wXrqYGUZ z4d464uH7O(2$Zbgcx`Axv^|>__`R z0FwoU!hB=?ckkIJI{w6>)AK#v`8Ab3tYKGRg{P^sGbS^H=}%yKKwZh-OabfD;AfiY zBNu+S0t@H$hypIH7KoiX4vJ;eAHa-&QibLCYR`Vrhb0nnK|X;%Ab%uaWsb?7%?N0Y zQD)pVRhRd`PGDw0p|BixPMZROZS{#SRL@ucsR3f*PS7hmB6e5g~IY+xwJg4f)AGy7in-%^E4Blm<=g>U}{uW z0J9Pl3d@7#(sWN#n13f0vkW3IMfLRSn6_#tu)}ZEf$O)UF=71SzVRG*I ziRp}?i~zF>6bj2#@@Bb=qj0DlQ}lqw(RImP>NA4Tr_ZVTG}a;Si7bI(X=aX;@|k#dZP1a%l;Jt|%dgoi+Z_ zoftHPu`r2lz_py%cNra2Tj4#h8W@%b%cUv8f;Ij%JB3@BQ^Q_0AuRMJ;=)&4ET&gh zx>>9QhUJl2#M{RQ)Cr4#%#zqIckj^+%MO2(Re!)RZ!$tYcnnXncDD!Fpp<$-Nlv1h zaAI=b*9XP=P1(%Y<+w8oZ14U9uxIbrn%6Of)x+hED>VxP8}mFxcp@PzG){4Ws|+l= zJ)9^rz_#&nXicyRQ4$IKj7;AXP&h35l+2gRxNBO%=GY&gR2czQzhjqJ1}>v8m#`wR zT7OPt)n$xrI>><~tTsKpy}WkS!}L3+##|QJpP*Ff0e1Ov4{ZDFbGh~M3LI%^%BoY} zMqpuRS>D*97YQGPYo&p{t*ycBVpVXo1_G2SSzwrNS>GHZ$_vpUcULWDW8LjlFgP_A z0~@sx+OVlkET5j;w@_ptL8;OLhW#z(TYpaM9i_XYI{M62u&^V;P+5!s4F@*HS6f@V zSZ9v@>xo4YgTHo~t;AOWx8F8iI)H$;zp8Z=zU$Uf#$4JRVo>E^ESEnvZ-3QF z7wxXhjC3IFo$$w}R{kKCb)(d2wijP|4N_A_ihN4y45LsT>w}oEHu!y8^9om#5n#{A zq<*TU74DckNjzzONb2qBh1-5KRu3p_gEh7O*io)=!%>t$z5k?_=ok!@mHCeS2O7AM zciytt9W(3TQP`R5iQPdl1~ot(FMq`QJT9yaWo4)HJCFBHDCqVE$)jlM@$FdmIY--pGz5Y%NyPqFMB z(y;oh+2`|?tT@Gg4h+*U)PJt9fcV0tiLv%|xX{(rxAuVXPP*FieH~B=Q;zbw{jzM0 zQl)znjP(_zWf**|h-iE;^~l3nqwi0j5wCs|f9g@cx}cje9Uk-Q{|AS^C)K)7;WuiJRjXP0M~MJ%4<>x2Co6*541v zE=MTw#Q|7_F~y8AH%5SBV56BdtrU6>a8S%Bsg5^<(IZPX8|w?c-h!dBOvD$H0xQxr zC-x4ySz>t)jLOn&>@cPnLuH*q)6j{8+u7w>nZAq^#y;lVLUppEx2n9n{F0SvTfRYJ zUre$8S4)@H4OWuE+i`7%!}IK0I>_L2Mj(Kb3rUJISY2IW%j4CytvBB-FYkbGP-7j& z>tPt0h(5!8ND99F&IDZQINkl4XhnS702&=6cSB|LPZb>yr@ORSiY%9`KYF? z&Ayo8z zB(tdWMO5_gjDOjVLq3nJakY=lGf3iO#P^Zoagqq-oJX0Z)m&d@tMN_SGs)LO9qwRx9>n}xqb$wfl@Ka&4$0NXQF#tV3~wtH z=c7e3QSQAf4PwSqpGA=JQ>F4G!V4kNDoC!SGIMr=LIhoe-Pi_6Ggiz4Ek=uqV85Y!U4E6jqi<7l=YZEe> z!k((BGhlp{`W{Ao|E{T2l@JQIjY4iG-A_|*pm>qGZqrt(O~}h!TbHmnUsG?OSk67< z4Q-`9o`14_%$ai}rL4D5kMTWrJn>vM-j=Bq}gmmbAz?Yq}Ks{FbCPU!0>x9eK z(~vT$-AOSmqsKQH0`HxR?M_NrEyJRe?)L~O4}VgzgUYa}fimtHag_Ew#E>$`L zF)d@d#>cTdXiA9bwY>ltNx_!8_HaVVR&-9JvJZvaQcTNGAp>0pB41)rK1KhYWAMLb z`==6ulCzLS6sp_&zk-ALLliD`DPV+@ATM89WKhR_Namw&7P{VVbU(@ZFLMl@O3Rl> zh<`W(ka?x~I8%pF`g6AJg~BB+g*BJ2;7HMW=`&>Xp=UsEu)LFDV+Gq!;TZfUDQ{W} zxhYCUOzC12R#CXvrO>gbua=YIn+yxq%a_Yu!H%qZIzv0p`jlr52a z%7HGrG9d8;6BshiQ6gitaj0kp`A+{^>VJQ4e8T`jjAzIdS&S=^61{vSk<#U&KLT;@ z8FZp4w~Q=gSS%pV`go6VyoD^zbEbM|DM^>FT`syK*nrH9u87>gHOLTMr)SN>zMm9N zoJy?Mcn={c9YZobZ()>4nPkq`ZdqqO0Nw4PH-g_8!Y3KseHz^%=Ck$cJY+=h4S(|f zg1oQU710)QF&p+NQYb@8P2uTFNI8~5&ZsopJ$tnr3DEO4x~H0Vf+zUD$yH~BjI5Wb z0g$T!`M<*A&+NK7Arw?Txt{4#IP>&XQ6NQpZJpkAn&^IuUwuUWTS9W*JDM0+Vp__uBqeg0 z(sTh556=c6W=Mdl>y9C%>U#MK4e~Ul)iPq3B3)DH-6<{P1RriYTKAMtFw`5kBf25v zc+w3*h&RVJt!|c9!!sFjoVp<7xB`-H!4ufg(Lf(?wf)MkiZ|=~0 zs-gbTN-kN;kWvQLKz!5Bmmxt;RFZd>X!2J1kxSNP7?&m@gDVF(9~AA|iUQqsNZPZ= zcU|?B1sP9B+0R9;sU+NIi+>!O$gzk*#w|(*<4y8M2$AmXIb7f6VC@%E@R6j#WCgna zLGq+oez8n<8Vyk1H5`Mr$e_WhtosB>o&l*_k-sFY-wye&+dq4Hd(Fz%V>`cqJeNc>B?6%EGmc(qzx zfV}F_hAKEN5~}b@L>wMIPI`c%`Zv+J33-#{UqMcN9E|w(eu0B@tD>PajK{cm*v~A% zmNU#4eeuasi5r}Nk$=c(#^dL789@dq4@nF05{N4Wqf4~qicucPNW!H^osjpGXmoRl zwwxfrEosrWlO_&d_JF#NwGg5;sIn=joRASxVCANOh#HrX@0k$gWsIY*M~)Ceyu0R5 zDe*n}q8U=wHlV!En}MkJmk==L5Aq{~$oOqAc|agD8ps() zIh~xPwIbum)d^iY?Eg9;DEvRlWqiVq3M!RYj1yh&8D@Z`ltaQzbn*XYbbif6XH5T6 z3Axu;>|aPKAqxvnsT8?<6^i#oKAH9B+UUAH-R~&uWq*gS=VjZFF+qupcpw*z=W@u9a(i1>yF0m*$SjgC`iiY_&kkKLUx#`Ca%k>%`+L+##j;!LdM3&Qi?qZ zlhF!NOzRjlhB5wuN?OSb39gkvp~@S0hDdB$v$Q4ZQ6?oV2wM_vC(nlX$^nE(xH*)5 zhsCwcG=D{I;J(Qy1&X9>$#SD-yL6Kze)bsj30DF$qErSU&Q8i;UL3L4=g4E+?L}6m*=3T!Nly zq!N9!kM*}S%a`hPB3&vZ-!599B!t>B;xMG#<=RqA-d-b_P?{T1yO3d_b@|GRQV8)F ziOnl4DU^Sj%hOjLlrepGS@EX4z>v~c7Ad}`?-nvvwJRAGhLlw@9l38(yb%)0OP>~~ z$A1)!m&c*tYd-a1MTUje8+|jV6GDOQ3*@jO!@`n+O!g7@%?BZKhASBsh7@u7Qx6uc zJ)gR`>}T%2Z*yG7rE4XZu9q~GY7;V%vuC8H&QKxsloOD}a0vl_Rn`|YTsv8lpgqC; zM$Ve`q~4-shXPa2q>z11&?ZEj2QHwn_kXcGMpJiSSclB?(Z}Uug8P5Ty`h@A9mJ=XjImu6aI|2HhPkj}M^Z|)? z#kU}4P`K3$cw^{YGNDjiAwnDx7MZ$uM_JQ%FNBn3aw+5QrPU!OuC!o(d5U>Jsec%m zU5T)}jWXUNpI|BbBTDu&q0H&K|4VY4hzt`$rm-wj&2={!3URa6EN$3$0-Gz+?rUt@ zPV!8pF-_kx_Dh_r);C%bIDpEoCs|LcnMZm!2Sdh#3po;N{RHHX$U64>rJ~K<*pVl| z$N2l{$R?RS@6X6%X%hyQiIzrkFeW{wTa7IDZBIP?D>=V$myLG-t8fkgGt^ya00000 LNkvXXu0mjf;_Gqz delta 5188 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 Ox8R!kP>lMs$o~P+#Aa3i literal 10676 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 diff --git a/Assets/Wide310x150Logo.scale-125.png b/Assets/Wide310x150Logo.scale-125.png index 51391d3f8e0c90dee83c11545d4f1e087c6b4f3f..3b49aa05a4d08332acbf4751803472f5d6eeb4bc 100644 GIT binary patch literal 2698 zcmds3`#TeS8(&Oq&f+SELXQfiS}dbEMxm9o;kkr7reheKYYKR?{#0_`@XO1b06>cc)P19>nj5Q05y+u zu6_W(j%@jx1==aUlc)GKasbDj3rqk2)H}A%jxOxeZ~#Er&coID&t%HHK<#!+gx;=S zf@Vt-7p(66X|J9+#V6-p?KU}TV*bkbkW!ObE$fC4sai?v(jMPSfl`!$zhDZme@%_R&sAy=g9Um(CwP6{gbfyn~sqghjPg*$Z z@YH9y#J=Du;mTG2gx#I< zugfkIAfPay>{o>zuLUz^Lc96m0X%zGH!cl^I0q(noVM_AZrJ{z{|h&yoaGB`uBxuw z6Lj3ed0UUa*RqAnygi?uIHJ`46sV98jlq21mBs#+FY8`^6383;C}11-52-cPE4m%h zEQ!Xn?={ttc$!gQJ*RWR;+8C;Z{ff|;T8R*wcW~V2X(wlJH^^NvFFsvOFEmw?gJIb zXY_y9f{FC(_uU35f=Oxy1m&-}&0+7?-O5?{O#e7^d!&ix>~rYY#YoCSL3veo8+@w# zCs%LW+`<#C!_0R6!9PlV9SJp}z5STWn?0K0bb@}zkj#zf(8zgbGj-2SItU$F8zIA0 ziJl>pPr-g~Ewt$c!ncL3v>=IrL*idFxZQG_ef}5JK81{Ny-imFI|=FNICYmLc}c2> zJ6{lDxa=KPJYPPWj2_5lpQ;U;n7_h2*kDiRwkcZQcY0hnPCm@!NsvND$I!iokICP4 zd)zhVeYzR~9A|ZRA}^2PbfMxoasu^%0H5?!soL9k;3%dFiR?(<2x`!9nfz2xF;Hhj zCE0S99Or$!wH;nZ2Dd57Re1|$ssSm8g6JFNaqhXPsK0>j-q$E8C>(naZM8 zzb9Z~OV>h7=`}Ues?CIi^q&pWIhfs`m2NFeLE-hZINnSpCMpg6;>$0pdq4M|zG3Ao zb-C~Oa2`0MxUf3xA>`7@b!Lp5X%KLAno;TbgP9KXFtN~%UHs-TqBx}~H#Ox(eG?^{ zEt#9J?|w3JA2OO2wt?4vE~yOa~p8! z{uA@hXkP|W!|W9@u6US@J>c3uqpkAmqA*uS2QI?I!RQMrd5Hhp+1@p>SYzoII>CIx z-0hk-1RQS3N>7S>S>LR>GwT>*#G_W$PF$E6P7UU8OK&zdG(0{bk4X_HHLz?7!Lj4E zSspKSwLeB_`qd7j6{KgblgmlmJKE1c9Ai0AqWrW>=;+(SzUL5*dQu?xLFHjzkY}cx zr?T<|qmw_Tv)OCKBvL&MWeQ#+5 zEYw!#wd>I#457U4!r%Y5MGJ}RaaGAYxgE1hLN*nexK5bv)S>`Zk_d)Wt(Dh>wX3@27|~8dCl; zG%8}WUBO-@_Y+UU~)JZUAhIN}LKbStKwe;phZ?e@$` z*DyG~pbN&npU`T?3zbrxJK#hjHT!+3f%s0;HH7hi`=Zw};ex(UJHU}2QF<*Oa&5iF zWxvMh7Tb$aZ>q1Ftxx>+?OW=w!((z!qg+71RMY&GR_Nf=kWCwj6IhP;xY=U2wPd|a zj(9@cpUWN%Aih_DTNnw*g4YoXenM;K_;g9JX^4S5#|99XDp*yK!%?-BZ+5Ixh1eJ? ztSNq{wv{UJc#k`_zcKV9Wy4Au+}c~vDlgRl{qNW&S?t#-*dM9^c`wq3S?`8}J-?sI zXs?D`J{&dfH7t&P>fZ9hdr~QuRQPyN-ny}$sq7pg`>wDoL@w!n{<(MAK$U zi%ouzUkBd51Ec3i=E%ZKv2E`uQSCbtB%ifdJ<*4~o|OI1|ia6~ZCSQRrX^ zwnwPQxcX0AnYe-t$pAQM{p&zx>(8Jv F{|8O{q^JM@ literal 5596 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 diff --git a/Assets/Wide310x150Logo.scale-150.png b/Assets/Wide310x150Logo.scale-150.png index 8077cd8d29473fa41ed87a95cff959bcdbef31f4..fc62c2141ba343d0ba777e33567f32a9304750ce 100644 GIT binary patch literal 3255 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 PJfIWD{XDANP-*`Kc2Q<) literal 9371 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 diff --git a/Assets/Wide310x150Logo.scale-400.png b/Assets/Wide310x150Logo.scale-400.png index e6eaf2b8821940c4ce3710699d1f1e9a9478a754..d3fc9197d3fa8dddf5822d58a7608f478d0e5d84 100644 GIT binary patch literal 11591 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%0A~zw+)uk>Ub+ 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 diff --git a/Assets/grid.png b/Assets/grid.png new file mode 100644 index 0000000000000000000000000000000000000000..02979cc267eadc0db99119dbc6942e7c35efda71 GIT binary patch literal 3977 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 YvUf!Gj{g7d43Qt}$m-}H7Dvke1K9u0EdT%j literal 0 HcmV?d00001 diff --git a/Assets/logo1.gif b/Assets/logo1.gif new file mode 100644 index 0000000000000000000000000000000000000000..70afe15e0b3451f44aa75b4ce22bab7ce1ff37d7 GIT binary patch literal 57234 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 AVgLXD literal 0 HcmV?d00001 diff --git a/Assets/logo2.gif b/Assets/logo2.gif new file mode 100644 index 0000000000000000000000000000000000000000..b155d32f218f698cbb3ea8f85951ba1f24d1ba52 GIT binary patch literal 57234 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 literal 0 HcmV?d00001 diff --git a/Assets/logo_a.gif b/Assets/logo_a.gif new file mode 100644 index 0000000000000000000000000000000000000000..f09d96abac09dd0cd24618a38306ce1383c6ee22 GIT binary patch literal 77685 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 LjPSo;gxY@t&Ywn) literal 0 HcmV?d00001 diff --git a/Assets/orbit_load.gif b/Assets/orbit_load.gif new file mode 100644 index 0000000000000000000000000000000000000000..8af9474d76fe11c22f6f1332083cddc0271e8afd GIT binary patch literal 138409 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 literal 0 HcmV?d00001 diff --git a/Assets/orbit_load3.gif b/Assets/orbit_load3.gif new file mode 100644 index 0000000000000000000000000000000000000000..804ed5edaee2bac3d4ba926cd006b61de2b0a69b GIT binary patch literal 136376 zcmeF4WmKFA)~yRKT!KS@0KqMU5Fi8yA-F}+V8I zpN9_(<)`nTpyQ(>Ke>>9fS>*|02SdEn%A>In2}g97Fa%6e?|zJm5p8u$X@NP+QjYH zRUAGHUOMeRx+KKI0k@{_KomJ*db9VigXMF+s4p? zp*>6cwHF}+Ye(k|2trCWAz5`(XGm-=ymMsv7)Zq~ET>`S5)hYH(>1zsf=$gKBCl!g z3XRXN?H*e_#i8N6r=Vrw7MK95>lt4=!=>dCRn)e04@xYk@10mb$GgsbU+JaQ+u)?a z_kG_sF7WAi#FTZcJwlR;8u}+UFA3;*#Z`1|JVR588waMg5QKcGP2T9Z)S?i+v=)Cn z8WDHkt@PFaQclf!$gPa_Uem9TIuHDNRqLY_7UK=U_o_F))w`Wt;)}w! zXIcYk#6v`Db{BdR?s*d2uiany0@G>?xnFmuk8l{aXhsFuBlm`OedD@3i*?9&;AL2X<QF(hXq#OG7aLi`l?a`zsp~vxT zsEWtQVoIpT>1y$S$Ju5hq38MTfQskE;Z&&S<>}UdC*l%8*aX0_^+M-LMInSVp>x=J z<5{Mn(F2=-^0q#t1*sUkkY+q{TVLAsR4j2|3lY@TkC`hCTLscWns4jRWtoPn3;aOY zZ3_}8NW-^*e4t&m1&gky5qbh!8F1_%Qe5f8p^#Q)4!ZzF%k*n0z&18{JLt25bh2Vd z8yCXdE>L?touU!g&Ih#%GUUpj8i2G5fyYOZRN_o6&lcM z$niR^*E0Kd3U;5V{OkBH1=)9s1Ntn@UnfkgXWwna?ze@$PMqh;5grKWcgTO8v~HPm zZwhNK>p9{G>_IOahg5X#+y{ivK|cUlH;y9nepbnYL+<9^;&`(kM4q04Qc?!BXpJTfnvIPqBlx(1% z6IUH_L^twOJaN9H;=IX~;?92@3jLDF@g`5vD*tH;&evS|H~G&B^Pd+(zZRIkfoX5# zt2N>bl|bJV7;?ij2B1R~`ELp>tzcSHIK%MnH$@JGu$NoV;ri7##qJw0T?EcZ6OLnv zA9sO1VcX$Fiux0wdnQ(Oz@M^2Ch-6LH+JL8xOzCU>EkO5oU+ ze8iUgB8&BMcX&fhbD|eAyXyC*=hf_`OL#v|K zDY)O3<(+E36c)WH4*a%m?o>CiQRLK!JGl*Ys-NdBb{Pnq+|PG$oI9cuWwLLP((z;yLayj3JNMJDr#$M8yg#&nwpxMn_F61 zK79Dl+S=OI)`ol;qa%nqX!XqN1-bKg6#>FQjCMBn&rln_OW@YE(=Hl>R}+dI2^`v-?d$0w&}=NFd<02&UhY)NXfFYp?Nc4tZ22QU$ne4=b=dRrjn zZS#rF(u|H!2E;=stz21VS0vlheC@8XtezOYSKWzn<=K4+La$dRy2^6~QY3tFuFF^C ze$0@KYVphzx7lXPc?d>;nFF>OJ+Vmu5s%0!b|5m!k8426>G{CdSY)|O!n54FAbzV z45U-4tyuY-`xK_rS6jI@T=J?XS*fmSV;uf^ZL+VfdTXl57nfeS9=JUthDg zG?<}~qWr%0U~Qz_Vygdr-O<+chd_FjhWeAe<KT%PtNl4hInBYj~y<4mopp9PIEI!(fPcx z5_cE#(o>BkywWhEG{^H|vsb;-irO970YF6tFJSCY?O0x1Ra0(pwhOf--Ukv+>a?^b_lg7BF@UAxsMYfB{xZ0Vk)OW4xTC;F@ZFc)% zGo^Q8O z*qrZlvKF83_TJq(-y4+Sz1aVvW^-{cVp4o@IN`K)aWoC)y*!?evAH~1&MCe;U9aA{ zJlpQzMV#*s+aNBEmx>XW=SN$}pCDc;086$Bm3rF?sF;dM(b zf$nr0EP;$L=rIY5!wsC0gK-GKG$*Vitco42TfqHlu zlpV6|{P8F_L7f# zbHt9i20TvpQULh55?JzsKGgfEK&4zsita&>$bK48aIO@q{70zfJ|dm6FIW0*_s0;| z{R{^DJXsm}PZ9C^nQTgV@@n0mqHFiF_=58kP2@kvkL_m*_2nr$b$?Di-OrK0&sPP@ ze@Ul4$dy&fe-hLEC0pbmPc=CIS&sbIe9eP=^}hTU)!ko3UQ*jbH5Cp)k1{e^kG$RO~w3GkI8h^nN?I*nLT1>U8X=;kd8ZW5ugQIoaLfKyey4yQL;CB3hfuIy3qc}^F zaNNqKTnbg|oh7e3ZsQ9n4Kh)jqaHtQ7wRt!aq69;J3H=>ASeq1E6y|0oOH@6mqo<% z&a>P*=~4|Ti^@@4;LtkhR_`y1uI^pnaXabJBPfsUP+a6sIO#Q2E{`AXT@9Y+f zPh3)55*a_~ckVAwKI&Z(3uQz7Yc=kVqy}bYW;Ql9LqkJjV`EQGPcRr95)u*~9v&SX z9UB{)l$4Z~mX@8Jos*N3o12@TpN}Mq($doM@^U0!R9042S69Q~@S2($Bx}^w)z$y> zyhlC_4Gn*d9zVm!PxSHcdN2_ZUVmUl`E5i&{Gl1;w-E*L|DRFvmXh5;H&C&?4yl+@ z{m40dQaLU)mec(iMbd##evXy&5c61XqHtyN|yNRPDNZ>8>U? z|8u^YWuYruLZK_Ike38X!|u#R^@@fq!VBP$7|7#FD>Z0cvX$L|QtdLK$?MiWIkVZY zqOr#N9^gB^MT;O$q{GJg5MI)3Q&D4=xyRJ}U zZOl}T6nDB3W<^U-7f*SP4YgN;=AK)4)r5cgy&kOBZJchj74G*Vn|D8cA?LVFA#nTK zF>`SW{I{*W9{l*sj?s-}0@;pduAZX<3rybP%FkkxtRK zEPe0vV^~NTb*#3&_yL~IlZl|A92`Y0DGD@pY5>CJ5Dp%&K^{u|BJN;S` zQdwZ4w5BXgGZX&2E0V}N=(438QDp!@o^sO7;I~yGgsjA3P+A!BT z8yo4bs;chWuy#8epC+h=cPMSzC!9?zD_7SJ_iZ}XoqgL5sjgpA+HxH~n>_BXZaC`O z@;E!20uaKRu#~rbXwIjBD)1JH{%z2`^BJO0cq^;&4pi%WmT~~zez$)o#O-{Jfv~1i zMtL_P;e4J=rKVf0e>b}Be1R{trq@JyFMj-dQD~s1->H8u`Rsg2g0OZFth}F2bFnO| zQu`^Ue?R-)#foZZ?Ux+ogM6)vRrP_|q3V9TgCe(!H9f+*5nW>RUk5x&N=jj2VFLpL zB;6s+p_rH$B-h2q$0MQcJI)mq6(NbPtgP%i)cuTgl~q+$zu>xGK;6%d(odGs&!*CM zU+Le8cHa^43h=G~@1HlJGJ^<3*^NlBNJ~+OnGN%dqMx#8+$3T4Dp`m%G#_0EXMkuR zO(<&@mf&loX~qc-A#p6g7a?&?3EbHlInicM^d(SaEvH#h7@;mXM9w63Ml{BZwKsq? zqT5ZVxckI;nbg*LUH}x+TJ{_&>r^j#aKu<1M!J38Du`SlzazKUoQ;#NVkrIwcIad{ ztcVhG6$WdvXEQFg@6{*(gC=pn@%=nn=CQ8<92;P0d)m#iVe43<3iItoBm=ple-xw%7Vzi4`x5;##=RQ{p9zZcTG0vB>;#60%-`kFH_S1V?izR`b zh&PLpgF0^Mbsf$YJ@5AjngB4YsXY{QQX5e)4Oi;_tq) z@YpC^GuovMQ-ATJ1g2#C3JV#HY@7_bRf8ETDNU=hOp?=KmsGL~dR|V7A6+R5w%HJ| zVOkK2U2dWS*VuCABU%wQ+K4Cl>;OxhRq5=6C*3UAVGStexsf57<}^9!W6Qb8>9?|B zX)Wd)*rlK3*$QCk;h9BgFBo!z8&T`>N+cGkrQfxuLoG{WV^yc_{}!)vbgrJP9vuh#wm-CPkns z=Di>3w%{uSc4$3n##I^HCMS{+-3%kRrs&g#_bRdNJ#B`_4jBztaV__Age8B;!zVOt zp`2(I#XYJ~w68lJ^s`~~-d%Gw?WaDnEC>@3M=2YuAUKmklVN(vTPHoTUNO)`eg93l zI3d@;*9x(5^mJ{SGwaVXme(CSrsCgL!J;{kR$i`Sd~1_!sDjfsys6;oPS>sX?5Pj; zH=NSA2^f$wM{8983YbYOrP|XLyCo(B7s5B{LaQmUfJ)uv^2yL6B!4L#EbqE9L_R9C zDsWjKu$FKs)UsMKY>oIsj~lN@?~3&P_O$WJ%KP7KM@L6zVq)U$ z?Hv{thMdxVXFQ~vmzkM~oX^5wu)@MZB;Ea`q4y)e`-P#0ToC(tMeI+8-uE@Jf7hV) z%FX+EqwJ43u5}`3lmcASV$Kov|3bX#;^!2VrH9|B_t0Z7G< z577c_D8Bbe@FoQTt$K4z8 zHU*=LiE*;n;6#T-IcyBa+R}_lRfESxb^F%DEX7ZIwYD16DDxhnp6+sP2i%^;ebj`;I-%dJ!BDXML1DrT1C`*R z{@XhfrSj(-ABAU=j{F_L<8O#<&fe}#KkT)FcU(|CKAydW zD58wX;#r6nboIG~!oZBNJpb`4Wmk-M#dueYcQvm4@woQUUohUE+`PK6 z;r_#_x{D3l(7K5w<)hm1i%sW&y2+#dqlUAKEic0QX)KlF7Mja#h)Vq|g=aj&58OW~ zDJeNQITaNZTU*=U;Nb73UutS9a(@r$`yu%d>HFp7U>cAq5hjBx zu1KA%0FbGrq5oy^>6Fz$C0sDT^R5ta#-h1#~bn>ApF7QzSZ9fwc>*w2!424JSaHgayVK^uHL)FvM?b^Y z&M<73dacnF(CmLBrvMU3SFcdg&BtVK)TXFTx!31iLhJt)YkK{4KcDo@&Vbb14pW~j z-pl<@4(=;{pA==TV}+zkavyxPQXJhSx&CUSl1kfy`;h*QhhaHC#&T1&$eJ*EpM@?@hzz$PwRk+nR=p3lLdi*Hr3K?mbZSCi##2PxMqdvsj zE+#gbx-6wS!MGRGVv;K=oDwE$+ViU~rA9OBzGn@7l??xO*x>#fTFsQnb_Q@^=|3TCa7Bh!WOzk}S7i8OGSvH*$#CGf{odtH zSZMt`tIA2Y*5z*OK>gy~fs=l>%RO0wAEZBMX=zzmS%rjzo;-P?rKN@R6_EiDWQsI0 zGV=T2`1{Bhng00hD-dq42!#Oa?Rr@zy@80jcpLF0c0G^*fNz2@EY(yHES zp&(a0R-!Gz1Y!^=F00npOcLOu;|T%0YWCI~?y&(SGJW5yPmg z&vHv2MD7Sde0F_*zggFhE;{$K>(e0F^Z>5@3Ioz>4I{HboI7g%P|{Di8FKq}Bj_Pw zn{1iV_jSBqg*;@53-`L|=ZtKU;9y^nIzKUK!;rxgUWf*g!7;+LK_{x=y4GxqaX33U zH%TcJmzm>jaatKDt|^)tB|2Z;W~DG+T*(nv6dY}Xc#`K2K%9)5=i@yK~Ea2asQV zSgTZIbjrac=shHYQT&00GZ>o6Zf0@Z&p4M?b&>vOscRLsrm`J)tHzVXicgyDa+dgBxEe9O^bK6 zY#~x8+ivPmKC^A5yvtuoaUq@7+)M@5?r34))xSkb8d$JPha1?;MLJf*wMQqzAJo>P z93{HfZ{no|f(q|(?bFeh@2rGAWVMn{>F3xGU{@69KHyk8$=&;+&QxYIq#gL=&9I)^ z^@XqcF&AJT@lT#p*62HQ%7bH>acf5KQ$`?*E%mg`qVM2i(;G zXBLR?)X<=+Sf5b8(=BqL;Tp)U3>P-O5~@2>OTRE z$b{;(E!$&r3dM%8xD*@5(^YVv$Jxt9Lh+&9d@=Fi(}=g??P~$4h|B-+gz6PEUP0p( zG+sgDkD<}{cZJ4O!uQKEh_65L8@ai;1qB5kK76RFtE;cCk8~Zs7my&)5g9P~PLJOs z!+$xC{>3mF85#Z_8vbj)@h^wbKLv+>DlYlA1x&8c@qZE>%fUk>FHImyXS}|^Un4JHC0hG1M`7 z)+SnA&P0ScKgcviZX1d{w4a&IP93_wB-XK6CvtQ9T> zvFDLv&91Hql@-Oa#5Iz`luB9*KEg_SjeXb4>Pg1eD?aAd&XVgtfo%v>p-MaeYf02? zz$acnOvth$V2GmI%m$nrwc9Lc))krU7Q8&nLvF1JM~7ztSodlYj96zD`_29f6~e z4BBFfPEV2KHr2hgaK=B^`XEcuoD^V!jw~7bUCT|b(D4c#uh8)d9e<3DfxkaGs+@jG zxZKZGdB3VQa5_|Xc~BDie%(anY;64U5I*pJ(`n#r^2}rW!4KxyEG#VS?Ci*?wt|9! zsi`TFFP)s6+}zwiAP~}{M6N#~!!5{qn(xULW0I8h*``lo(heC;A zlkY`GW*&(p@(o;ay4;u@u1=wUap87)oH9voZ{U83IK7#c>b}_ z(o=19rZkAOh*BJv`N!UoGYHv+ks?atP$xceiB} z57qW&(+&z5lNXMyuH7;+26&;OIoA4RNMGi!^R|&Z@Gi(Is$H+>&>neI{KZ%SuR^QD zw4iFbC7mJTg%F1&e0i4BfHv+kJOry${De_-4D6U=k0EF*xn z^?Ov-`i|@=o&Y{_Iu5R#mIQLznacah1*O;Xqnk4*=B&BwY4H+ftSE*;3ixTrQuIs6 z`jFNnm2bMYRrO_mK{y7z?3ZiY z&UAM$8>IYwSwm#%ltk+)X2U)Z?*%=Uv96&Zrnf#^NZjM9b_6-`h|oN$t2tQ~deU^r zWt#cNWyyaCUtS(H5jJdtRnBK=5XYS= z4ZAS|=Zp6cCxf94`#CBXt6GTDkpT}rpC3Rd1qB7NAW=+A3`wBK;W&~#6B84W8S3wK zh{$zHWQET6b;>^{Zhql#{<#msH? z1D9E7cnjk_DtT#MXa{?HF^YWCUZCSsjOB1lbmVCb%$oqVX&*{<2`SgLTWo34*O`T8 z{3)8%B>gBIV-ZAPLe1qF2&S;9ls{ucE~6xM)HLHYwrGF^8t0IDXdpW|F;fWt*m8z2 zPBMfE0$A^S6(-ISryR(G+Q}R$iky$%l_mFP2)Ql`ScrbEys{v0pGSW_TE@f7_zsz4 z9#foQzRO}lSN%w9q-8huQc|D5iByu%?8*}VTd`X#(Vpbcpk$Rnx!klM^2lUT?f8-9 z%%&#RwpdEl{5)Q3mNe6l1dwK6*3%$4WO}XPQyz&gC~g%N=)mBUq*)Lz1uJUFhcTp# zt@;(qxRblY zA}MLc^MUlU9bb#qi-Mi&p+&eiTWP@cZEb3{?7Lmo4vM@{$LZ_4RLn7W3q1k}k7p>< zcr^F=bxpCQ`+1am_XeNmB6||0SPR=gdh5WlPY-D}O8ZsSF3^1p)=dwFeA*SQqqM<= ze#5$ioQI^fj4nrW9W~m(|&<(Zd_r zzG;YswUU$x5h}GsfxtZK@4cDCJJjOcyDtmg;7gwtQ{E&wRA zSUn6Geb8xB0ob-SUia>$9y0u}!PzIkoJCRdXJ@v z;kO^%4+Hn*hXFl+e@BD!6@*?v=oN%sLFkVmbnw3kLK_aN2QD_<5NFebjmI4-m%9mw z^JSIB)8T>3!#c#pHkR#=dVi2hoNwN|LH7P24}Tz|;K;xavg_x2+UI*997(9(>wbQ2 z?f$8@`&S!({!FMp-wgTbXvh_${;Ow3{>z-wf7_Xnl(%3K0BVy2GWoMd?}fr|$KZoa zzC106%OC4aLd-wX>`STh^oB1Eb2DNZeAAF^R)B#&wiQGklAC_}CgZYxAV3E`8`PP) z%t*r5E;|>}J`a&X69#q8g|!?*{DSdG@|bz~cu+Ee6r}SM0?4?$8Ka&U2lRNWiv;M! zXoB(-!3NR7II+grRCuU2Q}Y%R>K($o6D_!aOG!Qs43ZI#p6N+oS$l*(2y`aT??|Oj zAN2;H_iA9K-^4Uu&a@;lm%_q82Q;N#gA!+EMu9ZjBV`2mq%f>pzF8GC83N9o!%L_m{&p`@TiA-P(zb7rv? zy30nj#xpjvRBdwCs=WgLTdh817Pd16-gPR zx=|BwxH+GQon_sY78k8Vua!`3ovQ+m&T^;SNR`X(11*>q)IsdZwcDkPH-VlxfrL~t zj^f1S9sw2N*VisRuxnrvqvJ}k{1=uLUWrp@5_AqT$Ouk;X>)XGmRCPQ+NsZFl1{vb1Tq= zLN>dhcvLN8Q>6CN#}09KvOL4WmD~f7a8?QP3_k{^ASTV1+n$)!SJv5h&-V8aj-Qje z`B2_3ed>faJ3kB-e!n;F6uO4IHNq{H)ez}@aFRv`Y2QdF4`n^kp5Q@b?!TTny6~?} z!5p5Ln+n?P@SnM%;$^_7htdabLZ=(VYqdy?JMRDi+f;m>Jkcvu^n0*%e;e=G+O)X@ zCRD5Ph99Fq9|*M*h*OK=&rXX!Di+=#C;|1qW!Q^%|M5c_9By#hs6MOWF(~mR5hRXy zmPr3doRN|EHs<;b!aG8k>|&+-P9OA{5|=9pb?o>*ellRvJ{IEjD77;#(4!tOO-Y=Y z5pR2y@fOYoMo1u3Le9H}uls;y1o%JV(-OU+Z;OQAFR%zZX?{sZ5YTxx7*0S&WXM#v zCo7CMtzws$>B@nPB@zBOa(jm1TN#GD%oAdqL9}Nj%o_6Bw)Qw*4D>ipnB-X|Zbd6~ z&vR{*r(&tHslz622FnkzXro5HYY8Iciy5szx%pJCH|pE%4~D&J%C~)CwJfKGc}fp& zl>b}Lj9gWC|JId}tKOgg-Mv4EKN6&dHl8o3AWp{-m&XH*mq!DLOXR^%0HqhIkQchT z7p61v9+wvm+zW5Si{RLch|-%x$UB%+-J9Imn=;m$8tzRy;!St#%|PkHDCEPe?!)5j z!xrnq0r%k=@!>i4;iL5B7xEQQ_Z4*Z6^iv0f%}S%_=+9-N>KVq3i(N?`$;?d$;SH0 z!~GOT{FIOVR4M(R2>Cx#_kZE+uO92K3HR3?@z*)_*P{d(2!RaMLB`G?(^!x>9Ar5H zvOWeu$$rqGj`V=#<>j9}dxl(eMXn%td3hoC50JF|=ax+*bANY*k(Mx$xsgY5el=(R zKZ4y?Ht>I;4gA}O{8H{?lLL?w?Y|~$pbwI;Wm~=zHqp<7&Gq*YHpUMLo9|{CmBO;ZF_3< z;TSNc0h4pRq?O!ay|j}BwqDjNxVBzCD9yQ1@#TfZM&*bxY@=#I@@cbYts`dD;K9R+ zxL_1z#;p7ZXz} z#^L9U%axS421gY#b|{l{9_pw3u{B@yIXOw}@p)B*)Y>KR1Wxcy&5YOm?k^ zo&e$INE3aDIsY`w2`XZE+0BU!oD`zP~AD>wIngqj$Y+^RZEw7{K0(LVA#K?yap9VS!ID zo^#9PJUgD?t{(1aq$FpnEr>Bc4PT4-A#JjZ&r5h3A+8iV<*qFRox_0A+`skAa{I}F z`Rv=%7nscWs?dXvGx>TBkGQ&J0yWiBFY1Ake4hrSS(usd``~S;`NU%KqP~gPA86kl%IpIkYnLFIBXrCV@_2ZuYK2*OzPp-h*Qr>uRTG~ zngMUmk%9;xTkwZx%C}ZZ7z|nVgwod^rX-#y&Q|OaVj1Y)@qVpj`NS@vT^Nvn8P00u z^ZM=_GdjHAEYWCHKWUv-S)j|T$GQ7clACG5{jJfid&{p=YW2hTuxC)zQNP^&#?=ZS z4}SsE%Zo6X6x)T9J%lJvZM^yU^B@7LTV1;eYMUywG=RMK#MeD=Gm& zblsOomxDHvW%a2*<)SF8lMQ8|B1sT`_KOEJ5vcSDSKv0=R*^n<0As|qFW6v9h&Q6RQ2N?YEEWWv^B zCSBFuS$|b|yD+c&ky?EnFS^(tU->z4!U*zAU+E$WNh2lh=v7R1Dhy=T7-gH)q~odgF0h_y5T{+ zBSHPgL4%aRpM-+Hs0R-@2am)CkHLc{MuI1ggQqD&W`#oLM?gPXPv+v{($LUA$^azy zf8PT^a{oU8{eQmB{k!JfkZlQn`CLhYKc*yji%ASX4f_?--Sj8ZJ@!wgJJ!#Vfbs8= z1g)q)lmw)|B?)9P>6o#>7{7}oApgfCL3z`6NkIB{NdgXR7+?>}x*?Qd)TVhLxtp&# zKK*pNl2|2ZBQHUdmb;zAf{VL@P77w%$#`4dzVdCfb6F*O0(`d;mj`(p`)}O#4%UXs zPt~UNDxsZPe@RzXx|8!l_odm8Cex``%FEC({xAB~Cn;ElF@Ni}_uzeiP&^}0%sCD~ z?E#NwGz{~e4V2MvMm3Ys(VIbax2l;ZL~aIQyt64v0Y1msNm+jA=q|D<;D{&DZq-UD zvQ}4v++ttXtSDgG=pEJJ*;G^PKC`bvlVII`mIyuHE+HcX!0SSK&+YmfC*jm&DmK~& zL%dt)9g`Nt)JF?Cx|k@$A9%!UCTJS(QLTU*FU>Z@5e<|K1N0MHO71Rs`=@9jC$ZCw zw@h*LQPzV`gH$DC-*R|2YUmN4GB+}%0)1M-^@t5#V0PdW`8Klak_rJ^_D#dQ@wxO} zlp!r=d8(pZi4320fFDjZY(WC&%6P#T_U`m9BBGPq=|x+U(*QayXTcC|Fb1uyK%REu~ zWck^vk1NNdAG?v<gs5w|24Z7a-vYj*5#ISqr@}$`m?RyH z;JS$?hY{WyyK9tfW00YA;q&=Hm~D>R3i}NrYM2S*JJ`~$uW0!v#5+#wY1~; z4CTLaOFoyHi+255YWSRZaE;@|Q)*yGz&q||SyDxC*P+7YD3YjG7FwlGp334esJ*_^ zqef%rrH#^=>T=MnL$UL@31n1qnGe&i^HI-@cvJesjEi9(U891nsvxScz$gGZN@!M< z7m?+wd;3hI(wUryg3C&aM+*$ekLJ$#>U&QYH)dokO{e1e+N&Dj@rmxXDvpJC8+{th z$wNoD14@yk5=c9P{3x{7s_3=Ty_RM9Bc5)f>^H@M-mZY0RgfyV|QTvF4}|sAJZNW z|1^%c{-3HnT)qFSJ*Ke#w)XJC;`zU=ZLCGdvq5(^kLL>8Vg={7D+`zw(3PPz&1u%j zx^2mGSgy+lcA~;7)7(UM?YY&}rz?eSSoZ6C!ODnx?(vN7XKpN8V)W34DOOZ?Glk9H zuKGo`wt;yskLQ0Y8cDK#`Lxf|fkNUNAXgq^qR&$V(1aebC=NW1MthxsN=(WCsEMDp z;*bU0z*F(9%uB5U0$Vz%(q7#Z&?ml(lpIMV0&rQT;i!yAP(4}n2c$m5x1yw?ZBfNw zp4T^Z2Lb>uB_LS=H%NBD2 zX?|XJRSAex4`b3GD`yp3SonN5a}rUx`TFWVRB_ukI0~#|YNEQi|MH*C*L}rPsvx zQJf50j9+TgEi$~$c=dp6Rw~0DH4l=+7vW6gjLhXv?TC{^AG4BYpP>6U@5PTOwv52B ztK1A8C9h>JUDIAs(O-P}lAh7@!3R`TF7={B+&c;n(Z+fp`4|td0ced}@zDVW4-$Ik zm(?#ZK=|y>GGE*~0q<^jsC&oY^x=P`&FJk8tv!kzd8<4F?7vymuF8mi|o{ydIn&oPIxZJ*;Walszr z4R#heDi=zd!L)o^pTUQuit6NHFW>qME3UjL7Fn^NS;kRM_NvG>8PwKC<9s17^1Adc z@j9&-#%NrWJ>fzAx&lufSR3PcIoAp^0}xa5ovb6FDEEe(avc~~RH+hXRdBuGCf`U1 zb0$b4%j#jv*xeb&YU-5@h7oM-5MT@ualVBevHipZ`Rf`&v!cdi+;9G}6vVuRJP#?O zo-06}*5L*au|lwP{GU10Gjbue9`vsT6z31q(w{D}@*nGXyF(OHAx%tcYlMfcvszyk zc<-MFP6BY9>CpRIZ3q)X(Vw8JGG>s?ZcEVCL*7E2 zTj_YDtW>y&u);=&IE`;XjbK=$UC-Jt-doe@1kN3-ju515*gYVDPB7rQbc%z}%tCu7 zn9G}Cijzf=A8o(lbl>g9cCn*P4O#&C!3of|^#UixjhvPp35e)k$K9}Bd~tuJ-QsX3 zs#3z6@rhvn$FKZUERT#uJ2LvroM9RVL0+w4#vSOg;;TTypH|9as5GRca2 zjozvJOZ_BPk;;CL-rmsHMyZ0*oBWne@2y9xZ`$4cjB6Dxb>P>C?pTR}%7_D$=pCJO zl#5;+wGDKe=n~wm2~D!bDZV$hF?3jGa^9mdaK^aUP;F+FvlYcqJ9SC7mybeVqi{`> z`IeZF0$!1=#nZYOqEL7%tMU$1>wK1S0N#GLe<#H4e2#&zrc*|FHzMJDo=v5uTdjXL zy6${|FSMrDM0qcM{CrX9N_+f+3qSwc#}SDC0_{-~!8IDea}vQv70E9gDWDN4=n^Ru z7b#K`DLNV{b`mK;6(uPgC8ZH1?Ghy$7bRa4r8pX;d=jNf_3nxAyJs5jUbwtdk9((C z^G-#S`!;I8jG|Q!>HmSgyW)4oPVT^ke!5zii)0|p2(7z zsHmtvUqAW9de$%6V*Vvb^zUmXywXO0Ol@?5MG8O#{E9Z}{7D-f|7k9R_p>%)`@6J} zANmjHGSt7NjXJTIk*iw2k2a$H$FvdCpR^J6f2uZ;|9{$u{@<^SZvKWgIzsvPX(Qww zE?z1COSTD>dfN-An2Ji#*@P*w?M)Pviq87O#BRJT-G^%}Udq5Y@|6&ONF3?9o-9R= zHv|-*ah-rLP7_B#4$Kq z9+q(9Ec)x&Crb|TtAO(icRhv*yD7_-$@o;bikO?_O z{wQ@6A`FnN>@&?r$`2eCo(f{fYfSnn>p?TQGtw29TJ2-xPKFec;CwWbPBGGS{ zqd39tht~C!bpgkO_>+rD!r)V&i>8VA9aIKviRN&$U~I{Rl0m;Rskl>tmPXoB5)ZMo zG*YE5@xZ3+penZ78yNb7 z!E2SX${HJ{;n@;Alq-0Y+r=<0qY zqTHnJQTdlfYPxjE-vnOO)GXF*EBe3bmFqNX4PP9Fb z&?5NHCSFrIRcIFQ2p8Q0eUVnHn^HBW$pro%_Rcac>OJrK12W9epp<}gw~B&*h)79; zGzf^&-3`MqLxU*Y-JL^&NJ)uEDUH%8(lz(&ara#3+C9C;vwNM}{{tTQdihZNzJH&Z zwzWmzlD|coyZP8nUpJ5wUF(_zrMPDS~zRIOwi8WGC zOTwMf&UkiWsH*EqS%jM=d*!aGXiGko@E2;X5* zJUP;H=_da;q0^=VGq`Yx7>D8#V<)T?-^)ngj$voGaTVjvsqt;K`$LIoyQLz!067P- zx53?IxyajS=00Ao!&%%n=vw{F6-9Sn+tm(b>m-}=zxHjbD!EZ~y<=53fc_2C^jWgk z6-%AU7m_cTooT^mAn8=?cbR~F>Igim2htPx7uap9CG4yO0j?9d%g#mBc$=nD31isY zE^O^#n|g&plm0iO3#XHVH4rs8C)o$=<6dk-t2!j7BHZ>!tH){{FPbr2BwFjXr(q5B z3YdxoHdpXJ!1eDwc$FiNz#syDXGTT(+E}3TjtEW&&9E~g=vG%^#B|hH+s6g*x?Z*B z9R@zT#Tr(k)JGKMujhqH*dw7Aea`LU_^*BtKXC9!tX;?|HN58?UH8@u&B+PRYqf?~ zbdU)f*`D*bX+F}4jPTj{o~)7?Ky3N=e(wOI_tmp$OHl3m4_6LqUSHXC(&(7zNOaMk zY}%4o6`T#Q+k0W-I_=ML+IndVhw*C4i$!4{-F@y45qxG+233DesJb15 z5}ZVT1ggk?n13Pq4yrhQ6;!qVwBSVZOHc*IVr9gV`a1a+y59y>4u6L#n*Ua)D)<3Z zVgKQR)74*qDyctEaQYLWs+{Bxg(@+MorplWEPAq6-I9Vk&muyz7+LPWlF{CI9@m$} z%>U|@g8NQn8g4eL)cqcngq^57xomcgS3TbGy9i)jQsa zZN<&u4Y=QDNbxzYTQ28X>?^q^JB9JY%H(`>&l}8cK5-sZl_sbRieSckmWYlv&;J68 z(C+zL+C^cOyN&q+P|M2XxvDF|XofEd)XY*Y#1oT$IwC0h^f?uPmnRC68H7?)xoHTq z3U%fyd6$)>9|1JP=(O! zRX`CS^8r(rZxZ835h+`NoM?f@V|H~qRi~nn?#N56!Rj_iH?obaEcj~bM@xE0$f8sR zv7Oz=Oms`I7J@DGqtn${(;UlgkrXu$hm0Ezj+9wiaq57H6>mf<~BIEK9I6uPPwwCXIl=Rw3Y)zXs`I7?Q;8 zbx80Y^B7`X9_ZZAT6O45cK1m}smBIR@UAtT@u^XVcdyVaz$T+L+bP~x(RX&{*yY8U z74H)wm>M`l?8T$JQaD571dq6B17!&eHX}4o$dl22$-gv+&3Epqr)fu$X*oIl6Aki- zyPXk3Wno&O^B5moyRbZL!{NA3B$wlGN!WK7z1Qc-I4)nrE!e({vHwJhx9*ij$qp@j zJ0`ouEg~s};Q-N7BN3QZ-$nM%bQN;`q|YFI<|P;WHsASAchnK+dixP?HG){W&>nks zzKq{nFA#n0+ixAGl#Yj2FqcrE9(wa$$_3vn>BPHEfunw5oMA>%OTKE+3y!2n%!h%w zFSLgBb{uI_f>zn(5-##t-w0B(3e+{y9#s#sXITufpqg@jRn=UXD)CHPm7syRuAtoO zDc6kp(<2g^IHf%J*_sjH>@nWJdz22)=?bhdU#wwQA>ABb%_o_m zcZwc)7_2FkFx?(=5F0tE=N0QVneB?GJoCchA!zIh;jJzD^w8G#KyYlt8P?{3XDeTJ zJQorsK+id5jU&`}kx~q;^J%-SB*5k)P3U@C}){7uo&z(ed9|Q{Gm57Dxv*iXG=)tvU1!VVPJ{Pb{KM9z z$q_f6M)#Nam>lfh+x6;xA7}`UULH&iL@l*V@kAf?S)_c@Xw~eR<36|=IQ{uiE8lXq zt@|*6);7;-xO|S)rkyGRgmP^J&DucHyH_04v}@hnCZ4kbWo4wsF7|&-ukO zuQO4q+h?gZKA5vervrq~)Dp`)pAUMVGw{$3H12|xXNbdUhDWZ3b_hY56Qbd{(qV}D zhCKJ7a5XJ`>+71!b_{~nOSm%D`cAt*?PrbIT|uW%53WNK5i-9ErVzeyb_Ul!K4JbJ zF}wZ$E;udyC!wk?vGHAE^HE~!<)pS-N$pxmoo-3p@ku>(Nqz5<29A;jFDDP(N*>lq z9(79|k57JImpu6{dHN`M_HxSnt(1>iDT{6?%ke3zbt&ubQZ|oLwlAmd+)CZmO5Jx$ zJ&aF1u1h_AmwJAbs!IN2-32JKpoN9SvuDpxwY4aN;1AWX?|BWCTl%iC;BQXB9|oAd zXky=Wh1L9`Q}8P_g?(eUzb3nR1)>D8A2SOs|4k4h|GQc6iy+4K>)7q51u?2$VmE0p zGb*U`bp$c$-^Om&|IThy|E=t%{XguM`o{}mbicrEWdBFm?T;12{_nBdZW>rVPmHW* z&|h#joiHplcg#uNd^#tzz_`uK;FFr z@pkw;g?cS_TdLRXHXq{j*X@9SWy_r~wacO#( z$LSc9rgx}ETBX=p>U9K}W*K&A@h~mDw2NaP3>Ikm%*C5`@l+F&LlbW%tj&i;IN)5; zS79r=?2`K0ET)A({RcITfIC!}7<8@Jbj+XJ7=(d1g#vh>81xYRq&(sW-JN{XVxdNI zvs6!g<;&a3pDGJ~U?+C#PPw=fCKdXG`np#8W$&vIh}n7OHPOgbdLFh`#HY_*Q(<%KmzY(ueu}%qoW+T2TH-)luWKl{b|hi5Uz&iNoE2Rf zdkqW!oK`-#DDIKpXC_&dN!et0PbYq;1YVVi1~C)nIewkIS(%OWqPni_MKO>*up1YU zWUgHLzN;AX9_*(4jOJ^tA!B203S)T-h17bSh564pRvRn&1o30>7xzx)4BM}_O02ldRTeR| z=EuKe`oP=(w6y#;;zEj||SWb6QCpk3iKWVnusJ zuO;xFwELw5VS|&Fu#klDsQ=n{ZK_wIBMvm*;A6R@sx>c#7ftu~97r5KEP<18j`b8c zOAO1FP~kWFK8%~nok6?5D1YiiGh@^LnNG?%)&heVd4gQ0?l=>ezdAYLmw|1mt>}SPnMKl1`rZO;5(~Y;Hc&^}Oym(W5t`wp26esg{^> z^g=&c_@(aIf_cxXAuxs9zWVHj)SZtj)Xw|K!l(C3XRzegYY(gS&&7FC`nT2(XPM-* zBO2Du(noyG4}5G9PiFWbaCJy969g;`!4|-nX0*4c@j*(?%FpuK6r1?snKF&AD3E~- zv=|gFn))!IDi#-fDFc1n#Q>fpzBJO0Ztgha#zE!N1nH%H`3_C_11)a3VhDBn-Cp+N zKZKSsL&XC{4OR;&(&0_*6cC) zKevN@W4E6ODt%2stm*$QyIB(d1d%B!Dx#+BP}LF7pFc-sw@_$?8nOG*+MJo0`MpsQ zH7)W5%>IeXD!*%S{`nH;=H?dE_oZ9nf1%mSe`spI*R=SJ$o`r{b{mWASG36ye5a;y z|0*IA|7kVtuiGZ~^{OL^cZcNoH+J7DNpbBBt7=&7jcA$_?~UrZZ0wC01aR$-KZ&*4 ze`lKebJY>E9jw=9*HKZexdZuNSFc?B=2};hJJk9gpEq=se~7k0h_+ugv8G;%^GLbU zolz;}wz6Ut%)7cA-&gO$-f~WjHQQs2T28KADmmF~JlH(hY6bC}ZnytY@vUD}(`*^5 zP-aB#yg1V0a6f3g>ar-9?4@rY%x{hg`$A?L$uZpFio+v41J;+#gJu36b#(Zj+pdyX z+6K4=je~tPV>*fKc(8@&^}#vf&7NgJN}(CVxzZi%+Mj|o`!Yxo-5uQj`B9M|LhnyQ zboWn<#8g@O{31%?FwE$#0f%0ab4SwfIxb${BkDIp&q^5GmAYPeL||lpDIxbHM?~&0 zojs_MCenTW*5|ogr~1#y+o8F_OZQ*9zyF+a*q1AE@anbq@ekCr{}hp3LNf?_xOY$J z<#8cZeq@mi#bs?Gy7p=cu>fll4BQtKV{$L5OAO(3=-b#}%bgpgR%aXrBkbcMx+P^k zt>lI$2@DS0qXcrE>$J=4?|QWNE8_Z#jQM-t4Y>cIZE_mdhq6j=SUDyKoVhJY>v|7N&4j?;#FRA{dE{MPi-4D#b!&M!IO6xh*we&d&{6x z7!o74hY$ayKkwQ#4%%DTFQPbO(rJx2h@8tG=hy{Y2+_RRQP3x~lVi{j3g<~AcQjRS zh@P{=Xc>4nAdyp)dQ?w)tcNzp^ZdHrN)a5_nk$C4^G)`pGvI}wzDC}3{W-RgnJve9e9w1`HHk}Y2ta&L9ic8VA zyv9zL*`v@?vDb+M4)SO2&)KJOPcNOSoG9MS+I_*Vm#0I0tQm5AC?_8C8ES&j8vp+2 z{&-$~d};eqwEK~CC{;l*&)VW68u!it>VkZmqqTgB-1lp9A2>jsbe11?8v`cstLx3z zG3oZhY@CjK!44XaUxUWdt9RW9AL%-j28cbnT{76>M|4EtEtrJe{Ulq2stRc~b4Pc? z&Dn=+U(h?0CKc!7lat52PcZ}esZNY$ecoU~wp{FzI()QA6%w56V>8$~_|>-NfdM4i z$cMS!Hj9u&Nyt-w-)z*Zm<#Z>I64ivHjyUKA=`>{8AH>>>@3F@r|Z@-i#DzRaN>z2 zc$q0{Q5#`m?0Qp&f82ycTIYHm_gzgLCNN-oL~d%)?=+U<%`POwi3y|i(VHmiEGGD_ z2E2I>ph558*Quihw%9R#{K%B+AKHQMCLT!oq@@3zwIdS5s3nG&D3f zH@CF3bar+|)pZ921%-r!pz1PEQW&Z$1GSQavMzo>E);M5t8ej#I@tHTRa;y8+xEnN z3uArbs=p>zd0}4$pqKrKOVRXiF2%UND_vyYan-e7$5o)8jD9ixqSA%UjZ(U>zDg|y z^KVkRoYyuGv;G{LwI5?FHtUvi3X$A;ttBU1g`3tPStXxtdYCtjn+LNuzg_L+sm`@N zE^Ru;r?P52p;#}u3XMc+OoV@cofmRp*qlgefNQx|Oj=T0|ctmV!t*4(0< z9!|viPA_j4=jWfR$vroxW=TT%J>0jFsoPvmlBY0^ zu{mp9;UMorCooTn-b{F8?h5Q#YM1GPk7DSNJKJgQ;bK|<_wjO8to89qUT(?pY7t`d zc&)69`((Xp?CWwB-C7bMCPwSasz7@nabK25tSF)pPaF?JZjOFA7E8sJ2QE7f+<5*1 zZ#CPGx*Z36&y)=I3Ibs)3TY;fYbzfwhOZwJ<8GmsUS0(H-t0@qcjuskB_AdZZk=vti`X_j3HRUq&&7U3oHh?wKapG|i_>cP!cZ zy!$HR>R?pYwm!9@?1u3tam4uP@p8A7QB@axIX4ln`mNnR|2OU6s50p-M@ek>r9MYt zlYC@83{0*{RNj_PY6w%u6UTg(F!JzGb)ihZ0Y>X_fWh+{MQXP>v@hq3iU3NAy(8Fl z&j_81*XYV5O-R@z1uVtdTiHO6o&PkkZ%vC-IS%EMKQiM8o~ zJ%ofVuo%~lG9B>rpcSl81|{p8#S}VxK^R^JW09M~);Vk=A1L$Z@0-K-IDAQqUmhsM zdE-LnsBJA18YHDa=WX1#cYMi@0eQnt_9mi>-zeH^CT<(GQ-^HS|te_q3TR)I3x^d}@Aq z3ggb(%Y(!!jwWmm&7=rov0OpuNEbiEu5p5u~6FBc?cH1qkh>)M@e`6qfiA~{cr

AA3vfMqfW(5pR&1-IF1&bz_zY$Nyg!MR~{M~A%F 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�w<)yD;Q(^Gq$$hvw(YHnrTzOkKbiVJifin-W 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 AlK=n! literal 0 HcmV?d00001 diff --git a/Common/DeviceResources.cpp b/Common/DeviceResources.cpp index ffc06598..0b2ff8af 100644 --- a/Common/DeviceResources.cpp +++ b/Common/DeviceResources.cpp @@ -1,11 +1,14 @@ #include "pch.h" #include "DeviceResources.h" +#define MLOG_TAG_OVERRIDE "DeviceResources" +#include "Utils.hpp" #include "DirectXHelper.h" +#include "UI\Utilities\EffectsLibrary.h" #include #include -#include -#include -#include +#include "UI\Pages\StreamPage.xaml.h" +#include "Streaming\FFmpegDecoder.h" +#include "Plot\ImGuiPlots.h" using namespace moonlight_xbox_dx; using namespace D2D1; @@ -220,7 +223,7 @@ void DX::DeviceResources::CreateWindowSizeDependentResources() 0 ); - Utils::Logf("m_swapChain->ResizeBuffers(%d x %d)\n", + MLOGF(Utils::LogLevel::Info, "m_swapChain->ResizeBuffers(%d x %d)\n", lround(m_d3dRenderTargetSize.Width), lround(m_d3dRenderTargetSize.Height)); if (hr == DXGI_ERROR_DEVICE_REMOVED || hr == DXGI_ERROR_DEVICE_RESET) @@ -286,7 +289,7 @@ void DX::DeviceResources::CreateWindowSizeDependentResources() ) ); - Utils::Logf("CreateSwapChainForComposition(%d x %d)\n", + MLOGF(Utils::LogLevel::Info, "CreateSwapChainForComposition(%d x %d)\n", lround(m_d3dRenderTargetSize.Width), lround(m_d3dRenderTargetSize.Height)); DX::ThrowIfFailed( @@ -374,9 +377,6 @@ void DX::DeviceResources::SetSwapChainPanel(SwapChainPanel^ panel) m_compositionScaleX = panel->CompositionScaleX; m_compositionScaleY = panel->CompositionScaleY; - Utils::Logf("SwapChain logical size: %.0fx%.0f @ composition scale %.1fx%.1f\n", - m_logicalSize.Width, m_logicalSize.Height, m_compositionScaleX, m_compositionScaleY); - CreateWindowSizeDependentResources(); ComPtr panelNative; @@ -387,6 +387,7 @@ void DX::DeviceResources::SetSwapChainPanel(SwapChainPanel^ panel) // Setup Dear ImGui context float dpi = currentDisplayInformation->LogicalDpi / 96.0f; ImGui_Init(panelNative, dpi); + } // This method is called in the event handler for the SizeChanged event. @@ -490,7 +491,7 @@ void DX::DeviceResources::HandleDeviceLost() m_swapChain = nullptr; - Utils::Log("HandleDeviceLost()\n"); + MLOG(Utils::LogLevel::Error, "HandleDeviceLost()\n"); if (m_deviceNotify != nullptr) { @@ -541,7 +542,7 @@ void DX::DeviceResources::Present() } else if (hr == DXGI_ERROR_INVALID_CALL) { // Try to reset - Utils::Logf("Present() failed with DXGI_ERROR_INVALID_CALL\n"); + MLOGF(Utils::LogLevel::Error, "Present() failed with DXGI_ERROR_INVALID_CALL\n"); HandleDeviceLost(); } else { diff --git a/Common/DirectXHelper.h b/Common/DirectXHelper.h index 7a342a41..3c9aec6b 100644 --- a/Common/DirectXHelper.h +++ b/Common/DirectXHelper.h @@ -12,7 +12,7 @@ namespace DX // Set a breakpoint on this line to catch Win32 API errors. char msg[4096]; sprintf(msg, "Got generic error from HRESULT: %x\n", hr); - moonlight_xbox_dx::Utils::Log(msg); + moonlight_xbox_dx::Utils::Log(moonlight_xbox_dx::Utils::LogLevel::Error, msg); throw Platform::Exception::CreateException(hr); } @@ -25,7 +25,7 @@ namespace DX // Set a breakpoint on this line to catch Win32 API errors. char msg[4096]; sprintf(msg, "Got generic error from %s: %x\n", reason, hr); - moonlight_xbox_dx::Utils::Log(msg); + moonlight_xbox_dx::Utils::Log(moonlight_xbox_dx::Utils::LogLevel::Error, msg); throw Platform::Exception::CreateException(hr); } diff --git a/Common/ModalDialog.xaml b/Common/ModalDialog.xaml deleted file mode 100644 index b4493103aeed41783e53b3b75c058c64704d5fd7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2640 zcmcguU2oGs5Zq@Z{zH^UB%B5iLXn|LiVA%QAW{f#DAZ13HE|-_rEY&bFtc}8f5ZtE z1!N_*?{?>Q=Vtf(^M^0TlFM3F(vj~HN+PL5vXNL`N>}>g$r0`y%*1km@c^^2EOhot zGMUR`c>#-Kc_k0!f%tNw-kEx2@*4Itpl{&A`HsDE8NhOdxfB=ITEm9xuHdndC9uCD zvTqp6@f6_SlQG@`#Vg}>Yskzdwy4C(WQ)~$xKiY>L}Y7S*TX%+NCMOpzd0;D&2kOf zYt3|yb$jvzEF8&mJmv5teh-)lkW!oH48Df9ebEfy%RK*8MElnz`xnX@*S|u{W)BA~ zINy)D22u{(I_9{6(@$~+X1`eOygQAM`4!@+uJRV62~civkHIVTQ&rUvBUDL@45-XZ zqd2k8RB;vYGiDOqA;ZJ{(5PD~CX^94puQOAIo@*{N!M0GS@FKS(X0Yg0M-9a*$6fA zk3iVK*IBUQrpPeB*itQ?LVczZKfo`*Rn%c0`lLq_Z*7?BNS&WHUZ&n%J-d66r63_t61FA8WbYYLjVM2`*b- zCSYWS8oCtjYK*+thwHe^(d_+uRgh79(Q!E6wDe@f_Q#ZJ^doyV0hm{;U#zuX@*;Mu?PW!6h=P_n>oRmSH};>txK^d6E&npTeL0 zb^_~L%T(X8>*l5yFW2V;?>uQo@Li#E89u8WxeYt}@? Lxmo@OGj_iKJe#MU diff --git a/Common/ModalDialog.xaml.cpp b/Common/ModalDialog.xaml.cpp deleted file mode 100644 index 55bd8058ea2e6b6bf499883861b504f233160ce4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 37156 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 VCrU=UU!umDMd#GL+q~bof_op&C1wBs diff --git a/Common/TextConsole.cpp b/Common/TextConsole.cpp index 6f842999..24ad0703 100644 --- a/Common/TextConsole.cpp +++ b/Common/TextConsole.cpp @@ -7,6 +7,7 @@ #include "pch.h" #include "TextConsole.h" +#define MLOG_TAG_OVERRIDE "TextConsole" #include "../Utils.hpp" #include "SimpleMath.h" @@ -28,6 +29,7 @@ using namespace moonlight_xbox_dx; TextConsole::TextConsole() noexcept : m_layout{}, m_textColor(1.f, 1.f, 1.f, 1.f), + m_currentWriteColor(1.f, 1.f, 1.f, 1.f), m_debugOutput(false), m_columns(0), m_rows(0) @@ -40,6 +42,7 @@ _Use_decl_annotations_ TextConsole::TextConsole(ID3D11DeviceContext* context, const wchar_t* fontName) noexcept(false) : m_layout{}, m_textColor(1.f, 1.f, 1.f, 1.f), + m_currentWriteColor(1.f, 1.f, 1.f, 1.f), m_debugOutput(false), m_columns(0), m_rows(0), @@ -63,7 +66,7 @@ void TextConsole::Render() const float x = float(m_layout.left); const float y = float(m_layout.top); - const XMVECTOR color = XMLoadFloat4(&m_textColor); + const XMVECTOR defaultColor = XMLoadFloat4(&m_textColor); m_batch->Begin(); @@ -75,6 +78,7 @@ void TextConsole::Render() if (*m_lines[textLine]) { + const XMVECTOR color = m_lineColors ? XMLoadFloat4(&m_lineColors[textLine]) : defaultColor; m_font->DrawString(m_batch.get(), m_lines[textLine], pos, color); } @@ -103,6 +107,23 @@ void TextConsole::Write(const wchar_t* str) { std::lock_guard lock(m_mutex); + m_currentWriteColor = m_textColor; + ProcessString(str); + +#ifndef NDEBUG + if (m_debugOutput) + { + OutputDebugStringW(str); + } +#endif +} + +_Use_decl_annotations_ +void XM_CALLCONV TextConsole::Write(const wchar_t* str, DirectX::FXMVECTOR color) +{ + std::lock_guard lock(m_mutex); + + XMStoreFloat4(&m_currentWriteColor, color); ProcessString(str); #ifndef NDEBUG @@ -119,6 +140,7 @@ void TextConsole::WriteLine(const wchar_t* str) { std::lock_guard lock(m_mutex); + m_currentWriteColor = m_textColor; ProcessString(str); IncrementLine(); @@ -151,6 +173,7 @@ void TextConsole::Format(const wchar_t* strFormat, ...) va_end(argList); + m_currentWriteColor = m_textColor; ProcessString(m_tempBuffer.data()); #ifndef NDEBUG @@ -185,6 +208,12 @@ void TextConsole::SetWindow(const RECT& layout) lines[line] = buffer.get() + (columns + 1) * line; } + auto lineColors = std::make_unique(rows); + for (unsigned int line = 0; line < rows; ++line) + { + lineColors[line] = m_textColor; + } + if (m_lines) { const unsigned int c = std::min(columns, m_columns); @@ -194,14 +223,23 @@ void TextConsole::SetWindow(const RECT& layout) { memcpy(lines[line], m_lines[line], c * sizeof(wchar_t)); } + + if (m_lineColors) + { + for (unsigned int line = 0; line < r; ++line) + { + lineColors[line] = m_lineColors[line]; + } + } } std::swap(columns, m_columns); std::swap(rows, m_rows); std::swap(buffer, m_buffer); std::swap(lines, m_lines); + std::swap(lineColors, m_lineColors); - Utils::Logf("TextConsole initialized at (%d,%d) - (%d,%d) with %u rows, %u columns\n", + MLOGF(Utils::LogLevel::Info, "TextConsole initialized at (%d,%d) - (%d,%d) with %u rows, %u columns\n", layout.left, layout.top, layout.right, layout.bottom, m_rows, m_columns); if ((m_currentColumn >= m_columns) || (m_currentLine >= m_rows)) @@ -283,6 +321,8 @@ void TextConsole::ProcessString(_In_z_ const wchar_t* str) bool increment = false; + if (m_lineColors) m_lineColors[m_currentLine] = m_currentWriteColor; + if (m_currentColumn >= m_columns) { increment = true; diff --git a/Common/TextConsole.h b/Common/TextConsole.h index 83cdc628..78717062 100644 --- a/Common/TextConsole.h +++ b/Common/TextConsole.h @@ -41,6 +41,7 @@ namespace DX void Clear() noexcept; void Write(_In_z_ const wchar_t *str); + void XM_CALLCONV Write(_In_z_ const wchar_t *str, DirectX::FXMVECTOR color); void WriteLine(_In_z_ const wchar_t *str); void Format(_In_z_ _Printf_format_string_ const wchar_t* strFormat, ...); @@ -77,6 +78,8 @@ namespace DX std::unique_ptr m_buffer; std::unique_ptr m_lines; + std::unique_ptr m_lineColors; + DirectX::XMFLOAT4 m_currentWriteColor; std::vector m_tempBuffer; std::unique_ptr m_batch; diff --git a/Converters/BoolToTextConverter.h b/Converters/BoolToTextConverter.h deleted file mode 100644 index 0a72174c..00000000 --- a/Converters/BoolToTextConverter.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once -#include "pch.h" - -namespace moonlight_xbox_dx { - - public ref class BoolToTextConverter sealed : Windows::UI::Xaml::Data::IValueConverter { - public: - property Platform::String^ TrueText; - property Platform::String^ FalseText; - - virtual Platform::Object^ Convert(Platform::Object^ value, Windows::UI::Xaml::Interop::TypeName targetType, Platform::Object^ parameter, Platform::String^ language); - virtual Platform::Object^ ConvertBack(Platform::Object^ value, Windows::UI::Xaml::Interop::TypeName targetType, Platform::Object^ parameter, Platform::String^ language); - }; - -} \ No newline at end of file diff --git a/MoonlightWelcome.xaml b/MoonlightWelcome.xaml deleted file mode 100644 index 3a22b3f3..00000000 --- a/MoonlightWelcome.xaml +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - Welcome to Moonlight - Moonlight allows your Xbox to control and stream your Computer! - - - - - Servers - To make Moonlight work, you need a Server, you can use one of the following ones: - - - - - Sunshine - The recommended open source solution. Compatible with Windows/macOS/Linux and with any GPU. - https://github.com/LizardByte/Sunshine - - - - - - - - - - Hints - - - After installing your server, wait or press + for adding your PC manually - - - - During the stream, you can press + to show more options and enable mouse mode - - - - In mouse mode, you can move and scroll using your sticks, Left click Right click Guide/Xbox button - - - - In mouse mode, you can press to show Keyboard. You can also use an USB Keyboard - - - - Is your screen a quarter of its size? Change the global composition scale inside the Global Settings of the App - - - - - - - - Support - Need some Help? - - - - GitHub - Check the Wiki for Support Links, documentation and more! - https://github.com/TheElixZammuto/moonlight-xbox/wiki - - - - - - - - - diff --git a/Pages/AppPage.xaml b/Pages/AppPage.xaml deleted file mode 100644 index a07b0028..00000000 --- a/Pages/AppPage.xaml +++ /dev/null @@ -1,82 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Useful Shortcuts: + for toggling Menu and enabling mouse mode. - In Mouse mode: Left click Right click Show Keyboard Guide/Xbox button - In Keyboard mode: Close Left Click Right Click Move Press to Scroll - - - diff --git a/Pages/AppPage.xaml.cpp b/Pages/AppPage.xaml.cpp deleted file mode 100644 index 2558f454..00000000 --- a/Pages/AppPage.xaml.cpp +++ /dev/null @@ -1,382 +0,0 @@ -#include "pch.h" -#include "AppPage.Xaml.h" -#include "Common\ModalDialog.xaml.h" -#include "HostSettingsPage.xaml.h" -#include "HostSelectorPage.xaml.h" -#include "State\MoonlightClient.h" -#include "StreamPage.xaml.h" -#include "Utils.hpp" -#include -#include -#include - -using namespace moonlight_xbox_dx; -using namespace Platform; -using namespace Windows::Foundation; -using namespace Windows::UI::Core; -using namespace Windows::UI::Xaml; -using namespace Windows::UI::Xaml::Controls; -using namespace Windows::UI::Xaml::Input; -using namespace Windows::UI::Xaml::Media; -using namespace Windows::UI::Xaml::Navigation; -using namespace concurrency; -using namespace Windows::UI::Xaml::Hosting; -using namespace Windows::UI::Composition; - -// The Blank Page item template is documented at https://go.microsoft.com/fwlink/?LinkId=234238 - -static MoonlightApp^ GetAppById(MoonlightHost^ host, int appId) { - if (host == nullptr) { - return nullptr; - } - - for (unsigned int i = 0; i < host->Apps->Size; ++i) { - auto app = host->Apps->GetAt(i); - if (app != nullptr && app->Id == appId) { - return app; - } - } - - return nullptr; -} - -AppPage::AppPage() -{ - InitializeComponent(); - Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->SetDesiredBoundsMode(Windows::UI::ViewManagement::ApplicationViewBoundsMode::UseVisible); - - this->Loaded += ref new Windows::UI::Xaml::RoutedEventHandler(this, &AppPage::OnLoaded); - this->Unloaded += ref new Windows::UI::Xaml::RoutedEventHandler(this, &AppPage::OnUnloaded); -} - -void AppPage::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) { - - MoonlightHost^ mhost = dynamic_cast(e->Parameter); - if (mhost == nullptr) return; - host = mhost; - host->UpdateHostInfo(true); - host->UpdateApps(); - - // Start background polling for app running state and connectivity - continueAppFetch.store(true); - wasConnected.store(host->Connected); - - Platform::WeakReference weakThis(this); - create_task([weakThis]() { - while (true) { - auto that = weakThis.Resolve(); - if (that == nullptr) break; - if (!that->continueAppFetch.load()) break; - try { - if (that->host != nullptr) { - that->host->UpdateAppRunningStates(); - if (that->wasConnected.load() && !that->host->Connected) { - that->wasConnected.store(false); - - // Show the disconnect dialog only if page instance still exists (no visible-page checks) - Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( - Windows::UI::Core::CoreDispatcherPriority::Normal, - ref new Windows::UI::Core::DispatchedHandler([weakThis]() { - auto inner = weakThis.Resolve(); - if (inner == nullptr) return; - - try { - auto dialog = ref new Windows::UI::Xaml::Controls::ContentDialog(); - dialog->Title = Utils::StringFromStdString("Disconnected"); - dialog->Content = Utils::StringFromStdString("Connection to host was lost."); - dialog->PrimaryButtonText = Utils::StringFromStdString("OK"); - concurrency::create_task(::moonlight_xbox_dx::ModalDialog::ShowOnceAsync(dialog)).then([weakThis](Windows::UI::Xaml::Controls::ContentDialogResult result) { - auto that2 = weakThis.Resolve(); - if (that2 == nullptr) return; - that2->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([that2]() { - try { - that2->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(HostSelectorPage::typeid)); - } catch (const std::exception &e) { - Utils::Logf("[AppPage] Failed to navigate to HostSelectorPage after disconnect. Exception: %s\n", e.what()); - } catch (...) { - Utils::Log("[AppPage] Failed to navigate to HostSelectorPage after disconnect. Unknown Exception.\n"); - } - })); - }); - } catch (const std::exception &e) { - Utils::Logf("[AppPage] Failed to show disconnect dialog. Exception: %s\n", e.what()); - } catch (...) { - Utils::Log("[AppPage] Failed to show disconnect dialog. Unknown Exception.\n"); - } - })); - } - else if (!that->wasConnected.load() && that->host->Connected) { - that->wasConnected.store(true); - } - } - } catch (const std::exception &e) { - Utils::Logf("[AppPage] Failed to poll app and host running state. Exception: %s\n", e.what()); - } catch (...) { - Utils::Log("[AppPage] Failed to poll app and host running state. Unknown Exception.\n"); - } - Sleep(3000); - } - }); - - if (host->AutostartID >= 0 && GetApplicationState()->shouldAutoConnect) { - GetApplicationState()->shouldAutoConnect = false; - Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( - Windows::UI::Core::CoreDispatcherPriority::High, ref new Windows::UI::Core::DispatchedHandler([this]() { - this->Connect(host->AutostartID); - })); - } - GetApplicationState()->shouldAutoConnect = false; -} - -void AppPage::OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) { - continueAppFetch.store(false); -} - -void AppPage::AppsGrid_ItemClick(Platform::Object ^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs ^ e) { - MoonlightApp ^ app = (MoonlightApp ^) e->ClickedItem; - this->currentApp = app; - - if (this->host != nullptr) { - for (unsigned int i = 0; i < this->host->Apps->Size; ++i) { - auto candidate = this->host->Apps->GetAt(i); - if (candidate != nullptr && candidate->CurrentlyRunning && candidate->Id != app->Id) { - this->closeAndStartButton_Click(nullptr, nullptr); - return; - } - } - } - - this->Connect(app->Id); -} - -void AppPage::Connect(int appId) { - - continueAppFetch.store(false); - - MoonlightApp^ app = GetAppById(host, appId); - - StreamConfiguration ^ config = ref new StreamConfiguration(); - config->hostname = host->LastHostname; - config->appID = appId; - config->appName = app ? app->Name : "App"; - config->width = host->Resolution->Width; - config->height = host->Resolution->Height; - config->bitrate = host->Bitrate; - config->FPS = host->FPS; - config->audioConfig = host->AudioConfig; - config->videoCodec = host->VideoCodec; - config->playAudioOnPC = host->PlayAudioOnPC; - config->enableHDR = host->EnableHDR; - config->enableSOPS = host->EnableSOPS; - config->framePacing = host->FramePacing; - config->enableStats = host->EnableStats; - config->enableGraphs = host->EnableGraphs; - if (config->enableHDR) { - host->VideoCodec = "HEVC (H.265)"; - } - bool result = this->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(StreamPage::typeid), config); - if (!result) { - printf("C"); - } -} - -void AppPage::AppsGrid_RightTapped(Platform::Object ^ sender, Windows::UI::Xaml::Input::RightTappedRoutedEventArgs ^ e) { - Utils::Log("AppPage::AppsGrid_RightTapped invoked\n"); - FrameworkElement ^ senderElement = (FrameworkElement ^) e->OriginalSource; - FrameworkElement ^ anchor = senderElement; - - if (senderElement != nullptr && senderElement->GetType()->FullName->Equals(GridViewItem::typeid->FullName)) { - auto gi = (GridViewItem ^) senderElement; - currentApp = (MoonlightApp ^)(gi->Content); - anchor = gi; - } else { - if (senderElement != nullptr) currentApp = (MoonlightApp ^)(senderElement->DataContext); - - if (currentApp == nullptr && this->HostsGrid != nullptr && this->HostsGrid->SelectedIndex >= 0) { - currentApp = (MoonlightApp ^) this->HostsGrid->SelectedItem; - auto container = (GridViewItem ^) this->HostsGrid->ContainerFromIndex(this->HostsGrid->SelectedIndex); - if (container != nullptr) - anchor = container; - else - anchor = this->HostsGrid; - } - } - - bool anyRunning = false; - MoonlightApp^ runningApp = nullptr; - if (this->host != nullptr) { - for (unsigned int i = 0; i < this->host->Apps->Size; ++i) { - auto candidate = this->host->Apps->GetAt(i); - if (candidate != nullptr && candidate->CurrentlyRunning) { - anyRunning = true; - runningApp = candidate; - break; - } - } - } - - this->resumeAppButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed; - this->closeAppButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed; - this->closeAndStartButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed; - - if (!anyRunning) { - this->resumeAppButton->Text = "Open App"; - this->resumeAppButton->Visibility = Windows::UI::Xaml::Visibility::Visible; - } else { - if (currentApp != nullptr && currentApp->CurrentlyRunning) { - this->resumeAppButton->Text = "Resume App"; - this->resumeAppButton->Visibility = Windows::UI::Xaml::Visibility::Visible; - this->closeAppButton->Visibility = Windows::UI::Xaml::Visibility::Visible; - } else { - if (currentApp != nullptr) { - this->closeAndStartButton->Visibility = Windows::UI::Xaml::Visibility::Visible; - } - } - } - - if (anchor != nullptr) { - this->ActionsFlyout->ShowAt(anchor); - } else { - this->ActionsFlyout->ShowAt(this->HostsGrid); - } -} - -void AppPage::resumeAppButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { - this->Connect(this->currentApp->Id); -} - -void AppPage::closeAndStartButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { - if (this->currentApp == nullptr) { - return; - } - - if (sender != nullptr) { - this->ExecuteCloseAndStart(); - return; - } - - auto dialog = ref new Windows::UI::Xaml::Controls::ContentDialog(); - dialog->Title = Utils::StringFromStdString("Confirm"); - dialog->Content = Utils::StringFromStdString("Close currently running app and connect?"); - dialog->PrimaryButtonText = Utils::StringFromStdString("Yes"); - dialog->CloseButtonText = Utils::StringFromStdString("Cancel"); - - Platform::WeakReference weakThis(this); - - concurrency::create_task(::moonlight_xbox_dx::ModalDialog::ShowOnceAsync(dialog)).then([weakThis](Windows::UI::Xaml::Controls::ContentDialogResult result) { - try { - if (result != Windows::UI::Xaml::Controls::ContentDialogResult::Primary) { - return; - } - - auto that = weakThis.Resolve(); - if (that == nullptr) return; - that->ExecuteCloseAndStart(); - } catch (const std::exception &e) { - Utils::Logf("closeAndStartButton_Click dialog task exception: %s\n", e.what()); - } catch (...) { - Utils::Log("closeAndStartButton_Click dialog task unknown exception\n"); - } - }); -} - -void AppPage::ExecuteCloseAndStart() { - auto that = this; - auto progressToken = ::moonlight_xbox_dx::ModalDialog::ShowProgressDialogToken(nullptr, Utils::StringFromStdString("Closing app...")); - moonlight_xbox_dx::Utils::Logf("AppPage::ExecuteCloseAndStart: progressToken=%llu\n", (unsigned long long)progressToken); - - concurrency::create_task(concurrency::create_async([that, progressToken]() { - try { - MoonlightClient client; - auto ipAddr = Utils::PlatformStringToStdString(that->host->LastHostname); - int status = client.Connect(ipAddr.c_str()); - if (status == 0) { - client.StopApp(); - Sleep(1000); - } - } catch (...) { - } - - that->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High, ref new Windows::UI::Core::DispatchedHandler([that, progressToken]() { - try { - if (that->currentApp != nullptr) { - that->Connect(that->currentApp->Id); - } - ::moonlight_xbox_dx::ModalDialog::HideDialogByToken(progressToken); - } catch (const std::exception &e) { - Utils::Logf("ExecuteCloseAndStart UI exception: %s\n", e.what()); - } catch (...) { - Utils::Log("ExecuteCloseAndStart UI unknown exception\n"); - } - })); - })).then([](concurrency::task t) { - try { - t.get(); - } catch (const std::exception &e) { - Utils::Logf("ExecuteCloseAndStart task exception: %s\n", e.what()); - } catch (...) { - Utils::Log("ExecuteCloseAndStart unknown task exception\n"); - } - }); -} - -void AppPage::closeAppButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { - auto that = this; - - auto progressToken = ::moonlight_xbox_dx::ModalDialog::ShowProgressDialogToken(Utils::StringFromStdString("Closing"), Utils::StringFromStdString("Closing app...")); - moonlight_xbox_dx::Utils::Logf("AppPage::closeAppButton_Click: progressToken=%llu\n", (unsigned long long)progressToken); - - concurrency::create_task(concurrency::create_async([that, progressToken]() { - try { - MoonlightClient client; - auto ipAddr = Utils::PlatformStringToStdString(that->host->LastHostname); - int status = client.Connect(ipAddr.c_str()); - if (status == 0) { - client.StopApp(); - Sleep(1000); - } - } catch (...) { - } - - that->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([that, progressToken]() { - try { - that->host->UpdateHostInfo(true); - that->host->UpdateAppRunningStates(); - } catch (...) { - } - ::moonlight_xbox_dx::ModalDialog::HideDialogByToken(progressToken); - })); - })); -} - -void AppPage::backButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { - this->Frame->GoBack(); -} - -void AppPage::settingsButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { - bool result = this->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(HostSettingsPage::typeid), Host); -} - -void AppPage::OnBackRequested(Platform::Object^ e, Windows::UI::Core::BackRequestedEventArgs^ args) -{ - // UWP on Xbox One triggers a back request whenever the B - // button is pressed which can result in the app being - // suspended if unhandled - if (this->Frame->CanGoBack) { - this->Frame->GoBack(); - args->Handled = true; - } - } - -void AppPage::OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { - auto navigation = Windows::UI::Core::SystemNavigationManager::GetForCurrentView(); - m_back_cookie = navigation->BackRequested += ref new EventHandler(this, &AppPage::OnBackRequested); -} - -void AppPage::OnUnloaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { - auto navigation = Windows::UI::Core::SystemNavigationManager::GetForCurrentView(); - navigation->BackRequested -= m_back_cookie; - // stop background polling loop - continueAppFetch.store(false); -} - diff --git a/Pages/AppPage.xaml.h b/Pages/AppPage.xaml.h deleted file mode 100644 index 9ca8e4ea..00000000 --- a/Pages/AppPage.xaml.h +++ /dev/null @@ -1,50 +0,0 @@ -// -// AppPage.xaml.h -// Declaration of the AppPage class -// - -#pragma once - -#include "Pages\AppPage.g.h" -#include "State\MoonlightApp.h" -#include - -namespace moonlight_xbox_dx -{ - ///

- /// An empty page that can be used on its own or navigated to within a Frame. - /// - [Windows::Foundation::Metadata::WebHostHidden] - public ref class AppPage sealed - { - private: - MoonlightHost^ host; - MoonlightApp^ currentApp; - Windows::Foundation::EventRegistrationToken m_back_cookie; - std::atomic continueAppFetch{ false }; - std::atomic wasConnected{ false }; - protected: - virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override; - virtual void OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override; - void Connect(int app); - public: - AppPage(); - property MoonlightHost^ Host { - MoonlightHost^ get() { - return this->host; - } - } - void OnBackRequested(Platform::Object^ e, Windows::UI::Core::BackRequestedEventArgs^ args); - private: - void AppsGrid_ItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ e); - void AppsGrid_RightTapped(Platform::Object^ sender, Windows::UI::Xaml::Input::RightTappedRoutedEventArgs^ e); - void resumeAppButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void closeAppButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void closeAndStartButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void ExecuteCloseAndStart(); - void backButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void settingsButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void OnUnloaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - }; - } diff --git a/Pages/HostSelectorPage.xaml b/Pages/HostSelectorPage.xaml deleted file mode 100644 index c2688e83..00000000 --- a/Pages/HostSelectorPage.xaml +++ /dev/null @@ -1,98 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Moonlight - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Pages/HostSelectorPage.xaml.cpp b/Pages/HostSelectorPage.xaml.cpp deleted file mode 100644 index 5a96ec39..00000000 --- a/Pages/HostSelectorPage.xaml.cpp +++ /dev/null @@ -1,486 +0,0 @@ -// -// HostSelectorPage.xaml.cpp -// Implementation of the HostSelectorPage class -// - -#include "pch.h" -#include "HostSelectorPage.xaml.h" -#include "AppPage.xaml.h" -#include -#include "HostSettingsPage.xaml.h" -#include "Utils.hpp" -#include "MoonlightSettings.xaml.h" -#include "State\MDNSHandler.h" -#include "MoonlightWelcome.xaml.h" -#include "Common\ModalDialog.xaml.h" -#include -#include -#include -#include - -using namespace moonlight_xbox_dx; - -using namespace Platform; -using namespace Windows::Foundation; -using namespace Windows::Foundation::Collections; -using namespace Windows::UI::Xaml; -using namespace Windows::UI::Xaml::Controls; -using namespace Windows::UI::Xaml::Controls::Primitives; -using namespace Windows::UI::Xaml::Data; -using namespace Windows::UI::Xaml::Input; -using namespace Windows::UI::Xaml::Media; -using namespace Windows::UI::Xaml::Navigation; -using namespace Windows::UI::ViewManagement::Core; - -HostSelectorPage::HostSelectorPage() -{ - state = GetApplicationState(); - InitializeComponent(); -} - -void HostSelectorPage::NewHostButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) -{ - dialogHostnameTextBox = ref new TextBox(); - dialogHostnameTextBox->AcceptsReturn = false; - dialogHostnameTextBox->KeyDown += ref new Windows::UI::Xaml::Input::KeyEventHandler(this, &HostSelectorPage::OnKeyDown); - ContentDialog^ dialog = ref new ContentDialog(); - dialog->Content = dialogHostnameTextBox; - dialog->Title = L"Add new Host"; - dialog->IsSecondaryButtonEnabled = true; - dialog->PrimaryButtonText = "Ok"; - dialog->SecondaryButtonText = "Cancel"; - concurrency::create_task(dialog->ShowAsync()); - dialog->PrimaryButtonClick += ref new Windows::Foundation::TypedEventHandler(this, &HostSelectorPage::OnNewHostDialogPrimaryClick); -} - -void HostSelectorPage::OnNewHostDialogPrimaryClick(Windows::UI::Xaml::Controls::ContentDialog^ sender, Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs^ args) -{ - sender->IsPrimaryButtonEnabled = false; - Platform::String^ hostname = dialogHostnameTextBox->Text; - auto def = args->GetDeferral(); - Concurrency::create_task([def, hostname, this, args, sender]() { - bool status = state->AddHost(hostname); - if (!status) { - Platform::WeakReference weakThis(this); - concurrency::create_task(Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High, ref new Windows::UI::Core::DispatchedHandler([sender, weakThis, hostname, def, args]() { - args->Cancel = true; - sender->Content = L"Failed to Connect to " + hostname; - def->Complete(); - }))) .then([](concurrency::task t) { - try { - t.get(); - } - catch (const std::exception &e) { - Utils::Logf("HostSelectorPage NewHost create_task exception: %s", e.what()); - } - catch (...) { - Utils::Log("HostSelectorPage NewHost create_task unknown exception"); - } - }); - return; - } - Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High, ref new Windows::UI::Core::DispatchedHandler([def]() { - def->Complete(); - })); - }); -} - -void HostSelectorPage::GridView_ItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ e) -{ - MoonlightHost^ host = (MoonlightHost^)e->ClickedItem; - - if (host->Connected && host->Paired) { - this->Connect(host); - return; - } - - if (host->Connected && !host->Paired) { - this->StartPairing(host); - return; - } - - FrameworkElement^ anchor = nullptr; - if (e && e->OriginalSource) { - anchor = dynamic_cast(e->OriginalSource); - } - - if (!anchor) { - auto gv = dynamic_cast(sender); - if (gv) { - auto container = dynamic_cast(gv->ContainerFromItem(host)); - if (container) anchor = container; - } - } - - this->ShowHostActions(anchor, host); -} - -void HostSelectorPage::ShowHostActions(Windows::UI::Xaml::FrameworkElement^ anchor, MoonlightHost^ host) -{ - currentHost = host; - - if (currentHost == nullptr) { - return; - } - - if (currentHost->Connected || currentHost->WolPolling) { - this->wakeHostButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed; - this->testConnectionButton->Visibility = Windows::UI::Xaml::Visibility::Visible; - } - else { - this->wakeHostButton->Visibility = Windows::UI::Xaml::Visibility::Visible; - this->testConnectionButton->Visibility = Windows::UI::Xaml::Visibility::Collapsed; - } - - if (anchor) { - this->ActionsFlyout->ShowAt(anchor); - } - else { - this->ActionsFlyout->ShowAt(this->HostsGrid); - } -} - -void HostSelectorPage::StartPairing(MoonlightHost^ host) { - MoonlightClient* client = new MoonlightClient(); - char ipAddressStr[2048]; - wcstombs_s(NULL, ipAddressStr, host->LastHostname->Data(), 2047); - int status = client->Connect(ipAddressStr); - if (status != 0)return; - char* pin = client->GeneratePIN(); - ContentDialog^ dialog = ref new ContentDialog(); - wchar_t msg[4096]; - swprintf(msg, 4096, L"We need to pair the host before continuing. Type %S on your host to continue", pin); - dialog->Content = ref new Platform::String(msg); - dialog->PrimaryButtonText = "Ok"; - concurrency::create_task(::moonlight_xbox_dx::ModalDialog::ShowOnceAsync(dialog)); - Concurrency::create_task([dialog, host, client, pin]() { - int a = client->Pair(); - Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High, ref new Windows::UI::Core::DispatchedHandler([a, dialog, host]() - { - if (a == 0) { - ::moonlight_xbox_dx::ModalDialog::HideDialog(dialog); - } - else { - //dialog->Content = Utils::StringFromStdString(std::string(gs_error)); - } - host->UpdateHostInfo(true); - } - )); - }) .then([](concurrency::task t) { - try { - t.get(); - } - catch (const std::exception &e) { - Utils::Logf("HostSelectorPage StartPairing task exception: %s", e.what()); - } - catch (...) { - Utils::Log("HostSelectorPage StartPairing task unknown exception"); - } - }); -} - -void HostSelectorPage::removeHostButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) -{ - State->RemoveHost(currentHost); -} - -void HostSelectorPage::HostsGrid_RightTapped(Platform::Object^ sender, Windows::UI::Xaml::Input::RightTappedRoutedEventArgs^ e) -{ - //Thanks to https://stackoverflow.com/questions/62878368/uwp-gridview-listview-get-the-righttapped-item-supporting-both-mouse-and-xbox-co - FrameworkElement^ senderElement = (FrameworkElement^)e->OriginalSource; - - // Determine the host from the tapped element - if (senderElement->GetType()->FullName->Equals(GridViewItem::typeid->FullName)) { - auto gi = (GridViewItem^)senderElement; - currentHost = (MoonlightHost^)(gi->Content); - } - else { - currentHost = (MoonlightHost^)(senderElement->DataContext); - } - - // Delegate to consolidated helper (use senderElement as anchor) - this->ShowHostActions(senderElement, currentHost); -} - -void HostSelectorPage::hostSettingsButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) -{ - bool result = this->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(HostSettingsPage::typeid), currentHost); -} - -void HostSelectorPage::SettingsButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) -{ - bool result = this->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(MoonlightSettings::typeid)); -} - -void HostSelectorPage::OnStateLoaded() { - if (GetApplicationState()->FirstTime) { - this->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(MoonlightWelcome::typeid)); - return; - } - - Concurrency::create_task([this]() { - for (auto a : GetApplicationState()->SavedHosts) { - a->UpdateHostInfo(false); - } - }).then([this]() { - if (GetApplicationState()->autostartInstance.size() > 0) { - auto pii = Utils::StringFromStdString(GetApplicationState()->autostartInstance); - for (unsigned int i = 0; i < GetApplicationState()->SavedHosts->Size; i++) { - auto host = GetApplicationState()->SavedHosts->GetAt(i); - if (host->InstanceId->Equals(pii)) { - auto that = this; - Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( - Windows::UI::Core::CoreDispatcherPriority::High, - ref new Windows::UI::Core::DispatchedHandler([that, host]() { - that->Connect(host); - }) - ); - break; - } - } - } - }).then([this](concurrency::task t) { - try { - t.get(); - } - catch (const std::exception &e) { - Utils::Logf("HostSelectorPage OnStateLoaded task exception: %s", e.what()); - } - catch (...) { - Utils::Log("HostSelectorPage OnStateLoaded task unknown exception"); - } - }); -} - -void HostSelectorPage::Connect(MoonlightHost^ host) { - if (!host->Connected)return; - if (!host->Paired) { - StartPairing(host); - return; - } - state->shouldAutoConnect = true; - continueFetch.store(false); - bool result = this->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(AppPage::typeid), host); -} - -void HostSelectorPage::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) { - Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->SetDesiredBoundsMode(Windows::UI::ViewManagement::ApplicationViewBoundsMode::UseVisible); - continueFetch.store(true); - Concurrency::create_task([this] { - init_mdns(); - while (continueFetch.load()) { - query_mdns(); - for (auto a : GetApplicationState()->SavedHosts) { - a->UpdateHostInfo(true); - } - Sleep(5000); - } - }) .then([](concurrency::task t) { - try { - t.get(); - } - catch (const std::exception &e) { - Utils::Logf("HostSelectorPage mdns loop task exception: %s", e.what()); - } - catch (...) { - Utils::Log("HostSelectorPage mdns loop task unknown exception"); - } - }); -} - -void HostSelectorPage::OnKeyDown(Platform::Object^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e) -{ - if (e->Key == Windows::System::VirtualKey::Enter) { - CoreInputView::GetForCurrentView()->TryHide(); - } -} - -void moonlight_xbox_dx::HostSelectorPage::wakeHostButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) -{ - if (currentHost == nullptr) { - return; - } - - try { - bool success = State->WakeHost(currentHost); - if (!success) { - ContentDialog^ fail = ref new ContentDialog(); - fail->Title = "Wake Host Failed"; - fail->Content = "Failed to send Wake-on-LAN packet.\n\nPlease check if Wake-on-LAN is enabled on the host."; - fail->PrimaryButtonText = "OK"; - concurrency::create_task(::moonlight_xbox_dx::ModalDialog::ShowOnceAsync(fail)); - } - - // After sending WoL successfully, aggressively poll this host for a short period - // to detect when it comes online (hosts can take time to boot/respond). - // Poll every 1s for up to 60s, stop early if Connected becomes true. - if (success) { - auto host = currentHost; - host->WolPolling = true; - concurrency::create_task(concurrency::create_async([host]() { - int consecutiveSuccess = 0; - for (int i = 0; i < 60; ++i) { - try { - host->UpdateHostInfo(false); - if (host->Connected) { - consecutiveSuccess++; - if (consecutiveSuccess >= 3) { - host->WolPolling = false; - break; - } - } else { - consecutiveSuccess = 0; - } - } catch (...) { - consecutiveSuccess = 0; - } - Sleep(1000); - } - })).then([host](concurrency::task t) { - try { t.get(); } catch (...) { } - Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([host]() { - host->WolPolling = false; - })); - }); - } - } - catch (std::exception ex) { - ContentDialog^ dialog = ref new ContentDialog(); - dialog->Title = "Wake Host Error"; - dialog->Content = "An error occurred while trying to wake the host:\n\n" + Utils::StringFromChars((char*)ex.what()); - dialog->PrimaryButtonText = "OK"; - concurrency::create_task(::moonlight_xbox_dx::ModalDialog::ShowOnceAsync(dialog)); - } -} - -void HostSelectorPage::hostDetailsButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { - if (currentHost == nullptr) return; - - auto panel = ref new Windows::UI::Xaml::Controls::StackPanel(); - panel->Spacing = 8; - - auto addLine = [panel](Platform::String^ label, Platform::String^ value) { - auto val = ref new Windows::UI::Xaml::Controls::TextBlock(); - val->Text = label + " " + value; - panel->Children->Append(val); - }; - - addLine(Utils::StringFromStdString("Hostname:"), currentHost->LastHostname == nullptr ? Utils::StringFromStdString("(null)") : currentHost->LastHostname); - addLine(Utils::StringFromStdString("Instance ID:"), currentHost->InstanceId == nullptr ? Utils::StringFromStdString("(null)") : currentHost->InstanceId); - addLine(Utils::StringFromStdString("Computer Name:"), currentHost->ComputerName == nullptr ? Utils::StringFromStdString("(null)") : currentHost->ComputerName); - addLine(Utils::StringFromStdString("Server Address:"), currentHost->ServerAddress == nullptr ? Utils::StringFromStdString("(null)") : currentHost->ServerAddress); - addLine(Utils::StringFromStdString("MAC Address:"), currentHost->MacAddress == nullptr ? Utils::StringFromStdString("(null)") : currentHost->MacAddress); - - auto scroll = ref new Windows::UI::Xaml::Controls::ScrollViewer(); - scroll->Content = panel; - scroll->VerticalScrollBarVisibility = Windows::UI::Xaml::Controls::ScrollBarVisibility::Auto; - scroll->MaxHeight = 400; - - auto dialog = ref new Windows::UI::Xaml::Controls::ContentDialog(); - dialog->Title = Utils::StringFromStdString("Host Details"); - dialog->Content = scroll; - dialog->PrimaryButtonText = Utils::StringFromStdString("OK"); - - try { - dialog->XamlRoot = this->XamlRoot; - } catch(...) {} - - concurrency::create_task(dialog->ShowAsync()); -} - -void HostSelectorPage::testConnectionButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { - if (currentHost == nullptr) return; - - std::string hostname = Utils::PlatformStringToStdString(currentHost->LastHostname); - std::string hostOnly; - int port = 0; - try { - auto split = GetApplicationState()->Split_IP_Address(hostname, ':'); - hostOnly = split.first; - port = split.second; - } - catch (...) { - // If parsing fails for any reason, fall back to original behaviour - auto pos = hostname.find(':'); - hostOnly = (pos == std::string::npos) ? hostname : hostname.substr(0, pos); - port = 0; - } - - concurrency::create_task([hostOnly, port]() { - WSADATA wsaData; - std::string resultMsg = "Unknown"; - if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { - resultMsg = "WSAStartup failed"; - } else { - struct addrinfo hints; - struct addrinfo *res = nullptr; - ZeroMemory(&hints, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - - // If port is known (non-zero) use it, otherwise default to 47989 - std::string portStorage = (port > 0) ? std::to_string(port) : std::string("47989"); - int gai = getaddrinfo(hostOnly.c_str(), portStorage.c_str(), &hints, &res); - if (gai != 0) { - resultMsg = std::string("DNS lookup failed: ") + std::to_string(gai); - } else { - bool ok = false; - double bestRttMs = -1.0; - for (struct addrinfo *p = res; p != nullptr; p = p->ai_next) { - SOCKET s = socket(p->ai_family, p->ai_socktype, p->ai_protocol); - if (s == INVALID_SOCKET) continue; - u_long mode = 1; - ioctlsocket(s, FIONBIO, &mode); - - using namespace std::chrono; - auto start = high_resolution_clock::now(); - int rc = connect(s, p->ai_addr, (int)p->ai_addrlen); - if (rc == 0) { - auto end = high_resolution_clock::now(); - double ms = duration_cast(end - start).count() / 1000.0; - if (bestRttMs < 0 || ms < bestRttMs) bestRttMs = ms; - ok = true; - closesocket(s); - break; - } - fd_set writeSet; - FD_ZERO(&writeSet); - FD_SET(s, &writeSet); - timeval tv; tv.tv_sec = 3; tv.tv_usec = 0; - int sel = select(0, NULL, &writeSet, NULL, &tv); - if (sel > 0 && FD_ISSET(s, &writeSet)) { - auto end = high_resolution_clock::now(); - double ms = duration_cast(end - start).count() / 1000.0; - if (bestRttMs < 0 || ms < bestRttMs) bestRttMs = ms; - ok = true; - closesocket(s); - break; - } - closesocket(s); - } - freeaddrinfo(res); - if (ok) { - if (bestRttMs >= 0) { - char buf[64]; - snprintf(buf, sizeof(buf), "Connection OK (RTT: %.1f ms)", bestRttMs); - resultMsg = buf; - } else { - resultMsg = "Connection OK"; - } - } else { - resultMsg = "Connection failed"; - } - } - WSACleanup(); - } - - Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High, ref new Windows::UI::Core::DispatchedHandler([resultMsg, hostOnly]() { - auto dialog = ref new Windows::UI::Xaml::Controls::ContentDialog(); - dialog->Title = Utils::StringFromStdString("Test Connection"); - dialog->Content = Utils::StringFromStdString(hostOnly + ": " + resultMsg); - dialog->PrimaryButtonText = Utils::StringFromStdString("OK"); - concurrency::create_task(::moonlight_xbox_dx::ModalDialog::ShowOnceAsync(dialog)); - })); - }); -} diff --git a/Pages/HostSelectorPage.xaml.h b/Pages/HostSelectorPage.xaml.h deleted file mode 100644 index a2008481..00000000 --- a/Pages/HostSelectorPage.xaml.h +++ /dev/null @@ -1,52 +0,0 @@ -// -// HostSelectorPage.xaml.h -// Declaration of the HostSelectorPage class -// - -#pragma once - -#include "Pages\HostSelectorPage.g.h" -#include "State\ApplicationState.h" - -#include - -using namespace Windows::UI::Core; -namespace moonlight_xbox_dx -{ - /// - /// An empty page that can be used on its own or navigated to within a Frame. - /// - [Windows::Foundation::Metadata::WebHostHidden] - public ref class HostSelectorPage sealed - { - public: - HostSelectorPage(); - property ApplicationState^ State { - ApplicationState^ get() { - return this->state; - } - } - void OnStateLoaded(); - void Connect(MoonlightHost^ host); - protected: - virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override; - private: - ApplicationState ^state; - void NewHostButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void OnNewHostDialogPrimaryClick(Windows::UI::Xaml::Controls::ContentDialog^ sender, Windows::UI::Xaml::Controls::ContentDialogButtonClickEventArgs^ args); - Windows::UI::Xaml::Controls::TextBox ^dialogHostnameTextBox; - void GridView_ItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ e); - void StartPairing(MoonlightHost^ host); - void removeHostButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void HostsGrid_RightTapped(Platform::Object^ sender, Windows::UI::Xaml::Input::RightTappedRoutedEventArgs^ e); - MoonlightHost^ currentHost; - void hostSettingsButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void hostDetailsButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void SettingsButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - std::atomic continueFetch; - void OnKeyDown(Platform::Object^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e); - void wakeHostButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void testConnectionButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void ShowHostActions(Windows::UI::Xaml::FrameworkElement^ anchor, MoonlightHost^ host); - }; -} diff --git a/Pages/HostSettingsPage.xaml b/Pages/HostSettingsPage.xaml deleted file mode 100644 index ae0f31fa..00000000 --- a/Pages/HostSettingsPage.xaml +++ /dev/null @@ -1,115 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Resolution - - - - - - - - - - - - FPS - - Bitrate (Kbps) - - Audio Configuration - - Play Audio on Host PC - - Autostart - - - Video Codecs - - - Enable HDR: - - - HDR requires Xbox system resolution set to 4K. - - - Optimize host resolution: - - - Frame Pacing: - - - Best for Xbox Series. Lowest latency. Renders frames at the incoming frame rate. - - - Best for Xbox One. Locks rendering frame rate to refresh rate and evenly spaces frames. - - - Show performance stats: - - - Show performance graphs: - - - Graphs are unavailable on Xbox One when system resolution is set to 4K. - - - Other: - - - - - diff --git a/Pages/HostSettingsPage.xaml.cpp b/Pages/HostSettingsPage.xaml.cpp deleted file mode 100644 index 0aeaa137..00000000 --- a/Pages/HostSettingsPage.xaml.cpp +++ /dev/null @@ -1,266 +0,0 @@ -// -// HostSettingsPage.xaml.cpp -// Implementation of the HostSettingsPage class -// - -#include "pch.h" -#include "HostSettingsPage.xaml.h" -#include "MoonlightSettings.xaml.h" -#include "Utils.hpp" -#include -#include // sqrtf, lround -using namespace Windows::UI::Core; - -using namespace moonlight_xbox_dx; - -using namespace Platform; -using namespace Windows::Foundation; -using namespace Windows::Foundation::Collections; -using namespace Windows::Graphics::Display::Core; -using namespace Windows::UI::Xaml; -using namespace Windows::UI::Xaml::Controls; -using namespace Windows::UI::Xaml::Controls::Primitives; -using namespace Windows::UI::Xaml::Data; -using namespace Windows::UI::Xaml::Input; -using namespace Windows::UI::Xaml::Media; -using namespace Windows::UI::Xaml::Navigation; -using namespace Windows::UI::ViewManagement::Core; - -HostSettingsPage::HostSettingsPage() -{ - InitializeComponent(); - Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->SetDesiredBoundsMode(Windows::UI::ViewManagement::ApplicationViewBoundsMode::UseVisible); - this->Loaded += ref new Windows::UI::Xaml::RoutedEventHandler(this, &HostSettingsPage::OnLoaded); - this->Unloaded += ref new Windows::UI::Xaml::RoutedEventHandler(this, &HostSettingsPage::OnUnloaded); -} - - -void HostSettingsPage::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) { - MoonlightHost^ mhost = dynamic_cast(e->Parameter); - if (mhost == nullptr)return; - GAMING_DEVICE_MODEL_INFORMATION info = {}; - GetGamingDeviceModelInformation(&info); - host = mhost; - AvailableResolutions->Append(ref new ScreenResolution(1280, 720)); - AvailableResolutions->Append(ref new ScreenResolution(1920, 1080)); - //No 4K for Old Xbox One - if (!(info.vendorId == GAMING_DEVICE_VENDOR_ID_MICROSOFT && info.deviceId == GAMING_DEVICE_DEVICE_ID_XBOX_ONE)) { - AvailableResolutions->Append(ref new ScreenResolution(2560, 1440)); - AvailableResolutions->Append(ref new ScreenResolution(3840, 2160)); - } - AvailableFPS->Append(30); - AvailableFPS->Append(60); - AvailableFPS->Append(120); - AvailableVideoCodecs->Append("H.264"); - AvailableVideoCodecs->Append("HEVC (H.265)"); - AvailableAudioConfigs->Append("Stereo"); - AvailableAudioConfigs->Append("Surround 5.1"); - AvailableAudioConfigs->Append("Surround 7.1"); - AvailableFramePacing->Append("Immediate"); - AvailableFramePacing->Append("Display-locked"); - CurrentResolutionIndex = 0; - for (int i = 0; i < AvailableResolutions->Size; i++) { - if (host->Resolution->Width == AvailableResolutions->GetAt(i)->Width && - host->Resolution->Height == AvailableResolutions->GetAt(i)->Height - ) { - CurrentResolutionIndex = i; - break; - } - } - CurrentAppIndex = 0; - auto item = ref new ComboBoxItem(); - item->Content = L"No App"; - AutoStartSelector->Items->Append(item); - for (int i = 0; i < Host->Apps->Size; i++) { - auto item = ref new ComboBoxItem(); - item->Content = Host->Apps->GetAt(i)->Name; - AutoStartSelector->Items->Append(item); - if (host->AutostartID == host->Apps->GetAt(i)->Id) { - CurrentAppIndex = i + 1; - } - } - AutoStartSelector->SelectedIndex = CurrentAppIndex; - - // Set frame pacing selection based on saved preference - for (int i = 0; i < AvailableFramePacing->Size; i++) { - if (host->FramePacing == AvailableFramePacing->GetAt(i)) { - FramePacingComboBox->SelectedIndex = i; - break; - } - } - - if (info.vendorId == GAMING_DEVICE_VENDOR_ID_MICROSOFT) { - // Old Xbox One can only use H264, remove from settings everything else - if (info.deviceId == GAMING_DEVICE_DEVICE_ID_XBOX_ONE) { - CodecComboBox->IsEnabled = false; - CodecComboBox->SelectedIndex = 0; - } - - // Disable HDR if console is not set to 4K - auto mode = HdmiDisplayInformation::GetForCurrentView()->GetCurrentDisplayMode(); - auto height = mode->ResolutionHeightInRawPixels; - if (height < 2160) { - EnableHDRCheckbox->IsEnabled = false; - EnableHDRCheckbox->IsChecked = false; - EnableHDRCheckbox->Visibility = Windows::UI::Xaml::Visibility::Collapsed; - HDR4KNote->Visibility = Windows::UI::Xaml::Visibility::Visible; - } else { - EnableHDRCheckbox->IsEnabled = true; - EnableHDRCheckbox->Visibility = Windows::UI::Xaml::Visibility::Visible; - HDR4KNote->Visibility = Windows::UI::Xaml::Visibility::Collapsed; - } - - // Disable graphs at 4K on Xbox One - if (IsXboxOne() && height >= 2160) { - EnableGraphsCheckbox->IsEnabled = false; - EnableGraphsCheckbox->Visibility = Windows::UI::Xaml::Visibility::Collapsed; - XboxOneGraphsNote->Visibility = Windows::UI::Xaml::Visibility::Visible; - } else { - EnableGraphsCheckbox->IsEnabled = true; - EnableGraphsCheckbox->Visibility = Windows::UI::Xaml::Visibility::Visible; - XboxOneGraphsNote->Visibility = Windows::UI::Xaml::Visibility::Collapsed; - } - } -} - -void HostSettingsPage::backButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) -{ - GetApplicationState()->UpdateFile(); - this->Frame->GoBack(); -} - -void HostSettingsPage::OnBackRequested(Platform::Object^ e, Windows::UI::Core::BackRequestedEventArgs^ args) -{ - // UWP on Xbox One triggers a back request whenever the B - // button is pressed which can result in the app being - // suspended if unhandled - GetApplicationState()->UpdateFile(); - this->Frame->GoBack(); - args->Handled = true; - -} - -void HostSettingsPage::ResolutionSelector_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e) -{ - auto selectedResolution = AvailableResolutions->GetAt(this->ResolutionSelector->SelectedIndex); - - // Default to a new bitrate if a new resolution was chosen - if (selectedResolution->Width != host->Resolution->Width) { - host->Bitrate = getDefaultBitrate(selectedResolution->Width, selectedResolution->Height, host->FPS); - } - - host->Resolution = selectedResolution; -} - -void HostSettingsPage::FPSSelector_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e) -{ - if (e->AddedItems->Size == 0) return; - int selectedFPS = (int)e->AddedItems->GetAt(0); - - // Default to a new bitrate if a new FPS was chosen - if (selectedFPS != host->FPS) { - host->Bitrate = getDefaultBitrate(host->Resolution->Width, host->Resolution->Height, selectedFPS); - } - - host->FPS = selectedFPS; -} - -void HostSettingsPage::AutoStartSelector_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e) -{ - int index = AutoStartSelector->SelectedIndex - 1; - if (index >= 0 && host->Apps->Size > index) { - host->AutostartID = host->Apps->GetAt(index)->Id; - } - else { - host->AutostartID = -1; - } -} - -void HostSettingsPage::FramePacing_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e) -{ - auto selectedFramePacing = AvailableFramePacing->GetAt(this->FramePacingComboBox->SelectedIndex); - - if (selectedFramePacing == "Immediate") { - FramePacingImmediateDesc->Visibility = Windows::UI::Xaml::Visibility::Visible; - FramePacingDisplayLockedDesc->Visibility = Windows::UI::Xaml::Visibility::Collapsed; - } else { - FramePacingImmediateDesc->Visibility = Windows::UI::Xaml::Visibility::Collapsed; - FramePacingDisplayLockedDesc->Visibility = Windows::UI::Xaml::Visibility::Visible; - } - - host->FramePacing = selectedFramePacing; -} - -void HostSettingsPage::GlobalSettingsOption_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) -{ - this->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(MoonlightSettings::typeid)); -} - - -void HostSettingsPage::BitrateInput_KeyDown(Platform::Object^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e) -{ - if (e->Key == Windows::System::VirtualKey::Enter) { - CoreInputView::GetForCurrentView()->TryHide(); - } -} - -void HostSettingsPage::OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) -{ - auto navigation = Windows::UI::Core::SystemNavigationManager::GetForCurrentView(); - m_back_cookie = navigation->BackRequested += ref new EventHandler(this, &HostSettingsPage::OnBackRequested); -} - -void HostSettingsPage::OnUnloaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) -{ - auto navigation = Windows::UI::Core::SystemNavigationManager::GetForCurrentView(); - navigation->BackRequested -= m_back_cookie; -} - -int HostSettingsPage::getDefaultBitrate(int width, int height, int fps) -{ - // Don't scale bitrate linearly beyond 60 FPS. It's definitely not a linear - // bitrate increase for frame rate once we get to values that high. - float frameRateFactor = (fps <= 60 ? fps : (std::sqrtf(fps / 60.f) * 60.f)) / 30.f; - - // TODO: Collect some empirical data to see if these defaults make sense. - // We're just using the values that the Shield used, as we have for years. - static const struct resTable { - int pixels; - float factor; - } resTable[] { - { 1280 * 720, 5.0f }, - { 1920 * 1080, 10.0f }, - { 2560 * 1440, 20.0f }, - { 3840 * 2160, 40.0f }, - { -1, -1.0f }, - }; - - // Calculate the resolution factor by linear interpolation of the resolution table - float resolutionFactor; - int pixels = width * height; - for (int i = 0;; i++) { - if (pixels == resTable[i].pixels) { - // We can bail immediately for exact matches - resolutionFactor = resTable[i].factor; - break; - } - else if (pixels < resTable[i].pixels) { - if (i == 0) { - // Never go below the lowest resolution entry - resolutionFactor = resTable[i].factor; - } - else { - // Interpolate between the entry greater than the chosen resolution (i) and the entry less than the chosen resolution (i-1) - resolutionFactor = ((float)(pixels - resTable[i-1].pixels) / (resTable[i].pixels - resTable[i-1].pixels)) * (resTable[i].factor - resTable[i-1].factor) + resTable[i-1].factor; - } - break; - } - else if (resTable[i].pixels == -1) { - // Never go above the highest resolution entry - resolutionFactor = resTable[i-1].factor; - break; - } - } - - return std::lround(resolutionFactor * frameRateFactor) * 1000; -} diff --git a/Pages/MoonlightSettings.xaml b/Pages/MoonlightSettings.xaml deleted file mode 100644 index a70203db..00000000 --- a/Pages/MoonlightSettings.xaml +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - Looking to change Resolution, FPS or Bitrate? Use the settings button in the app grid to change the settings of your host - - - - - - - - - - - - - - - - - - - - - Autostart Host: - - Screen Margin Height: - - Screen Margin Width: - - Change these parameters until you can see a green border around the screen - Mouse Sensitivity: - - Experimental Keyboard - - Enabled - - Layout - - - - - diff --git a/Pages/MoonlightSettings.xaml.cpp b/Pages/MoonlightSettings.xaml.cpp deleted file mode 100644 index 476786b7..00000000 --- a/Pages/MoonlightSettings.xaml.cpp +++ /dev/null @@ -1,115 +0,0 @@ -// -// MoonlightSettings.xaml.cpp -// Implementazione della classe MoonlightSettings -// - -#include "pch.h" -#include "MoonlightSettings.xaml.h" -#include "MoonlightWelcome.xaml.h" -#include "Utils.hpp" -#include "Keyboard/KeyboardCommon.h" -using namespace Windows::UI::Core; - -using namespace moonlight_xbox_dx; - -using namespace Platform; -using namespace Windows::Foundation; -using namespace Windows::Foundation::Collections; -using namespace Windows::UI::Xaml; -using namespace Windows::UI::Xaml::Controls; -using namespace Windows::UI::Xaml::Controls::Primitives; -using namespace Windows::UI::Xaml::Data; -using namespace Windows::UI::Xaml::Input; -using namespace Windows::UI::Xaml::Media; -using namespace Windows::UI::Xaml::Navigation; - -// Il modello di elemento Pagina vuota è documentato all'indirizzo https://go.microsoft.com/fwlink/?LinkId=234238 - -MoonlightSettings::MoonlightSettings() -{ - InitializeComponent(); - state = GetApplicationState(); - auto item = ref new ComboBoxItem(); - item->Content = "Don't autoconnect"; - item->DataContext = ""; - HostSelector->Items->Append(item); - - Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->SetDesiredBoundsMode(Windows::UI::ViewManagement::ApplicationViewBoundsMode::UseCoreWindow); - auto iid = Utils::StringFromStdString(state->autostartInstance); - for (int i = 0; i < state->SavedHosts->Size;i++) { - auto host = state->SavedHosts->GetAt(i); - auto item = ref new ComboBoxItem(); - item->Content = host->LastHostname; - item->DataContext = host->InstanceId; - HostSelector->Items->Append(item); - if (host->InstanceId->Equals(iid)) { - HostSelector->SelectedIndex = i+1; - } - } - int k = 0; - for (auto l : keyboardLayouts) { - auto item = ref new ComboBoxItem(); - auto s = Utils::StringFromStdString(l.first); - item->Content = s; - item->DataContext = s; - KeyboardLayoutSelector->Items->Append(item); - if (state->KeyboardLayout->Equals(s)) { - KeyboardLayoutSelector->SelectedIndex = k; - } - k++; - } - this->Loaded += ref new Windows::UI::Xaml::RoutedEventHandler(this, &MoonlightSettings::OnLoaded); - this->Unloaded += ref new Windows::UI::Xaml::RoutedEventHandler(this, &MoonlightSettings::OnUnloaded); -} - -void MoonlightSettings::backButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) -{ - GetApplicationState()->UpdateFile(); - this->Frame->GoBack(); -} - -void MoonlightSettings::HostSelector_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e) -{ - ComboBoxItem^ item = (ComboBoxItem^)this->HostSelector->SelectedItem; - - auto s = Utils::PlatformStringToStdString(item->DataContext->ToString()); - state->autostartInstance = s; - -} - -void MoonlightSettings::OnBackRequested(Platform::Object^ e, Windows::UI::Core::BackRequestedEventArgs^ args) -{ - // UWP on Xbox One triggers a back request whenever the B - // button is pressed which can result in the app being - // suspended if unhandled - GetApplicationState()->UpdateFile(); - this->Frame->GoBack(); - args->Handled = true; - -} - -void MoonlightSettings::WelcomeButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) -{ - this->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(MoonlightWelcome::typeid)); -} - -void MoonlightSettings::LayoutSelector_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e) -{ - ComboBoxItem^ item = (ComboBoxItem^)this->KeyboardLayoutSelector->SelectedItem; - - auto s = item->DataContext->ToString(); - state->KeyboardLayout = s; -} - -void MoonlightSettings::OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) -{ - auto navigation = Windows::UI::Core::SystemNavigationManager::GetForCurrentView(); - m_back_cookie = navigation->BackRequested += ref new EventHandler(this, &MoonlightSettings::OnBackRequested); -} - - -void MoonlightSettings::OnUnloaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) -{ - auto navigation = Windows::UI::Core::SystemNavigationManager::GetForCurrentView(); - navigation->BackRequested -= m_back_cookie; -} diff --git a/Pages/MoonlightSettings.xaml.h b/Pages/MoonlightSettings.xaml.h deleted file mode 100644 index cec3de75..00000000 --- a/Pages/MoonlightSettings.xaml.h +++ /dev/null @@ -1,38 +0,0 @@ -// -// MoonlightSettings.xaml.h -// Dichiarazione della classe MoonlightSettings -// - -#pragma once - -#include "Pages\MoonlightSettings.g.h" - -namespace moonlight_xbox_dx -{ - /// - /// Pagina vuota che può essere usata autonomamente oppure per l'esplorazione all'interno di un frame. - /// - [Windows::Foundation::Metadata::WebHostHidden] - public ref class MoonlightSettings sealed - { - private: - ApplicationState^ state; - Windows::Foundation::EventRegistrationToken m_back_cookie; - public: - MoonlightSettings(); - property ApplicationState^ State { - ApplicationState^ get() { - return this->state; - } - } - - void OnBackRequested(Platform::Object^ e, Windows::UI::Core::BackRequestedEventArgs^ args); - private: - void backButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void HostSelector_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e); - void WelcomeButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void LayoutSelector_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e); - void OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void OnUnloaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - }; -} diff --git a/Pages/StreamPage.xaml b/Pages/StreamPage.xaml deleted file mode 100644 index 1d15cde6..00000000 --- a/Pages/StreamPage.xaml +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/State/ApplicationState.cpp b/State/ApplicationState.cpp index 611792ca..3fdf809f 100644 --- a/State/ApplicationState.cpp +++ b/State/ApplicationState.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "ApplicationState.h" +#define MLOG_TAG_OVERRIDE "ApplicationState" #include #include @@ -30,6 +31,11 @@ Concurrency::task moonlight_xbox_dx::ApplicationState::Init() if (stateJson.contains("marginHeight"))this->ScreenMarginHeight = stateJson["marginHeight"]; if (stateJson.contains("mouseSensitivity"))this->MouseSensitivity = stateJson["mouseSensitivity"]; if (stateJson.contains("alternateCombination")) this->AlternateCombination = stateJson["alternateCombination"].get(); + if (stateJson.contains("recentHostnames")) { + for (auto& rh : stateJson["recentHostnames"]) { + recentHostnames.push_back(rh.get()); + } + } for (auto a : stateJson["hosts"]) { MoonlightHost^ h = ref new MoonlightHost(Utils::StringFromStdString(a["hostname"].get())); if (a.contains("instance_id")) h->InstanceId = Utils::StringFromStdString(a["instance_id"].get()); @@ -40,7 +46,6 @@ Concurrency::task moonlight_xbox_dx::ApplicationState::Init() if (a.contains("fps"))h->FPS = a["fps"]; if (a.contains("audioConfig"))h->AudioConfig = Utils::StringFromStdString(a["audioConfig"].get()); if (a.contains("videoCodec"))h->VideoCodec = Utils::StringFromStdString(a["videoCodec"].get()); - if (a.contains("framePacing"))h->FramePacing = Utils::StringFromStdString(a["framePacing"].get()); if (a.contains("autoStartID"))h->AutostartID = a["autoStartID"]; if (a.contains("computername")) h->ComputerName = Utils::StringFromStdString(a["computername"].get()); if (a.contains("playaudioonpc")) h->PlayAudioOnPC = a["playaudioonpc"].get(); @@ -48,19 +53,74 @@ Concurrency::task moonlight_xbox_dx::ApplicationState::Init() if (a.contains("enable_sops")) h->EnableSOPS = a["enable_sops"].get(); if (a.contains("enable_stats")) h->EnableStats = a["enable_stats"].get(); if (a.contains("enable_graphs")) h->EnableGraphs = a["enable_graphs"].get(); + if (a.contains("favoriteAppIds")) { + for (auto& fid : a["favoriteAppIds"]) { + h->favoriteAppIds.push_back(fid.get()); + } + } + if (a.contains("personalization")) { + auto& p = a["personalization"]; + if (p.contains("background")) h->Personalization->Background = Utils::StringFromStdString(p["background"].get()); + if (p.contains("app_view")) h->Personalization->AppView = (AppHostView)p["app_view"].get(); + if (p.contains("use_system_accent")) h->Personalization->UseSystemAccent = p["use_system_accent"].get(); + if (p.contains("accent_r") && p.contains("accent_g") && p.contains("accent_b")) { + Windows::UI::Color c; + c.A = 255; + c.R = (uint8_t)p["accent_r"].get(); + c.G = (uint8_t)p["accent_g"].get(); + c.B = (uint8_t)p["accent_b"].get(); + h->Personalization->AccentColor = c; + } + } if (a.contains("serverAddress")) h->ServerAddress = Utils::StringFromStdString(a["serverAddress"].get()); if (a.contains("macaddress")) h->MacAddress = Utils::StringFromStdString(a["macaddress"].get()); else h->ComputerName = h->LastHostname; this->SavedHosts->Append(h); } + OnPropertyChanged("HostSelectionTitle"); + OnPropertyChanged("HasSavedHosts"); + OnPropertyChanged("HasNoSavedHosts"); } + m_isStateLoaded = true; + OnPropertyChanged("ShowHostList"); + OnPropertyChanged("ShowEmptyState"); }); } +Windows::Foundation::Collections::IVector^ moonlight_xbox_dx::ApplicationState::RecentHostnames::get() +{ + auto result = ref new Platform::Collections::Vector(); + for (auto& s : recentHostnames) { + result->Append(Utils::StringFromStdString(s)); + } + return result; +} + +Windows::Foundation::Collections::IVector^ moonlight_xbox_dx::ApplicationState::RecentHostDisplayNames::get() +{ + auto result = ref new Platform::Collections::Vector(); + for (auto& s : recentHostnames) { + Platform::String^ hostname = Utils::StringFromStdString(s); + Platform::String^ computerName = ""; + for (auto h : SavedHosts) { + if (h->LastHostname == hostname && h->ComputerName != nullptr && h->ComputerName->Length() > 0) { + computerName = h->ComputerName; + break; + } + } + result->Append(computerName); + } + return result; +} + bool moonlight_xbox_dx::ApplicationState::AddHost(Platform::String^ hostname) { MoonlightHost^ host = ref new MoonlightHost(hostname); host->UpdateHostInfo(false); if (!host->Connected)return false; + std::string hostnameStd = Utils::PlatformStringToStdString(hostname); + recentHostnames.erase(std::remove(recentHostnames.begin(), recentHostnames.end(), hostnameStd), recentHostnames.end()); + recentHostnames.insert(recentHostnames.begin(), hostnameStd); + if (recentHostnames.size() > 5) recentHostnames.resize(5); for (auto h : SavedHosts) { if (host->InstanceId == h->InstanceId) { h->LastHostname = host->LastHostname; @@ -70,6 +130,11 @@ bool moonlight_xbox_dx::ApplicationState::AddHost(Platform::String^ hostname) { } Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High, ref new Windows::UI::Core::DispatchedHandler([this,host]() { SavedHosts->Append(host); + OnPropertyChanged("HostSelectionTitle"); + OnPropertyChanged("HasSavedHosts"); + OnPropertyChanged("HasNoSavedHosts"); + OnPropertyChanged("ShowHostList"); + OnPropertyChanged("ShowEmptyState"); })); UpdateFile(); return true; @@ -90,6 +155,7 @@ Concurrency::task moonlight_xbox_dx::ApplicationState::UpdateFile() stateJson["enableKeyboard"] = that->EnableKeyboard; stateJson["keyboardLayout"] = Utils::PlatformStringToStdString(that->KeyboardLayout); stateJson["alternateCombination"] = that->AlternateCombination; + stateJson["recentHostnames"] = that->recentHostnames; for (auto host : that->SavedHosts) { nlohmann::json hostJson; hostJson["hostname"] = Utils::PlatformStringToStdString(host->LastHostname); @@ -101,13 +167,23 @@ Concurrency::task moonlight_xbox_dx::ApplicationState::UpdateFile() hostJson["fps"] = host->FPS; hostJson["audioConfig"] = Utils::PlatformStringToStdString(host->AudioConfig); hostJson["videoCodec"] = Utils::PlatformStringToStdString(host->VideoCodec); - hostJson["framePacing"] = Utils::PlatformStringToStdString(host->FramePacing); hostJson["autoStartID"] = host->AutostartID; hostJson["playaudioonpc"] = host->PlayAudioOnPC; hostJson["enable_hdr"] = host->EnableHDR; hostJson["enable_sops"] = host->EnableSOPS; hostJson["enable_stats"] = host->EnableStats; hostJson["enable_graphs"] = host->EnableGraphs; + hostJson["favoriteAppIds"] = host->favoriteAppIds; + { + nlohmann::json pJson; + pJson["background"] = Utils::PlatformStringToStdString(host->Personalization->Background); + pJson["app_view"] = (int)host->Personalization->AppView; + pJson["use_system_accent"] = host->Personalization->UseSystemAccent; + pJson["accent_r"] = (int)host->Personalization->AccentColor.R; + pJson["accent_g"] = (int)host->Personalization->AccentColor.G; + pJson["accent_b"] = (int)host->Personalization->AccentColor.B; + hostJson["personalization"] = pJson; + } hostJson["serverAddress"] = Utils::PlatformStringToStdString(host->ServerAddress); std::string macAddr = Utils::PlatformStringToStdString(host->MacAddress); @@ -124,17 +200,49 @@ Concurrency::task moonlight_xbox_dx::ApplicationState::UpdateFile() } void moonlight_xbox_dx::ApplicationState::RemoveHost(MoonlightHost^ host) { - if (host == nullptr)return; + if (host == nullptr) return; unsigned int index; bool found = SavedHosts->IndexOf(host, &index); + if (!found) return; SavedHosts->RemoveAt(index); - if (!host->Connected) { - host->Connect(); - } - if (host->Connected) { - host->Unpair(); - } + OnPropertyChanged("HostSelectionTitle"); + OnPropertyChanged("HasSavedHosts"); + OnPropertyChanged("HasNoSavedHosts"); + OnPropertyChanged("ShowHostList"); + OnPropertyChanged("ShowEmptyState"); UpdateFile(); + Concurrency::create_task([host]() { + + if (host->InstanceId != nullptr && !host->InstanceId->IsEmpty()) { + std::wstring dir = std::wstring( + Windows::Storage::ApplicationData::Current->LocalFolder->Path->Data()) + + L"\\images\\" + host->InstanceId->Data() + L"\\"; + auto deleteAllInDir = [](const std::wstring& d) { + WIN32_FIND_DATA fd; + HANDLE h = FindFirstFile((d + L"*").c_str(), &fd); + if (h != INVALID_HANDLE_VALUE) { + do { + if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + DeleteFile((d + fd.cFileName).c_str()); + } while (FindNextFile(h, &fd)); + FindClose(h); + } + }; + std::wstring blurDir = dir + L"blur\\"; + deleteAllInDir(blurDir); + RemoveDirectory(blurDir.c_str()); + deleteAllInDir(dir); + RemoveDirectory(dir.c_str()); + } + try { + if (!host->Connected) { + host->Connect(); + } + if (host->Connected) { + host->Unpair(); + } + } catch (...) {} + }); } void moonlight_xbox_dx::ApplicationState::OnPropertyChanged(Platform::String^ propertyName) @@ -164,20 +272,38 @@ bool moonlight_xbox_dx::ApplicationState::WakeHost(MoonlightHost^ host) 47998, 47999, 48000, 48002, 48010 // Ports opened by GFE }; + std::vector sent; + std::vector failed; + bool result = Send_Payload(descriptor, wolPayload, "255.255.255.255", 0); + (result ? sent : failed).push_back("255.255.255.255:0"); for (int i = 0; i < addressList.size(); i++) { for (int j = 0; j < ports.size(); j++) { bool sendResult = Send_Payload(descriptor, wolPayload, addressList[i], ports[j]); + std::string target = addressList[i] + ":" + std::to_string(ports[j]); if (sendResult) { result = true; + sent.push_back(target); + } + else + { + failed.push_back(target); } } } closesocket(descriptor); + + std::string msg = "Wake-On-Lan packets sent to: " + Utils::Join(sent, ", "); + if (!failed.empty()) { + msg += "; failed to send to: " + Utils::Join(failed, ", "); + } + msg += "\n"; + MLOG(Utils::LogLevel::Info, msg.c_str()); + return result; } @@ -289,15 +415,7 @@ bool moonlight_xbox_dx::ApplicationState::Send_Payload(int descriptor, std::stri bind(descriptor, (struct sockaddr*)&addr, sizeof(addr)); int status = sendto(descriptor, payload.c_str(), (int)payload.length(), 0, (struct sockaddr*)&addr, sizeof(addr)); - if (status == SOCKET_ERROR) { - std::string msg = std::string() + "Error sending Wake-On-Lan packet to " + address.c_str() + ":" + Utils::PlatformStringToStdString(port.ToString()) + "\n"; - Utils::Log(msg.c_str()); - return false; - } - - std::string msg = std::string() + "Wake-On-Lan packet sent to " + address.c_str() + ":" + Utils::PlatformStringToStdString(port.ToString()) + "\n"; - Utils::Log(msg.c_str()); - return true; + return status != SOCKET_ERROR; } std::string moonlight_xbox_dx::ApplicationState::Get_Broadcast_IP(std::string ipAddress) @@ -335,7 +453,7 @@ std::string moonlight_xbox_dx::ApplicationState::Get_Broadcast_IP(std::string ip subnetMask = 4294967040; // 255.255.255.0 } else { - Utils::Log("Could not determine subnet mask from IP address.\n"); + MLOG(Utils::LogLevel::Warning, "Could not determine subnet mask from IP address.\n"); WSACleanup(); return ""; } @@ -380,6 +498,6 @@ std::pair moonlight_xbox_dx::ApplicationState::Split_IP_Addres void moonlight_xbox_dx::ApplicationState::Throw_Error(std::string message) { std::string msg = std::string() + message + "\n"; - Utils::Log(msg.c_str()); + MLOG(Utils::LogLevel::Error, msg.c_str()); throw std::runtime_error(message); } diff --git a/State/ApplicationState.h b/State/ApplicationState.h index 7841411d..5b31bdcc 100644 --- a/State/ApplicationState.h +++ b/State/ApplicationState.h @@ -15,6 +15,8 @@ namespace moonlight_xbox_dx { Platform::String^ keyboardLayout; bool firstTime; bool alternateCombination; + bool m_isStateLoaded = false; + std::vector recentHostnames; internal: Concurrency::task Init(); bool AddHost(Platform::String^ hostname); @@ -34,7 +36,6 @@ namespace moonlight_xbox_dx { std::pair Split_IP_Address(const std::string& address, char deliminator); void Throw_Error(std::string message); - public: //Thanks to https://phsucharee.wordpress.com/2013/06/19/data-binding-and-ccx-inotifypropertychanged/ virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged; @@ -50,6 +51,41 @@ namespace moonlight_xbox_dx { return this->hosts; }; } + property Windows::Foundation::Collections::IVector^ RecentHostnames + { + Windows::Foundation::Collections::IVector^ get(); + } + property Windows::Foundation::Collections::IVector^ RecentHostDisplayNames + { + Windows::Foundation::Collections::IVector^ get(); + } + property Platform::String^ HostSelectionTitle + { + Platform::String^ get() + { + return (hosts != nullptr && hosts->Size > 0) ? "Select Host" : "Connect to a host to continue"; + }; + } + property bool HasSavedHosts + { + bool get() { return hosts != nullptr && hosts->Size > 0; } + } + property bool HasNoSavedHosts + { + bool get() { return hosts == nullptr || hosts->Size == 0; } + } + property bool ShowHostList + { + bool get() { return m_isStateLoaded && hosts != nullptr && hosts->Size > 0; } + } + property bool ShowEmptyState + { + bool get() { return m_isStateLoaded && (hosts == nullptr || hosts->Size == 0); } + } + property bool IsStateLoaded + { + bool get() { return m_isStateLoaded; } + } property int ScreenMarginWidth { int get() diff --git a/State/GamepadState.h b/State/GamepadState.h index c1dc21d3..a8ca559e 100644 --- a/State/GamepadState.h +++ b/State/GamepadState.h @@ -8,7 +8,9 @@ enum class ComboState { None, ViewWaiting, MenuWaiting, - ComboActive + MenuLongPressWaiting, + ComboActive, + ComboReleasing }; struct GamepadComboState { @@ -16,12 +18,16 @@ struct GamepadComboState { bool menuPressed = false; int64_t startTime = 0; ComboState comboState = ComboState::None; + bool menuLongPressFired = false; + int64_t menuInjectionStartQpc = 0; + int64_t viewInjectionStartQpc = 0; }; struct ComboResult { Windows::Gaming::Input::GamepadReading currentReading; // untouched reading Windows::Gaming::Input::GamepadReading maskedReading; // reading with pending combo buttons masked out - bool comboTriggered; // True when combo completes + bool comboTriggered; + bool menuLongPressTriggered; }; struct GamepadState { @@ -36,6 +42,7 @@ struct GamepadState { Windows::Gaming::Input::GamepadReading previousReading; bool previousGuideButtonDown; GamepadComboState combo; + Windows::Gaming::Input::GamepadButtons buttonSuppressMask = Windows::Gaming::Input::GamepadButtons::None; short ltX, ltY, rtX, rtY; // after a call to normalizeAxes() these are unsigned char lTrig, rTrig; // populated with values expected by the protocol @@ -73,6 +80,10 @@ struct GamepadState { combo.viewPressed = false; combo.menuPressed = false; combo.startTime = 0; + combo.menuLongPressFired = false; + combo.menuInjectionStartQpc = 0; + combo.viewInjectionStartQpc = 0; + buttonSuppressMask = Windows::Gaming::Input::GamepadButtons::None; } void SetGuideButtonDown(bool isDown) { @@ -83,10 +94,10 @@ struct GamepadState { return isGuideButtonDown.load(); } - ComboResult GetComboResult(int comboTimeoutMs) { + ComboResult GetComboResult(int comboTimeoutMs, int menuLongPressMs = 600) { using namespace Windows::Gaming::Input; - ComboResult result = ComboResult{EmptyReading(), EmptyReading(), false}; + ComboResult result = ComboResult{EmptyReading(), EmptyReading(), false, false}; if (controller == nullptr) { return result; } @@ -115,17 +126,37 @@ struct GamepadState { // Handle simultaneous press in the same poll if (viewCurrentlyPressed && menuCurrentlyPressed && !combo.viewPressed && !combo.menuPressed) { combo.comboState = ComboState::ComboActive; + combo.menuInjectionStartQpc = 0; + combo.viewInjectionStartQpc = 0; result.comboTriggered = true; maskedButtons = clearButtons(buttons, GamepadButtons::View | GamepadButtons::Menu); // Check if either button is newly pressed } else if (viewCurrentlyPressed && !combo.viewPressed) { combo.comboState = ComboState::ViewWaiting; combo.startTime = QpcNow(); + combo.viewInjectionStartQpc = 0; maskedButtons = clearButtons(buttons, GamepadButtons::View); } else if (menuCurrentlyPressed && !combo.menuPressed) { combo.comboState = ComboState::MenuWaiting; combo.startTime = QpcNow(); + combo.menuInjectionStartQpc = 0; maskedButtons = clearButtons(buttons, GamepadButtons::Menu); + } else { + + if (!menuCurrentlyPressed && combo.menuInjectionStartQpc != 0) { + if (QpcToMs(QpcNow() - combo.menuInjectionStartQpc) < 100) { + maskedButtons = setButtons(maskedButtons, GamepadButtons::Menu); + } else { + combo.menuInjectionStartQpc = 0; + } + } + if (!viewCurrentlyPressed && combo.viewInjectionStartQpc != 0) { + if (QpcToMs(QpcNow() - combo.viewInjectionStartQpc) < 100) { + maskedButtons = setButtons(maskedButtons, GamepadButtons::View); + } else { + combo.viewInjectionStartQpc = 0; + } + } } break; @@ -133,14 +164,14 @@ struct GamepadState { // Check timeout if (QpcToMs(QpcNow() - combo.startTime) > comboTimeoutMs) { combo.comboState = ComboState::None; - maskedButtons = setButtons(buttons, GamepadButtons::View); + combo.viewInjectionStartQpc = QpcNow(); break; } // Check if View was released before combo completed if (!viewCurrentlyPressed) { combo.comboState = ComboState::None; - maskedButtons = setButtons(buttons, GamepadButtons::View); + combo.viewInjectionStartQpc = QpcNow(); break; } @@ -150,6 +181,7 @@ struct GamepadState { // Check if Menu is now pressed (combo complete) if (menuCurrentlyPressed && !combo.menuPressed) { combo.comboState = ComboState::ComboActive; + combo.viewInjectionStartQpc = 0; result.comboTriggered = true; maskedButtons = clearButtons(buttons, GamepadButtons::View | GamepadButtons::Menu); } @@ -157,14 +189,15 @@ struct GamepadState { case ComboState::MenuWaiting: if (QpcToMs(QpcNow() - combo.startTime) > comboTimeoutMs) { - combo.comboState = ComboState::None; - maskedButtons = setButtons(buttons, GamepadButtons::Menu); + combo.comboState = ComboState::MenuLongPressWaiting; + combo.menuLongPressFired = false; + maskedButtons = clearButtons(buttons, GamepadButtons::Menu); break; } if (!menuCurrentlyPressed) { combo.comboState = ComboState::None; - maskedButtons = setButtons(buttons, GamepadButtons::Menu); + combo.menuInjectionStartQpc = QpcNow(); break; } @@ -177,13 +210,36 @@ struct GamepadState { } break; + case ComboState::MenuLongPressWaiting: + if (!menuCurrentlyPressed) { + combo.comboState = ComboState::None; + if (!combo.menuLongPressFired) { + combo.menuInjectionStartQpc = QpcNow(); + } + break; + } + + maskedButtons = clearButtons(buttons, GamepadButtons::Menu); + + if (!combo.menuLongPressFired && QpcToMs(QpcNow() - combo.startTime) > menuLongPressMs) { + combo.menuLongPressFired = true; + result.menuLongPressTriggered = true; + } + break; + case ComboState::ComboActive: // Remain in combo state while both are held if (viewCurrentlyPressed && menuCurrentlyPressed) { - // Continue masking both buttons maskedButtons = clearButtons(buttons, GamepadButtons::View | GamepadButtons::Menu); } else { - // One or both released, reset state + combo.comboState = ComboState::ComboReleasing; + maskedButtons = clearButtons(buttons, GamepadButtons::View | GamepadButtons::Menu); + } + break; + + case ComboState::ComboReleasing: + maskedButtons = clearButtons(buttons, GamepadButtons::View | GamepadButtons::Menu); + if (!viewCurrentlyPressed && !menuCurrentlyPressed) { combo.comboState = ComboState::None; } break; @@ -310,6 +366,7 @@ struct GamepadState { char buttons[128]; DumpButtons(reading.Buttons, buttons, sizeof(buttons)); moonlight_xbox_dx::Utils::Logf( + moonlight_xbox_dx::Utils::LogLevel::Verbose, "GamepadState[localId: %d, hostId: %d] buttons: %s %s axes: %d %d, %d %d, triggers: %d %d, combo{ state: %d, viewPressed: %d, menuPressed: %d, startTime: %d }\n", localId, hostId, buttons, diff --git a/State/MDNSHandler.cpp b/State/MDNSHandler.cpp index 6f037aa9..b308dc28 100644 --- a/State/MDNSHandler.cpp +++ b/State/MDNSHandler.cpp @@ -123,6 +123,14 @@ void init_mdns() { } } +void mdns_send_query() { + auto str = "_nvstream._tcp.local"; + for (int i = 0; i < 8; i++) { + if (sockets[i] == 0) break; + mdns_query_send(sockets[i], MDNS_RECORDTYPE_PTR, str, strlen(str), &mdns_buffer, 4096, 0); + } +} + int query_mdns() { for (int i = 0; i < 8; i++) { if (sockets[i] == 0) break; diff --git a/State/MDNSHandler.h b/State/MDNSHandler.h index 4f53eb19..5bcbd95b 100644 --- a/State/MDNSHandler.h +++ b/State/MDNSHandler.h @@ -1,5 +1,5 @@ #pragma once - void init_mdns(); +void mdns_send_query(); int query_mdns(); \ No newline at end of file diff --git a/State/MoonlightApp.cpp b/State/MoonlightApp.cpp index e2b29b46..6e212f01 100644 --- a/State/MoonlightApp.cpp +++ b/State/MoonlightApp.cpp @@ -7,11 +7,19 @@ namespace moonlight_xbox_dx { void MoonlightApp::OnPropertyChanged(Platform::String^ propertyName) { - Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( - Windows::UI::Core::CoreDispatcherPriority::High, - ref new Windows::UI::Core::DispatchedHandler([this, propertyName]() - { - PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(propertyName)); - })); + auto dispatcher = Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher; + if (dispatcher->HasThreadAccess) + { + PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(propertyName)); + } + else + { + dispatcher->RunAsync( + Windows::UI::Core::CoreDispatcherPriority::High, + ref new Windows::UI::Core::DispatchedHandler([this, propertyName]() + { + PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(propertyName)); + })); + } } } \ No newline at end of file diff --git a/State/MoonlightApp.h b/State/MoonlightApp.h index 2c977675..e576da86 100644 --- a/State/MoonlightApp.h +++ b/State/MoonlightApp.h @@ -1,4 +1,5 @@ #pragma once +#include namespace moonlight_xbox_dx { [Windows::UI::Xaml::Data::Bindable] @@ -9,6 +10,14 @@ namespace moonlight_xbox_dx { Platform::String^ imagePath = "ms-appx:///Assets/gamepad.svg"; int id; bool currentlyRunning; + bool isGridLayout = false; + bool isSelected = false; + bool isFavorite = false; + Windows::UI::Xaml::Media::Imaging::BitmapImage^ image; + + Windows::UI::Xaml::Media::Imaging::BitmapImage^ blurredImage; + + Windows::UI::Xaml::Media::Imaging::BitmapImage^ glowImage; public: //Thanks to https://phsucharee.wordpress.com/2013/06/19/data-binding-and-ccx-inotifypropertychanged/ virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged; @@ -31,6 +40,26 @@ namespace moonlight_xbox_dx { void set(Platform::String^ path) { this->imagePath = path; OnPropertyChanged("ImagePath"); + + if (path != nullptr && this->image == nullptr) { + try { + auto uri = ref new Windows::Foundation::Uri(path); + Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + Windows::UI::Core::CoreDispatcherPriority::High, + ref new Windows::UI::Core::DispatchedHandler([this, uri]() { + try { + auto bitmap = ref new Windows::UI::Xaml::Media::Imaging::BitmapImage(); + bitmap->UriSource = uri; + bitmap->DecodePixelWidth = 1024; + bitmap->CreateOptions = Windows::UI::Xaml::Media::Imaging::BitmapCreateOptions::IgnoreImageCache; + this->image = bitmap; + OnPropertyChanged("Image"); + } catch(...) {} + }) + ); + } catch (...) { + } + } } } @@ -51,5 +80,66 @@ namespace moonlight_xbox_dx { OnPropertyChanged("CurrentlyRunning"); } } + + property bool IsGridLayout + { + bool get() { return this->isGridLayout; } + void set(bool value) { + if (this->isGridLayout == value) return; + this->isGridLayout = value; + OnPropertyChanged("IsGridLayout"); + } + } + + property bool IsSelected + { + bool get() { return this->isSelected; } + void set(bool value) { + if (this->isSelected == value) return; + this->isSelected = value; + OnPropertyChanged("IsSelected"); + } + } + + property bool IsFavorite + { + bool get() { return this->isFavorite; } + void set(bool value) { + if (this->isFavorite == value) return; + this->isFavorite = value; + OnPropertyChanged("IsFavorite"); + } + } + + property Windows::UI::Xaml::Media::Imaging::BitmapImage^ Image + { + Windows::UI::Xaml::Media::Imaging::BitmapImage^ get() { return this->image; } + void set(Windows::UI::Xaml::Media::Imaging::BitmapImage^ value) { + if (this->image == value) return; + this->image = value; + try { OnPropertyChanged("Image"); } catch(...) {} + } + } + + property Windows::UI::Xaml::Media::Imaging::BitmapImage^ BlurredImage + { + Windows::UI::Xaml::Media::Imaging::BitmapImage^ get() { return this->blurredImage; } + void set(Windows::UI::Xaml::Media::Imaging::BitmapImage^ value) { + if (this->blurredImage == value) return; + this->blurredImage = value; + try { OnPropertyChanged("BlurredImage"); } catch(...) {} + } + } + + property Windows::UI::Xaml::Media::Imaging::BitmapImage^ GlowImage + { + Windows::UI::Xaml::Media::Imaging::BitmapImage^ get() { return this->glowImage; } + void set(Windows::UI::Xaml::Media::Imaging::BitmapImage^ value) { + if (this->glowImage == value) return; + this->glowImage = value; + try { OnPropertyChanged("GlowImage"); } catch(...) {} + } + } + }; } \ No newline at end of file diff --git a/State/MoonlightClient.cpp b/State/MoonlightClient.cpp index 67731e5a..0b56321c 100644 --- a/State/MoonlightClient.cpp +++ b/State/MoonlightClient.cpp @@ -1,5 +1,6 @@ #include "MoonlightClient.h" #include "pch.h" +#define MLOG_TAG_OVERRIDE "MoonlightClient" extern "C" { #include @@ -10,7 +11,10 @@ extern "C" { #include #include #include +#include #include +#include +#include #include #include "Streaming\FFMpegDecoder.h" @@ -19,9 +23,7 @@ using namespace Windows::Gaming::Input; using namespace Windows::Graphics::Display; using namespace Windows::Graphics::Display::Core; -std::atomic g_connectionTerminated{false}; - -void log_message(const char* fmt, ...); +void log_message(const char *fmt, ...); void connection_started(); void connection_status_update(int status); void connection_status_completed(int status); @@ -51,7 +53,7 @@ void logDisplayMode(const char *str, HdmiDisplayMode ^ mode) { : mode->PixelEncoding == HdmiDisplayPixelEncoding::Ycc422 ? "Ycc422" : mode->PixelEncoding == HdmiDisplayPixelEncoding::Ycc420 ? "Ycc420" : "Unknown"); - Utils::Log(modeStr); + MLOG(Utils::LogLevel::Debug, modeStr); } // Based on CWIN32Util::ToggleWindowsHDR from xbmc @@ -71,21 +73,21 @@ bool MoonlightClient::SetDisplayHDR(bool enabled, const SS_HDR_METADATA &sunshin // HDR is enabled m_isHDR = true; if (enabled) { - Utils::Log("SetDisplayHDR(true): display is already in HDR mode\n"); + MLOG(Utils::LogLevel::Info, "SetDisplayHDR(true): display is already in HDR mode\n"); resendCurrentMode = true; } } else { // HDR is disabled m_isHDR = false; if (!enabled) { - Utils::Log("SetDisplayHDR(false): display is already in SDR mode\n"); + MLOG(Utils::LogLevel::Info, "SetDisplayHDR(false): display is already in SDR mode\n"); return true; } } // this method is only run on Series S/X, so we can bail out if the system is not set to 4K if (current->ResolutionWidthInRawPixels < 3840) { - Utils::Log("Warning: HDR may be unavailable when Xbox is not set to 4K resolution\n"); + MLOG(Utils::LogLevel::Warning, "Warning: HDR may be unavailable when Xbox is not set to 4K resolution\n"); // return false; } @@ -105,7 +107,7 @@ bool MoonlightClient::SetDisplayHDR(bool enabled, const SS_HDR_METADATA &sunshin hdrMetadata.MaxFrameAverageLightLevel = sunshineHdrMetadata.maxFrameAverageLightLevel; // log all available modes - Utils::Log("Supported display modes:\n"); + MLOG(Utils::LogLevel::Debug, "Supported display modes:\n"); for (auto mode : hdmi->GetSupportedDisplayModes()) { logDisplayMode(" ", mode); } @@ -125,7 +127,7 @@ bool MoonlightClient::SetDisplayHDR(bool enabled, const SS_HDR_METADATA &sunshin // A non-HDR display viewing an HDR stream will error out here with no mode found if (newMode == nullptr) { - Utils::Log("SetDisplayHDR(): HDR is unavailable, no suitable display mode found\n"); + MLOG(Utils::LogLevel::Warning, "SetDisplayHDR(): HDR is unavailable, no suitable display mode found\n"); return false; } @@ -137,7 +139,7 @@ bool MoonlightClient::SetDisplayHDR(bool enabled, const SS_HDR_METADATA &sunshin HdmiDisplayHdrOption hdrOption = HdmiDisplayHdrOption::None; if (enabled) { logDisplayMode("SetDisplayHDR(true): switching to HDR mode", newMode); - Utils::Logf("Sending HDR10 metadata: Min/MaxLuminance %.0f / %u\n", + MLOGF(Utils::LogLevel::Debug, "Sending HDR10 metadata: Min/MaxLuminance %.0f / %u\n", hdrMetadata.MinMasteringLuminance / 10000.0, hdrMetadata.MaxMasteringLuminance); hdrOption = HdmiDisplayHdrOption::Eotf2084; } else { @@ -164,7 +166,7 @@ bool MoonlightClient::SetDisplayHDR(bool enabled, const SS_HDR_METADATA &sunshin return true; } } else { - Utils::Log("SetDisplayHDR(): Error switching display mode.\n"); + MLOG(Utils::LogLevel::Error, "SetDisplayHDR(): Error switching display mode.\n"); } return false; @@ -174,7 +176,8 @@ MoonlightClient *connectedInstance; MoonlightClient::MoonlightClient() : m_isHDR(false), - m_isRGBFull(false) { + m_isRGBFull(false), + m_connectionTerminated(false) { HdmiDisplayInformation ^ hdmi = HdmiDisplayInformation::GetForCurrentView(); if (hdmi) { HdmiDisplayMode ^ current = hdmi->GetCurrentDisplayMode(); @@ -198,14 +201,10 @@ MoonlightClient::~MoonlightClient() { void MoonlightClient::StopApp() { gs_quit_app(&serverData); } -int MoonlightClient::StartStreaming(std::shared_ptr res, StreamConfiguration^ sConfig) { - g_connectionTerminated.store(false, std::memory_order_release); +int MoonlightClient::StartStreaming(std::shared_ptr res, StreamConfiguration ^ sConfig) { - //Thanks to https://stackoverflow.com/questions/11746146/how-to-convert-platformstring-to-char - std::wstring fooW(sConfig->hostname->Begin()); - std::string fooA(fooW.begin(), fooW.end()); - const char *charStr = fooA.c_str(); - this->Connect(charStr); + std::string fooA = Utils::PlatformStringToStdString(sConfig->hostname); + this->Connect(fooA.c_str()); STREAM_CONFIGURATION config; LiInitializeStreamConfiguration(&config); config.width = sConfig->width; @@ -254,19 +253,17 @@ int MoonlightClient::StartStreaming(std::shared_ptr res, St break; } - Utils::Logf("Requesting stream with clientRefreshRateX100=%d for %d FPS on %.2f Hz display\n", + MLOGF(Utils::LogLevel::Debug, "Requesting stream with clientRefreshRateX100=%d for %d FPS on %.2f Hz display\n", config.clientRefreshRateX100, config.fps, rr); } config.colorRange = this->IsRGBFull() ? COLOR_RANGE_FULL : COLOR_RANGE_LIMITED; config.colorSpace = COLORSPACE_REC_601; - config.encryptionFlags = ENCFLG_AUDIO; + config.encryptionFlags = 0; config.packetSize = 1024; config.supportedVideoFormats = VIDEO_FORMAT_H264; if (!IsXboxOneVCR()) { config.supportedVideoFormats |= VIDEO_FORMAT_H265; - if (sConfig->enableHDR) { - config.supportedVideoFormats |= VIDEO_FORMAT_H265_MAIN10; - } + config.supportedVideoFormats |= VIDEO_FORMAT_H265_MAIN10; } config.audioConfiguration = AUDIO_CONFIGURATION_STEREO; @@ -280,27 +277,27 @@ int MoonlightClient::StartStreaming(std::shared_ptr res, St config.streamingRemotely = STREAM_CFG_AUTO; char message[2048]; sprintf(message, "Inserted App ID %d\n", sConfig->appID); - Utils::Log(message); + MLOG(Utils::LogLevel::Info, message); auto gamepads = Windows::Gaming::Input::Gamepad::Gamepads; - this->SetGamepadCount(gamepads->Size); // sets activeGamepadMask + + this->SetGamepadCount(std::max((UINT)1, gamepads->Size)); int a = gs_start_app(&serverData, &config, sConfig->appID, sConfig->enableSOPS, sConfig->playAudioOnPC, activeGamepadMask); if (a != 0) { char message[2048]; sprintf(message, "gs_startapp failed with status code %d\n", a); - Utils::Log(message); - + MLOG(Utils::LogLevel::Error, message); if (gs_error) { char errorMessage[2048]; sprintf(errorMessage, "%s\n", gs_error); - - Utils::Log(errorMessage); - this->OnFailed(0, a, errorMessage); + MLOG(Utils::LogLevel::Error, errorMessage); + if (this->OnFailed != nullptr) { + this->OnFailed(0, a, errorMessage); + } } return a; } - - // Sleep(10000); connectedInstance = this; + m_connectionTerminated.store(false); CONNECTION_LISTENER_CALLBACKS callbacks; LiInitializeConnectionCallbacks(&callbacks); callbacks.logMessage = log_message; @@ -314,21 +311,15 @@ int MoonlightClient::StartStreaming(std::shared_ptr res, St callbacks.rumble = connection_rumble; callbacks.rumbleTriggers = connection_trigger_rumble; - FFMpegDecoder::instance().CompleteInitialization(res, &config, sConfig->framePacing == "Immediate"); + const bool framePacingImmediate = (sConfig->framePacing != nullptr && sConfig->framePacing == "Immediate"); + FFMpegDecoder::instance().CompleteInitialization(res, &config, framePacingImmediate); DECODER_RENDERER_CALLBACKS rCallbacks = FFMpegDecoder::getDecoder(); AUDIO_RENDERER_CALLBACKS aCallbacks = AudioPlayer::getDecoder(); - int k = LiStartConnection(&serverData.serverInfo, &config, &callbacks, &rCallbacks, &aCallbacks, NULL, 0, NULL, 0); - sprintf(message, "LiStartConnection %d\n", k); - Utils::Log(message); - - if (k != 0) { - this->OnFailed(0, k, "Connection failed"); - } - - return k; + MLOG(Utils::LogLevel::Debug, message); + return k; } void MoonlightClient::StopStreaming() { @@ -340,23 +331,13 @@ void log_message(const char *fmt, ...) { va_start(argp, fmt); char message[2048]; vsprintf_s(message, fmt, argp); - - // Append a single '\n' only if the string doesn't already end with one. - size_t len = strlen(message); - if (len == 0 || message[len - 1] != '\n') { - if (len + 1 < sizeof(message)) { - message[len] = '\n'; - message[len + 1] = '\0'; - } - } - - Utils::Log(message); + MLOG(Utils::LogLevel::Debug, message); } void connection_started() { char message[2048]; sprintf(message, "Connection Started\n"); - Utils::Log(message); + MLOG(Utils::LogLevel::Info, message); if (connectedInstance->OnCompleted != nullptr) { connectedInstance->OnCompleted(); } @@ -364,15 +345,14 @@ void connection_started() { void connection_status_update(int status) { char message[4096]; - auto stageName = LiGetFormattedStageName(status); - sprintf(message, "Stage %d: '%s' - Started\n", status, LiGetFormattedStageName(status)); - Utils::Log(message); + sprintf(message, "Stage %d started\n", status); + MLOG(Utils::LogLevel::Debug, message); } void connection_status_completed(int status) { char message[4096]; - sprintf(message, "Stage %d: '%s' - Completed\n", status, LiGetFormattedStageName(status)); - Utils::Log(message); + sprintf(message, "Stage %d completed\n", status); + MLOG(Utils::LogLevel::Debug, message); if (connectedInstance->OnStatusUpdate != nullptr) { connectedInstance->OnStatusUpdate(status); } @@ -387,37 +367,62 @@ void connection_set_hdr(bool enable) { void connection_terminated(int status) { char message[4096]; sprintf(message, "Connection terminated with status %d\n", status); - Utils::Log(message); - - g_connectionTerminated.store(true, std::memory_order_release); + MLOG(Utils::LogLevel::Info, message); + if (connectedInstance != nullptr) { + connectedInstance->SetConnectionTerminated(); + } } void stage_failed(int stage, int err) { char message[4096]; unsigned int portFlags = LiGetPortFlagsFromStage(stage); - // int portResult = LiTestClientConnectivity("qt.conntest.moonlight-stream.org", 443, portFlags); + int portResult = LiTestClientConnectivity("qt.conntest.moonlight-stream.org", 443, portFlags); char failingPorts[128]; LiStringifyPortFlags(portFlags, ", ", failingPorts, sizeof(failingPorts)); - sprintf(message, "Stage %d: '%s' - Failed with error: %d.\n", stage, LiGetFormattedStageName(stage), err, failingPorts); - Utils::Log(message); + sprintf(message, "%s failed with error %d.\n Check Firewall and Connections to port: %s\n", LiGetStageName(stage), err, failingPorts); + MLOG(Utils::LogLevel::Error, message); if (connectedInstance->OnFailed != nullptr) { connectedInstance->OnFailed(stage, err, message); } } void connection_rumble(unsigned short controllerNumber, unsigned short lowFreqMotor, unsigned short highFreqMotor) { - if (connectedInstance->OnRumble != nullptr) { + if (connectedInstance != nullptr && connectedInstance->OnRumble != nullptr) { connectedInstance->OnRumble(controllerNumber, lowFreqMotor, highFreqMotor); + return; } + if (Windows::Gaming::Input::Gamepad::Gamepads->Size <= controllerNumber) return; + auto gp = Windows::Gaming::Input::Gamepad::Gamepads->GetAt(controllerNumber); + float normalizedHigh = highFreqMotor / (float)(256 * 256); + float normalizedLow = lowFreqMotor / (float)(256 * 256); + Windows::Gaming::Input::GamepadVibration v = gp->Vibration; + + v.LeftMotor = normalizedLow; + v.RightMotor = normalizedHigh; + gp->Vibration = v; } void connection_trigger_rumble(unsigned short controllerNumber, unsigned short leftTriggerMotor, unsigned short rightTriggerMotor) { - if (connectedInstance->OnTriggerRumble != nullptr) { + if (connectedInstance != nullptr && connectedInstance->OnTriggerRumble != nullptr) { connectedInstance->OnTriggerRumble(controllerNumber, leftTriggerMotor, rightTriggerMotor); + return; } + if (Windows::Gaming::Input::Gamepad::Gamepads->Size <= controllerNumber) return; + auto gp = Windows::Gaming::Input::Gamepad::Gamepads->GetAt(controllerNumber); + float normalizedLeft = leftTriggerMotor / (float)(256 * 256); + float normalizedRight = rightTriggerMotor / (float)(256 * 256); + Windows::Gaming::Input::GamepadVibration v = gp->Vibration; + v.LeftTrigger = normalizedLeft; + v.RightTrigger = normalizedRight; + gp->Vibration = v; } int MoonlightClient::Connect(const char *hostname) { + std::lock_guard lock(m_connectMutex); + if (this->hostname != NULL) { + free(this->hostname); + this->hostname = NULL; + } this->hostname = (char *)malloc(2048 * sizeof(char)); strcpy_s(this->hostname, 2048, hostname); if (strchr(this->hostname, ':') != 0) { @@ -432,19 +437,10 @@ int MoonlightClient::Connect(const char *hostname) { char folder[2048]; wcstombs_s(NULL, folder, folderString->Data(), 2047); - int status = 0; - status = gs_init(&serverData, this->hostname, port, folder, 3, true); + int status = gs_init(&serverData, this->hostname, port, folder, 3, true); return status; } -bool MoonlightClient::IsConnectionTerminated() { - return g_connectionTerminated.load(std::memory_order_acquire); -} - -void MoonlightClient::SetConnectionTerminated() { - g_connectionTerminated.store(true, std::memory_order_release); -} - bool MoonlightClient::IsHDR() { return m_isHDR; } @@ -453,6 +449,14 @@ bool MoonlightClient::IsRGBFull() { return m_isRGBFull; } +bool MoonlightClient::IsConnectionTerminated() { + return m_connectionTerminated.load(); +} + +void MoonlightClient::SetConnectionTerminated() { + m_connectionTerminated.store(true); +} + bool MoonlightClient::IsPaired() { return serverData.paired; } @@ -468,7 +472,6 @@ int MoonlightClient::Pair() { if (serverData.paired) return -7; int status; if ((status = gs_pair(&serverData, &connectionPin[0])) != 0) { - // TODO: Handle gs WRONG STATE gs_unpair(&serverData); return status; } @@ -504,15 +507,23 @@ std::vector MoonlightClient::GetApplications(bool fetchAssets) { a->Name = s.Name; values.push_back(a); } - Platform::String ^ folderString = Windows::Storage::ApplicationData::Current->LocalFolder->Path; - folderString = folderString->Concat(folderString, "\\images\\"); + Platform::String ^ baseImages = Windows::Storage::ApplicationData::Current->LocalFolder->Path; + baseImages = Platform::String::Concat(baseImages, L"\\images\\"); + Platform::String ^ hostId = (serverData.uniqueId != nullptr) + ? Utils::StringFromChars(serverData.uniqueId) + : ref new Platform::String(L"unknown"); + Platform::String ^ folderString = Platform::String::Concat(baseImages, Platform::String::Concat(hostId, L"\\")); char folder[2048]; wcstombs_s(NULL, folder, folderString->Data(), 2047); + CreateDirectory(baseImages->Data(), NULL); CreateDirectory(folderString->Data(), NULL); if (fetchAssets) { Concurrency::create_task([folder, folderString, values, this]() { + std::unordered_set currentIds; + for (auto a : values) currentIds.insert(a->Id); + for (auto a : values) { - auto imgPath = folderString->Concat(folderString, a->Id + ".png"); + auto imgPath = Platform::String::Concat(folderString, a->Id + ".png"); // https://stackoverflow.com/a/6218957 DWORD dwAttrib = GetFileAttributes(imgPath->Data()); if (dwAttrib == INVALID_FILE_ATTRIBUTES) { @@ -520,12 +531,80 @@ std::vector MoonlightClient::GetApplications(bool fetchAssets) { } a->ImagePath = imgPath; } + + std::wstring searchPath(folderString->Data()); + searchPath += L"*.png"; + WIN32_FIND_DATAW findData; + HANDLE hFind = FindFirstFileW(searchPath.c_str(), &findData); + if (hFind != INVALID_HANDLE_VALUE) { + do { + std::wstring fname(findData.cFileName); + if (fname.size() > 4 && fname.substr(fname.size() - 4) == L".png") { + std::wstring numStr = fname.substr(0, fname.size() - 4); + wchar_t* end = nullptr; + long id = wcstol(numStr.c_str(), &end, 10); + if (end != numStr.c_str() && *end == L'\0' && currentIds.find((int)id) == currentIds.end()) { + std::wstring base(folderString->Data()); + DeleteFileW((base + fname).c_str()); + DeleteFileW((base + L"blur\\" + numStr + L"_bg.png").c_str()); + DeleteFileW((base + L"blur\\" + numStr + L"_glow.png").c_str()); + } + } + } while (FindNextFileW(hFind, &findData)); + FindClose(hFind); + } }); } return values; } +static bool hasGamepadReadingChanged(GamepadReading a, GamepadReading b) { + if (a.Buttons != b.Buttons) { + return true; + } + + short altX = (short)(a.LeftThumbstickX * 32767); + short altY = (short)(a.LeftThumbstickY * 32767); + short artX = (short)(a.RightThumbstickX * 32767); + short artY = (short)(a.RightThumbstickY * 32767); + short bltX = (short)(b.LeftThumbstickX * 32767); + short bltY = (short)(b.LeftThumbstickY * 32767); + short brtX = (short)(b.RightThumbstickX * 32767); + short brtY = (short)(b.RightThumbstickY * 32767); + if (altX != bltX || altY != bltY || artX != brtX || artY != brtY) { + return true; + } + + unsigned char alTrig = (unsigned char)(round(a.LeftTrigger * 255.0f)); + unsigned char arTrig = (unsigned char)(round(a.RightTrigger * 255.0f)); + unsigned char blTrig = (unsigned char)(round(b.LeftTrigger * 255.0f)); + unsigned char brTrig = (unsigned char)(round(b.RightTrigger * 255.0f)); + if (alTrig != blTrig || arTrig != brTrig) { + return true; + } + + return false; +} + +void MoonlightClient::SendGamepadReading(short controllerNumber, GamepadReading reading) { + int buttonFlags = 0; + GamepadButtons buttons[] = {GamepadButtons::A, GamepadButtons::B, GamepadButtons::X, GamepadButtons::Y, GamepadButtons::DPadLeft, GamepadButtons::DPadRight, GamepadButtons::DPadUp, GamepadButtons::DPadDown, GamepadButtons::LeftShoulder, GamepadButtons::RightShoulder, GamepadButtons::Menu, GamepadButtons::View, GamepadButtons::LeftThumbstick, GamepadButtons::RightThumbstick}; + int LiButtonFlags[] = {A_FLAG, B_FLAG, X_FLAG, Y_FLAG, LEFT_FLAG, RIGHT_FLAG, UP_FLAG, DOWN_FLAG, LB_FLAG, RB_FLAG, PLAY_FLAG, BACK_FLAG, LS_CLK_FLAG, RS_CLK_FLAG}; + for (int i = 0; i < 14; i++) { + if ((reading.Buttons & buttons[i]) == buttons[i]) { + buttonFlags |= LiButtonFlags[i]; + } + } + unsigned char leftTrigger = (unsigned char)(round(reading.LeftTrigger * 255.0f)); + unsigned char rightTrigger = (unsigned char)(round(reading.RightTrigger * 255.0f)); + + if (hasGamepadReadingChanged(reading, m_lastGamepadReading[controllerNumber])) { + LiSendMultiControllerEvent(controllerNumber, activeGamepadMask, buttonFlags, leftTrigger, rightTrigger, (short)(reading.LeftThumbstickX * 32767), (short)(reading.LeftThumbstickY * 32767), (short)(reading.RightThumbstickX * 32767), (short)(reading.RightThumbstickY * 32767)); + m_lastGamepadReading[controllerNumber] = reading; + } +} + void MoonlightClient::SendGuide(int controllerNumber, bool s) { LiSendMultiControllerEvent(controllerNumber, activeGamepadMask, s ? SPECIAL_FLAG : 0, 0, 0, 0, 0, 0, 0); } diff --git a/State/MoonlightClient.h b/State/MoonlightClient.h index a15836f4..013ac415 100644 --- a/State/MoonlightClient.h +++ b/State/MoonlightClient.h @@ -1,68 +1,73 @@ #pragma once #include "pch.h" -#include #include "../Common/DeviceResources.h" +#include #include "State\MoonlightApp.h" +#include extern "C" { -#include -#include -#include + #include + #include + #include } -typedef void (*MoonlightErrorCallback)(const char *msg); +typedef void(*MoonlightErrorCallback)(const char *msg); namespace moonlight_xbox_dx { -class MoonlightClient { - public: - MoonlightClient(); - ~MoonlightClient(); - bool SetDisplayHDR(bool enabled, const SS_HDR_METADATA &sunshineHdrMetadata); - int StartStreaming(std::shared_ptr res, StreamConfiguration ^ config); - int Connect(const char *hostname); - bool IsConnectionTerminated(); - void SetConnectionTerminated(); - bool IsHDR(); - bool IsPaired(); - bool IsRGBFull(); - int Pair(); - char *GeneratePIN(); - std::vector GetApplications(bool fetchAssets = true); - void SendMousePosition(float x, float y); - void SendMousePressed(int button); - void SendMouseReleased(int button); - void SendScroll(float value); - void SendScrollH(float value); - void SetSoftwareEncoder(bool value); - void SetGamepadCount(short count); - int GetRunningAppID(); - void StopStreaming(); - void StopApp(); - void Unpair(); - void KeyDown(unsigned short v, char modifiers); - void KeyUp(unsigned short v, char modifiers); - void SendGuide(int controllerNumber, bool v); - Platform::String ^ GetInstanceID(); - Platform::String ^ GetComputerName(); - Platform::String ^ GetServerAddress(); - Platform::String ^ GetServerMacAddress(); - std::function OnStatusUpdate; - std::function OnCompleted; - std::function SetHDR; - std::function OnFailed; - std::function OnRumble; - std::function OnTriggerRumble; - - private: - SERVER_DATA serverData; - char *connectionPin = NULL; - char *hostname = NULL; - int port = 0; - bool useSoftwareEncoder = false; - bool m_isHDR; - bool m_isRGBFull; - uint16_t activeGamepadMask = 0; - Windows::Gaming::Input::GamepadReading m_lastGamepadReading[16]; -}; -} // namespace moonlight_xbox_dx + class MoonlightClient + { + public: + MoonlightClient(); + ~MoonlightClient(); + bool SetDisplayHDR(bool enabled, const SS_HDR_METADATA& sunshineHdrMetadata); + int StartStreaming(std::shared_ptr res, StreamConfiguration ^config); + int Connect(const char* hostname); + bool IsHDR(); + bool IsPaired(); + bool IsRGBFull(); + bool IsConnectionTerminated(); + void SetConnectionTerminated(); + int Pair(); + char *GeneratePIN(); + std::vector GetApplications(bool fetchAssets = true); + void SendGamepadReading(short controllerNumber, Windows::Gaming::Input::GamepadReading reading); + void SendMousePosition(float x, float y); + void SendMousePressed(int button); + void SendMouseReleased(int button); + void SendScroll(float value); + void SendScrollH(float value); + void SetSoftwareEncoder(bool value); + void SetGamepadCount(short count); + int GetRunningAppID(); + void StopStreaming(); + void StopApp(); + void Unpair(); + void KeyDown(unsigned short v,char modifiers); + void KeyUp(unsigned short v, char modifiers); + void SendGuide(int controllerNumber, bool v); + Platform::String^ GetInstanceID(); + Platform::String^ GetComputerName(); + Platform::String^ GetServerAddress(); + Platform::String^ GetServerMacAddress(); + std::function OnStatusUpdate; + std::function OnCompleted; + std::function SetHDR; + std::function OnFailed; + std::function OnStartFailed; + std::function OnRumble; + std::function OnTriggerRumble; + private: + SERVER_DATA serverData; + char* connectionPin = NULL; + char* hostname = NULL; + int port = 0; + std::mutex m_connectMutex; + bool useSoftwareEncoder = false; + int activeGamepadMask = 0; + bool m_isHDR; + bool m_isRGBFull; + std::atomic m_connectionTerminated{ false }; + Windows::Gaming::Input::GamepadReading m_lastGamepadReading[16]; + }; +} diff --git a/State/MoonlightHost.cpp b/State/MoonlightHost.cpp index ad6bf2e6..3b1fcde3 100644 --- a/State/MoonlightHost.cpp +++ b/State/MoonlightHost.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "MoonlightHost.h" #include "State\MoonlightClient.h" +#include namespace moonlight_xbox_dx { MoonlightHost::MoonlightHost(Platform::String ^host) { @@ -48,11 +49,35 @@ namespace moonlight_xbox_dx { Apps->Clear(); for (auto a : apps) { if (a->Id == CurrentlyRunningAppId) a->CurrentlyRunning = true; + a->IsFavorite = IsAppFavorite(a->Id); Apps->Append(a); } })); } + bool MoonlightHost::IsAppFavorite(int appId) { + return std::find(favoriteAppIds.begin(), favoriteAppIds.end(), appId) != favoriteAppIds.end(); + } + + void MoonlightHost::ToggleFavorite(int appId) { + auto it = std::find(favoriteAppIds.begin(), favoriteAppIds.end(), appId); + bool nowFavorite; + if (it != favoriteAppIds.end()) { + favoriteAppIds.erase(it); + nowFavorite = false; + } else { + favoriteAppIds.push_back(appId); + nowFavorite = true; + } + for (unsigned int i = 0; i < Apps->Size; ++i) { + auto a = Apps->GetAt(i); + if (a != nullptr && a->Id == appId) { + a->IsFavorite = nowFavorite; + break; + } + } + } + void MoonlightHost::UpdateAppRunningStates() { try { UpdateHostInfo(false); @@ -83,4 +108,31 @@ namespace moonlight_xbox_dx { PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(propertyName)); })); } + + void MoonlightHost::ScheduleStatusDebounce() + { + int version = ++m_debounceVersion; + auto delay = Windows::Foundation::TimeSpan{ 10000000LL }; + Windows::System::Threading::ThreadPoolTimer::CreateTimer( + ref new Windows::System::Threading::TimerElapsedHandler([this, version](Windows::System::Threading::ThreadPoolTimer^) { + if (m_debounceVersion == version) { + CommitDisplayedStatus(); + } + }), + delay + ); + } + + void MoonlightHost::CommitDisplayedStatus() + { + this->displayedConnected = this->connected; + this->displayedPaired = this->paired; + this->displayedLoading = this->loading; + this->displayedWolPolling = this->wolPolling; + OnPropertyChanged("StatusIsPolling"); + OnPropertyChanged("StatusIsConnectedAndPaired"); + OnPropertyChanged("StatusIsNotPaired"); + OnPropertyChanged("StatusIsDisconnected"); + OnPropertyChanged("StatusIsUnavailable"); + } } diff --git a/State/MoonlightHost.h b/State/MoonlightHost.h index 52c2e126..753e4e12 100644 --- a/State/MoonlightHost.h +++ b/State/MoonlightHost.h @@ -2,6 +2,7 @@ #include "pch.h" #include "State\MoonlightClient.h" #include "State\ScreenResolution.h" +#include "UI\Models\UIPersonalization.h" namespace moonlight_xbox_dx { [Windows::UI::Xaml::Data::Bindable] @@ -17,6 +18,13 @@ namespace moonlight_xbox_dx { bool connected; bool loading = true; bool wolPolling = false; + bool displayedConnected = false; + bool displayedPaired = false; + bool displayedLoading = true; + bool displayedWolPolling = false; + std::atomic m_debounceVersion{ 0 }; + void ScheduleStatusDebounce(); + void CommitDisplayedStatus(); bool playAudioOnPC = false; MoonlightClient* client; int currentlyRunningAppId; @@ -31,7 +39,10 @@ namespace moonlight_xbox_dx { bool enableSOPS = false; bool enableStats = false; bool enableGraphs = true; + UIPersonalization^ personalization; Windows::Foundation::Collections::IVector^ apps; + internal: + std::vector favoriteAppIds; public: //Thanks to https://phsucharee.wordpress.com/2013/06/19/data-binding-and-ccx-inotifypropertychanged/ virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged; @@ -43,6 +54,8 @@ namespace moonlight_xbox_dx { void Unpair(); void UpdateApps(); void UpdateAppRunningStates(); + bool IsAppFavorite(int appId); + void ToggleFavorite(int appId); property Platform::String^ InstanceId { Platform::String^ get() { return this->instanceId; } @@ -96,6 +109,7 @@ namespace moonlight_xbox_dx { OnPropertyChanged("NotPaired"); OnPropertyChanged("Connected"); OnPropertyChanged("NotConnected"); + ScheduleStatusDebounce(); } } @@ -107,6 +121,7 @@ namespace moonlight_xbox_dx { OnPropertyChanged("Connected"); OnPropertyChanged("NotConnected"); OnPropertyChanged("NotPaired"); + ScheduleStatusDebounce(); } } @@ -120,6 +135,31 @@ namespace moonlight_xbox_dx { bool get() { return this->connected && !this->paired; } } + property bool StatusIsPolling + { + bool get() { return this->displayedWolPolling || this->displayedLoading; } + } + + property bool StatusIsConnectedAndPaired + { + bool get() { return this->displayedConnected && this->displayedPaired && !StatusIsPolling; } + } + + property bool StatusIsNotPaired + { + bool get() { return this->displayedConnected && !this->displayedPaired && !StatusIsPolling; } + } + + property bool StatusIsDisconnected + { + bool get() { return !this->displayedConnected && !StatusIsPolling; } + } + + property bool StatusIsUnavailable + { + bool get() { return !(this->displayedConnected && this->displayedPaired); } + } + property bool Loading { bool get() { return this->loading; } @@ -131,6 +171,7 @@ namespace moonlight_xbox_dx { OnPropertyChanged("NotConnected"); OnPropertyChanged("NotPaired"); OnPropertyChanged("Paired"); + ScheduleStatusDebounce(); } } @@ -146,6 +187,7 @@ namespace moonlight_xbox_dx { this->wolPolling = value; OnPropertyChanged("WolPolling"); OnPropertyChanged("WolPollingVisibility"); + ScheduleStatusDebounce(); } } @@ -284,5 +326,14 @@ namespace moonlight_xbox_dx { OnPropertyChanged("EnableGraphs"); } } + + property UIPersonalization^ Personalization + { + UIPersonalization^ get() { + if (this->personalization == nullptr) + this->personalization = ref new UIPersonalization(); + return this->personalization; + } + } }; } diff --git a/State/Stats.cpp b/State/Stats.cpp index c41027f4..e7fc00d7 100644 --- a/State/Stats.cpp +++ b/State/Stats.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "Stats.h" +#define MLOG_TAG_OVERRIDE "Stats" #include "Utils.hpp" #include "../Plot/ImGuiPlots.h" #include "../Streaming/FFMpegDecoder.h" @@ -290,7 +291,7 @@ void Stats::formatVideoStats(DX::StepTimer const& timer, VIDEO_STATS& stats, cha stats.totalFps, codecString); if (ret < 0 || (size_t)ret >= (length - offset)) { - Utils::Log("Error: stringifyVideoStats length overflow\n"); + MLOG(Utils::LogLevel::Error, "Error: stringifyVideoStats length overflow\n"); return; } @@ -313,7 +314,7 @@ void Stats::formatVideoStats(DX::StepTimer const& timer, VIDEO_STATS& stats, cha stats.renderedFps, Pacer::instance().getPacingImmediate() ? "immediate" : "display-locked"); if (ret < 0 || (size_t)ret >= (length - offset)) { - Utils::Log("Error: stringifyVideoStats length overflow\n"); + MLOG(Utils::LogLevel::Error, "Error: stringifyVideoStats length overflow\n"); return; } @@ -328,7 +329,7 @@ void Stats::formatVideoStats(DX::StepTimer const& timer, VIDEO_STATS& stats, cha (double)stats.maxHostProcessingLatency / 10, (double)stats.totalHostProcessingLatency / 10 / stats.framesWithHostProcessingLatency); if (ret < 0 || (size_t)ret >= (length - offset)) { - Utils::Log("Error: stringifyVideoStats length overflow\n"); + MLOG(Utils::LogLevel::Error, "Error: stringifyVideoStats length overflow\n"); return; } @@ -340,7 +341,7 @@ void Stats::formatVideoStats(DX::StepTimer const& timer, VIDEO_STATS& stats, cha length - offset, "Host processing latency min/max/avg: -/-/- ms\n"); if (ret < 0 || (size_t)ret >= (length - offset)) { - Utils::Log("Error: stringifyVideoStats length overflow\n"); + MLOG(Utils::LogLevel::Error, "Error: stringifyVideoStats length overflow\n"); return; } @@ -375,7 +376,7 @@ void Stats::formatVideoStats(DX::StepTimer const& timer, VIDEO_STATS& stats, cha stats.renderedFrames ? (double)stats.totalRenderTimeUs / 1000.0 / stats.renderedFrames : 0.0f, stats.renderedFrames ? (double)stats.totalPresentTimeUs / 1000.0 / stats.renderedFrames : 0.0f); if (ret < 0 || (size_t)ret >= (length - offset)) { - Utils::Log("Error: stringifyVideoStats length overflow\n"); + MLOG(Utils::LogLevel::Error, "Error: stringifyVideoStats length overflow\n"); return; } @@ -395,7 +396,7 @@ void Stats::formatVideoStats(DX::StepTimer const& timer, VIDEO_STATS& stats, cha (double)stats.totalPreWaitTimeUs / 1000.0 / stats.renderedFrames, (double)stats.totalRenderTimeUs / 1000.0 / stats.renderedFrames); if (ret < 0 || (size_t)ret >= (length - offset)) { - Utils::Log("Error: stringifyVideoStats length overflow\n"); + MLOG(Utils::LogLevel::Error, "Error: stringifyVideoStats length overflow\n"); return; } diff --git a/State/StreamConfiguration.h b/State/StreamConfiguration.h index f0baa3c6..84b4f0c7 100644 --- a/State/StreamConfiguration.h +++ b/State/StreamConfiguration.h @@ -21,6 +21,7 @@ namespace moonlight_xbox_dx property bool enableSOPS; property bool enableStats; property bool enableGraphs; + property Windows::UI::Xaml::Media::Imaging::BitmapImage^ backgroundImage; }; moonlight_xbox_dx::StreamConfiguration^ GetStreamConfig(); diff --git a/Streaming/AudioPlayer.cpp b/Streaming/AudioPlayer.cpp index a759591f..57905209 100644 --- a/Streaming/AudioPlayer.cpp +++ b/Streaming/AudioPlayer.cpp @@ -1,4 +1,5 @@ #include "pch.h" +#define MLOG_TAG_OVERRIDE "AudioPlayer" #include #include #include @@ -12,7 +13,7 @@ static void AudioPlayer_LogCallback(void* pUserData, ma_uint32 level, const char* pMessage) { if (level <= MA_LOG_LEVEL_INFO) { - moonlight_xbox_dx::Utils::Logf("[miniaudio] %s", pMessage); + MLOGF(moonlight_xbox_dx::Utils::LogLevel::Info, "[miniaudio] %s", pMessage); } } @@ -65,14 +66,14 @@ namespace moonlight_xbox_dx { ma_uint32 len = frameCount; ma_result res = ma_pcm_rb_acquire_read(&rb, &len, &buffer); if (res != MA_SUCCESS) { - Utils::Log("Failed to read audio data\n"); + MLOG(Utils::LogLevel::Error, "Failed to read audio data\n"); return; } if (len > 0) { memcpy(pOutput, buffer, len * ma_pcm_rb_get_bpf(&rb)); res = ma_pcm_rb_commit_read(&rb, len); if (res != MA_SUCCESS && res != MA_AT_END) { - Utils::Log("Failed to read audio data to shared buffer\n"); + MLOG(Utils::LogLevel::Error, "Failed to read audio data to shared buffer\n"); return; } } @@ -100,24 +101,24 @@ namespace moonlight_xbox_dx { config.pLog = &log; if (ma_context_init(NULL, 1, &config, &context) != MA_SUCCESS) { - Utils::Log("Failed to create miniaudio context.\n"); + MLOG(Utils::LogLevel::Error, "Failed to create miniaudio context.\n"); return -3; } if (ma_device_init(&context, &deviceConfig, &device) != MA_SUCCESS) { - Utils::Log("Failed to open playback device.\n"); + MLOG(Utils::LogLevel::Error, "Failed to open playback device.\n"); return -3; } ma_result r = ma_pcm_rb_init(ma_format_f32, opusConfig->channelCount, opusConfig->samplesPerFrame * 10, NULL, NULL, &rb); if (r != MA_SUCCESS) { - Utils::Log("Failed to create shared buffer\n"); + MLOG(Utils::LogLevel::Error, "Failed to create shared buffer\n"); } return r; } void AudioPlayer::Cleanup() { - Utils::Log("Audio Cleanup\n"); + MLOG(Utils::LogLevel::Info, "Audio Cleanup\n"); if (decoder != NULL) opus_multistream_decoder_destroy(decoder); ma_pcm_rb_uninit(&rb); ma_device_uninit(&device); @@ -130,24 +131,24 @@ namespace moonlight_xbox_dx { ma_uint32 bufferLen = (ma_uint32)this->samplePerFrame; ma_result r = ma_pcm_rb_acquire_write(&rb, &bufferLen, &buffer); if (r != MA_SUCCESS) { - Utils::Log("Failed to acquire shared buffer\n"); + MLOG(Utils::LogLevel::Error, "Failed to acquire shared buffer\n"); return -1; } if (bufferLen < (ma_uint32)this->samplePerFrame || buffer == nullptr) { - Utils::Logf("Audio buffer overflow (%d > %d)\n", bufferLen, this->samplePerFrame); + MLOGF(Utils::LogLevel::Warning, "Audio buffer overflow (%d > %d)\n", bufferLen, this->samplePerFrame); return -1; } int decodeLen = opus_multistream_decode_float(decoder, (unsigned char*)sampleData, sampleLength, (float *)buffer, this->samplePerFrame, 0); if (decodeLen < 0) { - Utils::Logf("opus_multistream_decode_float failed: %d\n", decodeLen); + MLOGF(Utils::LogLevel::Error, "opus_multistream_decode_float failed: %d\n", decodeLen); return -1; } if (decodeLen > 0) { r = ma_pcm_rb_commit_write(&rb, (ma_uint32)decodeLen); if (r != MA_SUCCESS && r != MA_AT_END) { - Utils::Log("Failed to write to shared buffer\n"); + MLOG(Utils::LogLevel::Error, "Failed to write to shared buffer\n"); return -1; } } @@ -156,7 +157,7 @@ namespace moonlight_xbox_dx { void AudioPlayer::Start() { if (ma_device_start(&device) != MA_SUCCESS) { - Utils::Log("Failed to start playback device.\n"); + MLOG(Utils::LogLevel::Error, "Failed to start playback device.\n"); ma_device_uninit(&device); } @@ -164,7 +165,7 @@ namespace moonlight_xbox_dx { void AudioPlayer::Stop() { if (ma_device_stop(&device) != MA_SUCCESS) { - Utils::Log("Failed to start playback device.\n"); + MLOG(Utils::LogLevel::Error, "Failed to start playback device.\n"); ma_device_uninit(&device); } } diff --git a/Streaming/FFmpegDecoder.cpp b/Streaming/FFmpegDecoder.cpp index 1a483fb8..823f54c2 100644 --- a/Streaming/FFmpegDecoder.cpp +++ b/Streaming/FFmpegDecoder.cpp @@ -1,4 +1,5 @@ #include "pch.h" +#define MLOG_TAG_OVERRIDE "FFmpegDecoder" #include "FFMpegDecoder.h" #include "../Plot/ImGuiPlots.h" #include "StatsRenderer.h" @@ -82,7 +83,7 @@ namespace moonlight_xbox_dx { bool shouldPrefixThisMessage = printPrefix != 0; av_log_format_line(ptr, level, fmt, vl, lineBuffer, sizeof(lineBuffer), &printPrefix); - Utils::Logf(shouldPrefixThisMessage ? "[ffmpeg] %s" : "%s", lineBuffer); + MLOGF(Utils::LogLevel::Error, shouldPrefixThisMessage ? "[ffmpeg] %s" : "%s", lineBuffer); } void FFMpegDecoder::CompleteInitialization(const std::shared_ptr& res, STREAM_CONFIGURATION *config, bool framePacingImmediate) { @@ -113,21 +114,21 @@ namespace moonlight_xbox_dx { if (videoFormat & VIDEO_FORMAT_MASK_H264) { decoder = avcodec_find_decoder(AV_CODEC_ID_H264); - Utils::Log("Using H264\n"); + MLOG(Utils::LogLevel::Info, "Using H264\n"); } else if (videoFormat & VIDEO_FORMAT_MASK_H265) { decoder = avcodec_find_decoder(AV_CODEC_ID_HEVC); - Utils::Log("Using HEVC\n"); + MLOG(Utils::LogLevel::Info, "Using HEVC\n"); } if (decoder == NULL) { - Utils::Log("Couldn't find decoder\n"); + MLOG(Utils::LogLevel::Error, "Couldn't find decoder\n"); return -1; } decoder_ctx = avcodec_alloc_context3(decoder); if (decoder_ctx == NULL) { - Utils::Log("Couldn't allocate context\n"); + MLOG(Utils::LogLevel::Error, "Couldn't allocate context\n"); return -1; } decoder_ctx->opaque = this; @@ -142,7 +143,7 @@ namespace moonlight_xbox_dx { d3d11va_device_ctx->lock_ctx = this; int err2; if ((err2 = av_hwdevice_ctx_init(hw_device_ctx)) < 0) { - Utils::Logf("Failed to create specified DirectX Video device: %d\n", err2); + MLOGF(Utils::LogLevel::Error, "Failed to create specified DirectX Video device: %d\n", err2); Cleanup(); return err2; } @@ -160,16 +161,16 @@ namespace moonlight_xbox_dx { if (err < 0) { char msg[2048]; sprintf(msg, "Failed to create FFMpeg Codec: %d\n", err); - Utils::Log(msg); + MLOG(Utils::LogLevel::Error, msg); return err; } if (decoder_ctx->pix_fmt != AV_PIX_FMT_D3D11) { - Utils::Log("Warning: decoder did not select AV_PIX_FMT_D3D11\n"); + MLOG(Utils::LogLevel::Warning, "Warning: decoder did not select AV_PIX_FMT_D3D11\n"); } if (!ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, INITIAL_DECODER_BUFFER_SIZE + AV_INPUT_BUFFER_PADDING_SIZE)) { - Utils::Log("Couldn't allocate initial ffmpeg_buffer\n"); + MLOG(Utils::LogLevel::Error, "Couldn't allocate initial ffmpeg_buffer\n"); Cleanup(); return -1; } @@ -188,7 +189,12 @@ namespace moonlight_xbox_dx { Pacer::instance().deinit(); - Utils::Log("FFMpegDecoder::Cleanup\n"); + + if (m_deviceResources) { + m_deviceResources->GetD3DDeviceContext()->AddRef(); + } + + MLOG(Utils::LogLevel::Info, "FFMpegDecoder::Cleanup\n"); } static inline int frame_attach_userdata(AVFrame *frame, int64_t decodeEndQpc) { @@ -218,7 +224,7 @@ namespace moonlight_xbox_dx { if (m_StreamEpochQpc == 0) m_StreamEpochQpc = decodeStart.QuadPart; if (!ensure_buf_size(&ffmpeg_buffer, &ffmpeg_buffer_size, decodeUnit->fullLength + AV_INPUT_BUFFER_PADDING_SIZE)) { - Utils::Logf("Couldn't realloc ffmpeg_buffer\n"); + MLOGF(Utils::LogLevel::Error, "Couldn't realloc ffmpeg_buffer\n"); return DR_NEED_IDR; } @@ -261,7 +267,7 @@ namespace moonlight_xbox_dx { if (err < 0) { char ffmpegError[1024]; av_strerror(err, ffmpegError, 1024); - Utils::Logf("avcodec_send_packet failed: %s\n", ffmpegError); + MLOGF(Utils::LogLevel::Error, "avcodec_send_packet failed: %s\n", ffmpegError); return DR_NEED_IDR; } @@ -275,7 +281,7 @@ namespace moonlight_xbox_dx { else if (err < 0) { char ffmpegError[1024]; av_strerror(err, ffmpegError, sizeof(ffmpegError)); - Utils::Logf("avcodec_receive_frame failed: %s\n", ffmpegError); + MLOGF(Utils::LogLevel::Error, "avcodec_receive_frame failed: %s\n", ffmpegError); av_frame_free(&frame); return DR_NEED_IDR; } diff --git a/Streaming/FrameQueue.cpp b/Streaming/FrameQueue.cpp index 75c417b1..e7fdeb2b 100644 --- a/Streaming/FrameQueue.cpp +++ b/Streaming/FrameQueue.cpp @@ -1,6 +1,7 @@ // clang-format off #include "pch.h" // clang-format on +#define MLOG_TAG_OVERRIDE "FrameQueue" #include "FrameQueue.h" #include "Utils.hpp" #include @@ -44,13 +45,13 @@ void FrameQueue::setPaused(bool p) { void FrameQueue::start() { setPaused(false); - Utils::Logf("FrameQueue started\n"); + MLOGF(Utils::LogLevel::Info, "FrameQueue started\n"); } void FrameQueue::stop() { setPaused(true); clear(); - Utils::Logf("FrameQueue stopped\n"); + MLOGF(Utils::LogLevel::Info, "FrameQueue stopped\n"); } std::size_t FrameQueue::count() const { diff --git a/Streaming/LogRenderer.cpp b/Streaming/LogRenderer.cpp index 578d0826..4e97ad5c 100644 --- a/Streaming/LogRenderer.cpp +++ b/Streaming/LogRenderer.cpp @@ -36,7 +36,17 @@ void LogRenderer::Update(DX::StepTimer const& timer) Utils::logMutex.lock(); std::vector lines = Utils::GetLogLines(); for (std::wstring line : lines) { - m_console->Write(line.c_str()); + switch (Utils::ParseLogLevelFromLine(line)) { + case Utils::LogLevel::Error: + m_console->Write(line.c_str(), Colors::Red); + break; + case Utils::LogLevel::Warning: + m_console->Write(line.c_str(), Colors::Orange); + break; + default: + m_console->Write(line.c_str()); + break; + } } Utils::logMutex.unlock(); diff --git a/Streaming/Pacer.cpp b/Streaming/Pacer.cpp index cc3382ed..20fa848a 100644 --- a/Streaming/Pacer.cpp +++ b/Streaming/Pacer.cpp @@ -1,6 +1,7 @@ // clang-format off #include "pch.h" // clang-format on +#define MLOG_TAG_OVERRIDE "Pacer" #include "Pacer.h" #include #include @@ -75,7 +76,7 @@ void Pacer::deinit() { m_CurrentFrame = nullptr; } - Utils::Logf("Pacer: deinit\n"); + MLOGF(Utils::LogLevel::Info, "Pacer: deinit\n"); } void Pacer::init(const std::shared_ptr &res, int streamFps, double refreshRate, bool framePacingImmediate) { @@ -87,7 +88,7 @@ void Pacer::init(const std::shared_ptr &res, int streamFps, m_FrameCadence.init(m_RefreshRate > 0.0 ? m_RefreshRate : 60.0, static_cast(streamFps)); - Utils::Logf("Frame Pacer init: mode %s, streamFps %d, refreshRate %.2f\n", + MLOGF(Utils::LogLevel::Info, "Frame Pacer init: mode %s, streamFps %d, refreshRate %.2f\n", framePacingImmediate ? "immediate" : "display-locked", m_StreamFps, m_RefreshRate); m_vhsum = 0; @@ -119,10 +120,10 @@ void Pacer::setPacingImmediate(bool framePacingImmediate) { void Pacer::vsyncHardware() { if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL)) { - Utils::Logf("Failed to set vsyncHardware priority: %d\n", GetLastError()); + MLOGF(Utils::LogLevel::Warning, "Failed to set vsyncHardware priority: %d\n", GetLastError()); } - Utils::Logf("vsyncHardware stats thread started, qpcFreq=%lld ticksPerMs=%lld\n", + MLOGF(Utils::LogLevel::Info, "vsyncHardware stats thread started, qpcFreq=%lld ticksPerMs=%lld\n", QpcFreq(), MsToQpc(1.0)); while (!stopping()) { @@ -133,7 +134,7 @@ void Pacer::vsyncHardware() { updateFrameStats(); } - Utils::Logf("vsyncHardware stats thread stopped\n"); + MLOGF(Utils::LogLevel::Info, "vsyncHardware stats thread stopped\n"); } // based on mpv's d3d11_get_vsync() diff --git a/Streaming/VideoRenderer.cpp b/Streaming/VideoRenderer.cpp index 1f6c1e8d..9bf61d75 100644 --- a/Streaming/VideoRenderer.cpp +++ b/Streaming/VideoRenderer.cpp @@ -1,11 +1,10 @@ #include "pch.h" +#define MLOG_TAG_OVERRIDE "VideoRenderer" #include "VideoRenderer.h" #include #include "..\Common\DirectXHelper.h" #include #include -#include "..\Common\ModalDialog.xaml.h" - #include #include @@ -173,7 +172,7 @@ bool VideoRenderer::Render(AVFrame *frame) { UINT colorSpaceSupport = 0; if (colorspace && SUCCEEDED(m_deviceResources->GetSwapChain()->CheckColorSpaceSupport(colorspace, &colorSpaceSupport)) && (colorSpaceSupport & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT)) { DX::ThrowIfFailed(m_deviceResources->GetSwapChain()->SetColorSpace1(colorspace)); - Utils::Logf("Colorspace changed to %s\n", + MLOGF(Utils::LogLevel::Info, "Colorspace changed to %s\n", colorspace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 ? "DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020" : "DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709"); @@ -187,7 +186,7 @@ bool VideoRenderer::Render(AVFrame *frame) { void VideoRenderer::CreateDeviceDependentResources() { - Utils::Log("Started with creation of DXView\n"); + MLOG(Utils::LogLevel::Info, "Started with creation of DXView\n"); // Vertex shader { @@ -282,7 +281,7 @@ void VideoRenderer::CreateDeviceDependentResources() int status = this->client->StartStreaming(devRes, cfg); if (status != 0) { - Utils::Logf("StartStreaming failed with status %d\n", status); + MLOGF(Utils::LogLevel::Error, "StartStreaming failed with status %d\n", status); m_loadingSuccessful.store(false, std::memory_order_release); m_loadingComplete.store(true, std::memory_order_release); return; @@ -290,7 +289,7 @@ void VideoRenderer::CreateDeviceDependentResources() m_loadingSuccessful.store(true, std::memory_order_release); m_loadingComplete.store(true, std::memory_order_release); - Utils::Log("Loading Complete!\n"); + MLOG(Utils::LogLevel::Info, "Loading Complete!\n"); })); } @@ -400,7 +399,7 @@ void VideoRenderer::setupVertexBuffer(D3D11_TEXTURE2D_DESC frameDesc) float uMax = m_TextureWidth > 0 ? (float)m_DecoderParams.width / m_TextureWidth : 1.0f; float vMax = m_TextureHeight > 0 ? (float)m_DecoderParams.height / m_TextureHeight : 1.0f; - Utils::Logf("Setup vertex shader params: uMax %f, vMax %f\n", uMax, vMax); + MLOGF(Utils::LogLevel::Debug, "Setup vertex shader params: uMax %f, vMax %f\n", uMax, vMax); VERTEX verts[] = { @@ -532,7 +531,7 @@ void VideoRenderer::getFramePremultipliedCscConstants(const AVFrame* frame, std: cscMatrix[i] *= uvScale; } - Utils::Logf("Shader config: %s %d-bit %s, (AVColorSpace %d, AVChromaLocation %d)\n", + MLOGF(Utils::LogLevel::Debug, "Shader config: %s %d-bit %s, (AVColorSpace %d, AVChromaLocation %d)\n", colorspace == COLORSPACE_REC_601 ? "Rec. 601" : colorspace == COLORSPACE_REC_709 ? "Rec. 709" : "Rec. 2020", @@ -657,7 +656,7 @@ void VideoRenderer::bindColorConversion(AVFrame* frame, D3D11_TEXTURE2D_DESC fra D3D11_SUBRESOURCE_DATA constData = {}; constData.pSysMem = &constBuf; - Utils::Logf("Setup pixel shader params: chromaOffset[0] %f, chromaOffset[1] %f, chromaUVMax[0] %f, chromaUVMax[1] %f\n", + MLOGF(Utils::LogLevel::Debug, "Setup pixel shader params: chromaOffset[0] %f, chromaOffset[1] %f, chromaUVMax[0] %f, chromaUVMax[1] %f\n", constBuf.chromaOffset[0], constBuf.chromaOffset[1], constBuf.chromaUVMax[0], constBuf.chromaUVMax[1]); diff --git a/Streaming/YUVRGBPixelShader.hlsl b/Streaming/YUVRGBPixelShader.hlsl new file mode 100644 index 00000000..cfbcdfcf --- /dev/null +++ b/Streaming/YUVRGBPixelShader.hlsl @@ -0,0 +1,43 @@ +Texture2D shaderTexture; +SamplerState SampleType; +struct PixelShaderInput +{ + float4 pos : SV_POSITION; + float2 texCoord : TEXCOORD0; +}; + +Texture2D luminanceChannel : t0; +Texture2D chrominanceChannel : t1; +SamplerState defaultSampler : s0; + +// Derived from https://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx +// Section: Converting 8-bit YUV to RGB888 +static const float3x3 YUVtoRGBCoeffMatrix = +{ + 1.164383f, 1.164383f, 1.164383f, + 0.000000f, -0.391762f, 2.017232f, + 1.596027f, -0.812968f, 0.000000f +}; + +float3 ConvertYUVtoRGB(float3 yuv) +{ + // Derived from https://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx + // Section: Converting 8-bit YUV to RGB888 + + // These values are calculated from (16 / 255) and (128 / 255) + yuv -= float3(0.062745f, 0.501960f, 0.501960f); + yuv = mul(yuv, YUVtoRGBCoeffMatrix); + + return saturate(yuv); +} + +// A pass-through function for the (interpolated) color data. +float4 main(PixelShaderInput input) : SV_TARGET +{ + float y = luminanceChannel.Sample(defaultSampler, input.texCoord); + float2 uv = chrominanceChannel.Sample(defaultSampler, input.texCoord); + /*float r = uv.r; + uv.r = uv.g; + uv.g = r;*/ + return float4(ConvertYUVtoRGB(float3(y, uv)), 1.f); +} diff --git a/Streaming/YUVRGBVertexShader.hlsl b/Streaming/YUVRGBVertexShader.hlsl new file mode 100644 index 00000000..bbcfc59d --- /dev/null +++ b/Streaming/YUVRGBVertexShader.hlsl @@ -0,0 +1,19 @@ +// Per-vertex data used as input to the vertex shader. +struct VertexShaderInput +{ + float4 Pos : POSITION; + float2 Tex : TEXCOORD; +}; + +// Per-pixel color data passed through the pixel shader. +struct PixelShaderInput +{ + float4 Pos : SV_POSITION; + float2 Tex : TEXCOORD; +}; + +// Simple shader to do vertex processing on the GPU. +PixelShaderInput main(VertexShaderInput input) +{ + return input; +} diff --git a/Streaming/moonlight_xbox_dxMain.cpp b/Streaming/moonlight_xbox_dxMain.cpp index 5d366b8b..d960c9bc 100644 --- a/Streaming/moonlight_xbox_dxMain.cpp +++ b/Streaming/moonlight_xbox_dxMain.cpp @@ -1,13 +1,15 @@ -#include "moonlight_xbox_dxMain.h" +#define MLOG_TAG_OVERRIDE "Main" +#include "moonlight_xbox_dxMain.h" #include "pch.h" -#include -#include -#include +#include +#include +#include #include #include "../Plot/ImGuiPlots.h" #include "Common\DirectXHelper.h" #include "State\GamepadState.h" #include "Utils.hpp" +#include #include @@ -20,8 +22,9 @@ using namespace Windows::Gaming::Input; using namespace Windows::System::Threading; using namespace Windows::UI::ViewManagement::Core; +#include + extern "C" { -#include #include } @@ -46,57 +49,52 @@ moonlight_xbox_dxMain::moonlight_xbox_dxMain(const std::shared_ptr>(); *showErrorDialog = [self, msgCopy, showLogsDialog, appName]() { - auto dialog1 = ref new Windows::UI::Xaml::Controls::ContentDialog(); - dialog1->Title = L"Failed to start " + appName; - dialog1->Content = Utils::StringFromStdString(msgCopy); - dialog1->PrimaryButtonText = L"OK"; - dialog1->SecondaryButtonText = L"Show Logs"; - - concurrency::create_task(::moonlight_xbox_dx::ModalDialog::ShowOnceAsync(dialog1)).then([self, showLogsDialog](concurrency::task t) { - auto result = t.get(); - if (result == Windows::UI::Xaml::Controls::ContentDialogResult::Primary) { + auto dialog = ref new ::moonlight_xbox_dx::StreamErrorDialog(); + dialog->Configure( + L"Failed to start " + appName, + Utils::StringFromStdString(msgCopy), + L"\xE783", + L"OK", L"\xE8FB", + L"Show Logs", L"\xF0B5", + ref new Windows::UI::Xaml::RoutedEventHandler([self](Platform::Object^, Windows::UI::Xaml::RoutedEventArgs^) { self->StopRenderLoop(); self->ExitStreamPage(); - } else if (result == Windows::UI::Xaml::Controls::ContentDialogResult::Secondary) { + }), + ref new Windows::UI::Xaml::RoutedEventHandler([showLogsDialog](Platform::Object^, Windows::UI::Xaml::RoutedEventArgs^) { (*showLogsDialog)(); - } - }); + }) + ); + concurrency::create_task(dialog->ShowAsync()); }; *showLogsDialog = [self, showErrorDialog]() { - auto dialog2 = ref new Windows::UI::Xaml::Controls::ContentDialog(); - - std::wstring m_text = L""; + std::wstring logText = L""; std::vector lines = Utils::GetLogLines(); - for (int i = 0; i < (int)lines.size(); i++) { - // Get only the last 8 lines - // More than that cannot be fully viewed on the screen at the current scaling if ((int)lines.size() - i <= 8) { - m_text += lines[i]; + logText += lines[i]; } } - Utils::showLogs = true; - dialog2->MaxWidth = 600; - dialog2->Title = "Logs"; - dialog2->Content = ref new Platform::String(m_text.c_str()); - dialog2->PrimaryButtonText = L"OK"; - dialog2->SecondaryButtonText = L"Show Error"; - - concurrency::create_task(::moonlight_xbox_dx::ModalDialog::ShowOnceAsync(dialog2)).then([self, showErrorDialog](concurrency::task t) { - auto result = t.get(); - if (result == Windows::UI::Xaml::Controls::ContentDialogResult::Primary) { + auto dialog = ref new ::moonlight_xbox_dx::StreamErrorDialog(); + dialog->Configure( + L"Logs", + ref new Platform::String(logText.c_str()), + L"\xF0B5", + L"OK", L"\xE8FB", + L"Show Error", L"\xE783", + ref new Windows::UI::Xaml::RoutedEventHandler([self](Platform::Object^, Windows::UI::Xaml::RoutedEventArgs^) { self->StopRenderLoop(); self->ExitStreamPage(); - } else if (result == Windows::UI::Xaml::Controls::ContentDialogResult::Secondary) { + }), + ref new Windows::UI::Xaml::RoutedEventHandler([showErrorDialog](Platform::Object^, Windows::UI::Xaml::RoutedEventArgs^) { (*showErrorDialog)(); - } - }); + }) + ); + concurrency::create_task(dialog->ShowAsync()); }; - // Start by showing the error dialog (*showErrorDialog)(); })); }); @@ -126,8 +124,24 @@ moonlight_xbox_dxMain::moonlight_xbox_dxMain(const std::shared_ptrm_sceneRenderer && this->m_sceneRenderer->IsLoadingSuccessful()) { DISPATCH_UI(([streamPage]() { Sleep(500); - streamPage->m_progressRing->IsActive = false; - streamPage->m_progressView->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + using namespace Windows::UI::Xaml::Media::Animation; + auto anim = ref new DoubleAnimation(); + anim->From = ref new Platform::Box(1.0); + anim->To = ref new Platform::Box(0.0); + anim->Duration = Windows::UI::Xaml::Duration(Windows::Foundation::TimeSpan{ 10000000LL }); + auto sb = ref new Storyboard(); + sb->Children->Append(anim); + Storyboard::SetTarget(anim, streamPage->m_progressView); + Storyboard::SetTargetProperty(anim, "(UIElement.Opacity)"); + Platform::WeakReference weakPage(streamPage); + sb->Completed += ref new Windows::Foundation::EventHandler( + [weakPage](Platform::Object^, Platform::Object^) { + auto page = weakPage.Resolve(); + if (page == nullptr) return; + page->m_progressView->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + page->m_progressView->Opacity = 1.0; + }); + sb->Begin(); })); } }); @@ -202,8 +216,10 @@ moonlight_xbox_dxMain::moonlight_xbox_dxMain(const std::shared_ptrRegisterDeviceNotify(nullptr); + + m_deviceResources->GetD3DDeviceContext()->ClearState(); + m_deviceResources->GetD3DDeviceContext()->Flush(); } void moonlight_xbox_dxMain::CreateDeviceDependentResources() { @@ -218,14 +234,14 @@ void moonlight_xbox_dxMain::CreateWindowSizeDependentResources() { void moonlight_xbox_dxMain::StartRenderLoop() { // If the animation render loop is already running then do not start another thread. - if (m_renderLoopWorker != nullptr && m_renderLoopWorker->Status == AsyncStatus::Started) { + if (m_renderLoopWorker != nullptr && m_renderLoopWorker->Status == Windows::Foundation::AsyncStatus::Started) { return; } // Create a task that will be run on a background thread. auto workItemHandler = ref new WorkItemHandler([this](IAsyncAction ^ action) { if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL)) { - Utils::Logf("Failed to set render thread priority: %d\n", GetLastError()); + MLOGF(Utils::LogLevel::Warning, "Failed to set render thread priority: %d\n", GetLastError()); } int64_t t0 = 0, t1 = 0, t2 = 0, t3 = 0; @@ -237,7 +253,7 @@ void moonlight_xbox_dxMain::StartRenderLoop() { double ewmaRenderMs = 3.0; // Initial guess for render cost // Calculate the updated frame and render once per vertical blanking interval. - while (action->Status == AsyncStatus::Started && !moonlightClient->IsConnectionTerminated()) { + while (action->Status == Windows::Foundation::AsyncStatus::Started && !moonlightClient->IsConnectionTerminated()) { // Get overall deadline we must hit by the Present for this frame int64_t deadline = Pacer::instance().getNextVBlankQpc(&t0); @@ -326,12 +342,12 @@ void moonlight_xbox_dxMain::StartRenderLoop() { StopRenderLoop(); // also stops input Disconnect(); - DISPATCH_UI([this]() { - ExitStreamPage(); - }); + DISPATCH_UI([this]() { + ExitStreamPage(); + }); }); m_renderLoopWorker = ThreadPool::RunAsync(workItemHandler, WorkItemPriority::High, WorkItemOptions::TimeSliced); - if (m_inputLoopWorker != nullptr && m_inputLoopWorker->Status == AsyncStatus::Started) { + if (m_inputLoopWorker != nullptr && m_inputLoopWorker->Status == Windows::Foundation::AsyncStatus::Started) { return; } auto inputItemHandler = ref new WorkItemHandler([this](IAsyncAction ^ action) { @@ -339,7 +355,7 @@ void moonlight_xbox_dxMain::StartRenderLoop() { const int64_t pollIntervalQpc = MsToQpc(1000.0 / pollingHz); int64_t lastProcessInput = 0; - while (action->Status == AsyncStatus::Started) { + while (action->Status == Windows::Foundation::AsyncStatus::Started) { int64_t now = QpcNow(); if (now - lastProcessInput >= pollIntervalQpc) { lastProcessInput = now; @@ -408,27 +424,51 @@ void moonlight_xbox_dxMain::ProcessInput() { auto result = state.GetComboResult(50); // hold buttons for a short time for View + Menu combo if (result.comboTriggered) { - DISPATCH_UI(([this]() { - Windows::UI::Xaml::Controls::Flyout::ShowAttachedFlyout(m_streamPage->m_flyoutButton); + bool newVisible = !insideMenu; + DISPATCH_UI(([this, newVisible]() { + m_streamPage->SetStreamMenuVisible(newVisible); })); // send an empty controller packet, otherwise Sunshine may see View being kept held down, // triggering the "Home/Guide Button Emulation Timeout" to send a Guide button press after a few seconds. SendGamepadReadingForState(state, EmptyReading()); - // disable future input until the flyout is closed - insideFlyout = true; + insideMenu = newVisible; continue; } - if (insideFlyout) { + if (insideMenu) { + if (PressedEdge(result.maskedReading, state.previousReading, GamepadButtons::B)) { + DISPATCH_UI(([this]() { + m_streamPage->SetStreamMenuVisible(false); + })); + insideMenu = false; + state.buttonSuppressMask = static_cast( + static_cast(state.buttonSuppressMask) | static_cast(GamepadButtons::B)); + SendGamepadReadingForState(state, EmptyReading()); + } state.reading = EmptyReading(); state.previousReading = EmptyReading(); continue; } + if (result.menuLongPressTriggered) { + DISPATCH_UI(([this]() { + bool newMode = !m_streamPage->MouseMode; + m_streamPage->SetMouseMode(newMode); + ShowToast(newMode ? L"Mouse Mode: On" : L"Mouse Mode: Off"); + })); + SendGamepadReadingForState(state, EmptyReading()); + continue; + } + // GetComboResult() will have masked off our combo buttons if they are pending auto reading = result.maskedReading; + + state.buttonSuppressMask = static_cast( + static_cast(state.buttonSuppressMask) & static_cast(reading.Buttons)); + reading.Buttons = GamepadState::clearButtons(reading.Buttons, state.buttonSuppressMask); + auto prevReading = state.previousReading; // If mouse mode is enabled the gamepad acts as a mouse, instead we pass the raw events to the host @@ -541,7 +581,9 @@ void moonlight_xbox_dxMain::ProcessInput() { })); keyboardMode = true; } else { - CoreInputView::GetForCurrentView()->TryShow(CoreInputViewKind::Keyboard); + m_streamPage->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([this]() { + CoreInputView::GetForCurrentView()->TryShow(CoreInputViewKind::Keyboard); + })); } } // Scroll @@ -582,7 +624,7 @@ void moonlight_xbox_dxMain::DumpGamepads() { // list all controllers with their connected status for (int i = 0; i < MAX_GAMEPADS; ++i) { if (m_GamepadState[i].controller != nullptr) { - Utils::Logf(" Gamepad #%d: hostId %d\n", m_GamepadState[i].localId, m_GamepadState[i].hostId); + MLOGF(Utils::LogLevel::Debug, " Gamepad #%d: hostId %d\n", m_GamepadState[i].localId, m_GamepadState[i].hostId); } } } @@ -607,7 +649,7 @@ void moonlight_xbox_dxMain::RefreshGamepads() { if (!state.didSendArrival) { SendGamepadArrival(state); state.didSendArrival = true; - Utils::Logf("RefreshGamepads: sent arrival packet for Gamepad #%d\n", localId); + MLOGF(Utils::LogLevel::Info, "RefreshGamepads: sent arrival packet for Gamepad #%d\n", localId); } found = true; break; @@ -629,7 +671,7 @@ void moonlight_xbox_dxMain::RefreshGamepads() { state.previousReading = EmptyReading(); SendGamepadArrival(state); state.didSendArrival = true; - Utils::Logf("RefreshGamepads: added new Gamepad #%d in host slot %d\n", state.localId, state.hostId); + MLOGF(Utils::LogLevel::Info, "RefreshGamepads: added new Gamepad #%d in host slot %d\n", state.localId, state.hostId); break; } } @@ -644,7 +686,7 @@ void moonlight_xbox_dxMain::RefreshGamepads() { uint16_t activeMaskMinus = MakeActiveMask(); activeMaskMinus &= ~(1 << state.hostId); LiSendMultiControllerEvent(state.hostId, activeMaskMinus, 0, 0, 0, 0, 0, 0, 0); - Utils::Logf("RefreshGamepads: removed Gamepad #%d from host slot %d\n", state.localId, state.hostId); + MLOGF(Utils::LogLevel::Info, "RefreshGamepads: removed Gamepad #%d from host slot %d\n", state.localId, state.hostId); state.Reset(); } } @@ -659,7 +701,7 @@ void moonlight_xbox_dxMain::SendGamepadArrival(GamepadState &state) { uint32_t capabilities = LI_CCAP_ANALOG_TRIGGERS | LI_CCAP_RUMBLE | LI_CCAP_TRIGGER_RUMBLE; int rc = LiSendControllerArrivalEvent(state.hostId, MakeActiveMask(), type, supportedButtonFlags, capabilities); if (rc != 0) { - Utils::Logf("LiSendControllerArrivalEvent error: %d\n", rc); + MLOGF(Utils::LogLevel::Error, "LiSendControllerArrivalEvent error: %d\n", rc); } } @@ -734,13 +776,14 @@ void moonlight_xbox_dxMain::OnDeviceRestored() { CreateWindowSizeDependentResources(); } -void moonlight_xbox_dxMain::SetFlyoutOpened(bool value) { - insideFlyout = value; +void moonlight_xbox_dxMain::SetMenuVisible(bool value) { + insideMenu = value; } void moonlight_xbox_dxMain::Disconnect() { moonlightClient->StopStreaming(); m_sceneRenderer->Stop(); + moonlightClient->SetDisplayHDR(false, SS_HDR_METADATA{}); } void moonlight_xbox_dxMain::CloseApp() { @@ -752,7 +795,7 @@ void moonlight_xbox_dxMain::ExitStreamPage() { bool reachedAppPage = false; try { - auto rootFrame = dynamic_cast(Windows::UI::Xaml::Window::Current->Content); + auto rootFrame = App::GetRootFrame(); if (!rootFrame) return; auto current = dynamic_cast(rootFrame->Content); @@ -763,7 +806,7 @@ void moonlight_xbox_dxMain::ExitStreamPage() { try { rootFrame->GoBack(); } catch (...) { - Utils::Log("ExitStreamPage: Failed to GoBack()\n"); + MLOG(Utils::LogLevel::Error, "ExitStreamPage: Failed to GoBack()\n"); } if (!reachedAppPage) { @@ -772,14 +815,16 @@ void moonlight_xbox_dxMain::ExitStreamPage() { if (!reachedAppPage) { try { - rootFrame->Navigate(Windows::UI::Xaml::Interop::TypeName(HostSelectorPage::typeid)); + auto slideBack = ref new Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionInfo(); + slideBack->Effect = Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionEffect::FromLeft; + rootFrame->Navigate(Windows::UI::Xaml::Interop::TypeName(HostSelectorPage::typeid), nullptr, slideBack); } catch (...) { rootFrame->Content = nullptr; - Utils::Log("ExitStreamPage: Failed to return to HostSelectorPage\n"); + MLOG(Utils::LogLevel::Error, "ExitStreamPage: Failed to return to HostSelectorPage\n"); } } } catch (...) { - Utils::Log("ExitStreamPage: An error occurred\n"); + MLOG(Utils::LogLevel::Error, "ExitStreamPage: An error occurred\n"); } } diff --git a/Streaming/moonlight_xbox_dxMain.h b/Streaming/moonlight_xbox_dxMain.h index 280c8593..fb23a327 100644 --- a/Streaming/moonlight_xbox_dxMain.h +++ b/Streaming/moonlight_xbox_dxMain.h @@ -5,7 +5,7 @@ #include "Streaming\VideoRenderer.h" #include "Streaming\LogRenderer.h" #include "Streaming\StatsRenderer.h" -#include "Pages\StreamPage.xaml.h" +#include "UI\Pages\StreamPage.xaml.h" // Xbox supports 8 controllers, this ought to be enough for anyone. #define MAX_GAMEPADS 8 @@ -23,7 +23,7 @@ namespace moonlight_xbox_dx void TrackingUpdate(float positionX) { m_pointerLocationX = positionX; } void StartRenderLoop(); void StopRenderLoop(); - void SetFlyoutOpened(bool value); + void SetMenuVisible(bool value); Concurrency::critical_section& GetCriticalSection() { return m_criticalSection; } bool keyboardMode = false; void OnKeyDown(unsigned short virtualKey, char modifiers); @@ -66,7 +66,7 @@ namespace moonlight_xbox_dx // Track current input pointer position. float m_pointerLocationX; - bool insideFlyout = false; + bool insideMenu = false; StreamPage^ m_streamPage; // Gamepad handling diff --git a/UI/Backgrounds/BackgroundRegistry.h b/UI/Backgrounds/BackgroundRegistry.h new file mode 100644 index 00000000..7fa02248 --- /dev/null +++ b/UI/Backgrounds/BackgroundRegistry.h @@ -0,0 +1,22 @@ +#pragma once + +namespace moonlight_xbox_dx { + + struct BackgroundEntry { + const wchar_t* key; + const wchar_t* displayName; + }; + + static const BackgroundEntry kBackgrounds[] = { + { L"streaks", L"Neon Streaks" }, + { L"particles", L"Floating Particles" }, + { L"spheres", L"Bouncing Bubbles" }, + { L"blobs", L"Morphing Blobs" }, + { L"swipereveal", L"Swipe Reveal" }, + { L"globegrid", L"Globe Grid" }, + { L"orbs", L"Dancing Orbs" }, + }; + + static const int kBackgroundCount = sizeof(kBackgrounds) / sizeof(kBackgrounds[0]); + +} diff --git a/UI/Backgrounds/BackgroundSettingsHelpers.h b/UI/Backgrounds/BackgroundSettingsHelpers.h new file mode 100644 index 00000000..34ec41f9 --- /dev/null +++ b/UI/Backgrounds/BackgroundSettingsHelpers.h @@ -0,0 +1,29 @@ +#pragma once +#include + +namespace moonlight_xbox_dx { + +inline Platform::String^ BgColorToHex(Windows::UI::Color c) +{ + wchar_t buf[7]; + swprintf_s(buf, L"%02X%02X%02X", (unsigned)c.R, (unsigned)c.G, (unsigned)c.B); + return ref new Platform::String(buf); +} + +inline Windows::UI::Color BgHexToColor(Platform::String^ s, Windows::UI::Color fallback) +{ + if (s == nullptr || s->Length() != 6) return fallback; + const wchar_t* p = s->Data(); + wchar_t buf[7]; wcsncpy_s(buf, p, 6); buf[6] = L'\0'; + wchar_t* end = nullptr; + unsigned long v = wcstoul(buf, &end, 16); + if (end != buf + 6) return fallback; + Windows::UI::Color c; + c.A = 255; + c.R = (uint8_t)((v >> 16) & 0xFF); + c.G = (uint8_t)((v >> 8) & 0xFF); + c.B = (uint8_t)( v & 0xFF); + return c; +} + +} diff --git a/UI/Backgrounds/Blobs/BlobsBackground.xaml b/UI/Backgrounds/Blobs/BlobsBackground.xaml new file mode 100644 index 00000000..85a76126 --- /dev/null +++ b/UI/Backgrounds/Blobs/BlobsBackground.xaml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Backgrounds/Blobs/BlobsBackground.xaml.cpp b/UI/Backgrounds/Blobs/BlobsBackground.xaml.cpp new file mode 100644 index 00000000..6aba4bf6 --- /dev/null +++ b/UI/Backgrounds/Blobs/BlobsBackground.xaml.cpp @@ -0,0 +1,255 @@ +#include "pch.h" +#include "UI\Backgrounds\Blobs\BlobsBackground.xaml.h" +#include + +using namespace moonlight_xbox_dx; +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::UI; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Shapes; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Media::Animation; + +static const float kPi = 3.14159265358979f; + +static bool ParseHex6(Platform::String^ s, Color& out) +{ + if (s == nullptr || s->Length() != 6) return false; + const wchar_t* p = s->Data(); + wchar_t buf[7]; wcsncpy_s(buf, p, 6); buf[6] = L'\0'; + wchar_t* end = nullptr; + unsigned long v = wcstoul(buf, &end, 16); + if (end != buf + 6) return false; + out.R = (uint8_t)((v >> 16) & 0xFF); + out.G = (uint8_t)((v >> 8) & 0xFF); + out.B = (uint8_t)( v & 0xFF); + return true; +} + +void BlobsBackground::LoadPalette() +{ + using namespace Windows::Storage; + Platform::String^ scheme = L"crimson"; + auto ls = ApplicationData::Current->LocalSettings->Values; + if (ls->HasKey("blobs.scheme")) + scheme = safe_cast(ls->Lookup("blobs.scheme")); + + Color schemes[5][4] = { + {{170,210,10,55},{170,185,0,185},{170,100,0,200},{255,8,0,16}}, + {{170,0,180,200},{170,0,100,220},{170,0,210,150},{255,0,6,20}}, + {{170,0,200,120},{170,60,190,255},{170,140,40,230},{255,2,4,10}}, + {{170,255,70,0},{170,210,20,20},{170,255,140,20},{255,12,3,0}}, + {{170,170,40,230},{170,255,60,180},{170,40,90,255},{255,5,0,18}}, + }; + + int idx = -1; + if (scheme->Equals(L"crimson")) idx = 0; + else if (scheme->Equals(L"ocean")) idx = 1; + else if (scheme->Equals(L"aurora")) idx = 2; + else if (scheme->Equals(L"ember")) idx = 3; + else if (scheme->Equals(L"nebula")) idx = 4; + + if (idx >= 0) { + for (int i = 0; i < 4; ++i) m_palette[i] = schemes[idx][i]; + return; + } + + static const wchar_t* kKeys[] = { + L"blobs.custom.0", L"blobs.custom.1", L"blobs.custom.2", L"blobs.custom.3" + }; + Color defaults[4] = { + {170,210,10,55},{170,185,0,185},{170,100,0,200},{255,8,0,16} + }; + for (int i = 0; i < 4; ++i) { + m_palette[i] = defaults[i]; + auto key = ref new Platform::String(kKeys[i]); + if (ls->HasKey(key)) { + Color c = m_palette[i]; + if (ParseHex6(safe_cast(ls->Lookup(key)), c)) { + c.A = (i < 3) ? 170 : 255; + m_palette[i] = c; + } + } + } +} + +BlobsBackground::BlobsBackground() +{ + m_rng = std::mt19937(std::random_device{}()); + InitializeComponent(); + this->Loaded += ref new RoutedEventHandler(this, &BlobsBackground::OnLoaded); + + BlobCanvas->Background = ref new SolidColorBrush(ColorHelper::FromArgb(255, 8, 0, 16)); + + TimeSpan interval; + interval.Duration = 16 * 10000LL; + m_timer = ref new DispatcherTimer(); + m_timer->Interval = interval; + Platform::WeakReference weakSelf(this); + m_tickToken = m_timer->Tick += ref new EventHandler( + [weakSelf](Object^, Object^) { + try { + auto self = weakSelf.Resolve(); + if (self) self->OnTick(nullptr, nullptr); + } catch (Platform::DisconnectedException^) {} + catch (...) {} + }); +} + +void BlobsBackground::Canvas_SizeChanged(Object^ sender, SizeChangedEventArgs^ e) +{ + m_canvasW = static_cast(e->NewSize.Width); + m_canvasH = static_cast(e->NewSize.Height); + if (m_canvasW > 0 && m_canvasH > 0 && !m_ready) { + m_ready = true; + InitBlobs(); + } +} + +void BlobsBackground::InitBlobs() +{ + LoadPalette(); + BlobCanvas->Background = ref new SolidColorBrush(m_palette[3]); + m_blobs.clear(); + BlobCanvas->Children->Clear(); + + std::uniform_real_distribution distR(170.0f, 240.0f); + std::uniform_real_distribution distWobR(70.0f, 160.0f); + std::uniform_real_distribution distWobA(0.2f, 0.5f); + std::uniform_real_distribution distPhase(0.0f, kPi * 2.0f); + std::uniform_real_distribution distFreqR(0.03f, 0.08f); + std::uniform_real_distribution distFreqA(0.02f, 0.06f); + std::uniform_real_distribution distTime(0.0f, 200.0f); + std::uniform_real_distribution distSpeed(0.2f, 0.5f); + + float positions[kBlobCount][2] = { + { m_canvasW * 0.0f, m_canvasH * 0.7f }, + { m_canvasW * 0.5f, m_canvasH * 0.2f }, + { m_canvasW * 0.9f, m_canvasH * 0.7f }, + }; + + for (int i = 0; i < kBlobCount; i++) { + BlobData b{}; + b.cx = positions[i][0]; + b.cy = positions[i][1]; + b.baseRadius = distR(m_rng); + b.time = distTime(m_rng); + b.timeSpeed = distSpeed(m_rng); + + for (int j = 0; j < kBlobPts; j++) { + b.rAmp[j] = distWobR(m_rng); + b.rPhase[j] = distPhase(m_rng); + b.rFreq[j] = distFreqR(m_rng); + b.aAmp[j] = distWobA(m_rng); + b.aFreq[j] = distFreqA(m_rng); + b.aPhase[j] = distPhase(m_rng); + } + + auto s0 = ref new BezierSegment(); + auto s1 = ref new BezierSegment(); + auto s2 = ref new BezierSegment(); + auto s3 = ref new BezierSegment(); + auto s4 = ref new BezierSegment(); + auto s5 = ref new BezierSegment(); + b.s0 = s0; b.s1 = s1; b.s2 = s2; + b.s3 = s3; b.s4 = s4; b.s5 = s5; + + auto figure = ref new PathFigure(); + figure->IsClosed = true; + figure->IsFilled = true; + figure->Segments->Append(s0); + figure->Segments->Append(s1); + figure->Segments->Append(s2); + figure->Segments->Append(s3); + figure->Segments->Append(s4); + figure->Segments->Append(s5); + b.figure = figure; + + auto geom = ref new PathGeometry(); + geom->Figures->Append(figure); + + auto path = ref new Path(); + path->Data = geom; + path->Fill = ref new SolidColorBrush(m_palette[i]); + + BlobCanvas->Children->Append(path); + m_blobs.push_back(b); + UpdateBlob(m_blobs.back()); + } +} + +void BlobsBackground::UpdateBlob(BlobData& b) +{ + b.time += b.timeSpeed; + + float px[kBlobPts], py[kBlobPts]; + for (int i = 0; i < kBlobPts; i++) { + float baseAngle = i * (2.0f * kPi / kBlobPts); + float angle = baseAngle + b.aAmp[i] * sinf(b.time * b.aFreq[i] + b.aPhase[i]); + float r = b.baseRadius + b.rAmp[i] * sinf(b.time * b.rFreq[i] + b.rPhase[i]); + px[i] = b.cx + r * cosf(angle); + py[i] = b.cy + r * sinf(angle); + } + + auto cp1x = [&](int i) { return px[i] + (px[(i+1)%kBlobPts] - px[(i-1+kBlobPts)%kBlobPts]) / 6.0f; }; + auto cp1y = [&](int i) { return py[i] + (py[(i+1)%kBlobPts] - py[(i-1+kBlobPts)%kBlobPts]) / 6.0f; }; + auto cp2x = [&](int i) { return px[(i+1)%kBlobPts] - (px[(i+2)%kBlobPts] - px[i]) / 6.0f; }; + auto cp2y = [&](int i) { return py[(i+1)%kBlobPts] - (py[(i+2)%kBlobPts] - py[i]) / 6.0f; }; + + b.figure->StartPoint = Point(px[0], py[0]); + + b.s0->Point1 = Point(cp1x(0), cp1y(0)); b.s0->Point2 = Point(cp2x(0), cp2y(0)); b.s0->Point3 = Point(px[1], py[1]); + b.s1->Point1 = Point(cp1x(1), cp1y(1)); b.s1->Point2 = Point(cp2x(1), cp2y(1)); b.s1->Point3 = Point(px[2], py[2]); + b.s2->Point1 = Point(cp1x(2), cp1y(2)); b.s2->Point2 = Point(cp2x(2), cp2y(2)); b.s2->Point3 = Point(px[3], py[3]); + b.s3->Point1 = Point(cp1x(3), cp1y(3)); b.s3->Point2 = Point(cp2x(3), cp2y(3)); b.s3->Point3 = Point(px[4], py[4]); + b.s4->Point1 = Point(cp1x(4), cp1y(4)); b.s4->Point2 = Point(cp2x(4), cp2y(4)); b.s4->Point3 = Point(px[5], py[5]); + b.s5->Point1 = Point(cp1x(5), cp1y(5)); b.s5->Point2 = Point(cp2x(5), cp2y(5)); b.s5->Point3 = Point(px[0], py[0]); +} + +void BlobsBackground::OnTick(Object^ sender, Object^ args) +{ + if (!m_ready) return; + for (auto& b : m_blobs) UpdateBlob(b); +} + +void BlobsBackground::OnLoaded(Object^ sender, RoutedEventArgs^ e) +{ + auto anim = ref new DoubleAnimation(); + anim->From = 0.0; + anim->To = 1.0; + TimeSpan ts; + ts.Duration = 5000000LL; + anim->Duration = Windows::UI::Xaml::Duration(ts); + BlobCanvas->Opacity = 0.0; + auto sb = ref new Storyboard(); + Storyboard::SetTarget(anim, BlobCanvas); + Storyboard::SetTargetProperty(anim, "Opacity"); + sb->Children->Append(anim); + sb->Begin(); +} + +void BlobsBackground::ReloadColors() +{ + if (!m_ready) return; + LoadPalette(); + for (int i = 0; i < kBlobCount; ++i) { + safe_cast(BlobCanvas->Children->GetAt(i))->Fill = + ref new SolidColorBrush(m_palette[i]); + } + BlobCanvas->Background = ref new SolidColorBrush(m_palette[3]); +} + +void BlobsBackground::StartAnimations() +{ + if (m_timer != nullptr) m_timer->Start(); +} + +void BlobsBackground::StopAnimations() +{ + if (m_timer != nullptr) { + m_timer->Stop(); + m_timer->Tick -= m_tickToken; + } +} diff --git a/UI/Backgrounds/Blobs/BlobsBackground.xaml.h b/UI/Backgrounds/Blobs/BlobsBackground.xaml.h new file mode 100644 index 00000000..f8fb69fc --- /dev/null +++ b/UI/Backgrounds/Blobs/BlobsBackground.xaml.h @@ -0,0 +1,57 @@ +#pragma once +#include "UI\Backgrounds\Blobs\BlobsBackground.g.h" +#include +#include + +namespace moonlight_xbox_dx { + +static const int kBlobCount = 3; +static const int kBlobPts = 6; + +struct BlobData { + float cx, cy; + float baseRadius; + float time; + float timeSpeed; + + float rAmp[kBlobPts]; + float rPhase[kBlobPts]; + float rFreq[kBlobPts]; + float aAmp[kBlobPts]; + float aFreq[kBlobPts]; + float aPhase[kBlobPts]; + + Windows::UI::Xaml::Media::PathFigure^ figure; + Windows::UI::Xaml::Media::BezierSegment^ s0; + Windows::UI::Xaml::Media::BezierSegment^ s1; + Windows::UI::Xaml::Media::BezierSegment^ s2; + Windows::UI::Xaml::Media::BezierSegment^ s3; + Windows::UI::Xaml::Media::BezierSegment^ s4; + Windows::UI::Xaml::Media::BezierSegment^ s5; +}; + +public ref class BlobsBackground sealed { +public: + BlobsBackground(); + void StartAnimations(); + void StopAnimations(); + void ReloadColors(); +private: + Windows::UI::Color m_palette[4]; + void LoadPalette(); + Windows::UI::Xaml::DispatcherTimer^ m_timer; + Windows::Foundation::EventRegistrationToken m_tickToken; + std::vector m_blobs; + float m_canvasW = 0; + float m_canvasH = 0; + bool m_ready = false; + std::mt19937 m_rng; + + void Canvas_SizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e); + void OnTick(Platform::Object^ sender, Platform::Object^ args); + void OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void InitBlobs(); + void UpdateBlob(BlobData& b); +}; + +} diff --git a/UI/Backgrounds/Blobs/BlobsSettingsControl.xaml b/UI/Backgrounds/Blobs/BlobsSettingsControl.xaml new file mode 100644 index 00000000..c7236506 --- /dev/null +++ b/UI/Backgrounds/Blobs/BlobsSettingsControl.xaml @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Backgrounds/Blobs/BlobsSettingsControl.xaml.cpp b/UI/Backgrounds/Blobs/BlobsSettingsControl.xaml.cpp new file mode 100644 index 00000000..c498207b --- /dev/null +++ b/UI/Backgrounds/Blobs/BlobsSettingsControl.xaml.cpp @@ -0,0 +1,166 @@ +#include "pch.h" +#include "BlobsSettingsControl.xaml.h" +#include "UI\Backgrounds\BackgroundSettingsHelpers.h" + +using namespace moonlight_xbox_dx; +using namespace Platform; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; + +static Windows::UI::Color kCrimsonUI[] = { {170,210,10,55},{170,185,0,185},{170,100,0,200},{255,8,0,16} }; +static Windows::UI::Color kOceanUI[] = { {170,0,180,200},{170,0,100,220},{170,0,210,150},{255,0,6,20} }; +static Windows::UI::Color kAuroraUI[] = { {170,0,200,120},{170,60,190,255},{170,140,40,230},{255,2,4,10} }; +static Windows::UI::Color kEmberUI[] = { {170,255,70,0},{170,210,20,20},{170,255,140,20},{255,12,3,0} }; +static Windows::UI::Color kNebulaUI[] = { {170,170,40,230},{170,255,60,180},{170,40,90,255},{255,5,0,18} }; + +static Windows::UI::Color kColorSwatches[] = { + {255,210, 10, 55}, {255,185, 0,185}, {255,100, 0,200}, + {255, 0,180,200}, {255, 0,210,150}, {255, 0,100,220}, + {255, 0,200,120}, {255,140, 40,230}, {255,255, 70, 0}, + {255,255,140, 20}, {255,255, 60,180}, {255,255,255,255}, +}; +static const int kColorSwatchCount = 12; + +static Windows::UI::Color kBgSwatches[] = { + {255, 8, 0, 16}, {255, 0, 6, 20}, {255, 2, 4, 10}, + {255, 12, 3, 0}, {255, 5, 0, 18}, {255, 0, 0, 0}, + {255, 5, 8, 0}, {255, 10, 0, 5}, +}; +static const int kBgSwatchCount = 8; + +static const wchar_t* kCustomKeys[] = { + L"blobs.custom.0", L"blobs.custom.1", L"blobs.custom.2", L"blobs.custom.3" +}; + +BlobsSettingsControl::BlobsSettingsControl() +{ + InitializeComponent(); +} + +void BlobsSettingsControl::Initialize(DynamicBackgroundHost^ host) +{ + m_host = host; + + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + Platform::String^ scheme = L"crimson"; + if (ls->HasKey("blobs.scheme")) + scheme = safe_cast(ls->Lookup("blobs.scheme")); + + struct { const wchar_t* key; const wchar_t* label; } schemes[] = { + { L"crimson", L"Crimson (Default)" }, + { L"ocean", L"Ocean" }, + { L"aurora", L"Aurora" }, + { L"ember", L"Ember" }, + { L"nebula", L"Nebula" }, + { L"custom", L"Custom" }, + }; + + int selectedIdx = 0; + for (int i = 0; i < 6; ++i) { + auto item = ref new ComboBoxItem(); + item->Content = ref new Platform::String(schemes[i].label); + item->DataContext = ref new Platform::String(schemes[i].key); + SchemeSelector->Items->Append(item); + if (scheme->Equals(ref new Platform::String(schemes[i].key))) selectedIdx = i; + } + SchemeSelector->SelectedIndex = selectedIdx; + UpdateCustomPanelVisibility(); + + Windows::UI::Color* defaults = kCrimsonUI; + if (scheme->Equals(L"ocean")) defaults = kOceanUI; + else if (scheme->Equals(L"aurora")) defaults = kAuroraUI; + else if (scheme->Equals(L"ember")) defaults = kEmberUI; + else if (scheme->Equals(L"nebula")) defaults = kNebulaUI; + + SwatchPicker^ blobPickers[] = { Color0, Color1, Color2 }; + for (int i = 0; i < 3; ++i) { + if (blobPickers[i] == nullptr) continue; + blobPickers[i]->SetSwatches(kColorSwatches, kColorSwatchCount); + Windows::UI::Color c = defaults[i]; + if (scheme->Equals(L"custom")) { + auto k = ref new Platform::String(kCustomKeys[i]); + if (ls->HasKey(k)) c = BgHexToColor(safe_cast(ls->Lookup(k)), c); + } + blobPickers[i]->SelectColor(c, false); + } + + if (Color3 != nullptr) { + Color3->SetSwatches(kBgSwatches, kBgSwatchCount); + Windows::UI::Color c3 = defaults[3]; + if (scheme->Equals(L"custom")) { + auto k = ref new Platform::String(kCustomKeys[3]); + if (ls->HasKey(k)) c3 = BgHexToColor(safe_cast(ls->Lookup(k)), c3); + } + Color3->SelectColor(c3, false); + } + + Color0->ColorChanged += ref new SwatchColorChangedHandler(this, &BlobsSettingsControl::Color0_ColorChanged); + Color1->ColorChanged += ref new SwatchColorChangedHandler(this, &BlobsSettingsControl::Color1_ColorChanged); + Color2->ColorChanged += ref new SwatchColorChangedHandler(this, &BlobsSettingsControl::Color2_ColorChanged); + Color3->ColorChanged += ref new SwatchColorChangedHandler(this, &BlobsSettingsControl::Color3_ColorChanged); + + m_initialized = true; +} + +void BlobsSettingsControl::UpdateCustomPanelVisibility() +{ + if (CustomPanel == nullptr || SchemeSelector == nullptr) return; + auto item = dynamic_cast(SchemeSelector->SelectedItem); + bool isCustom = (item != nullptr && item->DataContext != nullptr && + item->DataContext->ToString()->Equals(L"custom")); + CustomPanel->Visibility = isCustom ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; +} + +void BlobsSettingsControl::SchemeSelector_SelectionChanged(Platform::Object^, SelectionChangedEventArgs^) +{ + if (!m_initialized) return; + auto item = dynamic_cast(SchemeSelector->SelectedItem); + if (item == nullptr) return; + auto key = item->DataContext->ToString(); + + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert("blobs.scheme", key); + + if (!key->Equals(L"custom")) { + Windows::UI::Color* defaults = kCrimsonUI; + if (key->Equals(L"ocean")) defaults = kOceanUI; + else if (key->Equals(L"aurora")) defaults = kAuroraUI; + else if (key->Equals(L"ember")) defaults = kEmberUI; + else if (key->Equals(L"nebula")) defaults = kNebulaUI; + SwatchPicker^ pickers[] = { Color0, Color1, Color2, Color3 }; + for (int i = 0; i < 4; ++i) { + if (pickers[i] != nullptr) pickers[i]->SelectColor(defaults[i], false); + } + } + + UpdateCustomPanelVisibility(); + try { if (m_host != nullptr) m_host->ReloadBackgroundColors(); } catch (...) {} +} + +static void SaveColor(int slot, Windows::UI::Color color) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert(ref new Platform::String(kCustomKeys[slot]), BgColorToHex(color)); +} + +void BlobsSettingsControl::Color0_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) { SaveColor(0, color); try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} } +void BlobsSettingsControl::Color1_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) { SaveColor(1, color); try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} } +void BlobsSettingsControl::Color2_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) { SaveColor(2, color); try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} } +void BlobsSettingsControl::Color3_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) { SaveColor(3, color); try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} } + +void BlobsSettingsControl::ResetButton_Click(Platform::Object^, RoutedEventArgs^) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Remove("blobs.scheme"); + for (int i = 0; i < 4; ++i) + ls->Remove(ref new Platform::String(kCustomKeys[i])); + + SchemeSelector->SelectedIndex = 0; + SwatchPicker^ pickers[] = { Color0, Color1, Color2, Color3 }; + for (int i = 0; i < 4; ++i) { + if (pickers[i] != nullptr) pickers[i]->SelectColor(kCrimsonUI[i], false); + } + CustomPanel->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + + try { if (m_host != nullptr) m_host->ReloadBackgroundColors(); } catch (...) {} +} diff --git a/UI/Backgrounds/Blobs/BlobsSettingsControl.xaml.h b/UI/Backgrounds/Blobs/BlobsSettingsControl.xaml.h new file mode 100644 index 00000000..d3c7fc7f --- /dev/null +++ b/UI/Backgrounds/Blobs/BlobsSettingsControl.xaml.h @@ -0,0 +1,25 @@ +#pragma once +#include "UI\Backgrounds\Blobs\BlobsSettingsControl.g.h" +#include "UI\Backgrounds\DynamicBackgroundHost.xaml.h" +#include "UI\Controls\SwatchPicker.xaml.h" + +namespace moonlight_xbox_dx { + +public ref class BlobsSettingsControl sealed { +public: + BlobsSettingsControl(); + void Initialize(DynamicBackgroundHost^ host); +private: + DynamicBackgroundHost^ m_host; + bool m_initialized = false; + + void SchemeSelector_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e); + void Color0_ColorChanged(Platform::Object^ sender, Windows::UI::Color color, bool useSystemAccent); + void Color1_ColorChanged(Platform::Object^ sender, Windows::UI::Color color, bool useSystemAccent); + void Color2_ColorChanged(Platform::Object^ sender, Windows::UI::Color color, bool useSystemAccent); + void Color3_ColorChanged(Platform::Object^ sender, Windows::UI::Color color, bool useSystemAccent); + void ResetButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void UpdateCustomPanelVisibility(); +}; + +} diff --git a/UI/Backgrounds/DynamicBackgroundHost.xaml b/UI/Backgrounds/DynamicBackgroundHost.xaml new file mode 100644 index 00000000..d023ae19 --- /dev/null +++ b/UI/Backgrounds/DynamicBackgroundHost.xaml @@ -0,0 +1,20 @@ + + + + + + diff --git a/UI/Backgrounds/DynamicBackgroundHost.xaml.cpp b/UI/Backgrounds/DynamicBackgroundHost.xaml.cpp new file mode 100644 index 00000000..3524bc01 --- /dev/null +++ b/UI/Backgrounds/DynamicBackgroundHost.xaml.cpp @@ -0,0 +1,205 @@ +#include "pch.h" +#include "UI\Backgrounds\DynamicBackgroundHost.xaml.h" +#include "UI\Backgrounds\BackgroundRegistry.h" +#include "UI\Backgrounds\Particles\ParticleBackground.xaml.h" +#include "UI\Backgrounds\Spheres\SpheresBackground.xaml.h" +#include "UI\Backgrounds\Streaks\StreaksBackground.xaml.h" +#include "UI\Backgrounds\Blobs\BlobsBackground.xaml.h" +#include "UI\Backgrounds\SwipeReveal\SwipeRevealBackground.xaml.h" +#include "UI\Backgrounds\GlobeGrid\GlobeGridBackground.xaml.h" +#include "UI\Backgrounds\Orbs\OrbsBackground.xaml.h" + +using namespace moonlight_xbox_dx; + +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Media::Animation; +using namespace Windows::Storage; + +static void TryStartAnimations(UIElement^ el) +{ + if (auto p = dynamic_cast(el)) { p->StartAnimations(); return; } + if (auto s = dynamic_cast(el)) { s->StartAnimations(); return; } + if (auto k = dynamic_cast(el)) { k->StartAnimations(); return; } + if (auto l = dynamic_cast(el)) { l->StartAnimations(); return; } + if (auto sr = dynamic_cast(el)) { sr->StartAnimations(); return; } + if (auto gg = dynamic_cast(el)) { gg->StartAnimations(); return; } + if (auto ob = dynamic_cast(el)) { ob->StartAnimations(); return; } +} + +static void TryStopAnimations(UIElement^ el) +{ + if (auto p = dynamic_cast(el)) { p->StopAnimations(); return; } + if (auto s = dynamic_cast(el)) { s->StopAnimations(); return; } + if (auto k = dynamic_cast(el)) { k->StopAnimations(); return; } + if (auto l = dynamic_cast(el)) { l->StopAnimations(); return; } + if (auto sr = dynamic_cast(el)) { sr->StopAnimations(); return; } + if (auto gg = dynamic_cast(el)) { gg->StopAnimations(); return; } + if (auto ob = dynamic_cast(el)) { ob->StopAnimations(); return; } +} + +static UIElement^ CreateBackground(String^ key) +{ + if (key != nullptr) { + if (key->Equals(ref new String(L"particles"))) return ref new ParticleBackground(); + if (key->Equals(ref new String(L"spheres"))) return ref new SpheresBackground(); + if (key->Equals(ref new String(L"streaks"))) return ref new StreaksBackground(); + if (key->Equals(ref new String(L"blobs"))) return ref new BlobsBackground(); + if (key->Equals(ref new String(L"swipereveal"))) return ref new SwipeRevealBackground(); + if (key->Equals(ref new String(L"globegrid"))) return ref new GlobeGridBackground(); + if (key->Equals(ref new String(L"orbs"))) return ref new OrbsBackground(); + } + return ref new StreaksBackground(); +} + +DynamicBackgroundHost::DynamicBackgroundHost() +{ + InitializeComponent(); + this->Loaded += ref new RoutedEventHandler(this, &DynamicBackgroundHost::OnLoaded); +} + +void DynamicBackgroundHost::OnLoaded(Object^ sender, RoutedEventArgs^ e) +{ + Refresh(); +} + +void DynamicBackgroundHost::Refresh() +{ + try { + auto localSettings = ApplicationData::Current->LocalSettings->Values; + String^ key = L"streaks"; + if (localSettings->HasKey("background")) { + key = safe_cast(localSettings->Lookup("background")); + } + + m_discardedBg = nullptr; + + if (m_incomingKey != nullptr && m_incomingKey->Equals(key)) return; + if (m_incomingKey == nullptr && m_currentKey != nullptr && m_currentKey->Equals(key)) return; + + if (m_fadeStoryboard != nullptr) { + try { m_fadeStoryboard->Stop(); } catch (...) {} + m_fadeStoryboard = nullptr; + auto incomingEl = dynamic_cast(FadePresenter->Content); + if (incomingEl != nullptr) { + m_discardedBg = incomingEl; + TryStopAnimations(incomingEl); + } + FadePresenter->Content = nullptr; + FadePresenter->Opacity = 0.0; + m_incomingKey = nullptr; + } + + auto newBg = CreateBackground(key); + if (auto sr = dynamic_cast(newBg)) { sr->SetHosts(m_hosts); } + FadePresenter->Content = newBg; + FadePresenter->Opacity = 0.0; + m_incomingKey = key; + + auto anim = ref new DoubleAnimation(); + anim->From = 0.0; + anim->To = 1.0; + TimeSpan ts; ts.Duration = 3000000LL; + anim->Duration = DurationHelper::FromTimeSpan(ts); + + auto sb = ref new Storyboard(); + Storyboard::SetTarget(anim, FadePresenter); + Storyboard::SetTargetProperty(anim, "Opacity"); + sb->Children->Append(anim); + m_fadeStoryboard = sb; + + auto weakThis = WeakReference(this); + sb->Completed += ref new EventHandler( + [weakThis](Object^, Object^) { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + try { + + auto oldEl = dynamic_cast(that->BackgroundPresenter->Content); + if (oldEl != nullptr) { + that->m_discardedBg = oldEl; + TryStopAnimations(oldEl); + } + auto incoming = that->FadePresenter->Content; + that->FadePresenter->Content = nullptr; + that->FadePresenter->Opacity = 0.0; + that->BackgroundPresenter->Content = incoming; + that->m_currentKey = that->m_incomingKey; + that->m_incomingKey = nullptr; + that->m_fadeStoryboard = nullptr; + } catch (...) {} + }); + + sb->Begin(); + } catch (...) {} +} + +void DynamicBackgroundHost::SetHosts(Windows::Foundation::Collections::IVector^ hosts) +{ + m_hosts = hosts; + try { + auto el = dynamic_cast(BackgroundPresenter->Content); + if (auto sr = dynamic_cast(el)) { sr->SetHosts(hosts); } + } catch (...) {} + try { + auto el = dynamic_cast(FadePresenter->Content); + if (auto sr = dynamic_cast(el)) { sr->SetHosts(hosts); } + } catch (...) {} +} + +void DynamicBackgroundHost::StartAnimations() +{ + try { + auto el = dynamic_cast(BackgroundPresenter->Content); + if (el != nullptr) TryStartAnimations(el); + } catch (...) {} + try { + auto el = dynamic_cast(FadePresenter->Content); + if (el != nullptr) TryStartAnimations(el); + } catch (...) {} +} + +void DynamicBackgroundHost::StopAnimations() +{ + m_discardedBg = nullptr; + if (m_fadeStoryboard != nullptr) { + try { m_fadeStoryboard->Stop(); } catch (...) {} + m_fadeStoryboard = nullptr; + } + try { + auto el = dynamic_cast(BackgroundPresenter->Content); + if (el != nullptr) TryStopAnimations(el); + } catch (...) {} + try { + auto el = dynamic_cast(FadePresenter->Content); + if (el != nullptr) TryStopAnimations(el); + } catch (...) {} +} + +void DynamicBackgroundHost::ReloadBackgroundColors() +{ + try { + auto el = dynamic_cast(BackgroundPresenter->Content); + if (auto p = dynamic_cast(el)) { p->ReloadColors(); return; } + if (auto k = dynamic_cast(el)) { k->ReloadColors(); return; } + if (auto s = dynamic_cast(el)) { s->ReloadColors(); return; } + if (auto ob = dynamic_cast(el)) { ob->ReloadColors(); return; } + if (auto l = dynamic_cast(el)) { l->ReloadColors(); return; } + } catch (...) {} +} + +void DynamicBackgroundHost::ApplyBackground(Platform::String^ key, Platform::String^ scheme) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert("background", key); + if (scheme != nullptr) + ls->Insert(key + ".scheme", scheme); + Refresh(); + StartAnimations(); +} + +void DynamicBackgroundHost::ResetBackground() +{ + ApplyBackground(DefaultKey, "neon"); +} diff --git a/UI/Backgrounds/DynamicBackgroundHost.xaml.h b/UI/Backgrounds/DynamicBackgroundHost.xaml.h new file mode 100644 index 00000000..e59a31e1 --- /dev/null +++ b/UI/Backgrounds/DynamicBackgroundHost.xaml.h @@ -0,0 +1,29 @@ +#pragma once +#include "UI\Backgrounds\DynamicBackgroundHost.g.h" +#include "State\MoonlightHost.h" + +namespace moonlight_xbox_dx { + +public ref class DynamicBackgroundHost sealed { +public: + DynamicBackgroundHost(); + static property Platform::String^ DefaultKey { + Platform::String^ get() { return ref new Platform::String(L"streaks"); } + } + void Refresh(); + void StartAnimations(); + void StopAnimations(); + void ApplyBackground(Platform::String^ key, Platform::String^ scheme); + void ResetBackground(); + void ReloadBackgroundColors(); + void SetHosts(Windows::Foundation::Collections::IVector^ hosts); +private: + Platform::String^ m_currentKey; + Platform::String^ m_incomingKey; + Windows::UI::Xaml::Media::Animation::Storyboard^ m_fadeStoryboard; + Platform::Object^ m_discardedBg; + Windows::Foundation::Collections::IVector^ m_hosts; + void OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); +}; + +} diff --git a/UI/Backgrounds/GlobeGrid/GlobeGridBackground.xaml b/UI/Backgrounds/GlobeGrid/GlobeGridBackground.xaml new file mode 100644 index 00000000..a4b1e00b --- /dev/null +++ b/UI/Backgrounds/GlobeGrid/GlobeGridBackground.xaml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Backgrounds/GlobeGrid/GlobeGridBackground.xaml.cpp b/UI/Backgrounds/GlobeGrid/GlobeGridBackground.xaml.cpp new file mode 100644 index 00000000..aba01bf4 --- /dev/null +++ b/UI/Backgrounds/GlobeGrid/GlobeGridBackground.xaml.cpp @@ -0,0 +1,218 @@ +#include "pch.h" +#include "UI\Backgrounds\GlobeGrid\GlobeGridBackground.xaml.h" +#include +#include + +using namespace moonlight_xbox_dx; +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::UI; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Shapes; + +static const int kLats = 9; +static const float kLatDeg[] = {-80,-60,-40,-20,0,20,40,60,80}; +static const int kLons = 12; +static const int kFrontSamp = 48; +static const int kLonSamp = 30; +static const float kPi = 3.14159265f; +static const float kRotSpeed = 0.0015f; +static const float kGlobeScale= 0.48f; +static const int kStars = 180; + +GlobeGridBackground::GlobeGridBackground() +{ + m_rng = std::mt19937(std::random_device{}()); + InitializeComponent(); + TimeSpan ts; + ts.Duration = 16 * 10000LL; + m_timer = ref new DispatcherTimer(); + m_timer->Interval = ts; + Platform::WeakReference weakSelf(this); + m_tickToken = m_timer->Tick += ref new EventHandler( + [weakSelf](Object^, Object^) { + try { + auto self = weakSelf.Resolve(); + if (self) self->OnTick(nullptr, nullptr); + } catch (Platform::DisconnectedException^) {} + catch (...) {} + }); +} + +void GlobeGridBackground::Canvas_SizeChanged(Object^ sender, SizeChangedEventArgs^ e) +{ + m_canvasW = static_cast(e->NewSize.Width); + m_canvasH = static_cast(e->NewSize.Height); + m_initialized = (m_canvasW > 0 && m_canvasH > 0); +} + +Point GlobeGridBackground::ProjectSphere(float latRad, float lonRad) +{ + float r = kGlobeScale * std::min(m_canvasW, m_canvasH) * 0.5f; + float lonView= lonRad + m_rotY; + float x3 = r * cosf(latRad) * sinf(lonView); + float y3 = r * sinf(latRad); + return Point(m_canvasW * 0.5f + x3, m_canvasH * 0.5f - y3); +} + +void GlobeGridBackground::InitItems() +{ + WireCanvas->Children->Clear(); + StarCanvas->Children->Clear(); + m_stars.clear(); + + Color gold = ColorHelper::FromArgb(255, 255, 196, 64); + Color cyan = ColorHelper::FromArgb(255, 64, 210, 255); + + for (int i = 0; i < kLats; ++i) { + auto pl = ref new Polyline(); + pl->Stroke = ref new SolidColorBrush(gold); + pl->StrokeThickness = 1.0; + pl->Opacity = 0.18; + auto pts = ref new PointCollection(); + for (int k = 0; k <= kFrontSamp; ++k) pts->Append(Point(0, 0)); + pl->Points = pts; + WireCanvas->Children->Append(pl); + } + + for (int i = 0; i < kLats; ++i) { + auto pl = ref new Polyline(); + if (i == 4) { + pl->Stroke = ref new SolidColorBrush(cyan); + pl->StrokeThickness = 2.0; + } else { + pl->Stroke = ref new SolidColorBrush(gold); + pl->StrokeThickness = 1.5; + } + pl->Opacity = 1.0; + auto pts = ref new PointCollection(); + for (int k = 0; k <= kFrontSamp; ++k) pts->Append(Point(0, 0)); + pl->Points = pts; + WireCanvas->Children->Append(pl); + } + + for (int j = 0; j < kLons; ++j) { + auto pl = ref new Polyline(); + pl->Stroke = ref new SolidColorBrush(gold); + pl->StrokeThickness = 1.5; + pl->Opacity = 1.0; + auto pts = ref new PointCollection(); + for (int k = 0; k <= kLonSamp; ++k) pts->Append(Point(0, 0)); + pl->Points = pts; + WireCanvas->Children->Append(pl); + } + + float maxR = hypotf(m_canvasW, m_canvasH) * 0.56f; + std::uniform_real_distribution distUnit(0.0f, 1.0f); + std::uniform_real_distribution distAngle(0.0f, 2.0f * kPi); + + for (int i = 0; i < kStars; ++i) { + GlobeStarState s; + s.radius = sqrtf(distUnit(m_rng)) * maxR; + s.angle = distAngle(m_rng); + + float speedBase = 0.00025f + distUnit(m_rng) * 0.00035f; + s.speed = speedBase * (1.0f + 0.6f * (1.0f - s.radius / maxR)); + s.size = 1.0f + distUnit(m_rng) * 2.2f; + s.baseOpacity = 0.25f + distUnit(m_rng) * 0.75f; + s.phase = distAngle(m_rng); + m_stars.push_back(s); + + auto star = ref new Ellipse(); + star->Width = s.size; + star->Height = s.size; + star->Fill = ref new SolidColorBrush(Colors::White); + star->Opacity = s.baseOpacity; + StarCanvas->Children->Append(star); + } + + m_itemsCreated = true; + UpdateGlobe(); + UpdateStars(); +} + +void GlobeGridBackground::UpdateGlobe() +{ + + for (int i = 0; i < kLats; ++i) { + float latR = kLatDeg[i] * kPi / 180.0f; + auto pl = safe_cast(WireCanvas->Children->GetAt(i)); + auto pts = pl->Points; + for (int k = 0; k <= kFrontSamp; ++k) { + float lonView = kPi * 0.5f + k * kPi / static_cast(kFrontSamp); + pts->SetAt(k, ProjectSphere(latR, lonView - m_rotY)); + } + } + + for (int i = 0; i < kLats; ++i) { + float latR = kLatDeg[i] * kPi / 180.0f; + auto pl = safe_cast(WireCanvas->Children->GetAt(kLats + i)); + auto pts = pl->Points; + for (int k = 0; k <= kFrontSamp; ++k) { + float lonView = -kPi * 0.5f + k * kPi / static_cast(kFrontSamp); + pts->SetAt(k, ProjectSphere(latR, lonView - m_rotY)); + } + } + + for (int j = 0; j < kLons; ++j) { + float worldLon = j * 2.0f * kPi / static_cast(kLons); + float lonView = worldLon + m_rotY; + bool isFront = (cosf(lonView) > 0.0f); + auto pl = safe_cast(WireCanvas->Children->GetAt(2 * kLats + j)); + pl->Opacity = isFront ? 1.0 : 0.18; + auto pts = pl->Points; + for (int k = 0; k <= kLonSamp; ++k) { + float latR = (-80.0f + k * 160.0f / static_cast(kLonSamp)) * kPi / 180.0f; + pts->SetAt(k, ProjectSphere(latR, worldLon)); + } + } +} + +void GlobeGridBackground::UpdateStars() +{ + int count = static_cast(m_stars.size()); + float cx = m_canvasW * 0.5f; + float cy = m_canvasH * 0.5f; + for (int i = 0; i < count; ++i) { + auto& s = m_stars[i]; + s.angle += s.speed; + float x = cx + cosf(s.angle) * s.radius; + float y = cy + sinf(s.angle) * s.radius; + auto star = safe_cast(StarCanvas->Children->GetAt(i)); + Canvas::SetLeft(star, x - s.size * 0.5f); + Canvas::SetTop(star, y - s.size * 0.5f); + + star->Opacity = s.baseOpacity * (0.60f + 0.40f * sinf(s.angle * 7.0f + s.phase)); + } +} + +void GlobeGridBackground::OnTick(Object^ sender, Object^ args) +{ + if (!m_initialized) return; + + if (!m_itemsCreated) { + InitItems(); + return; + } + + m_rotY += kRotSpeed; + if (m_rotY > 2.0f * kPi) m_rotY -= 2.0f * kPi; + + UpdateGlobe(); + UpdateStars(); +} + +void GlobeGridBackground::StartAnimations() +{ + if (m_timer) m_timer->Start(); +} + +void GlobeGridBackground::StopAnimations() +{ + if (m_timer) { + m_timer->Stop(); + m_timer->Tick -= m_tickToken; + } +} diff --git a/UI/Backgrounds/GlobeGrid/GlobeGridBackground.xaml.h b/UI/Backgrounds/GlobeGrid/GlobeGridBackground.xaml.h new file mode 100644 index 00000000..10afba8d --- /dev/null +++ b/UI/Backgrounds/GlobeGrid/GlobeGridBackground.xaml.h @@ -0,0 +1,42 @@ +#pragma once +#include "UI\Backgrounds\GlobeGrid\GlobeGridBackground.g.h" +#include +#include + +namespace moonlight_xbox_dx { + +struct GlobeStarState { + float angle; + float radius; + float speed; + float size; + float baseOpacity; + float phase; +}; + +public ref class GlobeGridBackground sealed { +public: + GlobeGridBackground(); + void StartAnimations(); + void StopAnimations(); +private: + Windows::UI::Xaml::DispatcherTimer^ m_timer; + Windows::Foundation::EventRegistrationToken m_tickToken; + std::vector m_stars; + std::mt19937 m_rng; + + float m_canvasW = 0.0f; + float m_canvasH = 0.0f; + bool m_initialized = false; + bool m_itemsCreated = false; + float m_rotY = 0.0f; + + Windows::Foundation::Point ProjectSphere(float latRad, float lonRad); + void Canvas_SizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e); + void OnTick(Platform::Object^ sender, Platform::Object^ args); + void InitItems(); + void UpdateGlobe(); + void UpdateStars(); +}; + +} diff --git a/UI/Backgrounds/Orbs/OrbsBackground.xaml b/UI/Backgrounds/Orbs/OrbsBackground.xaml new file mode 100644 index 00000000..7c056d24 --- /dev/null +++ b/UI/Backgrounds/Orbs/OrbsBackground.xaml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Backgrounds/Orbs/OrbsBackground.xaml.cpp b/UI/Backgrounds/Orbs/OrbsBackground.xaml.cpp new file mode 100644 index 00000000..b234d7d2 --- /dev/null +++ b/UI/Backgrounds/Orbs/OrbsBackground.xaml.cpp @@ -0,0 +1,373 @@ +#include "pch.h" +#include "UI\Backgrounds\Orbs\OrbsBackground.xaml.h" +#include + +using namespace moonlight_xbox_dx; +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::UI; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Shapes; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Media::Animation; + +static const float kPi = 3.14159265358979f; + +static Color kOrbsDefaultGlow = { 255, 30, 160, 255 }; +static Color kOrbsDefaultBg = { 255, 0, 8, 20 }; + +static bool ParseHex6Orbs(Platform::String^ s, Color& out) +{ + if (s == nullptr || s->Length() != 6) return false; + const wchar_t* p = s->Data(); + wchar_t buf[7]; wcsncpy_s(buf, p, 6); buf[6] = L'\0'; + wchar_t* end = nullptr; + unsigned long v = wcstoul(buf, &end, 16); + if (end != buf + 6) return false; + out.A = 255; + out.R = (uint8_t)((v >> 16) & 0xFF); + out.G = (uint8_t)((v >> 8) & 0xFF); + out.B = (uint8_t)( v & 0xFF); + return true; +} + +static const float kOuterAuraR = 40.0f; +static const float kInnerGlowR = 30.0f; +static const float kCoreR = 20.0f; +static const float kMinTailR = 1.5f; +static const float kMaxTailR = 9.0f; +static const float kMaxTailOpacity = 0.88f; + +static const float kCircleTilt = 20.0f * kPi / 180.0f; +static const float kCircleRotSpeed = 0.007f; +static const int kDanceHoldTicks = 420; +static const int kCircleHoldTicks = 300; +static const int kTransitionTicks = 90; + +static const int kOuterAuraBase = kOrbCount * kOrbTailLen; +static const int kInnerGlowBase = kOrbCount * kOrbTailLen + kOrbCount; +static const int kCoreBase = kOrbCount * kOrbTailLen + kOrbCount * 2; + +static void OrbPos(const OrbState& o, float& outX, float& outY) +{ + float ca = cosf(o.angle), sa = sinf(o.angle); + float ct = cosf(o.tilt), st = sinf(o.tilt); + outX = o.cx + o.rx * ca * ct - o.ry * sa * st; + outY = o.cy + o.rx * ca * st + o.ry * sa * ct; +} + +OrbsBackground::OrbsBackground() +{ + InitializeComponent(); + this->Loaded += ref new RoutedEventHandler(this, &OrbsBackground::OnLoaded); + m_palette[0] = kOrbsDefaultGlow; + m_palette[1] = kOrbsDefaultBg; + OrbCanvas->Background = ref new SolidColorBrush(ColorHelper::FromArgb(255, 0, 8, 20)); + + TimeSpan interval; + interval.Duration = 16 * 10000LL; + m_timer = ref new DispatcherTimer(); + m_timer->Interval = interval; + Platform::WeakReference weakSelf(this); + m_tickToken = m_timer->Tick += ref new EventHandler( + [weakSelf](Object^, Object^) { + try { + auto self = weakSelf.Resolve(); + if (self) self->OnTick(nullptr, nullptr); + } catch (Platform::DisconnectedException^) {} + catch (...) {} + }); +} + +void OrbsBackground::Canvas_SizeChanged(Object^ sender, SizeChangedEventArgs^ e) +{ + m_canvasW = static_cast(e->NewSize.Width); + m_canvasH = static_cast(e->NewSize.Height); + if (!m_initialized && m_canvasW > 0 && m_canvasH > 0) { + InitOrbs(); + m_initialized = true; + } +} + +void OrbsBackground::LoadPalette() +{ + using namespace Windows::Storage; + Platform::String^ scheme = L"electric"; + auto ls = ApplicationData::Current->LocalSettings->Values; + if (ls->HasKey("orbs.scheme")) + scheme = safe_cast(ls->Lookup("orbs.scheme")); + + Color schemes[5][2] = { + { {255, 30,160,255}, {255, 0, 8, 20} }, + { {255, 0,220,150}, {255, 0, 10, 15} }, + { {255,255,140, 0}, {255, 12, 5, 0} }, + { {255,200, 80,255}, {255, 8, 0, 20} }, + { {255,255, 60,140}, {255, 15, 0, 12} }, + }; + + int idx = -1; + if (scheme->Equals(L"electric")) idx = 0; + else if (scheme->Equals(L"aurora")) idx = 1; + else if (scheme->Equals(L"solar")) idx = 2; + else if (scheme->Equals(L"nebula")) idx = 3; + else if (scheme->Equals(L"rose")) idx = 4; + + if (idx >= 0) { + m_palette[0] = schemes[idx][0]; + m_palette[1] = schemes[idx][1]; + return; + } + + static const wchar_t* kCustomKeys[] = { L"orbs.custom.0", L"orbs.custom.1" }; + Color defaults[2] = { kOrbsDefaultGlow, kOrbsDefaultBg }; + for (int i = 0; i < 2; ++i) { + Color c = defaults[i]; + auto key = ref new Platform::String(kCustomKeys[i]); + if (ls->HasKey(key)) + ParseHex6Orbs(safe_cast(ls->Lookup(key)), c); + m_palette[i] = c; + } +} + +void OrbsBackground::InitOrbs() +{ + LoadPalette(); + m_orbs.clear(); + OrbCanvas->Children->Clear(); + OrbCanvas->Background = ref new SolidColorBrush( + ColorHelper::FromArgb(m_palette[1].A, m_palette[1].R, m_palette[1].G, m_palette[1].B)); + + m_lerpT = 0.0f; + m_targetLerpT = 0.0f; + m_holdTicks = kDanceHoldTicks; + m_circleAngle = 0.0f; + + float W = m_canvasW, H = m_canvasH; + float cx = W * 0.5f, cy = H * 0.5f; + + struct OrbDef { float rxF, ryF, tilt, phase, speed; }; + static OrbDef kDefs[kOrbCount] = { + + { 0.13f, 0.09f, 30.0f * kPi/180.0f, 0.0f, 0.036f }, + { 0.13f, 0.09f, 30.0f * kPi/180.0f, kPi, -0.034f }, + { 0.13f, 0.09f, 30.0f * kPi/180.0f, kPi * 0.5f, 0.040f }, + + { 0.22f, 0.14f, 65.0f * kPi/180.0f, 0.0f, 0.024f }, + { 0.22f, 0.14f, 65.0f * kPi/180.0f, kPi * 2.0f/3.0f, -0.022f }, + { 0.22f, 0.14f, 65.0f * kPi/180.0f, kPi * 4.0f/3.0f, 0.026f }, + + { 0.32f, 0.20f, 15.0f * kPi/180.0f, kPi * 0.25f, 0.016f }, + { 0.32f, 0.20f, 15.0f * kPi/180.0f, kPi * 1.25f, 0.018f }, + }; + + m_orbs.reserve(kOrbCount); + for (int i = 0; i < kOrbCount; ++i) { + OrbState s; + s.cx = cx; + s.cy = cy; + s.rx = kDefs[i].rxF * W; + s.ry = kDefs[i].ryF * H; + s.tilt = kDefs[i].tilt; + s.angle = kDefs[i].phase; + s.speed = kDefs[i].speed; + + float startX, startY; + OrbPos(s, startX, startY); + for (int j = 0; j < kOrbTailLen; ++j) { + s.tailX[j] = startX; + s.tailY[j] = startY; + } + m_orbs.push_back(s); + } + + for (int i = 0; i < kOrbCount; ++i) { + const auto& s = m_orbs[i]; + for (int j = 0; j < kOrbTailLen; ++j) { + float t = static_cast(j) / (kOrbTailLen - 1); + float r = kMinTailR + t * (kMaxTailR - kMinTailR); + float op = t * kMaxTailOpacity; + + auto el = ref new Ellipse(); + el->Width = r * 2.0f; + el->Height = r * 2.0f; + Color gc = m_palette[0]; + el->Fill = ref new SolidColorBrush(ColorHelper::FromArgb(255, gc.R, gc.G, gc.B)); + el->Opacity = op; + Canvas::SetLeft(el, s.tailX[j] - r); + Canvas::SetTop(el, s.tailY[j] - r); + OrbCanvas->Children->Append(el); + } + } + + for (int i = 0; i < kOrbCount; ++i) { + const auto& s = m_orbs[i]; + Color col = m_palette[0]; + float x, y; OrbPos(s, x, y); + + auto el = ref new Ellipse(); + el->Width = kOuterAuraR * 2.0f; + el->Height = kOuterAuraR * 2.0f; + el->Fill = ref new SolidColorBrush(ColorHelper::FromArgb(80, col.R, col.G, col.B)); + Canvas::SetLeft(el, x - kOuterAuraR); + Canvas::SetTop(el, y - kOuterAuraR); + OrbCanvas->Children->Append(el); + } + + for (int i = 0; i < kOrbCount; ++i) { + const auto& s = m_orbs[i]; + Color col = m_palette[0]; + float x, y; OrbPos(s, x, y); + + auto el = ref new Ellipse(); + el->Width = kInnerGlowR * 2.0f; + el->Height = kInnerGlowR * 2.0f; + el->Fill = ref new SolidColorBrush(ColorHelper::FromArgb(215, col.R, col.G, col.B)); + Canvas::SetLeft(el, x - kInnerGlowR); + Canvas::SetTop(el, y - kInnerGlowR); + OrbCanvas->Children->Append(el); + } + + for (int i = 0; i < kOrbCount; ++i) { + const auto& s = m_orbs[i]; + float x, y; OrbPos(s, x, y); + + auto el = ref new Ellipse(); + el->Width = kCoreR * 2.0f; + el->Height = kCoreR * 2.0f; + el->Fill = ref new SolidColorBrush(ColorHelper::FromArgb(255, 255, 255, 255)); + Canvas::SetLeft(el, x - kCoreR); + Canvas::SetTop(el, y - kCoreR); + OrbCanvas->Children->Append(el); + } +} + +void OrbsBackground::OnTick(Object^ sender, Object^ args) +{ + if (!m_initialized) return; + + float W = m_canvasW, H = m_canvasH; + float cx = W * 0.5f, cy = H * 0.5f; + float circleR = (W < H ? W : H) * 0.26f; + + if (--m_holdTicks <= 0) { + if (m_targetLerpT < 0.5f) { + m_targetLerpT = 1.0f; + m_holdTicks = kCircleHoldTicks + kTransitionTicks; + } else { + m_targetLerpT = 0.0f; + m_holdTicks = kDanceHoldTicks + kTransitionTicks; + } + } + + float step = 1.0f / static_cast(kTransitionTicks); + float remaining = m_targetLerpT - m_lerpT; + if ((remaining < 0.0f ? -remaining : remaining) < step) { + m_lerpT = m_targetLerpT; + } else { + m_lerpT += remaining > 0.0f ? step : -step; + } + + float st = m_lerpT * m_lerpT * (3.0f - 2.0f * m_lerpT); + + m_circleAngle += kCircleRotSpeed; + float ctilt = cosf(kCircleTilt), stilt = sinf(kCircleTilt); + + for (int i = 0; i < kOrbCount; ++i) { + auto& s = m_orbs[i]; + + for (int j = 0; j < kOrbTailLen - 1; ++j) { + s.tailX[j] = s.tailX[j + 1]; + s.tailY[j] = s.tailY[j + 1]; + } + + if (m_lerpT == 0.0f) s.angle += s.speed; + float dx, dy; + OrbPos(s, dx, dy); + + float cAngle = m_circleAngle + i * (2.0f * kPi / kOrbCount); + float cca = cosf(cAngle), csa = sinf(cAngle); + float px = cx + circleR * cca * ctilt - circleR * csa * stilt; + float py = cy + circleR * cca * stilt + circleR * csa * ctilt; + + float nx = dx + st * (px - dx); + float ny = dy + st * (py - dy); + + s.tailX[kOrbTailLen - 1] = nx; + s.tailY[kOrbTailLen - 1] = ny; + + for (int j = 0; j < kOrbTailLen; ++j) { + float t = static_cast(j) / (kOrbTailLen - 1); + float r = kMinTailR + t * (kMaxTailR - kMinTailR); + auto el = safe_cast(OrbCanvas->Children->GetAt(i * kOrbTailLen + j)); + Canvas::SetLeft(el, s.tailX[j] - r); + Canvas::SetTop(el, s.tailY[j] - r); + } + + auto aura = safe_cast(OrbCanvas->Children->GetAt(kOuterAuraBase + i)); + Canvas::SetLeft(aura, nx - kOuterAuraR); + Canvas::SetTop(aura, ny - kOuterAuraR); + + auto glow = safe_cast(OrbCanvas->Children->GetAt(kInnerGlowBase + i)); + Canvas::SetLeft(glow, nx - kInnerGlowR); + Canvas::SetTop(glow, ny - kInnerGlowR); + + auto core = safe_cast(OrbCanvas->Children->GetAt(kCoreBase + i)); + Canvas::SetLeft(core, nx - kCoreR); + Canvas::SetTop(core, ny - kCoreR); + } +} + +void OrbsBackground::OnLoaded(Object^ sender, RoutedEventArgs^ e) +{ + auto anim = ref new DoubleAnimation(); + anim->From = 0.0; + anim->To = 1.0; + TimeSpan ts; + ts.Duration = 5000000LL; + anim->Duration = Windows::UI::Xaml::Duration(ts); + OrbCanvas->Opacity = 0.0; + auto sb = ref new Storyboard(); + Storyboard::SetTarget(anim, OrbCanvas); + Storyboard::SetTargetProperty(anim, "Opacity"); + sb->Children->Append(anim); + sb->Begin(); +} + +void OrbsBackground::ReloadColors() +{ + if (!m_initialized) return; + LoadPalette(); + + Color gc = m_palette[0]; + Color bc = m_palette[1]; + + OrbCanvas->Background = ref new SolidColorBrush( + ColorHelper::FromArgb(bc.A, bc.R, bc.G, bc.B)); + + for (int i = 0; i < kOrbCount; ++i) { + for (int j = 0; j < kOrbTailLen; ++j) { + auto el = safe_cast(OrbCanvas->Children->GetAt(i * kOrbTailLen + j)); + el->Fill = ref new SolidColorBrush(ColorHelper::FromArgb(255, gc.R, gc.G, gc.B)); + } + } + for (int i = 0; i < kOrbCount; ++i) { + auto el = safe_cast(OrbCanvas->Children->GetAt(kOuterAuraBase + i)); + el->Fill = ref new SolidColorBrush(ColorHelper::FromArgb(80, gc.R, gc.G, gc.B)); + } + for (int i = 0; i < kOrbCount; ++i) { + auto el = safe_cast(OrbCanvas->Children->GetAt(kInnerGlowBase + i)); + el->Fill = ref new SolidColorBrush(ColorHelper::FromArgb(215, gc.R, gc.G, gc.B)); + } + +} + +void OrbsBackground::StartAnimations() { if (m_timer) m_timer->Start(); } + +void OrbsBackground::StopAnimations() +{ + if (m_timer != nullptr) { + m_timer->Stop(); + m_timer->Tick -= m_tickToken; + } +} diff --git a/UI/Backgrounds/Orbs/OrbsBackground.xaml.h b/UI/Backgrounds/Orbs/OrbsBackground.xaml.h new file mode 100644 index 00000000..91de075e --- /dev/null +++ b/UI/Backgrounds/Orbs/OrbsBackground.xaml.h @@ -0,0 +1,47 @@ +#pragma once +#include "UI\Backgrounds\Orbs\OrbsBackground.g.h" +#include + +namespace moonlight_xbox_dx { + +static const int kOrbCount = 8; +static const int kOrbTailLen = 48; + +struct OrbState { + float cx, cy; + float rx, ry; + float tilt; + float angle; + float speed; + float tailX[kOrbTailLen]; + float tailY[kOrbTailLen]; +}; + +public ref class OrbsBackground sealed { +public: + OrbsBackground(); + void StartAnimations(); + void StopAnimations(); + void ReloadColors(); +private: + Windows::UI::Color m_palette[2]; + Windows::UI::Xaml::DispatcherTimer^ m_timer; + Windows::Foundation::EventRegistrationToken m_tickToken; + std::vector m_orbs; + float m_canvasW = 0; + float m_canvasH = 0; + bool m_initialized = false; + + float m_lerpT = 0.0f; + float m_targetLerpT = 0.0f; + int m_holdTicks = 0; + float m_circleAngle = 0.0f; + + void Canvas_SizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e); + void OnTick(Platform::Object^ sender, Platform::Object^ args); + void OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void InitOrbs(); + void LoadPalette(); +}; + +} diff --git a/UI/Backgrounds/Orbs/OrbsSettingsControl.xaml b/UI/Backgrounds/Orbs/OrbsSettingsControl.xaml new file mode 100644 index 00000000..d43e57df --- /dev/null +++ b/UI/Backgrounds/Orbs/OrbsSettingsControl.xaml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Backgrounds/Orbs/OrbsSettingsControl.xaml.cpp b/UI/Backgrounds/Orbs/OrbsSettingsControl.xaml.cpp new file mode 100644 index 00000000..76ccf146 --- /dev/null +++ b/UI/Backgrounds/Orbs/OrbsSettingsControl.xaml.cpp @@ -0,0 +1,156 @@ +#include "pch.h" +#include "OrbsSettingsControl.xaml.h" +#include "UI\Backgrounds\BackgroundSettingsHelpers.h" + +using namespace moonlight_xbox_dx; +using namespace Platform; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; + +static Windows::UI::Color kElectricUI[] = { {255, 30,160,255}, {255, 0, 8, 20} }; +static Windows::UI::Color kAuroraUI[] = { {255, 0,220,150}, {255, 0, 10, 15} }; +static Windows::UI::Color kSolarUI[] = { {255,255,140, 0}, {255, 12, 5, 0} }; +static Windows::UI::Color kNebulaUI[] = { {255,200, 80,255}, {255, 8, 0, 20} }; +static Windows::UI::Color kRoseUI[] = { {255,255, 60,140}, {255, 15, 0, 12} }; + +static Windows::UI::Color kGlowSwatches[] = { + {255, 30,160,255}, {255, 0,220,150}, {255,255,140, 0}, {255,200, 80,255}, + {255,255, 60,140}, {255, 0,240,240}, {255,255, 0, 0}, {255, 0,255,128}, + {255,255,255, 0}, {255,255,128, 0}, {255,128, 0,255}, {255,255,255,255}, +}; +static const int kGlowSwatchCount = 12; + +static Windows::UI::Color kBgSwatches[] = { + {255, 0, 8, 20}, {255, 0, 10, 15}, {255, 12, 5, 0}, {255, 8, 0, 20}, + {255, 15, 0, 12}, {255, 0, 0, 0}, {255, 5, 10, 0}, {255, 10, 5, 5}, +}; +static const int kBgSwatchCount = 8; + +static const wchar_t* kCustomKeys[] = { L"orbs.custom.0", L"orbs.custom.1" }; + +OrbsSettingsControl::OrbsSettingsControl() +{ + InitializeComponent(); +} + +void OrbsSettingsControl::Initialize(DynamicBackgroundHost^ host) +{ + m_host = host; + + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + Platform::String^ scheme = L"electric"; + if (ls->HasKey("orbs.scheme")) + scheme = safe_cast(ls->Lookup("orbs.scheme")); + + struct { const wchar_t* key; const wchar_t* label; } schemes[] = { + { L"electric", L"Electric (Default)" }, + { L"aurora", L"Aurora" }, + { L"solar", L"Solar" }, + { L"nebula", L"Nebula" }, + { L"rose", L"Rose" }, + { L"custom", L"Custom" }, + }; + + int selectedIdx = 0; + for (int i = 0; i < 6; ++i) { + auto item = ref new ComboBoxItem(); + item->Content = ref new Platform::String(schemes[i].label); + item->DataContext = ref new Platform::String(schemes[i].key); + SchemeSelector->Items->Append(item); + if (scheme->Equals(ref new Platform::String(schemes[i].key))) selectedIdx = i; + } + SchemeSelector->SelectedIndex = selectedIdx; + UpdateCustomPanelVisibility(); + + Windows::UI::Color* defaults = kElectricUI; + if (scheme->Equals(L"aurora")) defaults = kAuroraUI; + else if (scheme->Equals(L"solar")) defaults = kSolarUI; + else if (scheme->Equals(L"nebula")) defaults = kNebulaUI; + else if (scheme->Equals(L"rose")) defaults = kRoseUI; + + if (Color0 != nullptr) { + Color0->SetSwatches(kGlowSwatches, kGlowSwatchCount); + Windows::UI::Color c0 = defaults[0]; + if (scheme->Equals(L"custom")) { + auto k = ref new Platform::String(kCustomKeys[0]); + if (ls->HasKey(k)) c0 = BgHexToColor(safe_cast(ls->Lookup(k)), c0); + } + Color0->SelectColor(c0, false); + Color0->ColorChanged += ref new SwatchColorChangedHandler(this, &OrbsSettingsControl::Color0_ColorChanged); + } + + if (Color1 != nullptr) { + Color1->SetSwatches(kBgSwatches, kBgSwatchCount); + Windows::UI::Color c1 = defaults[1]; + if (scheme->Equals(L"custom")) { + auto k = ref new Platform::String(kCustomKeys[1]); + if (ls->HasKey(k)) c1 = BgHexToColor(safe_cast(ls->Lookup(k)), c1); + } + Color1->SelectColor(c1, false); + Color1->ColorChanged += ref new SwatchColorChangedHandler(this, &OrbsSettingsControl::Color1_ColorChanged); + } + + m_initialized = true; +} + +void OrbsSettingsControl::UpdateCustomPanelVisibility() +{ + if (CustomPanel == nullptr || SchemeSelector == nullptr) return; + auto item = dynamic_cast(SchemeSelector->SelectedItem); + bool isCustom = (item != nullptr && item->DataContext != nullptr && + item->DataContext->ToString()->Equals(L"custom")); + CustomPanel->Visibility = isCustom ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; +} + +void OrbsSettingsControl::SchemeSelector_SelectionChanged(Platform::Object^, SelectionChangedEventArgs^) +{ + if (!m_initialized) return; + auto item = dynamic_cast(SchemeSelector->SelectedItem); + if (item == nullptr) return; + auto key = item->DataContext->ToString(); + + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert("orbs.scheme", key); + + if (!key->Equals(L"custom")) { + Windows::UI::Color* defaults = kElectricUI; + if (key->Equals(L"aurora")) defaults = kAuroraUI; + else if (key->Equals(L"solar")) defaults = kSolarUI; + else if (key->Equals(L"nebula")) defaults = kNebulaUI; + else if (key->Equals(L"rose")) defaults = kRoseUI; + if (Color0 != nullptr) Color0->SelectColor(defaults[0], false); + if (Color1 != nullptr) Color1->SelectColor(defaults[1], false); + } + + UpdateCustomPanelVisibility(); + try { if (m_host != nullptr) m_host->ReloadBackgroundColors(); } catch (...) {} +} + +void OrbsSettingsControl::Color0_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert(ref new Platform::String(kCustomKeys[0]), BgColorToHex(color)); + try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} +} + +void OrbsSettingsControl::Color1_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert(ref new Platform::String(kCustomKeys[1]), BgColorToHex(color)); + try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} +} + +void OrbsSettingsControl::ResetButton_Click(Platform::Object^, RoutedEventArgs^) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Remove("orbs.scheme"); + ls->Remove(ref new Platform::String(kCustomKeys[0])); + ls->Remove(ref new Platform::String(kCustomKeys[1])); + + SchemeSelector->SelectedIndex = 0; + if (Color0 != nullptr) Color0->SelectColor(kElectricUI[0], false); + if (Color1 != nullptr) Color1->SelectColor(kElectricUI[1], false); + CustomPanel->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + + try { if (m_host != nullptr) m_host->ReloadBackgroundColors(); } catch (...) {} +} diff --git a/UI/Backgrounds/Orbs/OrbsSettingsControl.xaml.h b/UI/Backgrounds/Orbs/OrbsSettingsControl.xaml.h new file mode 100644 index 00000000..08d14fb4 --- /dev/null +++ b/UI/Backgrounds/Orbs/OrbsSettingsControl.xaml.h @@ -0,0 +1,23 @@ +#pragma once +#include "UI\Backgrounds\Orbs\OrbsSettingsControl.g.h" +#include "UI\Backgrounds\DynamicBackgroundHost.xaml.h" +#include "UI\Controls\SwatchPicker.xaml.h" + +namespace moonlight_xbox_dx { + +public ref class OrbsSettingsControl sealed { +public: + OrbsSettingsControl(); + void Initialize(DynamicBackgroundHost^ host); +private: + DynamicBackgroundHost^ m_host; + bool m_initialized = false; + + void SchemeSelector_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e); + void Color0_ColorChanged(Platform::Object^ sender, Windows::UI::Color color, bool useSystemAccent); + void Color1_ColorChanged(Platform::Object^ sender, Windows::UI::Color color, bool useSystemAccent); + void ResetButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void UpdateCustomPanelVisibility(); +}; + +} diff --git a/UI/Backgrounds/Particles/ParticleBackground.xaml b/UI/Backgrounds/Particles/ParticleBackground.xaml new file mode 100644 index 00000000..0002d93e --- /dev/null +++ b/UI/Backgrounds/Particles/ParticleBackground.xaml @@ -0,0 +1,12 @@ + + + diff --git a/UI/Backgrounds/Particles/ParticleBackground.xaml.cpp b/UI/Backgrounds/Particles/ParticleBackground.xaml.cpp new file mode 100644 index 00000000..428023f1 --- /dev/null +++ b/UI/Backgrounds/Particles/ParticleBackground.xaml.cpp @@ -0,0 +1,312 @@ +#include "pch.h" +#include "UI\Backgrounds\Particles\ParticleBackground.xaml.h" +#include + +using namespace moonlight_xbox_dx; + +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::UI; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Shapes; +using namespace Windows::UI::Xaml::Media; + +static Color LerpRGB(Color a, Color b, float t, uint8_t alpha = 255) { + auto ch = [](uint8_t x, uint8_t y, float f) -> uint8_t { + return static_cast(x + (static_cast(y) - static_cast(x)) * f); + }; + return ColorHelper::FromArgb(alpha, ch(a.R, b.R, t), ch(a.G, b.G, t), ch(a.B, b.B, t)); +} + +static Color ScaleRGB(Color c, float s, uint8_t alpha = 255) { + return ColorHelper::FromArgb(alpha, + static_cast(fminf(c.R * s, 255.0f)), + static_cast(fminf(c.G * s, 255.0f)), + static_cast(fminf(c.B * s, 255.0f))); +} + +static bool ParseHex6(Platform::String^ s, Color& out) +{ + if (s == nullptr || s->Length() != 6) return false; + const wchar_t* p = s->Data(); + wchar_t buf[7]; wcsncpy_s(buf, p, 6); buf[6] = L'\0'; + wchar_t* end = nullptr; + unsigned long v = wcstoul(buf, &end, 16); + if (end != buf + 6) return false; + out.A = 255; + out.R = (uint8_t)((v >> 16) & 0xFF); + out.G = (uint8_t)((v >> 8) & 0xFF); + out.B = (uint8_t)( v & 0xFF); + return true; +} + +static const int kBokehCount = 50; +static const int kSmallCount = 130; +static const int kParticleCount = kBokehCount + kSmallCount; +static const float kPi = 3.14159265f; +static const float kSpreadRange = 0.18f; + +float ParticleBackground::WaveY(float t) { + float centerY = m_canvasH * (0.80f - t * 0.60f); + return centerY + m_canvasH * 0.10f * sinf(t * kPi * 3.0f + m_wavePhase); +} + +void ParticleBackground::LoadPalette() +{ + using namespace Windows::Storage; + Platform::String^ scheme = L"champagne"; + auto ls = ApplicationData::Current->LocalSettings->Values; + if (ls->HasKey("particles.scheme")) + scheme = safe_cast(ls->Lookup("particles.scheme")); + + struct { Color particle; Color gradient; } presets[] = { + { {255,210,165, 75}, {255, 50, 30,120} }, + { {255,255,120, 40}, {255, 15, 80, 90} }, + { {255, 80,215,190}, {255, 90, 15,150} }, + { {255,180, 90,230}, {255, 10, 20,100} }, + { {255,235,110,150}, {255, 15, 85, 60} }, + }; + int idx = 0; + if (scheme->Equals(L"ember")) idx = 1; + else if (scheme->Equals(L"aurora")) idx = 2; + else if (scheme->Equals(L"nebula")) idx = 3; + else if (scheme->Equals(L"blossom")) idx = 4; + + if (scheme->Equals(L"custom")) { + m_colorA = presets[0].particle; + m_gradientColor = presets[0].gradient; + if (ls->HasKey("particles.custom.0")) + ParseHex6(safe_cast(ls->Lookup("particles.custom.0")), m_colorA); + if (ls->HasKey("particles.custom.1")) + ParseHex6(safe_cast(ls->Lookup("particles.custom.1")), m_gradientColor); + } else { + m_colorA = presets[idx].particle; + m_gradientColor = presets[idx].gradient; + } + m_colorB = ScaleRGB(m_colorA, 0.35f); +} + +ParticleBackground::ParticleBackground() +{ + m_rng = std::mt19937(std::random_device{}()); + InitializeComponent(); + + TimeSpan interval; + interval.Duration = 33 * 10000LL; + m_timer = ref new DispatcherTimer(); + m_timer->Interval = interval; + Platform::WeakReference weakSelf(this); + m_tickToken = m_timer->Tick += ref new EventHandler( + [weakSelf](Object^, Object^) { + try { + auto self = weakSelf.Resolve(); + if (self) self->OnTick(nullptr, nullptr); + } catch (Platform::DisconnectedException^) {} + catch (...) {} + }); +} + +void ParticleBackground::Canvas_SizeChanged(Object^ sender, SizeChangedEventArgs^ e) +{ + m_canvasW = static_cast(e->NewSize.Width); + m_canvasH = static_cast(e->NewSize.Height); + + if (!m_initialized && m_canvasW > 0 && m_canvasH > 0) { + InitParticles(); + m_initialized = true; + } +} + +void ParticleBackground::ApplyGradient() +{ + Color stop0 = ScaleRGB(m_gradientColor, 0.15f); stop0.A = 255; + Color stop1 = { 255, 5, 5, 10 }; + + if (m_bgBrush == nullptr) { + auto gs0 = ref new GradientStop(); gs0->Color = stop0; gs0->Offset = 0.0; + auto gs1 = ref new GradientStop(); gs1->Color = stop1; gs1->Offset = 1.0; + m_bgBrush = ref new LinearGradientBrush(); + m_bgBrush->StartPoint = { 0.0, 1.0 }; + m_bgBrush->EndPoint = { 1.0, 0.0 }; + m_bgBrush->GradientStops->Append(gs0); + m_bgBrush->GradientStops->Append(gs1); + ParticleCanvas->Background = m_bgBrush; + } else { + m_bgBrush->GradientStops->GetAt(0)->Color = stop0; + m_bgBrush->GradientStops->GetAt(1)->Color = stop1; + } +} + +void ParticleBackground::ReloadColors() +{ + if (!m_initialized) return; + LoadPalette(); + ApplyGradient(); + + SolidColorBrush^ bokehBrushes[2]; + bokehBrushes[0] = ref new SolidColorBrush(LerpRGB(m_colorA, m_colorB, 0.3f, 45)); + bokehBrushes[1] = ref new SolidColorBrush(LerpRGB(m_colorA, m_colorB, 0.7f, 40)); + + Color white = { 255, 255, 255, 255 }; + Color coreCol = LerpRGB(m_colorA, white, 0.45f, 220); + SolidColorBrush^ smallBrushes[5]; + for (int b = 0; b < 5; ++b) { + float t = b / 4.0f; + uint8_t a = static_cast(220 - t * 65); + smallBrushes[b] = ref new SolidColorBrush(LerpRGB(coreCol, m_colorB, t, a)); + } + + int count = static_cast(m_particles.size()); + for (int i = 0; i < count; ++i) { + auto& s = m_particles[i]; + auto el = safe_cast(ParticleCanvas->Children->GetAt(i)); + el->Fill = s.isBokeh ? bokehBrushes[s.colorSlot] : smallBrushes[s.colorSlot]; + } +} + +void ParticleBackground::InitParticles() +{ + LoadPalette(); + ApplyGradient(); + m_particles.clear(); + m_particles.reserve(kParticleCount); + ParticleCanvas->Children->Clear(); + + SolidColorBrush^ bokehBrushes[2]; + bokehBrushes[0] = ref new SolidColorBrush(LerpRGB(m_colorA, m_colorB, 0.3f, 45)); + bokehBrushes[1] = ref new SolidColorBrush(LerpRGB(m_colorA, m_colorB, 0.7f, 40)); + + Color white = { 255, 255, 255, 255 }; + Color coreCol = LerpRGB(m_colorA, white, 0.45f, 220); + SolidColorBrush^ smallBrushes[5]; + for (int b = 0; b < 5; ++b) { + float t = b / 4.0f; + uint8_t a = static_cast(220 - t * 65); + smallBrushes[b] = ref new SolidColorBrush(LerpRGB(coreCol, m_colorB, t, a)); + } + + std::uniform_real_distribution distT(0.0f, 1.0f); + + std::uniform_real_distribution bSpread(-0.35f, 0.35f); + std::uniform_real_distribution bSpeed(0.0005f, 0.0018f); + std::uniform_real_distribution bSize(20.0f, 80.0f); + std::uniform_real_distribution bOp(0.03f, 0.15f); + std::uniform_real_distribution bDelta(0.001f, 0.003f); + + for (int i = 0; i < kBokehCount; ++i) { + ParticleState s; + s.isBokeh = true; + s.t = distT(m_rng); + s.tSpeed = bSpeed(m_rng); + s.spreadY = bSpread(m_rng) * m_canvasH; + s.size = bSize(m_rng); + s.opacityMin = 0.16f; + s.opacityMax = 0.32f; + s.opacity = bOp(m_rng); + s.opacityDelta = bDelta(m_rng) * (m_rng() % 2 == 0 ? 1.0f : -1.0f); + s.colorSlot = m_rng() % 2; + m_particles.push_back(s); + + float x = s.t * m_canvasW; + float y = WaveY(s.t) + s.spreadY; + auto el = ref new Ellipse(); + el->Width = s.size; el->Height = s.size; + el->Fill = bokehBrushes[s.colorSlot]; + el->Opacity = s.opacity; + Canvas::SetLeft(el, x - s.size * 0.5f); + Canvas::SetTop(el, y - s.size * 0.5f); + ParticleCanvas->Children->Append(el); + } + + std::uniform_real_distribution sSpread(-kSpreadRange, kSpreadRange); + std::uniform_real_distribution sSpeed(0.0010f, 0.0030f); + std::uniform_real_distribution sSize(1.5f, 5.5f); + std::uniform_real_distribution sDelta(0.004f, 0.012f); + + for (int i = 0; i < kSmallCount; ++i) { + float spread = sSpread(m_rng) * m_canvasH; + float normDist = fabsf(spread) / (kSpreadRange * m_canvasH); + + ParticleState s; + s.isBokeh = false; + s.t = distT(m_rng); + s.tSpeed = sSpeed(m_rng); + s.spreadY = spread; + s.size = sSize(m_rng); + s.opacityMax = fmaxf(0.28f, 0.95f - normDist * 0.65f); + s.opacityMin = s.opacityMax * 0.15f; + s.opacity = s.opacityMin + (s.opacityMax - s.opacityMin) + * static_cast(m_rng() % 100) / 100.0f; + s.opacityDelta = sDelta(m_rng) * (m_rng() % 2 == 0 ? 1.0f : -1.0f); + int band = static_cast(normDist * 5.0f); + if (band >= 5) band = 4; + s.colorSlot = band; + m_particles.push_back(s); + + float x = s.t * m_canvasW; + float y = WaveY(s.t) + s.spreadY; + auto el = ref new Ellipse(); + el->Width = s.size; el->Height = s.size; + el->Fill = smallBrushes[band]; + el->Opacity = s.opacity; + Canvas::SetLeft(el, x - s.size * 0.5f); + Canvas::SetTop(el, y - s.size * 0.5f); + ParticleCanvas->Children->Append(el); + } +} + +void ParticleBackground::OnTick(Object^ sender, Object^ args) +{ + if (!m_initialized) return; + + m_wavePhase += 0.008f; + if (m_wavePhase > kPi * 2.0f) m_wavePhase -= kPi * 2.0f; + + std::uniform_real_distribution bSpread(-0.35f, 0.35f); + std::uniform_real_distribution sSpread(-kSpreadRange, kSpreadRange); + + int count = static_cast(m_particles.size()); + for (int i = 0; i < count; ++i) { + auto& s = m_particles[i]; + + s.t += s.tSpeed; + if (s.t > 1.05f) { + s.t = -0.05f; + if (s.isBokeh) { + s.spreadY = bSpread(m_rng) * m_canvasH; + } else { + float spread = sSpread(m_rng) * m_canvasH; + s.spreadY = spread; + float normDist = fabsf(spread) / (kSpreadRange * m_canvasH); + s.opacityMax = fmaxf(0.28f, 0.95f - normDist * 0.65f); + s.opacityMin = s.opacityMax * 0.15f; + } + } + + float x = s.t * m_canvasW; + float y = WaveY(s.t) + s.spreadY; + + s.opacity += s.opacityDelta; + if (s.opacity > s.opacityMax) { s.opacity = s.opacityMax; s.opacityDelta = -fabsf(s.opacityDelta); } + if (s.opacity < s.opacityMin) { s.opacity = s.opacityMin; s.opacityDelta = fabsf(s.opacityDelta); } + + auto el = safe_cast(ParticleCanvas->Children->GetAt(i)); + Canvas::SetLeft(el, x - s.size * 0.5f); + Canvas::SetTop(el, y - s.size * 0.5f); + el->Opacity = s.opacity; + } +} + +void ParticleBackground::StartAnimations() +{ + if (m_timer != nullptr) m_timer->Start(); +} + +void ParticleBackground::StopAnimations() +{ + if (m_timer != nullptr) { + m_timer->Stop(); + m_timer->Tick -= m_tickToken; + } +} diff --git a/UI/Backgrounds/Particles/ParticleBackground.xaml.h b/UI/Backgrounds/Particles/ParticleBackground.xaml.h new file mode 100644 index 00000000..e5262f2e --- /dev/null +++ b/UI/Backgrounds/Particles/ParticleBackground.xaml.h @@ -0,0 +1,49 @@ +#pragma once +#include "UI\Backgrounds\Particles\ParticleBackground.g.h" +#include +#include + +namespace moonlight_xbox_dx { + +struct ParticleState { + float t; + float tSpeed; + float spreadY; + float opacity; + float opacityDelta; + float opacityMin; + float opacityMax; + float size; + bool isBokeh; + int colorSlot; +}; + +public ref class ParticleBackground sealed { +public: + ParticleBackground(); + void StartAnimations(); + void StopAnimations(); + void ReloadColors(); +private: + Windows::UI::Xaml::DispatcherTimer^ m_timer; + Windows::Foundation::EventRegistrationToken m_tickToken; + std::vector m_particles; + float m_canvasW = 0; + float m_canvasH = 0; + float m_wavePhase = 0.0f; + bool m_initialized = false; + std::mt19937 m_rng; + Windows::UI::Color m_colorA; + Windows::UI::Color m_colorB; + Windows::UI::Color m_gradientColor; + Windows::UI::Xaml::Media::LinearGradientBrush^ m_bgBrush; + + void Canvas_SizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e); + void OnTick(Platform::Object^ sender, Platform::Object^ args); + void InitParticles(); + void LoadPalette(); + void ApplyGradient(); + float WaveY(float t); +}; + +} diff --git a/UI/Backgrounds/Particles/ParticleSettingsControl.xaml b/UI/Backgrounds/Particles/ParticleSettingsControl.xaml new file mode 100644 index 00000000..4957af37 --- /dev/null +++ b/UI/Backgrounds/Particles/ParticleSettingsControl.xaml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Backgrounds/Particles/ParticleSettingsControl.xaml.cpp b/UI/Backgrounds/Particles/ParticleSettingsControl.xaml.cpp new file mode 100644 index 00000000..6468b80d --- /dev/null +++ b/UI/Backgrounds/Particles/ParticleSettingsControl.xaml.cpp @@ -0,0 +1,146 @@ +#include "pch.h" +#include "ParticleSettingsControl.xaml.h" +#include "UI\Backgrounds\BackgroundSettingsHelpers.h" + +using namespace moonlight_xbox_dx; +using namespace Platform; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; + +static Windows::UI::Color kChampagneScheme[] = { {255,210,165, 75}, {255, 50, 30,120} }; +static Windows::UI::Color kEmberScheme[] = { {255,255,120, 40}, {255, 15, 80, 90} }; +static Windows::UI::Color kAuroraScheme[] = { {255, 80,215,190}, {255, 90, 15,150} }; +static Windows::UI::Color kNebulaScheme[] = { {255,180, 90,230}, {255, 10, 20,100} }; +static Windows::UI::Color kBlossomScheme[] = { {255,235,110,150}, {255, 15, 85, 60} }; + +static Windows::UI::Color kSwatches[] = { + {255,210,165, 75}, {255,230,120,140}, {255, 80,200,180}, + {255,160,100,220}, {255,160,200,255}, {255,255,100, 80}, + {255,100,200,100}, {255,255,180, 80}, {255, 80,120,255}, + {255,220,180,255}, {255,255,255,255}, {255,180,180,180}, +}; +static const int kSwatchCount = 12; + +static const wchar_t* kKey0 = L"particles.custom.0"; +static const wchar_t* kKey1 = L"particles.custom.1"; + +ParticleSettingsControl::ParticleSettingsControl() +{ + InitializeComponent(); +} + +void ParticleSettingsControl::Initialize(DynamicBackgroundHost^ host) +{ + m_host = host; + + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + Platform::String^ scheme = L"champagne"; + if (ls->HasKey("particles.scheme")) + scheme = safe_cast(ls->Lookup("particles.scheme")); + + struct { const wchar_t* key; const wchar_t* label; } schemes[] = { + { L"champagne", L"Champagne (Default)" }, + { L"ember", L"Ember" }, + { L"aurora", L"Aurora" }, + { L"nebula", L"Nebula" }, + { L"blossom", L"Blossom" }, + { L"custom", L"Custom" }, + }; + + int selectedIdx = 0; + for (int i = 0; i < 6; ++i) { + auto item = ref new ComboBoxItem(); + item->Content = ref new Platform::String(schemes[i].label); + item->DataContext = ref new Platform::String(schemes[i].key); + SchemeSelector->Items->Append(item); + if (scheme->Equals(ref new Platform::String(schemes[i].key))) selectedIdx = i; + } + SchemeSelector->SelectedIndex = selectedIdx; + UpdateCustomPanelVisibility(); + + Windows::UI::Color* defaults = kChampagneScheme; + if (scheme->Equals(L"ember")) defaults = kEmberScheme; + else if (scheme->Equals(L"aurora")) defaults = kAuroraScheme; + else if (scheme->Equals(L"nebula")) defaults = kNebulaScheme; + else if (scheme->Equals(L"blossom")) defaults = kBlossomScheme; + + Color0->SetSwatches(kSwatches, kSwatchCount); + Color1->SetSwatches(kSwatches, kSwatchCount); + + Windows::UI::Color c0 = defaults[0], c1 = defaults[1]; + if (scheme->Equals(L"custom")) { + auto k0 = ref new Platform::String(kKey0); + auto k1 = ref new Platform::String(kKey1); + if (ls->HasKey(k0)) c0 = BgHexToColor(safe_cast(ls->Lookup(k0)), c0); + if (ls->HasKey(k1)) c1 = BgHexToColor(safe_cast(ls->Lookup(k1)), c1); + } + Color0->SelectColor(c0, false); + Color1->SelectColor(c1, false); + + Color0->ColorChanged += ref new SwatchColorChangedHandler(this, &ParticleSettingsControl::Color0_ColorChanged); + Color1->ColorChanged += ref new SwatchColorChangedHandler(this, &ParticleSettingsControl::Color1_ColorChanged); + + m_initialized = true; +} + +void ParticleSettingsControl::UpdateCustomPanelVisibility() +{ + if (CustomPanel == nullptr || SchemeSelector == nullptr) return; + auto item = dynamic_cast(SchemeSelector->SelectedItem); + bool isCustom = (item != nullptr && item->DataContext != nullptr && + item->DataContext->ToString()->Equals(L"custom")); + CustomPanel->Visibility = isCustom ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; +} + +void ParticleSettingsControl::SchemeSelector_SelectionChanged(Platform::Object^, SelectionChangedEventArgs^) +{ + if (!m_initialized) return; + auto item = dynamic_cast(SchemeSelector->SelectedItem); + if (item == nullptr) return; + auto key = item->DataContext->ToString(); + + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert("particles.scheme", key); + + if (!key->Equals(L"custom")) { + Windows::UI::Color* defaults = kChampagneScheme; + if (key->Equals(L"ember")) defaults = kEmberScheme; + else if (key->Equals(L"aurora")) defaults = kAuroraScheme; + else if (key->Equals(L"nebula")) defaults = kNebulaScheme; + else if (key->Equals(L"blossom")) defaults = kBlossomScheme; + if (Color0 != nullptr) Color0->SelectColor(defaults[0], false); + if (Color1 != nullptr) Color1->SelectColor(defaults[1], false); + } + + UpdateCustomPanelVisibility(); + try { if (m_host != nullptr) m_host->ReloadBackgroundColors(); } catch (...) {} +} + +void ParticleSettingsControl::Color0_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert(ref new Platform::String(kKey0), BgColorToHex(color)); + try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} +} + +void ParticleSettingsControl::Color1_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert(ref new Platform::String(kKey1), BgColorToHex(color)); + try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} +} + +void ParticleSettingsControl::ResetButton_Click(Platform::Object^, RoutedEventArgs^) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Remove("particles.scheme"); + ls->Remove(ref new Platform::String(kKey0)); + ls->Remove(ref new Platform::String(kKey1)); + + SchemeSelector->SelectedIndex = 0; + if (Color0 != nullptr) Color0->SelectColor(kChampagneScheme[0], false); + if (Color1 != nullptr) Color1->SelectColor(kChampagneScheme[1], false); + CustomPanel->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + + try { if (m_host != nullptr) m_host->ReloadBackgroundColors(); } catch (...) {} +} diff --git a/UI/Backgrounds/Particles/ParticleSettingsControl.xaml.h b/UI/Backgrounds/Particles/ParticleSettingsControl.xaml.h new file mode 100644 index 00000000..5ea3a39d --- /dev/null +++ b/UI/Backgrounds/Particles/ParticleSettingsControl.xaml.h @@ -0,0 +1,23 @@ +#pragma once +#include "UI\Backgrounds\Particles\ParticleSettingsControl.g.h" +#include "UI\Backgrounds\DynamicBackgroundHost.xaml.h" +#include "UI\Controls\SwatchPicker.xaml.h" + +namespace moonlight_xbox_dx { + +public ref class ParticleSettingsControl sealed { +public: + ParticleSettingsControl(); + void Initialize(DynamicBackgroundHost^ host); +private: + DynamicBackgroundHost^ m_host; + bool m_initialized = false; + + void SchemeSelector_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e); + void Color0_ColorChanged(Platform::Object^ sender, Windows::UI::Color color, bool useSystemAccent); + void Color1_ColorChanged(Platform::Object^ sender, Windows::UI::Color color, bool useSystemAccent); + void ResetButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void UpdateCustomPanelVisibility(); +}; + +} diff --git a/UI/Backgrounds/README.md b/UI/Backgrounds/README.md new file mode 100644 index 00000000..227ee52e --- /dev/null +++ b/UI/Backgrounds/README.md @@ -0,0 +1,519 @@ +# Dynamic Backgrounds + +This directory contains all animated backgrounds for the moonlight-xbox app. Each background is an +independent C++/CX XAML UserControl that plugs into `DynamicBackgroundHost`. + +Each background lives in its own subfolder: + +``` +UI\Backgrounds\ + Particles\ ParticleBackground + ParticleSettingsControl + Streaks\ StreaksBackground + StreaksSettingsControl + Spheres\ SpheresBackground + SpheresSettingsControl + Blobs\ BlobsBackground + BlobsSettingsControl + Orbs\ OrbsBackground + OrbsSettingsControl + SwipeReveal\ SwipeRevealBackground + GlobeGrid\ GlobeGridBackground +``` + +--- + +## Architecture + +`DynamicBackgroundHost` is a `UserControl` that manages two `ContentPresenter` slots: + +- **`BackgroundPresenter`** — the currently visible, stable background. +- **`FadePresenter`** — the incoming background, crossfaded in over 300ms, then promoted to stable. + +On load (and when `Refresh()` is called) the host reads +`ApplicationData::Current->LocalSettings->Values["background"]`, calls `CreateBackground(key)` to +instantiate the right class, and fades it in. It routes `StartAnimations()` / `StopAnimations()` to +the active child via `TryStartAnimations` / `TryStopAnimations` — manual `dynamic_cast` chains +because there is **no base class or interface**. + +Each background exposes exactly two required public methods: + +```cpp +void StartAnimations(); +void StopAnimations(); +``` + +Optional public methods (only implement if needed): + +```cpp +void ReloadOptions(); // apply updated LocalSettings without recreating canvas +void SetHosts(IVector^ hosts); // only if background renders app art +``` + +### Per-background settings controls + +Backgrounds that expose user-configurable settings (colors, presets, shape, etc.) each own a +companion **settings UserControl** in the same subfolder — e.g. `Orbs\OrbsSettingsControl.xaml`. +`HostSettingsPage` instantiates the right one dynamically via `UpdateBackgroundSettingsContent(key)` +and places it in a `ContentControl`. No changes to `HostSettingsPage` are needed when adding a new +background with settings. + +--- + +## Adding a New Background — Step-by-Step + +### 1. Register the key + +Open `BackgroundRegistry.h` and add one entry to the `kBackgrounds` array: + +```cpp +{ L"mykey", L"My Display Name" }, +``` + +`kBackgroundCount` is computed automatically via `sizeof` — do not touch it. + +--- + +### 2. Create a subfolder and the three source files + +``` +UI\Backgrounds\Foo\FooBackground.xaml +UI\Backgrounds\Foo\FooBackground.xaml.h +UI\Backgrounds\Foo\FooBackground.xaml.cpp +``` + +See the [canonical templates](#canonical-templates) below. + +--- + +### 3. Wire into DynamicBackgroundHost.xaml.cpp + +Add the `#include` at the top: + +```cpp +#include "UI\Backgrounds\Foo\FooBackground.xaml.h" +``` + +Add one branch to each of the three static functions: + +```cpp +// TryStartAnimations +if (auto f = dynamic_cast(el)) { f->StartAnimations(); return; } + +// TryStopAnimations +if (auto f = dynamic_cast(el)) { f->StopAnimations(); return; } + +// CreateBackground +if (key->Equals(ref new String(L"mykey"))) return ref new FooBackground(); +``` + +--- + +### 4. Register in moonlight-xbox-dx.vcxproj + +Add three XML entries (copy the pattern from any existing background): + +```xml + + + UI\Backgrounds\Foo\FooBackground.xaml + + + + + + + + UI\Backgrounds\Foo\FooBackground.xaml + +``` + +--- + +### 5. Register in moonlight-xbox-dx.vcxproj.filters + +Only the `Page` item needs an explicit entry (the `.h` and `.cpp` are pulled in via `DependentUpon`): + +```xml + +``` + +--- + +### 6. (Optional) SetHosts — only if your background renders app art + +If your background needs the list of `MoonlightHost^` objects (like `SwipeRevealBackground`), add +to **both** `Refresh()` and `SetHosts()` in `DynamicBackgroundHost.xaml.cpp`: + +```cpp +if (auto f = dynamic_cast(newBg)) { f->SetHosts(m_hosts); } +``` + +And declare the method on the class: + +```cpp +void SetHosts(Windows::Foundation::Collections::IVector^ hosts); +``` + +--- + +### 7. (Optional) Custom options — only if your background exposes user-configurable settings + +If users can adjust settings (colors, speed, density, presets, etc.), create a companion +**settings UserControl** in the same subfolder. `HostSettingsPage` picks it up automatically — +no edits to `HostSettingsPage` are required. + +See [Adding Custom Options](#adding-custom-options) for the full pattern. + +--- + +## Canonical Templates + +### FooBackground.xaml + +```xml + + + + + +``` + +### FooBackground.xaml.h + +```cpp +#pragma once +#include "UI\Backgrounds\Foo\FooBackground.g.h" +#include +#include + +namespace moonlight_xbox_dx { + +struct FooState { + float x, y; + float vx, vy; +}; + +public ref class FooBackground sealed { +public: + FooBackground(); + void StartAnimations(); + void StopAnimations(); +private: + Windows::UI::Xaml::DispatcherTimer^ m_timer; + Windows::Foundation::EventRegistrationToken m_tickToken; + std::vector m_items; + std::mt19937 m_rng; + float m_canvasW = 0.0f; + float m_canvasH = 0.0f; + bool m_initialized = false; + + void Canvas_SizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e); + void OnTick(Platform::Object^ sender, Platform::Object^ args); + void InitItems(); +}; + +} +``` + +### FooBackground.xaml.cpp + +```cpp +#include "pch.h" +#include "UI\Backgrounds\Foo\FooBackground.xaml.h" +#include + +using namespace moonlight_xbox_dx; +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::UI; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Shapes; +using namespace Windows::UI::Xaml::Media; + +FooBackground::FooBackground() +{ + m_rng = std::mt19937(std::random_device{}()); + InitializeComponent(); + + TimeSpan interval; + interval.Duration = 16 * 10000LL; // 16ms ≈ 60fps; use 33*10000LL for 30fps + m_timer = ref new DispatcherTimer(); + m_timer->Interval = interval; + m_tickToken = m_timer->Tick += ref new EventHandler(this, &FooBackground::OnTick); +} + +void FooBackground::Canvas_SizeChanged(Object^ sender, SizeChangedEventArgs^ e) +{ + m_canvasW = static_cast(e->NewSize.Width); + m_canvasH = static_cast(e->NewSize.Height); + if (!m_initialized && m_canvasW > 0 && m_canvasH > 0) { + InitItems(); + m_initialized = true; + } +} + +void FooBackground::InitItems() +{ + m_items.clear(); + FooCanvas->Children->Clear(); + // Create shapes, push to m_items, append to FooCanvas->Children in a fixed order. + // OnTick retrieves children by index — order here must match retrieval order there. +} + +void FooBackground::OnTick(Object^ sender, Object^ args) +{ + if (!m_initialized) return; + int count = static_cast(m_items.size()); + for (int i = 0; i < count; ++i) { + auto& s = m_items[i]; + // update s.x, s.y, etc. + auto el = safe_cast(FooCanvas->Children->GetAt(i)); + Canvas::SetLeft(el, s.x); + Canvas::SetTop(el, s.y); + } +} + +void FooBackground::StartAnimations() { if (m_timer) m_timer->Start(); } +void FooBackground::StopAnimations() { if (m_timer) m_timer->Stop(); } +``` + +--- + +## Key Patterns and Invariants + +### Timer intervals + +| Duration constant | Frame rate | Used by | +|---|---|---| +| `16 * 10000LL` | ~60 fps | Streaks, Spheres, Orbs | +| `33 * 10000LL` | ~30 fps | Particles, GlobeGrid | + +### Canvas children index contract + +`InitItems()` appends children in a **fixed, known order**. `OnTick` retrieves them by index via +`safe_cast`. Never shuffle or conditionally skip insertions. If each item has multiple visual layers +(e.g., glow + core), push all glows first (indices `0..N-1`) then all cores (indices `N..2N-1`). +See `StreaksBackground` for an example using `kGlowBase` / `kCoreBase` constants. + +### SizeChanged / lazy initialization + +Canvas dimensions are unknown at construction time. Always gate `InitItems()`: + +```cpp +if (!m_initialized && m_canvasW > 0 && m_canvasH > 0) { + InitItems(); + m_initialized = true; +} +``` + +### RNG + +```cpp +// Seed in constructor: +m_rng = std::mt19937(std::random_device{}()); + +// Use: +std::uniform_real_distribution dist(minVal, maxVal); +float val = dist(m_rng); +``` + +### WinRT color construction + +```cpp +ColorHelper::FromArgb(alpha, r, g, b); // all uint8_t; A, R, G, B order +``` + +### Color helpers (copy from Particles\ParticleBackground.xaml.cpp if needed) + +```cpp +// Linear interpolate two Colors +static Color LerpRGB(Color a, Color b, float t, uint8_t alpha = 255); + +// Scale (darken) a Color +static Color ScaleRGB(Color c, float s, uint8_t alpha = 255); +``` + +### C++/CX palette array restrictions + +Palette arrays **must be non-const** — `const Windows::UI::Color` fails to compile in C++/CX. +Also do **not** use `ARRAYSIZE` on member arrays; use a literal count instead. + +```cpp +// .h +Windows::UI::Color m_palette[4]; // non-const, literal count + +// .cpp — OK +int n = 4; +``` + +--- + +## Adding Custom Options + +Backgrounds with user-configurable settings (colors, speed, density, presets, etc.) expose them +via a companion **settings UserControl** that lives in the same subfolder: + +``` +UI\Backgrounds\Foo\FooSettingsControl.xaml +UI\Backgrounds\Foo\FooSettingsControl.xaml.h +UI\Backgrounds\Foo\FooSettingsControl.xaml.cpp +``` + +`HostSettingsPage` calls `UpdateBackgroundSettingsContent(key)` whenever the background selection +changes. That function instantiates the right settings control and places it in a `ContentControl` +— **you do not touch `HostSettingsPage` at all**. Just register the new control in +`UpdateBackgroundSettingsContent` (in `HostSettingsPage.xaml.cpp`): + +```cpp +} else if (key->Equals(L"mykey")) { + auto c = ref new FooSettingsControl(); + c->Initialize(BackgroundHost); + ctrl = c; +} +``` + +And add the corresponding `#include` at the top of `HostSettingsPage.xaml.cpp` and +`HostSettingsPage.xaml.h`. + +### Settings control structure + +**FooSettingsControl.xaml** — A `UserControl` with a scheme `ComboBox`, a collapsed `CustomPanel` +with `SwatchPicker` rows, and a Reset button. See `OrbsSettingsControl.xaml` for a minimal 2-color +example and `BlobsSettingsControl.xaml` for a 4-color example. + +**FooSettingsControl.xaml.h** + +```cpp +#pragma once +#include "UI\Backgrounds\Foo\FooSettingsControl.g.h" +#include "UI\Backgrounds\DynamicBackgroundHost.xaml.h" +#include "UI\Controls\SwatchPicker.xaml.h" + +namespace moonlight_xbox_dx { + +public ref class FooSettingsControl sealed { +public: + FooSettingsControl(); + void Initialize(DynamicBackgroundHost^ host); +private: + DynamicBackgroundHost^ m_host = nullptr; + bool m_initialized = false; + void SchemeSelector_SelectionChanged(Platform::Object^, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^); + void Color0_ColorChanged(Platform::Object^, Windows::UI::Color, bool); + void ResetButton_Click(Platform::Object^, Windows::UI::Xaml::RoutedEventArgs^); + void UpdateCustomPanelVisibility(); +}; + +} +``` + +**FooSettingsControl.xaml.cpp** + +```cpp +#include "pch.h" +#include "FooSettingsControl.xaml.h" +#include "UI\Backgrounds\BackgroundSettingsHelpers.h" + +using namespace moonlight_xbox_dx; +using namespace Platform; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; + +// Scheme palette arrays — must be non-const (C++/CX restriction) +static Windows::UI::Color kDefaultScheme[] = { {255, 30,160,255}, {255, 0, 8, 20} }; +// ... additional schemes ... + +static const wchar_t* kCustomKeys[] = { L"mykey.custom.0", L"mykey.custom.1" }; + +FooSettingsControl::FooSettingsControl() { InitializeComponent(); } + +void FooSettingsControl::Initialize(DynamicBackgroundHost^ host) +{ + m_host = host; + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + + // Populate scheme ComboBox, restore saved index + // Set up SwatchPicker swatches and call SelectColor(savedColor, false) + // Wire ColorChanged events + + m_initialized = true; +} + +void FooSettingsControl::SchemeSelector_SelectionChanged(Platform::Object^, SelectionChangedEventArgs^) +{ + if (!m_initialized) return; + // Save scheme key to LocalSettings, update pickers, call UpdateCustomPanelVisibility() + try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} +} + +void FooSettingsControl::Color0_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert(ref new Platform::String(kCustomKeys[0]), BgColorToHex(color)); + try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} +} + +void FooSettingsControl::ResetButton_Click(Platform::Object^, RoutedEventArgs^) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Remove("mykey.scheme"); + for (int i = 0; i < 2; ++i) ls->Remove(ref new Platform::String(kCustomKeys[i])); + SchemeSelector->SelectedIndex = 0; + // Reset pickers to default scheme colors + try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} +} +``` + +### Init guard pattern + +Gate every `SelectionChanged` / `ColorChanged` handler with `if (!m_initialized) return;` to +prevent spurious LocalSettings writes while controls are being programmatically initialized. + +### Color hex helpers + +Use the shared utilities from `BackgroundSettingsHelpers.h` (already included above): + +```cpp +Platform::String^ BgColorToHex(Windows::UI::Color c); // Color → "RRGGBB" +Windows::UI::Color BgHexToColor(Platform::String^ s, Windows::UI::Color fallback); +``` + +### Reloading colors in the background + +When a setting changes, call `m_host->ReloadBackgroundColors()`. This dispatches to the active +background's `ReloadOptions()` (or equivalent) without recreating the canvas. + +> **Important:** Never call `Refresh()` to apply option changes. `Refresh()` is a no-op when the +> background key is unchanged. Always go through the dedicated reload path. + +--- + +## Files Modified for Every New Background + +| File | What to add | +|---|---| +| `UI\Backgrounds\BackgroundRegistry.h` | New `{ L"key", L"Display Name" }` entry | +| `UI\Backgrounds\DynamicBackgroundHost.xaml.cpp` | `#include` + branches in `TryStartAnimations`, `TryStopAnimations`, `CreateBackground` | +| `UI\Backgrounds\Foo\FooBackground.xaml` | New file | +| `UI\Backgrounds\Foo\FooBackground.xaml.h` | New file | +| `UI\Backgrounds\Foo\FooBackground.xaml.cpp` | New file | +| `moonlight-xbox-dx.vcxproj` | `ClInclude` + `Page` + `ClCompile` entries (subfolder paths) | +| `moonlight-xbox-dx.vcxproj.filters` | `Page` entry (subfolder path) | + +## Additional Files for Backgrounds with Custom Settings + +| File | What to add | +|---|---| +| `UI\Backgrounds\Foo\FooSettingsControl.xaml` | New file | +| `UI\Backgrounds\Foo\FooSettingsControl.xaml.h` | New file | +| `UI\Backgrounds\Foo\FooSettingsControl.xaml.cpp` | New file | +| `UI\Pages\HostSettingsPage.xaml.h` | `#include` for `FooSettingsControl.xaml.h` | +| `UI\Pages\HostSettingsPage.xaml.cpp` | `#include` + new `else if` branch in `UpdateBackgroundSettingsContent()` | +| `moonlight-xbox-dx.vcxproj` | `ClInclude` + `Page` + `ClCompile` entries for the settings control | +| `moonlight-xbox-dx.vcxproj.filters` | `Page` entry for the settings control | diff --git a/UI/Backgrounds/Spheres/SpheresBackground.xaml b/UI/Backgrounds/Spheres/SpheresBackground.xaml new file mode 100644 index 00000000..0c41050d --- /dev/null +++ b/UI/Backgrounds/Spheres/SpheresBackground.xaml @@ -0,0 +1,19 @@ + + + + + + + + + + diff --git a/UI/Backgrounds/Spheres/SpheresBackground.xaml.cpp b/UI/Backgrounds/Spheres/SpheresBackground.xaml.cpp new file mode 100644 index 00000000..e99cc602 --- /dev/null +++ b/UI/Backgrounds/Spheres/SpheresBackground.xaml.cpp @@ -0,0 +1,306 @@ +#include "pch.h" +#include "UI\Backgrounds\Spheres\SpheresBackground.xaml.h" +#include + +using namespace moonlight_xbox_dx; + +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::UI; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Shapes; +using namespace Windows::UI::Xaml::Media; + +static const int kSphereCount = 14; + +static Color kSpheresClassicScheme[3] = { {255,255,255,255}, {255, 0, 68,170}, {255, 0, 34,119} }; +static Color kSpheresNeonScheme[3] = { {255, 0,238,255}, {255, 0, 32,128}, {255, 0, 8, 48} }; +static Color kSpheresSunsetScheme[3] = { {255,255,112, 64}, {255,139, 21, 0}, {255, 32, 0, 8} }; +static Color kSpheresOceanScheme[3] = { {255, 0,200,176}, {255, 0, 64, 96}, {255, 0, 16, 32} }; +static Color kSpheresNebulaScheme[3] = { {255,192, 96,255}, {255, 64, 0,128}, {255, 13, 0, 32} }; + +static void SetSphereCanvasBg(Canvas^ canvas, Windows::UI::Color top, Windows::UI::Color bottom) +{ + auto lgb = ref new LinearGradientBrush(); + lgb->StartPoint = Point(0.5, 0.0); + lgb->EndPoint = Point(0.5, 1.1); + auto gs0 = ref new GradientStop(); + gs0->Color = ColorHelper::FromArgb(255, top.R, top.G, top.B); + gs0->Offset = 0.0; + auto gs1 = ref new GradientStop(); + gs1->Color = ColorHelper::FromArgb(255, bottom.R, bottom.G, bottom.B); + gs1->Offset = 1.0; + lgb->GradientStops->Append(gs0); + lgb->GradientStops->Append(gs1); + canvas->Background = lgb; +} + +static bool ParseHex6Spheres(Platform::String^ s, Color& out) +{ + if (s == nullptr || s->Length() != 6) return false; + const wchar_t* p = s->Data(); + wchar_t buf[7]; wcsncpy_s(buf, p, 6); buf[6] = L'\0'; + wchar_t* end = nullptr; + unsigned long v = wcstoul(buf, &end, 16); + if (end != buf + 6) return false; + out.A = 255; + out.R = (uint8_t)((v >> 16) & 0xFF); + out.G = (uint8_t)((v >> 8) & 0xFF); + out.B = (uint8_t)( v & 0xFF); + return true; +} + +SpheresBackground::SpheresBackground() +{ + m_rng = std::mt19937(std::random_device{}()); + InitializeComponent(); + + TimeSpan interval; + interval.Duration = 16 * 10000LL; + m_timer = ref new DispatcherTimer(); + m_timer->Interval = interval; + Platform::WeakReference weakSelf(this); + m_tickToken = m_timer->Tick += ref new EventHandler( + [weakSelf](Object^, Object^) { + try { + auto self = weakSelf.Resolve(); + if (self) self->OnTick(nullptr, nullptr); + } catch (Platform::DisconnectedException^) {} + catch (...) {} + }); +} + +void SpheresBackground::LoadPalette() +{ + using namespace Windows::Storage; + Platform::String^ scheme = L"classic"; + auto ls = ApplicationData::Current->LocalSettings->Values; + if (ls->HasKey("spheres.scheme")) + scheme = safe_cast(ls->Lookup("spheres.scheme")); + + Color* src = kSpheresClassicScheme; + if (scheme->Equals(L"neon")) src = kSpheresNeonScheme; + else if (scheme->Equals(L"sunset")) src = kSpheresSunsetScheme; + else if (scheme->Equals(L"ocean")) src = kSpheresOceanScheme; + else if (scheme->Equals(L"nebula")) src = kSpheresNebulaScheme; + + if (!scheme->Equals(L"custom")) { + m_palette[0] = src[0]; + m_palette[1] = src[1]; + m_palette[2] = src[2]; + return; + } + + static const wchar_t* kCustomKeys[] = { L"spheres.custom.0", L"spheres.custom.1" }; + for (int i = 0; i < 2; ++i) { + Color c = kSpheresClassicScheme[i]; + auto key = ref new Platform::String(kCustomKeys[i]); + if (ls->HasKey(key)) + ParseHex6Spheres(safe_cast(ls->Lookup(key)), c); + m_palette[i] = c; + } + Color top = m_palette[1]; + m_palette[2] = ColorHelper::FromArgb(255, + static_cast(top.R * 0.40f), + static_cast(top.G * 0.40f), + static_cast(top.B * 0.40f)); +} + +void SpheresBackground::LoadShapeMode() +{ + using namespace Windows::Storage; + auto ls = ApplicationData::Current->LocalSettings->Values; + if (!ls->HasKey("spheres.shape")) { m_shapeMode = 0; return; } + auto val = safe_cast(ls->Lookup("spheres.shape")); + if (val->Equals(L"squares")) m_shapeMode = 1; + else if (val->Equals(L"triangles")) m_shapeMode = 2; + else if (val->Equals(L"all")) m_shapeMode = 3; + else m_shapeMode = 0; +} + +void SpheresBackground::Canvas_SizeChanged(Object^ sender, SizeChangedEventArgs^ e) +{ + m_canvasW = static_cast(e->NewSize.Width); + m_canvasH = static_cast(e->NewSize.Height); + + if (!m_initialized && m_canvasW > 0 && m_canvasH > 0) { + InitSpheres(); + m_initialized = true; + } +} + +static UIElement^ MakeBubbleShape(int shapeType, float r, + Windows::UI::Color shapeColor, float opacity) +{ + auto xf = ref new RotateTransform(); + xf->CenterX = static_cast(r); + xf->CenterY = static_cast(r); + + if (shapeType == kShapeSquare) { + auto rect = ref new Rectangle(); + rect->Width = r * 2.0f; + rect->Height = r * 2.0f; + rect->Stroke = ref new SolidColorBrush( + ColorHelper::FromArgb(255, shapeColor.R, shapeColor.G, shapeColor.B)); + rect->StrokeThickness = 1.5; + rect->Opacity = opacity; + rect->RenderTransform = xf; + return rect; + } + + if (shapeType == kShapeTriangle) { + + auto tri = ref new Polygon(); + auto pts = ref new PointCollection(); + pts->Append(Point(r, 0.0f )); + pts->Append(Point(r + r * 0.866f, r + r * 0.5f )); + pts->Append(Point(r - r * 0.866f, r + r * 0.5f )); + tri->Points = pts; + tri->Stroke = ref new SolidColorBrush( + ColorHelper::FromArgb(255, shapeColor.R, shapeColor.G, shapeColor.B)); + tri->StrokeThickness = 1.5; + tri->Opacity = opacity; + tri->RenderTransform = xf; + return tri; + } + + auto el = ref new Ellipse(); + el->Width = r * 2.0f; + el->Height = r * 2.0f; + el->Stroke = ref new SolidColorBrush( + ColorHelper::FromArgb(255, shapeColor.R, shapeColor.G, shapeColor.B)); + el->StrokeThickness = 1.0; + el->Opacity = opacity; + el->RenderTransform = xf; + return el; +} + +void SpheresBackground::InitSpheres() +{ + LoadPalette(); + LoadShapeMode(); + + m_spheres.clear(); + m_spheres.reserve(kSphereCount); + SphereCanvas->Children->Clear(); + SetSphereCanvasBg(SphereCanvas, m_palette[1], m_palette[2]); + + std::uniform_real_distribution distAngle(0.0f, 6.28318f); + std::uniform_real_distribution distOpacity(0.05f, 0.25f); + + for (int i = 0; i < kSphereCount; ++i) { + SphereState s; + float minSpeed, maxSpeed, minSpin, maxSpin; + + if (i < 3) { + s.radius = 110.0f + static_cast(m_rng() % 70); + minSpeed = 0.15f; maxSpeed = 0.45f; + minSpin = 0.2f; maxSpin = 0.6f; + } else if (i < 8) { + s.radius = 50.0f + static_cast(m_rng() % 50); + minSpeed = 0.40f; maxSpeed = 1.0f; + minSpin = 0.5f; maxSpin = 1.2f; + } else { + s.radius = 18.0f + static_cast(m_rng() % 28); + minSpeed = 0.75f; maxSpeed = 1.6f; + minSpin = 1.0f; maxSpin = 2.5f; + } + + std::uniform_real_distribution distX(s.radius, m_canvasW - s.radius); + std::uniform_real_distribution distY(s.radius, m_canvasH - s.radius); + std::uniform_real_distribution distSpeed(minSpeed, maxSpeed); + std::uniform_real_distribution distSpin(minSpin, maxSpin); + + s.x = distX(m_rng); + s.y = distY(m_rng); + s.opacity = distOpacity(m_rng); + s.spinAngle = distAngle(m_rng) * (180.0f / 3.14159265f); + float spinMag = distSpin(m_rng); + s.spinSpeed = (m_rng() % 2 == 0) ? spinMag : -spinMag; + + float moveAngle = distAngle(m_rng); + float speed = distSpeed(m_rng); + s.vx = cosf(moveAngle) * speed; + s.vy = sinf(moveAngle) * speed; + + s.shapeType = (m_shapeMode == 3) ? (i % 3) : m_shapeMode; + + m_spheres.push_back(s); + + auto el = MakeBubbleShape(s.shapeType, s.radius, m_palette[0], s.opacity); + auto rt = dynamic_cast(el->RenderTransform); + if (rt != nullptr) rt->Angle = static_cast(s.spinAngle); + Canvas::SetLeft(el, s.x - s.radius); + Canvas::SetTop(el, s.y - s.radius); + SphereCanvas->Children->Append(el); + } +} + +void SpheresBackground::OnTick(Object^ sender, Object^ args) +{ + if (!m_initialized) return; + + int count = static_cast(m_spheres.size()); + for (int i = 0; i < count; ++i) { + auto& s = m_spheres[i]; + + s.x += s.vx; + s.y += s.vy; + + if (s.x - s.radius < 0.0f) { s.x = s.radius; s.vx = fabsf(s.vx); } + else if (s.x + s.radius > m_canvasW) { s.x = m_canvasW - s.radius; s.vx = -fabsf(s.vx); } + if (s.y - s.radius < 0.0f) { s.y = s.radius; s.vy = fabsf(s.vy); } + else if (s.y + s.radius > m_canvasH) { s.y = m_canvasH - s.radius; s.vy = -fabsf(s.vy); } + + s.spinAngle += s.spinSpeed; + + auto el = SphereCanvas->Children->GetAt(i); + Canvas::SetLeft(el, s.x - s.radius); + Canvas::SetTop(el, s.y - s.radius); + auto rt = dynamic_cast(el->RenderTransform); + if (rt != nullptr) rt->Angle = static_cast(s.spinAngle); + } +} + +void SpheresBackground::ReloadColors() +{ + if (!m_initialized) return; + + int prevShapeMode = m_shapeMode; + LoadShapeMode(); + + if (m_shapeMode != prevShapeMode) { + InitSpheres(); + return; + } + + LoadPalette(); + + SetSphereCanvasBg(SphereCanvas, m_palette[1], m_palette[2]); + + Color shapeCol = m_palette[0]; + auto brush = ref new SolidColorBrush( + ColorHelper::FromArgb(255, shapeCol.R, shapeCol.G, shapeCol.B)); + + int count = static_cast(m_spheres.size()); + for (int i = 0; i < count; ++i) { + auto el = SphereCanvas->Children->GetAt(i); + if (auto shape = dynamic_cast(el)) { + shape->Stroke = brush; + } + } +} + +void SpheresBackground::StartAnimations() +{ + if (m_timer != nullptr) m_timer->Start(); +} + +void SpheresBackground::StopAnimations() +{ + if (m_timer != nullptr) { + m_timer->Stop(); + m_timer->Tick -= m_tickToken; + } +} diff --git a/UI/Backgrounds/Spheres/SpheresBackground.xaml.h b/UI/Backgrounds/Spheres/SpheresBackground.xaml.h new file mode 100644 index 00000000..3d267e68 --- /dev/null +++ b/UI/Backgrounds/Spheres/SpheresBackground.xaml.h @@ -0,0 +1,46 @@ +#pragma once +#include "UI\Backgrounds\Spheres\SpheresBackground.g.h" +#include +#include + +namespace moonlight_xbox_dx { + +static const int kShapeCircle = 0; +static const int kShapeSquare = 1; +static const int kShapeTriangle = 2; + +struct SphereState { + float x, y; + float vx, vy; + float radius; + float opacity; + float spinAngle; + float spinSpeed; + int shapeType; +}; + +public ref class SpheresBackground sealed { +public: + SpheresBackground(); + void StartAnimations(); + void StopAnimations(); + void ReloadColors(); +private: + Windows::UI::Xaml::DispatcherTimer^ m_timer; + Windows::Foundation::EventRegistrationToken m_tickToken; + std::vector m_spheres; + float m_canvasW = 0; + float m_canvasH = 0; + bool m_initialized = false; + std::mt19937 m_rng; + Windows::UI::Color m_palette[3]; + int m_shapeMode = 0; + + void Canvas_SizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e); + void OnTick(Platform::Object^ sender, Platform::Object^ args); + void InitSpheres(); + void LoadPalette(); + void LoadShapeMode(); +}; + +} diff --git a/UI/Backgrounds/Spheres/SpheresSettingsControl.xaml b/UI/Backgrounds/Spheres/SpheresSettingsControl.xaml new file mode 100644 index 00000000..d55ecae7 --- /dev/null +++ b/UI/Backgrounds/Spheres/SpheresSettingsControl.xaml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Backgrounds/Spheres/SpheresSettingsControl.xaml.cpp b/UI/Backgrounds/Spheres/SpheresSettingsControl.xaml.cpp new file mode 100644 index 00000000..b90c0788 --- /dev/null +++ b/UI/Backgrounds/Spheres/SpheresSettingsControl.xaml.cpp @@ -0,0 +1,192 @@ +#include "pch.h" +#include "SpheresSettingsControl.xaml.h" +#include "UI\Backgrounds\BackgroundSettingsHelpers.h" + +using namespace moonlight_xbox_dx; +using namespace Platform; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; + +static Windows::UI::Color kClassicUI[] = { {255,255,255,255}, {255, 0, 68,170} }; +static Windows::UI::Color kNeonUI[] = { {255, 0,238,255}, {255, 0, 32,128} }; +static Windows::UI::Color kSunsetUI[] = { {255,255,112, 64}, {255,139, 21, 0} }; +static Windows::UI::Color kOceanUI[] = { {255, 0,200,176}, {255, 0, 64, 96} }; +static Windows::UI::Color kNebulaUI[] = { {255,192, 96,255}, {255, 64, 0,128} }; + +static Windows::UI::Color kShapeSwatches[] = { + {255,255,255,255}, {255, 0,238,255}, {255,255,112, 64}, {255, 0,200,176}, + {255,192, 96,255}, {255,255,224, 0}, {255, 0,255,128}, {255,255, 80,120}, + {255,100,200,255}, {255,220,180,255}, {255,255,200,100}, {255,180,255,180}, +}; +static const int kShapeSwatchCount = 12; + +static Windows::UI::Color kBgSwatches[] = { + {255, 0, 68,170}, {255, 0, 32,128}, {255,139, 21, 0}, {255, 0, 64, 96}, + {255, 64, 0,128}, {255, 0,100, 60}, {255,120, 60, 0}, {255, 80, 20, 80}, +}; +static const int kBgSwatchCount = 8; + +static const wchar_t* kCustomKeys[] = { L"spheres.custom.0", L"spheres.custom.1" }; + +SpheresSettingsControl::SpheresSettingsControl() +{ + InitializeComponent(); +} + +void SpheresSettingsControl::Initialize(DynamicBackgroundHost^ host) +{ + m_host = host; + + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + Platform::String^ scheme = L"classic"; + if (ls->HasKey("spheres.scheme")) + scheme = safe_cast(ls->Lookup("spheres.scheme")); + + struct { const wchar_t* key; const wchar_t* label; } schemes[] = { + { L"classic", L"Classic (Default)" }, + { L"neon", L"Neon" }, + { L"sunset", L"Sunset" }, + { L"ocean", L"Ocean" }, + { L"nebula", L"Nebula" }, + { L"custom", L"Custom" }, + }; + + int selectedIdx = 0; + for (int i = 0; i < 6; ++i) { + auto item = ref new ComboBoxItem(); + item->Content = ref new Platform::String(schemes[i].label); + item->DataContext = ref new Platform::String(schemes[i].key); + SchemeSelector->Items->Append(item); + if (scheme->Equals(ref new Platform::String(schemes[i].key))) selectedIdx = i; + } + SchemeSelector->SelectedIndex = selectedIdx; + UpdateCustomPanelVisibility(); + + struct { const wchar_t* key; const wchar_t* label; } shapes[] = { + { L"circles", L"Circles (Default)" }, + { L"squares", L"Squares" }, + { L"triangles", L"Triangles" }, + { L"all", L"All Shapes" }, + }; + + Platform::String^ savedShape = L"circles"; + if (ls->HasKey("spheres.shape")) + savedShape = safe_cast(ls->Lookup("spheres.shape")); + + int shapeIdx = 0; + for (int i = 0; i < 4; ++i) { + auto item = ref new ComboBoxItem(); + item->Content = ref new Platform::String(shapes[i].label); + item->DataContext = ref new Platform::String(shapes[i].key); + ShapeSelector->Items->Append(item); + if (savedShape->Equals(ref new Platform::String(shapes[i].key))) shapeIdx = i; + } + ShapeSelector->SelectedIndex = shapeIdx; + + Windows::UI::Color* defaults = kClassicUI; + if (scheme->Equals(L"neon")) defaults = kNeonUI; + else if (scheme->Equals(L"sunset")) defaults = kSunsetUI; + else if (scheme->Equals(L"ocean")) defaults = kOceanUI; + else if (scheme->Equals(L"nebula")) defaults = kNebulaUI; + + if (Color0 != nullptr) { + Color0->SetSwatches(kShapeSwatches, kShapeSwatchCount); + Windows::UI::Color c0 = defaults[0]; + if (scheme->Equals(L"custom")) { + auto k = ref new Platform::String(kCustomKeys[0]); + if (ls->HasKey(k)) c0 = BgHexToColor(safe_cast(ls->Lookup(k)), c0); + } + Color0->SelectColor(c0, false); + Color0->ColorChanged += ref new SwatchColorChangedHandler(this, &SpheresSettingsControl::Color0_ColorChanged); + } + + if (Color1 != nullptr) { + Color1->SetSwatches(kBgSwatches, kBgSwatchCount); + Windows::UI::Color c1 = defaults[1]; + if (scheme->Equals(L"custom")) { + auto k = ref new Platform::String(kCustomKeys[1]); + if (ls->HasKey(k)) c1 = BgHexToColor(safe_cast(ls->Lookup(k)), c1); + } + Color1->SelectColor(c1, false); + Color1->ColorChanged += ref new SwatchColorChangedHandler(this, &SpheresSettingsControl::Color1_ColorChanged); + } + + m_initialized = true; +} + +void SpheresSettingsControl::UpdateCustomPanelVisibility() +{ + if (CustomPanel == nullptr || SchemeSelector == nullptr) return; + auto item = dynamic_cast(SchemeSelector->SelectedItem); + bool isCustom = (item != nullptr && item->DataContext != nullptr && + item->DataContext->ToString()->Equals(L"custom")); + CustomPanel->Visibility = isCustom ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; +} + +void SpheresSettingsControl::SchemeSelector_SelectionChanged(Platform::Object^, SelectionChangedEventArgs^) +{ + if (!m_initialized) return; + auto item = dynamic_cast(SchemeSelector->SelectedItem); + if (item == nullptr) return; + auto key = item->DataContext->ToString(); + + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert("spheres.scheme", key); + + if (!key->Equals(L"custom")) { + Windows::UI::Color* defaults = kClassicUI; + if (key->Equals(L"neon")) defaults = kNeonUI; + else if (key->Equals(L"sunset")) defaults = kSunsetUI; + else if (key->Equals(L"ocean")) defaults = kOceanUI; + else if (key->Equals(L"nebula")) defaults = kNebulaUI; + if (Color0 != nullptr) Color0->SelectColor(defaults[0], false); + if (Color1 != nullptr) Color1->SelectColor(defaults[1], false); + } + + UpdateCustomPanelVisibility(); + try { if (m_host != nullptr) m_host->ReloadBackgroundColors(); } catch (...) {} +} + +void SpheresSettingsControl::ShapeSelector_SelectionChanged(Platform::Object^, SelectionChangedEventArgs^) +{ + if (!m_initialized) return; + auto item = dynamic_cast(ShapeSelector->SelectedItem); + if (item == nullptr) return; + auto key = item->DataContext->ToString(); + + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert("spheres.shape", key); + + try { if (m_host != nullptr) m_host->ReloadBackgroundColors(); } catch (...) {} +} + +void SpheresSettingsControl::Color0_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert(ref new Platform::String(kCustomKeys[0]), BgColorToHex(color)); + try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} +} + +void SpheresSettingsControl::Color1_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert(ref new Platform::String(kCustomKeys[1]), BgColorToHex(color)); + try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} +} + +void SpheresSettingsControl::ResetButton_Click(Platform::Object^, RoutedEventArgs^) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Remove("spheres.scheme"); + ls->Remove("spheres.shape"); + ls->Remove(ref new Platform::String(kCustomKeys[0])); + ls->Remove(ref new Platform::String(kCustomKeys[1])); + + SchemeSelector->SelectedIndex = 0; + ShapeSelector->SelectedIndex = 0; + if (Color0 != nullptr) Color0->SelectColor(kClassicUI[0], false); + if (Color1 != nullptr) Color1->SelectColor(kClassicUI[1], false); + CustomPanel->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + + try { if (m_host != nullptr) m_host->ReloadBackgroundColors(); } catch (...) {} +} diff --git a/UI/Backgrounds/Spheres/SpheresSettingsControl.xaml.h b/UI/Backgrounds/Spheres/SpheresSettingsControl.xaml.h new file mode 100644 index 00000000..3f96188e --- /dev/null +++ b/UI/Backgrounds/Spheres/SpheresSettingsControl.xaml.h @@ -0,0 +1,24 @@ +#pragma once +#include "UI\Backgrounds\Spheres\SpheresSettingsControl.g.h" +#include "UI\Backgrounds\DynamicBackgroundHost.xaml.h" +#include "UI\Controls\SwatchPicker.xaml.h" + +namespace moonlight_xbox_dx { + +public ref class SpheresSettingsControl sealed { +public: + SpheresSettingsControl(); + void Initialize(DynamicBackgroundHost^ host); +private: + DynamicBackgroundHost^ m_host; + bool m_initialized = false; + + void SchemeSelector_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e); + void ShapeSelector_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e); + void Color0_ColorChanged(Platform::Object^ sender, Windows::UI::Color color, bool useSystemAccent); + void Color1_ColorChanged(Platform::Object^ sender, Windows::UI::Color color, bool useSystemAccent); + void ResetButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void UpdateCustomPanelVisibility(); +}; + +} diff --git a/UI/Backgrounds/Streaks/StreaksBackground.xaml b/UI/Backgrounds/Streaks/StreaksBackground.xaml new file mode 100644 index 00000000..39fde889 --- /dev/null +++ b/UI/Backgrounds/Streaks/StreaksBackground.xaml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Backgrounds/Streaks/StreaksBackground.xaml.cpp b/UI/Backgrounds/Streaks/StreaksBackground.xaml.cpp new file mode 100644 index 00000000..7a3eb8db --- /dev/null +++ b/UI/Backgrounds/Streaks/StreaksBackground.xaml.cpp @@ -0,0 +1,280 @@ +#include "pch.h" +#include "UI\Backgrounds\Streaks\StreaksBackground.xaml.h" +#include + +using namespace moonlight_xbox_dx; + +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::UI; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Shapes; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Media::Animation; + +static const float kSqrt2 = 1.41421356f; +static const float kGlowExtend = 5.0f; +static const int kStreakCount = 24; +static const int kGlowBase = 0; +static const int kCoreBase = kStreakCount; + +static const Color kStreakPalette[] = { + { 255, 255, 0, 0 }, + { 255, 0, 60, 255 }, + { 255, 255, 0, 220 }, + { 255, 140, 0, 255 }, + { 255, 0, 220, 255 }, +}; +static const int kStreakColors = 5; + +static bool ParseHex6(Platform::String^ s, Color& out) +{ + if (s == nullptr || s->Length() != 6) return false; + const wchar_t* p = s->Data(); + wchar_t buf[7]; wcsncpy_s(buf, p, 6); buf[6] = L'\0'; + wchar_t* end = nullptr; + unsigned long v = wcstoul(buf, &end, 16); + if (end != buf + 6) return false; + out.A = 255; + out.R = (uint8_t)((v >> 16) & 0xFF); + out.G = (uint8_t)((v >> 8) & 0xFF); + out.B = (uint8_t)( v & 0xFF); + return true; +} + +StreaksBackground::StreaksBackground() +{ + m_rng = std::mt19937(std::random_device{}()); + InitializeComponent(); + this->Loaded += ref new RoutedEventHandler(this, &StreaksBackground::OnLoaded); + StreakCanvas->Background = ref new SolidColorBrush(ColorHelper::FromArgb(255, 3, 3, 15)); + + TimeSpan interval; + interval.Duration = 16 * 10000LL; + m_timer = ref new DispatcherTimer(); + m_timer->Interval = interval; + Platform::WeakReference weakSelf(this); + m_tickToken = m_timer->Tick += ref new EventHandler( + [weakSelf](Object^, Object^) { + try { + auto self = weakSelf.Resolve(); + if (self) self->OnTick(nullptr, nullptr); + } catch (Platform::DisconnectedException^) {} + catch (...) {} + }); +} + +void StreaksBackground::Canvas_SizeChanged(Object^ sender, SizeChangedEventArgs^ e) +{ + m_canvasW = static_cast(e->NewSize.Width); + m_canvasH = static_cast(e->NewSize.Height); + + if (!m_initialized && m_canvasW > 0 && m_canvasH > 0) { + InitStreaks(); + m_initialized = true; + } +} + +static float ExitT(const StreakState& s, float W, float H) +{ + float txW = 2.0f * W + 2.0f * s.halfLen - s.lane; + float txH = 2.0f * H + 2.0f * s.halfLen + s.lane; + return txW > txH ? txW : txH; +} + +static float EntryT(const StreakState& s) +{ + return fabsf(s.lane) - 2.0f * s.halfLen; +} + +static Rectangle^ MakeStreakRect(float rectW, float rectH, Color col, float opacity) +{ + auto rect = ref new Rectangle(); + rect->Width = rectW; + rect->Height = rectH; + rect->RadiusX = rectH * 0.5; + rect->RadiusY = rectH * 0.5; + rect->Fill = ref new SolidColorBrush(ColorHelper::FromArgb(255, col.R, col.G, col.B)); + rect->Opacity = opacity; + + auto xf = ref new RotateTransform(); + xf->CenterX = rectW * 0.5; + xf->CenterY = rectH * 0.5; + xf->Angle = 45.0; + rect->RenderTransform = xf; + + return rect; +} + +void StreaksBackground::LoadPalette() +{ + using namespace Windows::Storage; + Platform::String^ scheme = L"neon"; + auto ls = ApplicationData::Current->LocalSettings->Values; + if (ls->HasKey("streaks.scheme")) + scheme = safe_cast(ls->Lookup("streaks.scheme")); + + Color schemes[5][5] = { + {{255,255,0,0},{255,0,60,255},{255,255,0,220},{255,140,0,255},{255,0,220,255}}, + {{255,0,80,255},{255,0,200,200},{255,0,229,255},{255,0,184,122},{255,26,58,255}}, + {{255,255,106,0},{255,255,45,120},{255,255,26,26},{255,255,194,0},{255,160,32,240}}, + {{255,255,224,0},{255,255,140,0},{255,255,173,0},{255,255,215,0},{255,255,69,0}}, + {{255,224,224,224},{255,255,255,255},{255,191,191,191},{255,160,160,160},{255,207,207,207}}, + }; + + int schemeIdx = -1; + if (scheme->Equals(L"neon")) schemeIdx = 0; + else if (scheme->Equals(L"ocean")) schemeIdx = 1; + else if (scheme->Equals(L"sunset")) schemeIdx = 2; + else if (scheme->Equals(L"warm")) schemeIdx = 3; + else if (scheme->Equals(L"mono")) schemeIdx = 4; + + if (schemeIdx >= 0) { + for (int i = 0; i < kStreakColors; ++i) m_palette[i] = schemes[schemeIdx][i]; + return; + } + + static const wchar_t* kCustomKeys[] = { + L"streaks.custom.0", L"streaks.custom.1", L"streaks.custom.2", + L"streaks.custom.3", L"streaks.custom.4" + }; + for (int i = 0; i < kStreakColors; ++i) { + Color c = kStreakPalette[i]; + auto key = ref new Platform::String(kCustomKeys[i]); + if (ls->HasKey(key)) { + ParseHex6(safe_cast(ls->Lookup(key)), c); + } + m_palette[i] = c; + } +} + +void StreaksBackground::InitStreaks() +{ + LoadPalette(); + m_streaks.clear(); + m_streaks.reserve(kStreakCount); + StreakCanvas->Children->Clear(); + + float W = m_canvasW, H = m_canvasH; + + std::uniform_real_distribution distLane(-H, W); + std::uniform_real_distribution distSpeed(3.0f, 9.5f); + + for (int i = 0; i < kStreakCount; ++i) { + StreakState s; + s.colorIndex = m_rng() % kStreakColors; + s.lane = distLane(m_rng); + s.halfLen = 120.0f + static_cast(m_rng() % 380); + s.speed = distSpeed(m_rng); + s.glowH = 10.0f + static_cast(m_rng() % 22); + s.coreH = 2.0f + static_cast(m_rng() % 3); + + float tMin = EntryT(s); + float tMax = ExitT(s, W, H); + std::uniform_real_distribution distT(tMin, tMax); + s.t = distT(m_rng); + + m_streaks.push_back(s); + } + + for (int i = 0; i < kStreakCount; ++i) { + const auto& s = m_streaks[i]; + Color col = m_palette[s.colorIndex]; + float glowRectW = 2.0f * (s.halfLen + kGlowExtend) * kSqrt2; + float opacity = 0.55f + static_cast(m_rng() % 45) / 100.0f; + + auto glow = MakeStreakRect(glowRectW, s.glowH, col, opacity); + float cx = (s.t + s.lane) * 0.5f; + float cy = (s.t - s.lane) * 0.5f; + Canvas::SetLeft(glow, cx - glowRectW * 0.5f); + Canvas::SetTop(glow, cy - s.glowH * 0.5f); + StreakCanvas->Children->Append(glow); + } + + for (int i = 0; i < kStreakCount; ++i) { + const auto& s = m_streaks[i]; + Color col = kStreakPalette[s.colorIndex]; + float rectW = 2.0f * s.halfLen * kSqrt2; + float opacity = 0.90f + static_cast(m_rng() % 10) / 100.0f; + + auto core = MakeStreakRect(rectW, s.coreH, col, opacity); + float cx = (s.t + s.lane) * 0.5f; + float cy = (s.t - s.lane) * 0.5f; + Canvas::SetLeft(core, cx - rectW * 0.5f); + Canvas::SetTop(core, cy - s.coreH * 0.5f); + StreakCanvas->Children->Append(core); + } + +} + +void StreaksBackground::OnTick(Object^ sender, Object^ args) +{ + if (!m_initialized) return; + + float W = m_canvasW, H = m_canvasH; + int count = static_cast(m_streaks.size()); + + for (int i = 0; i < count; ++i) { + auto& s = m_streaks[i]; + s.t += s.speed; + + if (s.t > ExitT(s, W, H)) { + s.t = EntryT(s) - 50.0f - static_cast(m_rng() % 200); + } + + float cx = (s.t + s.lane) * 0.5f; + float cy = (s.t - s.lane) * 0.5f; + float coreRectW = 2.0f * s.halfLen * kSqrt2; + float glowRectW = 2.0f * (s.halfLen + kGlowExtend) * kSqrt2; + + auto glow = safe_cast(StreakCanvas->Children->GetAt(kGlowBase + i)); + Canvas::SetLeft(glow, cx - glowRectW * 0.5f); + Canvas::SetTop(glow, cy - s.glowH * 0.5f); + + auto core = safe_cast(StreakCanvas->Children->GetAt(kCoreBase + i)); + Canvas::SetLeft(core, cx - coreRectW * 0.5f); + Canvas::SetTop(core, cy - s.coreH * 0.5f); + } +} + +void StreaksBackground::OnLoaded(Object^ sender, RoutedEventArgs^ e) +{ + StreakCanvas->Opacity = 0.0; + auto anim = ref new DoubleAnimation(); + anim->From = 0.0; + anim->To = 1.0; + TimeSpan ts; + ts.Duration = 5000000LL; + anim->Duration = Windows::UI::Xaml::Duration(ts); + auto sb = ref new Storyboard(); + Storyboard::SetTarget(anim, StreakCanvas); + Storyboard::SetTargetProperty(anim, "Opacity"); + sb->Children->Append(anim); + sb->Begin(); +} + +void StreaksBackground::ReloadColors() +{ + if (!m_initialized) return; + LoadPalette(); + for (int i = 0; i < kStreakCount; ++i) { + Color col = m_palette[m_streaks[i].colorIndex]; + auto brush = ref new SolidColorBrush(ColorHelper::FromArgb(255, col.R, col.G, col.B)); + safe_cast(StreakCanvas->Children->GetAt(kGlowBase + i))->Fill = brush; + safe_cast(StreakCanvas->Children->GetAt(kCoreBase + i))->Fill = brush; + } +} + +void StreaksBackground::StartAnimations() +{ + if (m_timer != nullptr) m_timer->Start(); +} + +void StreaksBackground::StopAnimations() +{ + if (m_timer != nullptr) { + m_timer->Stop(); + m_timer->Tick -= m_tickToken; + } +} diff --git a/UI/Backgrounds/Streaks/StreaksBackground.xaml.h b/UI/Backgrounds/Streaks/StreaksBackground.xaml.h new file mode 100644 index 00000000..9d4954ef --- /dev/null +++ b/UI/Backgrounds/Streaks/StreaksBackground.xaml.h @@ -0,0 +1,41 @@ +#pragma once +#include "UI\Backgrounds\Streaks\StreaksBackground.g.h" +#include +#include + +namespace moonlight_xbox_dx { + +struct StreakState { + float t; + float lane; + float halfLen; + float speed; + float glowH; + float coreH; + int colorIndex; +}; + +public ref class StreaksBackground sealed { +public: + StreaksBackground(); + void StartAnimations(); + void StopAnimations(); + void ReloadColors(); +private: + Windows::UI::Xaml::DispatcherTimer^ m_timer; + Windows::Foundation::EventRegistrationToken m_tickToken; + std::vector m_streaks; + Windows::UI::Color m_palette[5]; + float m_canvasW = 0; + float m_canvasH = 0; + bool m_initialized = false; + std::mt19937 m_rng; + + void Canvas_SizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e); + void OnTick(Platform::Object^ sender, Platform::Object^ args); + void OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void InitStreaks(); + void LoadPalette(); +}; + +} diff --git a/UI/Backgrounds/Streaks/StreaksSettingsControl.xaml b/UI/Backgrounds/Streaks/StreaksSettingsControl.xaml new file mode 100644 index 00000000..8cc9caa3 --- /dev/null +++ b/UI/Backgrounds/Streaks/StreaksSettingsControl.xaml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Backgrounds/Streaks/StreaksSettingsControl.xaml.cpp b/UI/Backgrounds/Streaks/StreaksSettingsControl.xaml.cpp new file mode 100644 index 00000000..2b2d52e1 --- /dev/null +++ b/UI/Backgrounds/Streaks/StreaksSettingsControl.xaml.cpp @@ -0,0 +1,151 @@ +#include "pch.h" +#include "StreaksSettingsControl.xaml.h" +#include "UI\Backgrounds\BackgroundSettingsHelpers.h" + +using namespace moonlight_xbox_dx; +using namespace Platform; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; + +static Windows::UI::Color kNeonScheme[] = { {255,255,0,0},{255,0,60,255},{255,255,0,220},{255,140,0,255},{255,0,220,255} }; +static Windows::UI::Color kOceanScheme[] = { {255,0,80,255},{255,0,200,200},{255,0,229,255},{255,0,184,122},{255,26,58,255} }; +static Windows::UI::Color kSunsetScheme[] = { {255,255,106,0},{255,255,45,120},{255,255,26,26},{255,255,194,0},{255,160,32,240} }; +static Windows::UI::Color kWarmScheme[] = { {255,255,224,0},{255,255,140,0},{255,255,173,0},{255,255,215,0},{255,255,69,0} }; +static Windows::UI::Color kMonoScheme[] = { {255,224,224,224},{255,255,255,255},{255,191,191,191},{255,160,160,160},{255,207,207,207} }; + +static Windows::UI::Color kSwatches[] = { + {255,255, 0, 0}, {255,255,106, 0}, {255,255,224, 0}, {255,128,255, 0}, + {255, 0,192, 96}, {255, 0,220,255}, {255, 0, 60,255}, {255,140, 0,255}, + {255,255, 0,220}, {255,255, 45,120}, {255,255,255,255}, {255,128,128,128}, +}; +static const int kSwatchCount = 12; + +static const wchar_t* kCustomKeys[] = { + L"streaks.custom.0", L"streaks.custom.1", L"streaks.custom.2", + L"streaks.custom.3", L"streaks.custom.4" +}; + +StreaksSettingsControl::StreaksSettingsControl() +{ + InitializeComponent(); +} + +void StreaksSettingsControl::Initialize(DynamicBackgroundHost^ host) +{ + m_host = host; + + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + Platform::String^ scheme = L"neon"; + if (ls->HasKey("streaks.scheme")) + scheme = safe_cast(ls->Lookup("streaks.scheme")); + + struct { const wchar_t* key; const wchar_t* label; } schemes[] = { + { L"neon", L"Neon (Default)" }, + { L"ocean", L"Ocean" }, + { L"sunset", L"Sunset" }, + { L"warm", L"Warm" }, + { L"mono", L"Monochrome" }, + { L"custom", L"Custom" }, + }; + + int selectedIdx = 0; + for (int i = 0; i < 6; ++i) { + auto item = ref new ComboBoxItem(); + item->Content = ref new Platform::String(schemes[i].label); + item->DataContext = ref new Platform::String(schemes[i].key); + SchemeSelector->Items->Append(item); + if (scheme->Equals(ref new Platform::String(schemes[i].key))) selectedIdx = i; + } + SchemeSelector->SelectedIndex = selectedIdx; + UpdateCustomPanelVisibility(); + + Windows::UI::Color* defaults = kNeonScheme; + if (scheme->Equals(L"ocean")) defaults = kOceanScheme; + else if (scheme->Equals(L"sunset")) defaults = kSunsetScheme; + else if (scheme->Equals(L"warm")) defaults = kWarmScheme; + else if (scheme->Equals(L"mono")) defaults = kMonoScheme; + + SwatchPicker^ pickers[] = { Color0, Color1, Color2, Color3, Color4 }; + for (int i = 0; i < 5; ++i) { + if (pickers[i] == nullptr) continue; + pickers[i]->SetSwatches(kSwatches, kSwatchCount); + Windows::UI::Color c = defaults[i]; + if (scheme->Equals(L"custom")) { + auto k = ref new Platform::String(kCustomKeys[i]); + if (ls->HasKey(k)) c = BgHexToColor(safe_cast(ls->Lookup(k)), c); + } + pickers[i]->SelectColor(c, false); + } + + Color0->ColorChanged += ref new SwatchColorChangedHandler(this, &StreaksSettingsControl::Color0_ColorChanged); + Color1->ColorChanged += ref new SwatchColorChangedHandler(this, &StreaksSettingsControl::Color1_ColorChanged); + Color2->ColorChanged += ref new SwatchColorChangedHandler(this, &StreaksSettingsControl::Color2_ColorChanged); + Color3->ColorChanged += ref new SwatchColorChangedHandler(this, &StreaksSettingsControl::Color3_ColorChanged); + Color4->ColorChanged += ref new SwatchColorChangedHandler(this, &StreaksSettingsControl::Color4_ColorChanged); + + m_initialized = true; +} + +void StreaksSettingsControl::UpdateCustomPanelVisibility() +{ + if (CustomPanel == nullptr || SchemeSelector == nullptr) return; + auto item = dynamic_cast(SchemeSelector->SelectedItem); + bool isCustom = (item != nullptr && item->DataContext != nullptr && + item->DataContext->ToString()->Equals(L"custom")); + CustomPanel->Visibility = isCustom ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; +} + +void StreaksSettingsControl::SchemeSelector_SelectionChanged(Platform::Object^, SelectionChangedEventArgs^) +{ + if (!m_initialized) return; + auto item = dynamic_cast(SchemeSelector->SelectedItem); + if (item == nullptr) return; + auto key = item->DataContext->ToString(); + + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert("streaks.scheme", key); + + if (!key->Equals(L"custom")) { + Windows::UI::Color* defaults = kNeonScheme; + if (key->Equals(L"ocean")) defaults = kOceanScheme; + else if (key->Equals(L"sunset")) defaults = kSunsetScheme; + else if (key->Equals(L"warm")) defaults = kWarmScheme; + else if (key->Equals(L"mono")) defaults = kMonoScheme; + SwatchPicker^ pickers[] = { Color0, Color1, Color2, Color3, Color4 }; + for (int i = 0; i < 5; ++i) { + if (pickers[i] != nullptr) pickers[i]->SelectColor(defaults[i], false); + } + } + + UpdateCustomPanelVisibility(); + try { if (m_host != nullptr) m_host->ReloadBackgroundColors(); } catch (...) {} +} + +static void SaveColor(int slot, Windows::UI::Color color) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Insert(ref new Platform::String(kCustomKeys[slot]), BgColorToHex(color)); +} + +void StreaksSettingsControl::Color0_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) { SaveColor(0, color); try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} } +void StreaksSettingsControl::Color1_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) { SaveColor(1, color); try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} } +void StreaksSettingsControl::Color2_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) { SaveColor(2, color); try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} } +void StreaksSettingsControl::Color3_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) { SaveColor(3, color); try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} } +void StreaksSettingsControl::Color4_ColorChanged(Platform::Object^, Windows::UI::Color color, bool) { SaveColor(4, color); try { if (m_host) m_host->ReloadBackgroundColors(); } catch (...) {} } + +void StreaksSettingsControl::ResetButton_Click(Platform::Object^, RoutedEventArgs^) +{ + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + ls->Remove("streaks.scheme"); + for (int i = 0; i < 5; ++i) + ls->Remove(ref new Platform::String(kCustomKeys[i])); + + SchemeSelector->SelectedIndex = 0; + SwatchPicker^ pickers[] = { Color0, Color1, Color2, Color3, Color4 }; + for (int i = 0; i < 5; ++i) { + if (pickers[i] != nullptr) pickers[i]->SelectColor(kNeonScheme[i], false); + } + CustomPanel->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + + try { if (m_host != nullptr) m_host->ReloadBackgroundColors(); } catch (...) {} +} diff --git a/UI/Backgrounds/Streaks/StreaksSettingsControl.xaml.h b/UI/Backgrounds/Streaks/StreaksSettingsControl.xaml.h new file mode 100644 index 00000000..409d15c6 --- /dev/null +++ b/UI/Backgrounds/Streaks/StreaksSettingsControl.xaml.h @@ -0,0 +1,26 @@ +#pragma once +#include "UI\Backgrounds\Streaks\StreaksSettingsControl.g.h" +#include "UI\Backgrounds\DynamicBackgroundHost.xaml.h" +#include "UI\Controls\SwatchPicker.xaml.h" + +namespace moonlight_xbox_dx { + +public ref class StreaksSettingsControl sealed { +public: + StreaksSettingsControl(); + void Initialize(DynamicBackgroundHost^ host); +private: + DynamicBackgroundHost^ m_host; + bool m_initialized = false; + + void SchemeSelector_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e); + void Color0_ColorChanged(Platform::Object^ sender, Windows::UI::Color color, bool useSystemAccent); + void Color1_ColorChanged(Platform::Object^ sender, Windows::UI::Color color, bool useSystemAccent); + void Color2_ColorChanged(Platform::Object^ sender, Windows::UI::Color color, bool useSystemAccent); + void Color3_ColorChanged(Platform::Object^ sender, Windows::UI::Color color, bool useSystemAccent); + void Color4_ColorChanged(Platform::Object^ sender, Windows::UI::Color color, bool useSystemAccent); + void ResetButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void UpdateCustomPanelVisibility(); +}; + +} diff --git a/UI/Backgrounds/SwipeReveal/SwipeRevealBackground.xaml b/UI/Backgrounds/SwipeReveal/SwipeRevealBackground.xaml new file mode 100644 index 00000000..f14a23c4 --- /dev/null +++ b/UI/Backgrounds/SwipeReveal/SwipeRevealBackground.xaml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Backgrounds/SwipeReveal/SwipeRevealBackground.xaml.cpp b/UI/Backgrounds/SwipeReveal/SwipeRevealBackground.xaml.cpp new file mode 100644 index 00000000..7637756b --- /dev/null +++ b/UI/Backgrounds/SwipeReveal/SwipeRevealBackground.xaml.cpp @@ -0,0 +1,444 @@ +#include "pch.h" +#include "UI\Backgrounds\SwipeReveal\SwipeRevealBackground.xaml.h" +#include +#include +#include +#include + +using namespace moonlight_xbox_dx; +using namespace Platform; +using namespace Platform::Collections; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Shapes; +using namespace concurrency; + +static const int kHoldTicks = 240; +static const int kWipeTicks = 100; +static const float kGlassWidth = 64.0f; +static const float kPanMax = 0.04f; +static const float kSlantH = 180.0f; + +static const float kWipeMargin = kGlassWidth * 0.5f + kSlantH * 0.5f; +static const int kLoadRetry = 500; +static const int kIntroBufferTicks = 30; + +static float EaseInOut(float t) +{ + + return t < 0.5f ? 4.0f * t * t * t + : 1.0f - powf(-2.0f * t + 2.0f, 3.0f) * 0.5f; +} +static const float kPi = 3.14159265f; + +SwipeRevealBackground::SwipeRevealBackground() +{ + InitializeComponent(); + + m_frontPan = ref new CompositeTransform(); + m_frontPan->ScaleX = 1.1; + m_frontPan->ScaleY = 1.1; + m_frontPan->CenterX = 0.5; + m_frontPan->CenterY = 0.5; + + m_frontBrush = ref new ImageBrush(); + m_frontBrush->Stretch = Stretch::UniformToFill; + m_frontBrush->RelativeTransform = m_frontPan; + + m_frontClipRect = ref new RectangleGeometry(); + FrontGrid->Clip = m_frontClipRect; + + m_frontGridSkew = ref new SkewTransform(); + FrontGrid->RenderTransform = m_frontGridSkew; + + m_frontRectInvSkew = ref new SkewTransform(); + + auto frontRect = ref new Rectangle(); + frontRect->Fill = m_frontBrush; + frontRect->RenderTransform = m_frontRectInvSkew; + FrontGrid->Children->Append(frontRect); + + TimeSpan ts; + ts.Duration = 16 * 10000LL; + m_timer = ref new DispatcherTimer(); + m_timer->Interval = ts; + Platform::WeakReference weakSelf(this); + m_tickToken = m_timer->Tick += ref new EventHandler( + [weakSelf](Object^, Object^) { + try { + auto self = weakSelf.Resolve(); + if (self) self->OnTick(nullptr, nullptr); + } catch (Platform::DisconnectedException^) {} + catch (...) {} + }); +} + +void SwipeRevealBackground::SetHosts(IVector^ hosts) +{ + m_hosts = hosts; + m_appsLoaded = false; + m_apps = nullptr; + m_frontAppIdx = -1; + m_backAppIdx = -1; + m_introTick = 0; +} + +void SwipeRevealBackground::Grid_SizeChanged(Object^ sender, SizeChangedEventArgs^ e) +{ + m_canvasW = static_cast(e->NewSize.Width); + m_canvasH = static_cast(e->NewSize.Height); + m_initialized = (m_canvasW > 0 && m_canvasH > 0); + if (m_initialized) { + GlassEdgeSkew->CenterY = m_canvasH * 0.5f; + m_frontGridSkew->CenterY = m_canvasH * 0.5f; + m_frontRectInvSkew->CenterY = m_canvasH * 0.5f; + + auto r = m_frontClipRect->Rect; + m_frontClipRect->Rect = Rect(r.X, 0.0f, r.Width, m_canvasH); + UpdateGlassEdgeSkew(); + } +} + +void SwipeRevealBackground::ZeroFrontClips() +{ + m_frontClipRect->Rect = Rect(0.0f, 0.0f, 0.0f, m_canvasH > 0.0f ? m_canvasH : 1.0f); +} + +void SwipeRevealBackground::UpdateGlassEdgeSkew() +{ + if (m_canvasH <= 0) return; + + float angle = atanf(kSlantH / m_canvasH) * 180.0f / kPi; + float gridAngle = (m_wipeDir > 0) ? -angle : angle; + GlassEdgeSkew->AngleX = gridAngle; + m_frontGridSkew->AngleX = gridAngle; + m_frontRectInvSkew->AngleX = -gridAngle; +} + +void SwipeRevealBackground::ShuffleAndApply(Platform::Collections::Vector^ collected) +{ + std::vector vec; + vec.reserve(collected->Size); + for (auto a : collected) vec.push_back(a); + std::mt19937 rng(std::random_device{}()); + std::shuffle(vec.begin(), vec.end(), rng); + auto shuffled = ref new Platform::Collections::Vector(); + for (auto a : vec) shuffled->Append(a); + m_apps = shuffled; + m_appsLoaded = true; + if (m_introTick >= kIntroBufferTicks) + InitSlidesWithWipe(); + else + InitSlides(); +} + +void SwipeRevealBackground::LoadAppsAsync() +{ + if (m_hosts == nullptr) return; + + { + auto inMemory = ref new Vector(); + for (auto h : m_hosts) + if (h->Paired) + for (auto a : h->Apps) inMemory->Append(a); + if (inMemory->Size > 0) { + ShuffleAndApply(inMemory); + + auto targets = ref new Vector(); + for (auto h : m_hosts) + if (h->Paired && h->Connected) targets->Append(h); + if (targets->Size > 0) + create_task([targets]() { + for (auto h : targets) + try { h->UpdateApps(); } catch (...) {} + }); + return; + } + } + + Platform::WeakReference weakThis(this); + Platform::String^ baseImages = Windows::Storage::ApplicationData::Current->LocalFolder->Path; + baseImages = Platform::String::Concat(baseImages, L"\\images\\"); + + auto hostDirs = std::make_shared>(); + for (auto h : m_hosts) { + if (h->InstanceId != nullptr && !h->InstanceId->IsEmpty()) + hostDirs->push_back(std::wstring(Platform::String::Concat(baseImages, + Platform::String::Concat(h->InstanceId, L"\\"))->Data())); + } + + if (hostDirs->empty()) { + LoadFromNetworkAsync(); + return; + } + + auto imagePaths = std::make_shared>(); + create_task([hostDirs, imagePaths]() { + for (auto& dir : *hostDirs) { + std::wstring search = dir + L"*.png"; + WIN32_FIND_DATAW fd; + HANDLE h = FindFirstFileW(search.c_str(), &fd); + if (h == INVALID_HANDLE_VALUE) continue; + do { + imagePaths->push_back(dir + fd.cFileName); + } while (FindNextFileW(h, &fd)); + FindClose(h); + } + }).then([weakThis, imagePaths]() { + auto dispatcher = Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher; + dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Low, + ref new Windows::UI::Core::DispatchedHandler([weakThis, imagePaths]() { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + + if (!imagePaths->empty()) { + auto fromDisk = ref new Platform::Collections::Vector(); + for (auto& path : *imagePaths) { + auto app = ref new MoonlightApp(); + app->ImagePath = ref new Platform::String(path.c_str()); + fromDisk->Append(app); + } + that->ShuffleAndApply(fromDisk); + + if (that->m_hosts != nullptr) { + auto targets = ref new Platform::Collections::Vector(); + for (auto h : that->m_hosts) + if (h->Paired && h->Connected) targets->Append(h); + if (targets->Size > 0) + create_task([targets]() { + for (auto h : targets) + try { h->UpdateApps(); } catch (...) {} + }); + } + return; + } + + that->LoadFromNetworkAsync(); + })); + }); +} + +void SwipeRevealBackground::LoadFromNetworkAsync() +{ + if (m_hosts == nullptr) return; + auto targets = ref new Vector(); + for (auto h : m_hosts) + if (h->Paired && h->Connected) targets->Append(h); + if (targets->Size == 0) return; + + Platform::WeakReference weakThis(this); + create_task([targets]() { + for (auto h : targets) + try { h->UpdateApps(); } catch (...) {} + }).then([weakThis, targets]() { + auto dispatcher = Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher; + dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Low, + ref new Windows::UI::Core::DispatchedHandler([weakThis, targets]() { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + auto collected = ref new Vector(); + for (auto h : targets) + for (auto a : h->Apps) collected->Append(a); + if (collected->Size > 0) + that->ShuffleAndApply(collected); + })); + }); +} + +int SwipeRevealBackground::FindNextAppWithImage(int startIdx) +{ + if (!m_appsLoaded || m_apps == nullptr || m_apps->Size == 0) return -1; + int sz = static_cast(m_apps->Size); + startIdx = ((startIdx % sz) + sz) % sz; + for (int i = 0; i < sz; i++) { + int idx = (startIdx + i) % sz; + if (m_apps->GetAt(idx)->Image != nullptr) return idx; + } + return -1; +} + +void SwipeRevealBackground::InitPanForLayer(float& px, float& py, float& vx, float& vy) +{ + static const float dirX[] = { 1, -1, 1, -1 }; + static const float dirY[] = { 1, -1, -1, 1 }; + int d = (m_panDirIdx++) % 4; + float sx = dirX[d], sy = dirY[d]; + float speed = 2.0f * kPanMax / static_cast(kHoldTicks + kWipeTicks); + px = -sx * kPanMax; + py = -sy * kPanMax; + vx = sx * speed; + vy = sy * speed; +} + +void SwipeRevealBackground::AdvancePan(float& px, float& py, float vx, float vy, CompositeTransform^ xf) +{ + px += vx; + py += vy; + xf->TranslateX = px; + xf->TranslateY = py; +} + +void SwipeRevealBackground::UpdateDiagonalClip(float swept) +{ + if (m_wipeDir > 0) { + + float clipW = swept + kSlantH * 0.5f; + if (clipW <= 0.0f) + m_frontClipRect->Rect = Rect(0.0f, 0.0f, 0.0f, m_canvasH); + else + m_frontClipRect->Rect = Rect(-kSlantH * 0.5f, 0.0f, clipW, m_canvasH); + } else { + + float clipW = swept + kSlantH * 0.5f; + if (clipW <= 0.0f) + m_frontClipRect->Rect = Rect(m_canvasW, 0.0f, 0.0f, m_canvasH); + else + m_frontClipRect->Rect = Rect(m_canvasW - swept, 0.0f, clipW, m_canvasH); + } +} + +void SwipeRevealBackground::InitSlides() +{ + + m_backAppIdx = FindNextAppWithImage(0); + if (m_backAppIdx < 0) return; + + BackBrush->ImageSource = m_apps->GetAt(m_backAppIdx)->Image; + + m_frontAppIdx = FindNextAppWithImage(m_backAppIdx + 1); + m_frontBrush->ImageSource = (m_frontAppIdx >= 0) + ? m_apps->GetAt(m_frontAppIdx)->Image : nullptr; + + InitPanForLayer(m_backPanX, m_backPanY, m_backVX, m_backVY); + InitPanForLayer(m_frontPanX, m_frontPanY, m_frontVX, m_frontVY); + BackPan->TranslateX = m_backPanX; BackPan->TranslateY = m_backPanY; + m_frontPan->TranslateX = m_frontPanX; m_frontPan->TranslateY = m_frontPanY; + + m_wipeDir = 1; + m_wipeTick = 0; + m_phase = 0; + m_holdTick = 0; + GlassEdge->Opacity = 0.0; + ZeroFrontClips(); + UpdateGlassEdgeSkew(); +} + +void SwipeRevealBackground::InitSlidesWithWipe() +{ + + m_frontAppIdx = FindNextAppWithImage(0); + if (m_frontAppIdx < 0) { InitSlides(); return; } + + BackBrush->ImageSource = nullptr; + m_backAppIdx = -1; + m_frontBrush->ImageSource = m_apps->GetAt(m_frontAppIdx)->Image; + + InitPanForLayer(m_backPanX, m_backPanY, m_backVX, m_backVY); + InitPanForLayer(m_frontPanX, m_frontPanY, m_frontVX, m_frontVY); + BackPan->TranslateX = m_backPanX; BackPan->TranslateY = m_backPanY; + m_frontPan->TranslateX = m_frontPanX; m_frontPan->TranslateY = m_frontPanY; + + m_wipeDir = 1; + m_wipeTick = 0; + m_phase = 1; + m_holdTick = 0; + GlassEdge->Opacity = 1.0; + ZeroFrontClips(); + UpdateGlassEdgeSkew(); +} + +void SwipeRevealBackground::AdvanceSlide() +{ + + BackBrush->ImageSource = m_frontBrush->ImageSource; + BackPan->TranslateX = m_frontPan->TranslateX; + BackPan->TranslateY = m_frontPan->TranslateY; + m_backPanX = m_frontPanX; m_backPanY = m_frontPanY; + m_backVX = m_frontVX; m_backVY = m_frontVY; + m_backAppIdx = m_frontAppIdx; + + m_frontAppIdx = FindNextAppWithImage(m_backAppIdx + 1); + if (m_frontAppIdx >= 0) + m_frontBrush->ImageSource = m_apps->GetAt(m_frontAppIdx)->Image; + + InitPanForLayer(m_frontPanX, m_frontPanY, m_frontVX, m_frontVY); + m_frontPan->TranslateX = m_frontPanX; + m_frontPan->TranslateY = m_frontPanY; + + m_wipeDir = -m_wipeDir; + m_wipeTick = 0; + m_phase = 0; + m_holdTick = 0; + GlassEdge->Opacity = 0.0; + ZeroFrontClips(); + UpdateGlassEdgeSkew(); +} + +void SwipeRevealBackground::OnTick(Object^ sender, Object^ args) +{ + if (!m_initialized) return; + + m_introTick++; + + if (!m_appsLoaded) { + if (++m_loadRetryTick >= kLoadRetry) { + m_loadRetryTick = 0; + LoadAppsAsync(); + } + return; + } + + if (m_frontAppIdx < 0) { + if (++m_imageRetryTick >= 60) { + m_imageRetryTick = 0; + if (m_introTick >= kIntroBufferTicks) + InitSlidesWithWipe(); + else + InitSlides(); + } + return; + } + + AdvancePan(m_backPanX, m_backPanY, m_backVX, m_backVY, BackPan); + + if (m_phase == 0) { + if (++m_holdTick >= kHoldTicks) { + m_phase = 1; + m_wipeTick = 0; + GlassEdge->Opacity = 1.0; + } + } else { + AdvancePan(m_frontPanX, m_frontPanY, m_frontVX, m_frontVY, m_frontPan); + + m_wipeTick++; + float t = std::min(static_cast(m_wipeTick) / static_cast(kWipeTicks), 1.0f); + + float swept = EaseInOut(t) * (m_canvasW + 2.0f * kWipeMargin) - kWipeMargin; + + UpdateDiagonalClip(swept); + + float cx = (m_wipeDir > 0) ? swept : (m_canvasW - swept); + GlassEdgeTranslate->X = cx - kGlassWidth * 0.5f; + + if (m_wipeTick >= kWipeTicks) + AdvanceSlide(); + } +} + +void SwipeRevealBackground::StartAnimations() +{ + LoadAppsAsync(); + if (m_timer) m_timer->Start(); +} + +void SwipeRevealBackground::StopAnimations() +{ + if (m_timer) { + m_timer->Stop(); + m_timer->Tick -= m_tickToken; + } + GlassEdge->Opacity = 0.0; +} diff --git a/UI/Backgrounds/SwipeReveal/SwipeRevealBackground.xaml.h b/UI/Backgrounds/SwipeReveal/SwipeRevealBackground.xaml.h new file mode 100644 index 00000000..17c4c372 --- /dev/null +++ b/UI/Backgrounds/SwipeReveal/SwipeRevealBackground.xaml.h @@ -0,0 +1,64 @@ +#pragma once +#include "UI\Backgrounds\SwipeReveal\SwipeRevealBackground.g.h" +#include "State\MoonlightHost.h" +#include + +namespace moonlight_xbox_dx { + +public ref class SwipeRevealBackground sealed { +public: + SwipeRevealBackground(); + void StartAnimations(); + void StopAnimations(); + void SetHosts(Windows::Foundation::Collections::IVector^ hosts); +private: + Windows::UI::Xaml::DispatcherTimer^ m_timer; + Windows::Foundation::EventRegistrationToken m_tickToken; + Windows::Foundation::Collections::IVector^ m_hosts; + Platform::Collections::Vector^ m_apps; + + Windows::UI::Xaml::Media::ImageBrush^ m_frontBrush; + Windows::UI::Xaml::Media::CompositeTransform^ m_frontPan; + Windows::UI::Xaml::Media::RectangleGeometry^ m_frontClipRect; + Windows::UI::Xaml::Media::SkewTransform^ m_frontGridSkew; + Windows::UI::Xaml::Media::SkewTransform^ m_frontRectInvSkew; + + float m_canvasW = 0.0f; + float m_canvasH = 0.0f; + bool m_initialized = false; + bool m_appsLoaded = false; + + int m_phase = 0; + int m_holdTick = 0; + int m_wipeTick = 0; + int m_wipeDir = 1; + int m_loadRetryTick = 0; + int m_imageRetryTick = 0; + int m_introTick = 0; + int m_backAppIdx = -1; + int m_frontAppIdx = -1; + int m_panDirIdx = 0; + + float m_backPanX = 0.0f, m_backPanY = 0.0f; + float m_backVX = 0.0f, m_backVY = 0.0f; + float m_frontPanX = 0.0f, m_frontPanY = 0.0f; + float m_frontVX = 0.0f, m_frontVY = 0.0f; + + void Grid_SizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e); + void OnTick(Platform::Object^ sender, Platform::Object^ args); + void LoadAppsAsync(); + void LoadFromNetworkAsync(); + void ShuffleAndApply(Platform::Collections::Vector^ apps); + void InitSlides(); + void InitSlidesWithWipe(); + void AdvanceSlide(); + int FindNextAppWithImage(int startIdx); + void InitPanForLayer(float& px, float& py, float& vx, float& vy); + void AdvancePan(float& px, float& py, float vx, float vy, + Windows::UI::Xaml::Media::CompositeTransform^ xf); + void UpdateDiagonalClip(float swept); + void ZeroFrontClips(); + void UpdateGlassEdgeSkew(); +}; + +} diff --git a/UI/Controls/AspectRatioBox.xaml b/UI/Controls/AspectRatioBox.xaml new file mode 100644 index 00000000..72719856 --- /dev/null +++ b/UI/Controls/AspectRatioBox.xaml @@ -0,0 +1,13 @@ + + + + + + \ No newline at end of file diff --git a/UI/Controls/AspectRatioBox.xaml.cpp b/UI/Controls/AspectRatioBox.xaml.cpp new file mode 100644 index 00000000..ba15bce5 --- /dev/null +++ b/UI/Controls/AspectRatioBox.xaml.cpp @@ -0,0 +1,85 @@ +#include "pch.h" +#include "UI\Controls\AspectRatioBox.xaml.h" +#include "Utils.hpp" + +using namespace moonlight_xbox_dx; +using namespace Platform; +using namespace Windows::UI::Xaml; +using namespace Windows::Foundation; +using namespace Windows::UI::Xaml::Controls; + +Windows::UI::Xaml::DependencyProperty^ AspectRatioBox::m_ratioProperty = + Windows::UI::Xaml::DependencyProperty::Register( + "Ratio", + double::typeid, + AspectRatioBox::typeid, + ref new Windows::UI::Xaml::PropertyMetadata(1.0) + ); + +AspectRatioBox::AspectRatioBox() { + InitializeComponent(); +} + +double AspectRatioBox::Ratio::get() { + return (double)this->GetValue(AspectRatioBox::m_ratioProperty); +} + +void AspectRatioBox::Ratio::set(double v) { + this->SetValue(AspectRatioBox::m_ratioProperty, v); +} + +Windows::Foundation::Size AspectRatioBox::MeasureOverride(Windows::Foundation::Size availableSize) { + + double ratio = this->Ratio; + double width = availableSize.Width; + double height = availableSize.Height; + + if (isnan(width) || isnan(height) || (width == std::numeric_limits::infinity() && height == std::numeric_limits::infinity())) { + return Windows::Foundation::Size(100 * ratio, 100); + } + + if (!(height == std::numeric_limits::infinity())) { + width = height * ratio; + } else if (!(width == std::numeric_limits::infinity())) { + + height = width / ratio; + } + + auto content = this->Content; + if (content != nullptr) { + auto fe = dynamic_cast(content); + if (fe != nullptr) { + fe->Measure(Size(width, height)); + } + } + + return Size(width, height); +} + +Windows::Foundation::Size AspectRatioBox::ArrangeOverride(Windows::Foundation::Size finalSize) { + + double ratio = this->Ratio; + + double targetWidth = finalSize.Width; + double targetHeight = finalSize.Height; + + double idealWidth = targetHeight * ratio; + double idealHeight = targetHeight; + if (idealWidth > targetWidth) { + idealWidth = targetWidth; + idealHeight = targetWidth / ratio; + } + + double offsetX = (targetWidth - idealWidth) / 2.0; + double offsetY = (targetHeight - idealHeight) / 2.0; + + auto content = this->Content; + if (content != nullptr) { + auto fe = dynamic_cast(content); + if (fe != nullptr) { + fe->Arrange(Rect(offsetX, offsetY, idealWidth, idealHeight)); + } + } + + return finalSize; +} diff --git a/UI/Controls/AspectRatioBox.xaml.h b/UI/Controls/AspectRatioBox.xaml.h new file mode 100644 index 00000000..ed3692bc --- /dev/null +++ b/UI/Controls/AspectRatioBox.xaml.h @@ -0,0 +1,29 @@ +#pragma once + +#include "UI\Controls\AspectRatioBox.g.h" + +namespace moonlight_xbox_dx { + + [Windows::UI::Xaml::Data::Bindable] + public ref class AspectRatioBox sealed + { + public: + AspectRatioBox(); + + static property Windows::UI::Xaml::DependencyProperty^ RatioProperty { + Windows::UI::Xaml::DependencyProperty^ get() { return m_ratioProperty; } + } + + property double Ratio { + double get(); + void set(double v); + } + + protected: + virtual Windows::Foundation::Size MeasureOverride(Windows::Foundation::Size availableSize) override; + virtual Windows::Foundation::Size ArrangeOverride(Windows::Foundation::Size finalSize) override; + + private: + static Windows::UI::Xaml::DependencyProperty^ m_ratioProperty; + }; +} diff --git a/UI/Controls/LunarPhaseControl.xaml b/UI/Controls/LunarPhaseControl.xaml new file mode 100644 index 00000000..7f342b55 --- /dev/null +++ b/UI/Controls/LunarPhaseControl.xaml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Controls/LunarPhaseControl.xaml.cpp b/UI/Controls/LunarPhaseControl.xaml.cpp new file mode 100644 index 00000000..c2625ee8 --- /dev/null +++ b/UI/Controls/LunarPhaseControl.xaml.cpp @@ -0,0 +1,424 @@ +#include "pch.h" +#include "UI\Controls\LunarPhaseControl.xaml.h" +#define MLOG_TAG_OVERRIDE "LunarPhaseControl" +#include "..\..\Utils.hpp" +#include + +using namespace moonlight_xbox_dx; +using namespace Platform; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Media::Animation; +using namespace Windows::UI::Xaml::Media::Imaging; +using namespace Windows::Foundation; +using namespace Windows::Storage; +using namespace Windows::Storage::Streams; +using namespace concurrency; + +static constexpr long long kAnimationMs = 150; +static constexpr double kShadowTravelPx = 100.0; +static constexpr long long kOrbitLoopMs = 2040; + +static std::atomic s_orbitLoopMs { kOrbitLoopMs }; +static std::once_flag s_orbitDurationFlag; + +static void ReadOrbitGifDurationAsync() +{ + std::call_once(s_orbitDurationFlag, []() { + auto uri = ref new Uri(L"ms-appx:///Assets/orbit_load3.gif"); + create_task(StorageFile::GetFileFromApplicationUriAsync(uri)) + .then([](task fileTask) -> task { + try { + return create_task(FileIO::ReadBufferAsync(fileTask.get())); + } catch (Platform::COMException^ ex) { + MLOGF(Utils::LogLevel::Warning, "failed to open orbit gif hr=0x%08x — using fallback %lldms\n", ex->HResult, kOrbitLoopMs); + return task_from_result(nullptr); + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "failed to open orbit gif (unknown) — using fallback %lldms\n", kOrbitLoopMs); + return task_from_result(nullptr); + } + }) + .then([](IBuffer^ buf) -> long long { + if (buf == nullptr) return kOrbitLoopMs; + + auto reader = DataReader::FromBuffer(buf); + unsigned int len = buf->Length; + auto bytes = ref new Array(len); + reader->ReadBytes(bytes); + + int frameCount = 0; + long long totalMs = 0; + for (unsigned int i = 0; i + 5 < len; i++) { + if (bytes[i] == 0x21 && bytes[i+1] == 0xF9 && bytes[i+2] == 0x04) { + unsigned short cs = bytes[i+4] | (unsigned short)(bytes[i+5] << 8); + totalMs += (long long)cs * 10; + frameCount++; + i += 7; + } + } + + if (totalMs > 0) { + return totalMs; + } + MLOGF(Utils::LogLevel::Warning, "orbit gif parse found no GCE frames (len=%u) — using fallback %lldms\n", len, kOrbitLoopMs); + return kOrbitLoopMs; + }) + .then([](task t) { + try { s_orbitLoopMs = t.get(); } catch (...) {} + }); + }); +} + +Windows::UI::Xaml::DependencyProperty^ LunarPhaseControl::m_showOrbitProperty = + DependencyProperty::Register( + "ShowOrbit", bool::typeid, LunarPhaseControl::typeid, + ref new PropertyMetadata(false, + ref new PropertyChangedCallback(&LunarPhaseControl::OnShowOrbitChanged))); + +Windows::UI::Xaml::DependencyProperty^ LunarPhaseControl::m_showLockProperty = + DependencyProperty::Register( + "ShowLock", Windows::UI::Xaml::Visibility::typeid, LunarPhaseControl::typeid, + ref new PropertyMetadata(Windows::UI::Xaml::Visibility::Collapsed, + ref new PropertyChangedCallback(&LunarPhaseControl::OnShowLockChanged))); + +Windows::UI::Xaml::DependencyProperty^ LunarPhaseControl::m_showDisconnectedProperty = + DependencyProperty::Register( + "ShowDisconnected", Windows::UI::Xaml::Visibility::typeid, LunarPhaseControl::typeid, + ref new PropertyMetadata(Windows::UI::Xaml::Visibility::Collapsed, + ref new PropertyChangedCallback(&LunarPhaseControl::OnShowDisconnectedChanged))); + +Windows::UI::Xaml::DependencyProperty^ LunarPhaseControl::m_shadowCenterXProperty = + DependencyProperty::Register( + "ShadowCenterX", double::typeid, LunarPhaseControl::typeid, + ref new PropertyMetadata(80.0, + ref new PropertyChangedCallback(&LunarPhaseControl::OnShadowCenterXChanged))); + +Windows::UI::Xaml::DependencyProperty^ LunarPhaseControl::m_isDashedProperty = + DependencyProperty::Register( + "IsDashed", bool::typeid, LunarPhaseControl::typeid, + ref new PropertyMetadata(false, + ref new PropertyChangedCallback(&LunarPhaseControl::OnIsDashedChanged))); + +LunarPhaseControl::LunarPhaseControl() +{ + InitializeComponent(); + m_phaseStoryboard = nullptr; + m_selectionStoryboard = nullptr; + m_orbitHideTimer = nullptr; + m_orbitShownTick = 0; + ReadOrbitGifDurationAsync(); + + m_outerArc = ref new ArcSegment(); + m_outerArc->Size = Size(76.0f, 76.0f); + m_outerArc->IsLargeArc = true; + m_outerArc->SweepDirection = SweepDirection::Clockwise; + + m_innerArc = ref new ArcSegment(); + m_innerArc->Size = Size(76.0f, 76.0f); + m_innerArc->IsLargeArc = false; + m_innerArc->SweepDirection = SweepDirection::Counterclockwise; + + m_crescentFigure = ref new PathFigure(); + m_crescentFigure->IsClosed = true; + m_crescentFigure->IsFilled = true; + m_crescentFigure->Segments->Append(m_outerArc); + m_crescentFigure->Segments->Append(m_innerArc); + + auto pathGeo = ref new PathGeometry(); + pathGeo->Figures->Append(m_crescentFigure); + + CrescentPath->Data = pathGeo; + + m_dashedOuterArc = ref new ArcSegment(); + m_dashedOuterArc->Size = Size(76.0f, 76.0f); + m_dashedOuterArc->IsLargeArc = true; + m_dashedOuterArc->SweepDirection = SweepDirection::Clockwise; + + m_dashedInnerArc = ref new ArcSegment(); + m_dashedInnerArc->Size = Size(76.0f, 76.0f); + m_dashedInnerArc->IsLargeArc = false; + m_dashedInnerArc->SweepDirection = SweepDirection::Counterclockwise; + + m_dashedCrescentFigure = ref new PathFigure(); + m_dashedCrescentFigure->IsClosed = true; + m_dashedCrescentFigure->IsFilled = true; + m_dashedCrescentFigure->Segments->Append(m_dashedOuterArc); + m_dashedCrescentFigure->Segments->Append(m_dashedInnerArc); + + auto dashedPathGeo = ref new PathGeometry(); + dashedPathGeo->Figures->Append(m_dashedCrescentFigure); + DashedCrescentPath->Data = dashedPathGeo; + + CrescentPath->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + DashedCrescentPath->Visibility = Windows::UI::Xaml::Visibility::Collapsed; +} + +bool LunarPhaseControl::ShowOrbit::get() +{ + return (bool)GetValue(m_showOrbitProperty); +} +void LunarPhaseControl::ShowOrbit::set(bool v) +{ + SetValue(m_showOrbitProperty, v); +} + +Windows::UI::Xaml::Visibility LunarPhaseControl::ShowLock::get() +{ + return (Windows::UI::Xaml::Visibility)GetValue(m_showLockProperty); +} +void LunarPhaseControl::ShowLock::set(Windows::UI::Xaml::Visibility v) +{ + SetValue(m_showLockProperty, v); +} + +Windows::UI::Xaml::Visibility LunarPhaseControl::ShowDisconnected::get() +{ + return (Windows::UI::Xaml::Visibility)GetValue(m_showDisconnectedProperty); +} +void LunarPhaseControl::ShowDisconnected::set(Windows::UI::Xaml::Visibility v) +{ + SetValue(m_showDisconnectedProperty, v); +} + +double LunarPhaseControl::ShadowCenterX::get() +{ + return (double)GetValue(m_shadowCenterXProperty); +} +void LunarPhaseControl::ShadowCenterX::set(double v) +{ + SetValue(m_shadowCenterXProperty, v); +} + +void LunarPhaseControl::OnShowOrbitChanged( + DependencyObject^ d, DependencyPropertyChangedEventArgs^ e) +{ + auto ctrl = dynamic_cast(d); + if (ctrl == nullptr) return; + bool show = (bool)e->NewValue; + + if (show) { + if (ctrl->m_orbitHideTimer != nullptr) { + try { ctrl->m_orbitHideTimer->Stop(); } catch (...) {} + ctrl->m_orbitHideTimer = nullptr; + } + + ctrl->OrbitGif->Source = ref new BitmapImage( + ref new Uri(L"ms-appx:///Assets/orbit_load3.gif")); + ctrl->OrbitGif->Visibility = Windows::UI::Xaml::Visibility::Visible; + ctrl->m_orbitShownTick = QpcNow(); + return; + } + + static constexpr long long kTimerMarginMs = 20; + long long loopMs = s_orbitLoopMs.load(); + long long rem; + if (ctrl->m_orbitShownTick == 0) { + rem = loopMs - kTimerMarginMs; + } else { + long long elapsed = (long long)QpcToMs(QpcNow() - ctrl->m_orbitShownTick); + if (elapsed < 0) elapsed = 0; + rem = loopMs - (elapsed % loopMs) - kTimerMarginMs; + if (rem <= 50) { + + ctrl->OrbitGif->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + return; + } + } + + if (ctrl->m_orbitHideTimer != nullptr) { + try { ctrl->m_orbitHideTimer->Stop(); } catch (...) {} + } + + auto timer = ref new Windows::UI::Xaml::DispatcherTimer(); + TimeSpan ts; + ts.Duration = rem * 10000LL; + timer->Interval = ts; + + Platform::WeakReference weakCtrl(ctrl); + timer->Tick += ref new Windows::Foundation::EventHandler( + [weakCtrl](Platform::Object^ sender, Platform::Object^) { + auto that = weakCtrl.Resolve(); + try { dynamic_cast(sender)->Stop(); } catch (...) {} + if (that == nullptr) return; + that->m_orbitHideTimer = nullptr; + that->OrbitGif->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + }); + + ctrl->m_orbitHideTimer = timer; + timer->Start(); +} + +void LunarPhaseControl::OnShowLockChanged( + DependencyObject^ d, DependencyPropertyChangedEventArgs^ e) +{ + auto ctrl = dynamic_cast(d); + if (ctrl == nullptr) return; + ctrl->LockIcon->Visibility = (Windows::UI::Xaml::Visibility)e->NewValue; +} + +void LunarPhaseControl::OnShowDisconnectedChanged( + DependencyObject^ d, DependencyPropertyChangedEventArgs^ e) +{ + auto ctrl = dynamic_cast(d); + if (ctrl == nullptr) return; + ctrl->DisconnectedIcon->Visibility = (Windows::UI::Xaml::Visibility)e->NewValue; +} + +void LunarPhaseControl::OnShadowCenterXChanged( + DependencyObject^ d, DependencyPropertyChangedEventArgs^ e) +{ + auto ctrl = dynamic_cast(d); + if (ctrl == nullptr) return; + ctrl->SetCrescentPath((double)e->NewValue); +} + +bool LunarPhaseControl::IsDashed::get() +{ + return (bool)GetValue(m_isDashedProperty); +} +void LunarPhaseControl::IsDashed::set(bool v) +{ + SetValue(m_isDashedProperty, v); +} + +void LunarPhaseControl::OnIsDashedChanged( + DependencyObject^ d, DependencyPropertyChangedEventArgs^ e) +{ + auto ctrl = dynamic_cast(d); + if (ctrl == nullptr) return; + VisualStateManager::GoToState(ctrl, (bool)e->NewValue ? "Dashed" : "Solid", true); +} + +void LunarPhaseControl::SetCrescentPath(double sx) +{ + const double cx = 80.0, cy = 80.0, r = 76.0; + double d = std::abs(sx - cx); + + if (d < 0.5) { + CrescentPath->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + DashedCrescentPath->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + return; + } + + CrescentPath->Visibility = Windows::UI::Xaml::Visibility::Visible; + DashedCrescentPath->Visibility = Windows::UI::Xaml::Visibility::Visible; + + if (d > 2.0 * r - 0.5) d = 2.0 * r - 0.5; + + sx = (sx < cx) ? cx - d : cx + d; + + double ix = (cx + sx) / 2.0; + double h = std::sqrt(r * r - (d / 2.0) * (d / 2.0)); + double iy1 = cy - h; + double iy2 = cy + h; + + bool crescentOnRight = (sx < cx); + + Point topPt((float)ix, (float)iy1); + Point botPt((float)ix, (float)iy2); + SweepDirection outerSweep = crescentOnRight ? SweepDirection::Clockwise : SweepDirection::Counterclockwise; + SweepDirection innerSweep = crescentOnRight ? SweepDirection::Counterclockwise : SweepDirection::Clockwise; + + m_crescentFigure->StartPoint = topPt; + m_outerArc->Point = botPt; + m_outerArc->IsLargeArc = true; + m_outerArc->SweepDirection = outerSweep; + m_innerArc->Point = topPt; + m_innerArc->IsLargeArc = false; + m_innerArc->SweepDirection = innerSweep; + + m_dashedCrescentFigure->StartPoint = topPt; + m_dashedOuterArc->Point = botPt; + m_dashedOuterArc->IsLargeArc = true; + m_dashedOuterArc->SweepDirection = outerSweep; + m_dashedInnerArc->Point = topPt; + m_dashedInnerArc->IsLargeArc = false; + m_dashedInnerArc->SweepDirection = innerSweep; +} + +void LunarPhaseControl::SetSelected(bool selected, bool animated) +{ + VisualStateManager::GoToState(this, selected ? "Selected" : "Normal", animated); + + double targetWidth = selected ? 160.0 : 96.0; + + double fromWidth = this->Width; + + if (m_selectionStoryboard != nullptr) { + try { m_selectionStoryboard->Stop(); } catch (...) {} + m_selectionStoryboard = nullptr; + } + + if (!animated) { + this->Width = targetWidth; + return; + } + + this->Width = fromWidth; + + auto anim = ref new DoubleAnimation(); + anim->From = fromWidth; + anim->To = targetWidth; + + TimeSpan ts; + ts.Duration = 125LL * 10000LL; + anim->Duration = Windows::UI::Xaml::Duration(ts); + anim->EnableDependentAnimation = true; + + auto sb = ref new Storyboard(); + Storyboard::SetTarget(anim, this); + Storyboard::SetTargetProperty(anim, "Width"); + sb->Children->Append(anim); + + m_selectionStoryboard = sb; + sb->Begin(); +} + +void LunarPhaseControl::UpdatePhase(double fillAmount, int side, bool animated) +{ + + double targetX; + if (side == 0) { + targetX = 80.0; + } else if (side > 0) { + targetX = 80.0 - fillAmount * kShadowTravelPx; + } else { + targetX = 80.0 + fillAmount * kShadowTravelPx; + } + + double fromX = ShadowCenterX; + + if (m_phaseStoryboard != nullptr) { + try { m_phaseStoryboard->Stop(); } catch (...) {} + m_phaseStoryboard = nullptr; + } + + if (!animated) { + ShadowCenterX = targetX; + return; + } + + ShadowCenterX = fromX; + + auto anim = ref new DoubleAnimation(); + anim->From = fromX; + anim->To = targetX; + + TimeSpan ts; + ts.Duration = kAnimationMs * 10000LL; + anim->Duration = Windows::UI::Xaml::Duration(ts); + anim->EnableDependentAnimation = true; + + auto ease = ref new CubicEase(); + ease->EasingMode = EasingMode::EaseOut; + anim->EasingFunction = ease; + + auto sb = ref new Storyboard(); + Storyboard::SetTarget(anim, this); + Storyboard::SetTargetProperty(anim, "ShadowCenterX"); + sb->Children->Append(anim); + + m_phaseStoryboard = sb; + sb->Begin(); +} diff --git a/UI/Controls/LunarPhaseControl.xaml.h b/UI/Controls/LunarPhaseControl.xaml.h new file mode 100644 index 00000000..6acaa0cf --- /dev/null +++ b/UI/Controls/LunarPhaseControl.xaml.h @@ -0,0 +1,92 @@ +#pragma once + +#include "UI\Controls\LunarPhaseControl.g.h" + +namespace moonlight_xbox_dx { + + [Windows::Foundation::Metadata::WebHostHidden] + public ref class LunarPhaseControl sealed + { + public: + LunarPhaseControl(); + + static property Windows::UI::Xaml::DependencyProperty^ ShowOrbitProperty { + Windows::UI::Xaml::DependencyProperty^ get() { return m_showOrbitProperty; } + } + property bool ShowOrbit { + bool get(); + void set(bool v); + } + + static property Windows::UI::Xaml::DependencyProperty^ IsDashedProperty { + Windows::UI::Xaml::DependencyProperty^ get() { return m_isDashedProperty; } + } + property bool IsDashed { + bool get(); + void set(bool v); + } + + static property Windows::UI::Xaml::DependencyProperty^ ShowLockProperty { + Windows::UI::Xaml::DependencyProperty^ get() { return m_showLockProperty; } + } + property Windows::UI::Xaml::Visibility ShowLock { + Windows::UI::Xaml::Visibility get(); + void set(Windows::UI::Xaml::Visibility v); + } + + static property Windows::UI::Xaml::DependencyProperty^ ShowDisconnectedProperty { + Windows::UI::Xaml::DependencyProperty^ get() { return m_showDisconnectedProperty; } + } + property Windows::UI::Xaml::Visibility ShowDisconnected { + Windows::UI::Xaml::Visibility get(); + void set(Windows::UI::Xaml::Visibility v); + } + + static property Windows::UI::Xaml::DependencyProperty^ ShadowCenterXProperty { + Windows::UI::Xaml::DependencyProperty^ get() { return m_shadowCenterXProperty; } + } + property double ShadowCenterX { + double get(); + void set(double v); + } + + void UpdatePhase(double fillAmount, int side, bool animated); + void SetSelected(bool selected, bool animated); + + private: + static Windows::UI::Xaml::DependencyProperty^ m_showOrbitProperty; + static Windows::UI::Xaml::DependencyProperty^ m_showLockProperty; + static Windows::UI::Xaml::DependencyProperty^ m_showDisconnectedProperty; + static Windows::UI::Xaml::DependencyProperty^ m_shadowCenterXProperty; + static Windows::UI::Xaml::DependencyProperty^ m_isDashedProperty; + + static void OnShowOrbitChanged( + Windows::UI::Xaml::DependencyObject^ d, + Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ e); + static void OnShowLockChanged( + Windows::UI::Xaml::DependencyObject^ d, + Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ e); + static void OnShowDisconnectedChanged( + Windows::UI::Xaml::DependencyObject^ d, + Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ e); + static void OnShadowCenterXChanged( + Windows::UI::Xaml::DependencyObject^ d, + Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ e); + static void OnIsDashedChanged( + Windows::UI::Xaml::DependencyObject^ d, + Windows::UI::Xaml::DependencyPropertyChangedEventArgs^ e); + + void SetCrescentPath(double shadowCenterX); + + Windows::UI::Xaml::Media::PathFigure^ m_crescentFigure; + Windows::UI::Xaml::Media::ArcSegment^ m_outerArc; + Windows::UI::Xaml::Media::ArcSegment^ m_innerArc; + Windows::UI::Xaml::Media::PathFigure^ m_dashedCrescentFigure; + Windows::UI::Xaml::Media::ArcSegment^ m_dashedOuterArc; + Windows::UI::Xaml::Media::ArcSegment^ m_dashedInnerArc; + Windows::UI::Xaml::Media::Animation::Storyboard^ m_phaseStoryboard; + Windows::UI::Xaml::Media::Animation::Storyboard^ m_selectionStoryboard; + Windows::UI::Xaml::DispatcherTimer^ m_orbitHideTimer; + long long m_orbitShownTick; + }; +} diff --git a/UI/Controls/SlidingMenu.xaml b/UI/Controls/SlidingMenu.xaml new file mode 100644 index 00000000..006d5a4b --- /dev/null +++ b/UI/Controls/SlidingMenu.xaml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Controls/SlidingMenu.xaml.cpp b/UI/Controls/SlidingMenu.xaml.cpp new file mode 100644 index 00000000..252a8c9c --- /dev/null +++ b/UI/Controls/SlidingMenu.xaml.cpp @@ -0,0 +1,134 @@ +#include "pch.h" +#include "SlidingMenu.xaml.h" +#include "Utils.hpp" + +using namespace moonlight_xbox_dx::Controls; +using namespace Windows::UI::Xaml::Media::Animation; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Core; +using namespace concurrency; + +SlidingMenu::SlidingMenu() +{ + InitializeComponent(); + auto app = dynamic_cast(Application::Current); + + if (app != nullptr && app->GlobalMenuItems != nullptr) { + GlobalItems = app->GlobalMenuItems; + } + else { + GlobalItems = ref new Platform::Collections::Vector(); + } + PageItems = ref new Platform::Collections::Vector(); + + try { GlobalItemsList->ItemsSource = GlobalItems; } catch(...) {} + try { PageItemsList->ItemsSource = PageItems; } catch(...) {} +} + +void SlidingMenu::OnApplyTemplate() +{ + __super::OnApplyTemplate(); +} + +bool SlidingMenu::IsOpen::get() { + return m_isOpen; +} + +void SlidingMenu::AddPageItem(moonlight_xbox_dx::MenuItem^ item) { + if (item == nullptr) return; + PageItems->Append(item); +} + +void SlidingMenu::ClearPageItems() { + PageItems->Clear(); +} + +void SlidingMenu::Open() +{ + if (m_isOpen) return; + m_isOpen = true; + try { + + if (this->Dispatcher != nullptr && this->Dispatcher->HasThreadAccess) { + auto asyncOp = this->ShowAsync(); + + try { VisualStateManager::GoToState(this, "Opened", true); } catch(...) {} + concurrency::create_task(asyncOp).then([this](Windows::UI::Xaml::Controls::ContentDialogResult result) { + m_isOpen = false; + try { VisualStateManager::GoToState(this, "Closed", false); } catch(...) {} + }); + } else { + + Platform::WeakReference weakThis(this); + auto disp = this->Dispatcher; + if (disp == nullptr) { + try { auto coreView = Windows::ApplicationModel::Core::CoreApplication::MainView; if (coreView != nullptr && coreView->CoreWindow != nullptr) disp = coreView->CoreWindow->Dispatcher; } catch(...) { disp = nullptr; } + } + if (disp != nullptr) { + disp->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([weakThis]() { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + try { + auto asyncOp = that->ShowAsync(); + try { VisualStateManager::GoToState(that, "Opened", true); } catch(...) {} + concurrency::create_task(asyncOp).then([that](Windows::UI::Xaml::Controls::ContentDialogResult result) { + that->m_isOpen = false; + try { VisualStateManager::GoToState(that, "Closed", false); } catch(...) {} + }); + } catch(...) { + that->m_isOpen = false; + } + })); + } else { + + try { + auto asyncOp = this->ShowAsync(); + try { VisualStateManager::GoToState(this, "Opened", true); } catch(...) {} + concurrency::create_task(asyncOp).then([this](Windows::UI::Xaml::Controls::ContentDialogResult result) { + m_isOpen = false; + try { VisualStateManager::GoToState(this, "Closed", false); } catch(...) {} + }); + } catch(...) { + m_isOpen = false; + } + } + } + } catch(...) { + m_isOpen = false; + } +} + +void SlidingMenu::Close() +{ + if (!m_isOpen) return; + + try { + + try { VisualStateManager::GoToState(this, "Closed", true); } catch(...) {} + this->Hide(); + } catch(...) {} + m_isOpen = false; +} + +void SlidingMenu::OnMenuItemClicked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) +{ + auto btn = safe_cast(sender); + auto item = safe_cast(btn->DataContext); + if (item != nullptr) { + + try { + if (item->ClickAction != nullptr) { + try { item->ClickAction(item, nullptr); } catch(...) {} + } + } catch(...) {} + + try { MenuItemInvoked(this, item); } catch(...) {} + } + + Close(); +} + +void SlidingMenu::Overlay_Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e) +{ + Close(); +} diff --git a/UI/Controls/SlidingMenu.xaml.h b/UI/Controls/SlidingMenu.xaml.h new file mode 100644 index 00000000..61e9595c --- /dev/null +++ b/UI/Controls/SlidingMenu.xaml.h @@ -0,0 +1,31 @@ +#pragma once +#include "UI\Controls\SlidingMenu.g.h" +#include "UI\Models\MenuItem.h" + +namespace moonlight_xbox_dx::Controls +{ + public ref class SlidingMenu sealed + { + public: + SlidingMenu(); + + property Windows::Foundation::Collections::IObservableVector^ GlobalItems; + + property Windows::Foundation::Collections::IObservableVector^ PageItems; + + void AddPageItem(moonlight_xbox_dx::MenuItem^ item); + void ClearPageItems(); + + void Open(); + void Close(); + property bool IsOpen { bool get(); } + event Windows::Foundation::TypedEventHandler^ MenuItemInvoked; + protected: + virtual void OnApplyTemplate() override; + private: + Platform::Object^ m_prevFocusedElement = nullptr; + bool m_isOpen = false; + void OnMenuItemClicked(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void Overlay_Tapped(Platform::Object^ sender, Windows::UI::Xaml::Input::TappedRoutedEventArgs^ e); + }; +} diff --git a/UI/Controls/SwatchPicker.xaml b/UI/Controls/SwatchPicker.xaml new file mode 100644 index 00000000..2c34f5dd --- /dev/null +++ b/UI/Controls/SwatchPicker.xaml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Controls/SwatchPicker.xaml.cpp b/UI/Controls/SwatchPicker.xaml.cpp new file mode 100644 index 00000000..696b5421 --- /dev/null +++ b/UI/Controls/SwatchPicker.xaml.cpp @@ -0,0 +1,274 @@ +#include "pch.h" +#include "UI\Controls\SwatchPicker.xaml.h" + +using namespace moonlight_xbox_dx; +using namespace Platform; +using namespace Platform::Collections; +using namespace Windows::UI; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::ViewManagement; +using namespace Windows::Foundation::Collections; + +static const Windows::UI::Color kDefaultSwatches[] = { + { 255, 255, 185, 0 }, + { 255, 247, 99, 12 }, + { 255, 202, 80, 16 }, + { 255, 232, 17, 35 }, + { 255, 234, 0, 94 }, + { 255, 195, 0, 82 }, + { 255, 135, 100, 184 }, + { 255, 116, 77, 169 }, + { 255, 107, 105, 214 }, + { 255, 0, 120, 215 }, + { 255, 0, 153, 188 }, + { 255, 3, 131, 135 }, + { 255, 73, 130, 5 }, + { 255, 16, 137, 62 }, + { 255, 105, 121, 126 }, +}; +static const int kDefaultSwatchCount = sizeof(kDefaultSwatches) / sizeof(kDefaultSwatches[0]); + +SwatchPicker::SwatchPicker() +{ + InitializeComponent(); + m_entries = ref new Vector(); + SwatchGrid->ItemsSource = m_entries; + BuildDefaultSwatches(); +} + +Windows::UI::Color SwatchPicker::GetSystemAccentColor() +{ + auto uiSettings = ref new UISettings(); + return uiSettings->GetColorValue(UIColorType::Accent); +} + +void SwatchPicker::BuildDefaultSwatches() +{ + m_entries->Clear(); + m_selectedEntry = nullptr; + m_customEntry = nullptr; + + auto sysColor = GetSystemAccentColor(); + auto sysEntry = ref new SwatchEntry(sysColor, true, false); + m_entries->Append(sysEntry); + + for (int i = 0; i < kDefaultSwatchCount; ++i) + m_entries->Append(ref new SwatchEntry(kDefaultSwatches[i], false, false)); + + m_customEntry = ref new SwatchEntry(Windows::UI::Color{ 255, 108, 108, 108 }, false, true); + m_entries->Append(m_customEntry); + + SelectEntry(sysEntry, false); +} + +void SwatchPicker::SelectEntry(SwatchEntry^ entry, bool fireEvent) +{ + if (m_selectedEntry != nullptr) + m_selectedEntry->IsSelected = false; + + m_selectedEntry = entry; + + if (entry != nullptr) { + entry->IsSelected = true; + m_useSystemAccent = entry->IsSystemAccent; + m_selectedColor = entry->DisplayColor; + } + + if (fireEvent && entry != nullptr) + ColorChanged(this, m_selectedColor, m_useSystemAccent); +} + +void SwatchPicker::SwatchGrid_ItemClick(Platform::Object^ sender, ItemClickEventArgs^ e) +{ + auto entry = dynamic_cast(e->ClickedItem); + if (entry == nullptr) return; + + if (entry->IsCustom) + OpenCustomColorPicker(entry); + else + SelectEntry(entry, true); +} + +static void RGBtoHSL(uint8_t r, uint8_t g, uint8_t b, double* outH, double* outS, double* outL) +{ + double rd = r / 255.0, gd = g / 255.0, bd = b / 255.0; + double cmax = rd > gd ? (rd > bd ? rd : bd) : (gd > bd ? gd : bd); + double cmin = rd < gd ? (rd < bd ? rd : bd) : (gd < bd ? gd : bd); + double delta = cmax - cmin; + double l = (cmax + cmin) * 0.5; + double h = 0.0, s = 0.0; + + if (delta > 0.0001) { + double denom = 1.0 - fabs(2.0 * l - 1.0); + s = (denom > 0.0001) ? delta / denom : 0.0; + + if (fabs(cmax - rd) < 0.0001) h = 60.0 * fmod((gd - bd) / delta, 6.0); + else if (fabs(cmax - gd) < 0.0001) h = 60.0 * ((bd - rd) / delta + 2.0); + else h = 60.0 * ((rd - gd) / delta + 4.0); + + if (h < 0.0) h += 360.0; + } + + *outH = h; + *outS = s * 100.0; + *outL = l * 100.0; +} + +static Windows::UI::Color HSLtoRGB(double h, double s, double l) +{ + h = fmod(h, 360.0); + if (h < 0.0) h += 360.0; + + double c = (1.0 - fabs(2.0 * l - 1.0)) * s; + double x = c * (1.0 - fabs(fmod(h / 60.0, 2.0) - 1.0)); + double m = l - c * 0.5; + double rd = 0, gd = 0, bd = 0; + + if (h < 60) { rd = c; gd = x; bd = 0; } + else if (h < 120) { rd = x; gd = c; bd = 0; } + else if (h < 180) { rd = 0; gd = c; bd = x; } + else if (h < 240) { rd = 0; gd = x; bd = c; } + else if (h < 300) { rd = x; gd = 0; bd = c; } + else { rd = c; gd = 0; bd = x; } + + Windows::UI::Color nc; + nc.A = 255; + nc.R = (uint8_t)((rd + m) * 255.0 + 0.5); + nc.G = (uint8_t)((gd + m) * 255.0 + 0.5); + nc.B = (uint8_t)((bd + m) * 255.0 + 0.5); + return nc; +} + +void SwatchPicker::OpenCustomColorPicker(SwatchEntry^ entry) +{ + auto c = entry->DisplayColor; + double hVal, sVal, lVal; + RGBtoHSL(c.R, c.G, c.B, &hVal, &sVal, &lVal); + + auto previewBrush = ref new Windows::UI::Xaml::Media::SolidColorBrush(c); + auto preview = ref new Windows::UI::Xaml::Shapes::Rectangle(); + preview->Height = 52; + preview->RadiusX = 8; + preview->RadiusY = 8; + preview->Margin = Thickness(0, 0, 0, 16); + preview->Fill = previewBrush; + + auto makeSlider = [](Platform::String^ label, double val, double maxVal) -> Slider^ { + auto sl = ref new Slider(); + sl->Header = label; + sl->Minimum = 0; + sl->Maximum = maxVal; + sl->Value = val; + sl->StepFrequency = 1; + sl->SnapsTo = Windows::UI::Xaml::Controls::Primitives::SliderSnapsTo::StepValues; + sl->Margin = Thickness(0, 0, 0, 8); + return sl; + }; + + auto hSlider = makeSlider("Hue", hVal, 360); + auto sSlider = makeSlider("Saturation", sVal, 100); + auto lSlider = makeSlider("Lightness", lVal, 100); + + auto onChanged = ref new Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventHandler( + [previewBrush, hSlider, sSlider, lSlider] + (Platform::Object^, Windows::UI::Xaml::Controls::Primitives::RangeBaseValueChangedEventArgs^) { + previewBrush->Color = HSLtoRGB( + hSlider->Value, + sSlider->Value / 100.0, + lSlider->Value / 100.0); + }); + hSlider->ValueChanged += onChanged; + sSlider->ValueChanged += onChanged; + lSlider->ValueChanged += onChanged; + + auto content = ref new StackPanel(); + content->Width = 320; + content->Children->Append(preview); + content->Children->Append(hSlider); + content->Children->Append(sSlider); + content->Children->Append(lSlider); + + auto dialog = ref new ContentDialog(); + dialog->Title = "Custom Color"; + dialog->Content = content; + dialog->PrimaryButtonText = "Apply"; + dialog->CloseButtonText = "Cancel"; + dialog->XamlRoot = this->XamlRoot; + + auto that = this; + dialog->PrimaryButtonClick += ref new Windows::Foundation::TypedEventHandler( + [that, entry, hSlider, sSlider, lSlider](ContentDialog^, ContentDialogButtonClickEventArgs^) { + entry->UpdateColor(HSLtoRGB( + hSlider->Value, + sSlider->Value / 100.0, + lSlider->Value / 100.0)); + that->SelectEntry(entry, true); + }); + + dialog->ShowAsync(); +} + +void SwatchPicker::SetSwatches(IVector^ colors) +{ + m_entries->Clear(); + m_selectedEntry = nullptr; + m_customEntry = nullptr; + + auto sysColor = GetSystemAccentColor(); + auto sysEntry = ref new SwatchEntry(sysColor, true, false); + m_entries->Append(sysEntry); + + for (auto c : colors) + m_entries->Append(ref new SwatchEntry(c, false, false)); + + m_customEntry = ref new SwatchEntry(Windows::UI::Color{ 255, 108, 108, 108 }, false, true); + m_entries->Append(m_customEntry); + + SelectEntry(sysEntry, false); +} + +void SwatchPicker::SetSwatches(Windows::UI::Color* colors, int count) +{ + m_entries->Clear(); + m_selectedEntry = nullptr; + m_customEntry = nullptr; + + auto sysColor = GetSystemAccentColor(); + auto sysEntry = ref new SwatchEntry(sysColor, true, false); + m_entries->Append(sysEntry); + + for (int i = 0; i < count; ++i) + m_entries->Append(ref new SwatchEntry(colors[i], false, false)); + + m_customEntry = ref new SwatchEntry(Windows::UI::Color{ 255, 108, 108, 108 }, false, true); + m_entries->Append(m_customEntry); + + SelectEntry(sysEntry, false); +} + +void SwatchPicker::SelectColor(Windows::UI::Color color, bool useSystem) +{ + if (useSystem) { + if (m_entries->Size > 0) + SelectEntry(m_entries->GetAt(0), false); + return; + } + + for (unsigned int i = 0; i < m_entries->Size; ++i) { + auto entry = m_entries->GetAt(i); + if (entry->IsSystemAccent || entry->IsCustom) continue; + if (entry->DisplayColor.R == color.R + && entry->DisplayColor.G == color.G + && entry->DisplayColor.B == color.B) + { + SelectEntry(entry, false); + return; + } + } + + if (m_customEntry != nullptr) { + m_customEntry->UpdateColor(color); + SelectEntry(m_customEntry, false); + } +} diff --git a/UI/Controls/SwatchPicker.xaml.h b/UI/Controls/SwatchPicker.xaml.h new file mode 100644 index 00000000..f0f3fe39 --- /dev/null +++ b/UI/Controls/SwatchPicker.xaml.h @@ -0,0 +1,111 @@ +#pragma once +#include "UI\Controls\SwatchPicker.g.h" + +namespace moonlight_xbox_dx { + +[Windows::UI::Xaml::Data::Bindable] +public ref class SwatchEntry sealed : Windows::UI::Xaml::Data::INotifyPropertyChanged +{ +private: + bool m_isSelected = false; + bool m_isSystemAccent = false; + bool m_isCustom = false; + Windows::UI::Color m_color = {}; + +public: + virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged; + + SwatchEntry(Windows::UI::Color color, bool isSystem, bool isCustom) + : m_color(color), m_isSystemAccent(isSystem), m_isCustom(isCustom) {} + + void UpdateColor(Windows::UI::Color color) { + m_color = color; + PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("DisplayColor")); + } + + property Windows::UI::Color DisplayColor { + Windows::UI::Color get() { return m_color; } + } + + property bool IsSystemAccent { + bool get() { return m_isSystemAccent; } + } + + property bool IsCustom { + bool get() { return m_isCustom; } + } + + property bool IsSelected { + bool get() { return m_isSelected; } + void set(bool v) { + if (m_isSelected == v) return; + m_isSelected = v; + PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("IsSelected")); + PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs("CheckVisibility")); + } + } + + property Windows::UI::Xaml::Visibility CheckVisibility { + Windows::UI::Xaml::Visibility get() { + return m_isSelected ? Windows::UI::Xaml::Visibility::Visible + : Windows::UI::Xaml::Visibility::Collapsed; + } + } + + property Windows::UI::Xaml::Visibility SystemIndicatorVisibility { + Windows::UI::Xaml::Visibility get() { + return m_isSystemAccent ? Windows::UI::Xaml::Visibility::Visible + : Windows::UI::Xaml::Visibility::Collapsed; + } + } + + property Windows::UI::Xaml::Visibility CustomIndicatorVisibility { + Windows::UI::Xaml::Visibility get() { + return m_isCustom ? Windows::UI::Xaml::Visibility::Visible + : Windows::UI::Xaml::Visibility::Collapsed; + } + } +}; + +public delegate void SwatchColorChangedHandler( + Platform::Object^ sender, + Windows::UI::Color color, + bool useSystemAccent); + +[Windows::UI::Xaml::Data::Bindable] +public ref class SwatchPicker sealed +{ +public: + SwatchPicker(); + + property Windows::UI::Color SelectedColor { + Windows::UI::Color get() { return m_selectedColor; } + } + + property bool UseSystemAccent { + bool get() { return m_useSystemAccent; } + } + + void SetSwatches(Windows::Foundation::Collections::IVector^ colors); + void SetSwatches(Windows::UI::Color* colors, int count); + + void SelectColor(Windows::UI::Color color, bool useSystem); + + event SwatchColorChangedHandler^ ColorChanged; + +private: + Windows::UI::Color m_selectedColor; + bool m_useSystemAccent = true; + Platform::Collections::Vector^ m_entries; + SwatchEntry^ m_selectedEntry = nullptr; + SwatchEntry^ m_customEntry = nullptr; + + void SwatchGrid_ItemClick(Platform::Object^ sender, + Windows::UI::Xaml::Controls::ItemClickEventArgs^ e); + void BuildDefaultSwatches(); + void SelectEntry(SwatchEntry^ entry, bool fireEvent); + void OpenCustomColorPicker(SwatchEntry^ entry); + Windows::UI::Color GetSystemAccentColor(); +}; + +} diff --git a/UI/Controls/TabsLayout.xaml b/UI/Controls/TabsLayout.xaml new file mode 100644 index 00000000..c49bfdd7 --- /dev/null +++ b/UI/Controls/TabsLayout.xaml @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Modals/AddHostDialog.xaml.cpp b/UI/Modals/AddHostDialog.xaml.cpp new file mode 100644 index 00000000..2a7c80b4 --- /dev/null +++ b/UI/Modals/AddHostDialog.xaml.cpp @@ -0,0 +1,123 @@ +#include "pch.h" +#include "UI\Modals\AddHostDialog.xaml.h" + +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Input; +using namespace Windows::Foundation::Collections; + +namespace moonlight_xbox_dx +{ + +AddHostDialog::AddHostDialog() +{ + InitializeComponent(); +} + +void AddHostDialog::Configure( + RoutedEventHandler^ onAdd, + RoutedEventHandler^ onCancel) +{ + m_onAdd = onAdd; + m_onCancel = onCancel; + + try { + AddButton->Click += ref new RoutedEventHandler([this](Platform::Object^ sender, RoutedEventArgs^ args) { + if (ErrorText != nullptr) ErrorText->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + if (m_onAdd != nullptr) m_onAdd->Invoke(sender, args); + }); + + CancelButton->Click += ref new RoutedEventHandler([this](Platform::Object^ sender, RoutedEventArgs^ args) { + try { this->Hide(); } catch (...) {} + if (m_onCancel != nullptr) m_onCancel->Invoke(sender, args); + }); + + HostnameTextBox->KeyDown += ref new KeyEventHandler([this](Platform::Object^, KeyRoutedEventArgs^ e) { + if (e->Key == Windows::System::VirtualKey::Enter) { + try { Windows::UI::ViewManagement::InputPane::GetForCurrentView()->TryHide(); } catch (...) {} + if (ErrorText != nullptr) ErrorText->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + if (m_onAdd != nullptr) m_onAdd->Invoke(this, ref new RoutedEventArgs()); + } + }); + } catch (...) {} +} + +Platform::String^ AddHostDialog::GetHostname() +{ + try { if (HostnameTextBox != nullptr) return HostnameTextBox->Text; } catch (...) {} + return nullptr; +} + +void AddHostDialog::ShowError(Platform::String^ message) +{ + try { + if (ErrorText != nullptr) { + ErrorText->Text = message; + ErrorText->Visibility = Windows::UI::Xaml::Visibility::Visible; + } + } catch (...) {} +} + +void AddHostDialog::SetAddButtonEnabled(bool enabled) +{ + try { if (AddButton != nullptr) AddButton->IsEnabled = enabled; } catch (...) {} +} + +void AddHostDialog::SetRecentHostnames(IVector^ hostnames, IVector^ displayNames) +{ + try { + if (hostnames == nullptr || hostnames->Size == 0) return; + RecentHostsBorder->Visibility = Windows::UI::Xaml::Visibility::Visible; + for (unsigned int i = 0; i < hostnames->Size; i++) { + auto hostname = hostnames->GetAt(i); + auto computerName = (displayNames != nullptr && displayNames->Size > i) ? displayNames->GetAt(i) : nullptr; + auto btn = ref new Button(); + if (computerName != nullptr && computerName->Length() > 0) { + auto content = ref new StackPanel(); + content->Orientation = Windows::UI::Xaml::Controls::Orientation::Horizontal; + content->Spacing = 6; + auto nameText = ref new TextBlock(); + nameText->Text = computerName; + nameText->FontFamily = ref new Windows::UI::Xaml::Media::FontFamily("Bahnschrift"); + nameText->Foreground = ref new Windows::UI::Xaml::Media::SolidColorBrush(Windows::UI::Colors::White); + nameText->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Center; + auto addressText = ref new TextBlock(); + addressText->Text = "(" + hostname + ")"; + addressText->FontFamily = ref new Windows::UI::Xaml::Media::FontFamily("Bahnschrift"); + addressText->FontSize = 10; + Windows::UI::Color dimWhite; + dimWhite.A = 0x99; dimWhite.R = 0xFF; dimWhite.G = 0xFF; dimWhite.B = 0xFF; + addressText->Foreground = ref new Windows::UI::Xaml::Media::SolidColorBrush(dimWhite); + addressText->VerticalAlignment = Windows::UI::Xaml::VerticalAlignment::Center; + content->Children->Append(nameText); + content->Children->Append(addressText); + btn->Content = content; + } else { + btn->Content = hostname; + } + btn->FontFamily = ref new Windows::UI::Xaml::Media::FontFamily("Bahnschrift"); + btn->FontSize = 12; + btn->Height = 36; + btn->HorizontalAlignment = Windows::UI::Xaml::HorizontalAlignment::Stretch; + btn->HorizontalContentAlignment = Windows::UI::Xaml::HorizontalAlignment::Left; + Windows::UI::Xaml::CornerRadius cr; + cr.TopLeft = cr.TopRight = cr.BottomRight = cr.BottomLeft = 8.0; + btn->CornerRadius = cr; + btn->Padding = Windows::UI::Xaml::Thickness{ 12, 0, 12, 0 }; + Windows::UI::Color bgColor; + bgColor.A = 0x88; bgColor.R = 0; bgColor.G = 0; bgColor.B = 0; + btn->Background = ref new Windows::UI::Xaml::Media::SolidColorBrush(bgColor); + btn->Foreground = ref new Windows::UI::Xaml::Media::SolidColorBrush(Windows::UI::Colors::White); + btn->Click += ref new RoutedEventHandler([this, hostname](Platform::Object^ sender, RoutedEventArgs^ args) { + try { + HostnameTextBox->Text = hostname; + if (ErrorText != nullptr) ErrorText->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + if (m_onAdd != nullptr) m_onAdd->Invoke(sender, args); + } catch (...) {} + }); + RecentHostsPanel->Children->Append(btn); + } + } catch (...) {} +} + +} diff --git a/UI/Modals/AddHostDialog.xaml.h b/UI/Modals/AddHostDialog.xaml.h new file mode 100644 index 00000000..2cea9ec9 --- /dev/null +++ b/UI/Modals/AddHostDialog.xaml.h @@ -0,0 +1,26 @@ +#pragma once + +#include "UI\Modals\AddHostDialog.g.h" + +namespace moonlight_xbox_dx +{ + [Windows::Foundation::Metadata::WebHostHidden] + public ref class AddHostDialog sealed + { + public: + AddHostDialog(); + void Configure( + Windows::UI::Xaml::RoutedEventHandler^ onAdd, + Windows::UI::Xaml::RoutedEventHandler^ onCancel); + Platform::String^ GetHostname(); + void ShowError(Platform::String^ message); + void SetAddButtonEnabled(bool enabled); + void SetRecentHostnames( + Windows::Foundation::Collections::IVector^ hostnames, + Windows::Foundation::Collections::IVector^ displayNames); + + private: + Windows::UI::Xaml::RoutedEventHandler^ m_onAdd; + Windows::UI::Xaml::RoutedEventHandler^ m_onCancel; + }; +} diff --git a/UI/Modals/AlertDialog.xaml b/UI/Modals/AlertDialog.xaml new file mode 100644 index 00000000..37b0b9a9 --- /dev/null +++ b/UI/Modals/AlertDialog.xaml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Modals/AlertDialog.xaml.cpp b/UI/Modals/AlertDialog.xaml.cpp new file mode 100644 index 00000000..558fc2e9 --- /dev/null +++ b/UI/Modals/AlertDialog.xaml.cpp @@ -0,0 +1,37 @@ +#include "pch.h" +#include "UI\Modals\AlertDialog.xaml.h" + +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; + +namespace moonlight_xbox_dx +{ + +AlertDialog::AlertDialog() +{ + InitializeComponent(); +} + +void AlertDialog::Configure(Platform::String^ title, Platform::String^ message) +{ + Configure(title, message, nullptr); +} + +void AlertDialog::Configure(Platform::String^ title, Platform::String^ message, Platform::String^ glyph) +{ + try { if (TitleHeader != nullptr) TitleHeader->Text = title; } catch (...) {} + try { if (MessageText != nullptr) MessageText->Text = message; } catch (...) {} + try { + if (TitleIcon != nullptr && glyph != nullptr && glyph->Length() > 0) + TitleIcon->Glyph = glyph; + } catch (...) {} + try { + if (OkButton != nullptr) { + OkButton->Click += ref new RoutedEventHandler([this](Platform::Object^, RoutedEventArgs^) { + try { this->Hide(); } catch (...) {} + }); + } + } catch (...) {} +} + +} diff --git a/UI/Modals/AlertDialog.xaml.h b/UI/Modals/AlertDialog.xaml.h new file mode 100644 index 00000000..d706af67 --- /dev/null +++ b/UI/Modals/AlertDialog.xaml.h @@ -0,0 +1,15 @@ +#pragma once + +#include "UI\Modals\AlertDialog.g.h" + +namespace moonlight_xbox_dx +{ + [Windows::Foundation::Metadata::WebHostHidden] + public ref class AlertDialog sealed + { + public: + AlertDialog(); + void Configure(Platform::String^ title, Platform::String^ message); + void Configure(Platform::String^ title, Platform::String^ message, Platform::String^ glyph); + }; +} diff --git a/UI/Modals/AppActionsDialog.xaml b/UI/Modals/AppActionsDialog.xaml new file mode 100644 index 00000000..b63b2d66 --- /dev/null +++ b/UI/Modals/AppActionsDialog.xaml @@ -0,0 +1,127 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Modals/AppActionsDialog.xaml.cpp b/UI/Modals/AppActionsDialog.xaml.cpp new file mode 100644 index 00000000..6b14de97 --- /dev/null +++ b/UI/Modals/AppActionsDialog.xaml.cpp @@ -0,0 +1,94 @@ +#include "pch.h" +#include "UI\Modals\AppActionsDialog.xaml.h" + +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; + +namespace moonlight_xbox_dx +{ + +namespace { + void HideIfOpen(ContentDialog^ dialog) { + try { if (dialog != nullptr) dialog->Hide(); } catch (...) {} + } +} + +AppActionsDialog::AppActionsDialog() +{ + InitializeComponent(); + + try { + this->ResumeButton->Click += ref new RoutedEventHandler([this](Platform::Object^ sender, RoutedEventArgs^ args) { + HideIfOpen(this); + if (m_onResume != nullptr) m_onResume->Invoke(sender, args); + }); + this->CloseButton->Click += ref new RoutedEventHandler([this](Platform::Object^ sender, RoutedEventArgs^ args) { + HideIfOpen(this); + if (m_onClose != nullptr) m_onClose->Invoke(sender, args); + }); + this->CloseAndStartButton->Click += ref new RoutedEventHandler([this](Platform::Object^ sender, RoutedEventArgs^ args) { + HideIfOpen(this); + if (m_onCloseAndStart != nullptr) m_onCloseAndStart->Invoke(sender, args); + }); + this->StartButton->Click += ref new RoutedEventHandler([this](Platform::Object^ sender, RoutedEventArgs^ args) { + HideIfOpen(this); + if (m_onStart != nullptr) m_onStart->Invoke(sender, args); + }); + this->MoonlightSettingsButton->Click += ref new RoutedEventHandler([this](Platform::Object^ sender, RoutedEventArgs^ args) { + HideIfOpen(this); + if (m_onMoonlightSettings != nullptr) m_onMoonlightSettings->Invoke(sender, args); + }); + this->HostSettingsButton->Click += ref new RoutedEventHandler([this](Platform::Object^ sender, RoutedEventArgs^ args) { + HideIfOpen(this); + if (m_onHostSettings != nullptr) m_onHostSettings->Invoke(sender, args); + }); + this->FavoriteButton->Click += ref new RoutedEventHandler([this](Platform::Object^ sender, RoutedEventArgs^ args) { + HideIfOpen(this); + if (m_onToggleFavorite != nullptr) m_onToggleFavorite->Invoke(sender, args); + }); + } catch (...) {} +} + +void AppActionsDialog::Configure( + Platform::String^ appName, + bool showResumeClose, + bool showCloseAndStart, + bool showStart, + bool isFavorite, + RoutedEventHandler^ onResume, + RoutedEventHandler^ onClose, + RoutedEventHandler^ onCloseAndStart, + RoutedEventHandler^ onStart, + RoutedEventHandler^ onMoonlightSettings, + RoutedEventHandler^ onHostSettings, + RoutedEventHandler^ onToggleFavorite) +{ + m_onResume = onResume; + m_onClose = onClose; + m_onCloseAndStart = onCloseAndStart; + m_onStart = onStart; + m_onMoonlightSettings = onMoonlightSettings; + m_onHostSettings = onHostSettings; + m_onToggleFavorite = onToggleFavorite; + + try { + if (this->AppNameHeader != nullptr) this->AppNameHeader->Text = appName; + + if (this->FavoriteIcon != nullptr) + this->FavoriteIcon->Glyph = isFavorite ? L"\xE735" : L"\xE734"; + if (this->FavoriteLabel != nullptr) + this->FavoriteLabel->Text = isFavorite ? "Remove from Favorites" : "Add to Favorites"; + + if (this->ResumeButton != nullptr) + this->ResumeButton->Visibility = showResumeClose ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; + if (this->CloseButton != nullptr) + this->CloseButton->Visibility = showResumeClose ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; + + if (this->CloseAndStartButton != nullptr) + this->CloseAndStartButton->Visibility = showCloseAndStart ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; + if (this->StartButton != nullptr) + this->StartButton->Visibility = showStart ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; + } catch (...) {} +} + +} diff --git a/UI/Modals/AppActionsDialog.xaml.h b/UI/Modals/AppActionsDialog.xaml.h new file mode 100644 index 00000000..668aac28 --- /dev/null +++ b/UI/Modals/AppActionsDialog.xaml.h @@ -0,0 +1,36 @@ +#pragma once + +#include "UI\Modals\AppActionsDialog.g.h" + +namespace moonlight_xbox_dx +{ + [Windows::Foundation::Metadata::WebHostHidden] + public ref class AppActionsDialog sealed + { + public: + AppActionsDialog(); + + void Configure( + Platform::String^ appName, + bool showResumeClose, + bool showCloseAndStart, + bool showStart, + bool isFavorite, + Windows::UI::Xaml::RoutedEventHandler^ onResume, + Windows::UI::Xaml::RoutedEventHandler^ onClose, + Windows::UI::Xaml::RoutedEventHandler^ onCloseAndStart, + Windows::UI::Xaml::RoutedEventHandler^ onStart, + Windows::UI::Xaml::RoutedEventHandler^ onMoonlightSettings, + Windows::UI::Xaml::RoutedEventHandler^ onHostSettings, + Windows::UI::Xaml::RoutedEventHandler^ onToggleFavorite); + + private: + Windows::UI::Xaml::RoutedEventHandler^ m_onResume; + Windows::UI::Xaml::RoutedEventHandler^ m_onClose; + Windows::UI::Xaml::RoutedEventHandler^ m_onCloseAndStart; + Windows::UI::Xaml::RoutedEventHandler^ m_onStart; + Windows::UI::Xaml::RoutedEventHandler^ m_onMoonlightSettings; + Windows::UI::Xaml::RoutedEventHandler^ m_onHostSettings; + Windows::UI::Xaml::RoutedEventHandler^ m_onToggleFavorite; + }; +} diff --git a/UI/Modals/ConfirmDialog.xaml b/UI/Modals/ConfirmDialog.xaml new file mode 100644 index 00000000..fd2166c4 --- /dev/null +++ b/UI/Modals/ConfirmDialog.xaml @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Modals/ConfirmDialog.xaml.cpp b/UI/Modals/ConfirmDialog.xaml.cpp new file mode 100644 index 00000000..0dc83f41 --- /dev/null +++ b/UI/Modals/ConfirmDialog.xaml.cpp @@ -0,0 +1,61 @@ +#include "pch.h" +#include "UI\Modals\ConfirmDialog.xaml.h" + +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; + +namespace moonlight_xbox_dx +{ + +ConfirmDialog::ConfirmDialog() +{ + InitializeComponent(); + try { + if (ConfirmButton != nullptr) { + ConfirmButton->Click += ref new RoutedEventHandler([this](Platform::Object^ sender, RoutedEventArgs^ args) { + try { this->Hide(); } catch (...) {} + if (m_onConfirm != nullptr) m_onConfirm->Invoke(sender, args); + }); + } + if (CancelButton != nullptr) { + CancelButton->Click += ref new RoutedEventHandler([this](Platform::Object^, RoutedEventArgs^) { + try { this->Hide(); } catch (...) {} + }); + } + } catch (...) {} +} + +void ConfirmDialog::Configure(Platform::String^ title, Platform::String^ message, + RoutedEventHandler^ onConfirm) +{ + Configure(title, message, nullptr, nullptr, nullptr, onConfirm); +} + +void ConfirmDialog::Configure(Platform::String^ title, Platform::String^ message, Platform::String^ glyph, + RoutedEventHandler^ onConfirm) +{ + Configure(title, message, glyph, nullptr, nullptr, onConfirm); +} + +void ConfirmDialog::Configure(Platform::String^ title, Platform::String^ message, Platform::String^ glyph, + Platform::String^ confirmText, Platform::String^ cancelText, + RoutedEventHandler^ onConfirm) +{ + m_onConfirm = onConfirm; + try { if (TitleHeader != nullptr) TitleHeader->Text = title; } catch (...) {} + try { if (MessageText != nullptr) MessageText->Text = message; } catch (...) {} + try { + if (TitleIcon != nullptr && glyph != nullptr && glyph->Length() > 0) + TitleIcon->Glyph = glyph; + } catch (...) {} + try { + if (ConfirmButtonText != nullptr && confirmText != nullptr && confirmText->Length() > 0) + ConfirmButtonText->Text = confirmText; + } catch (...) {} + try { + if (CancelButtonText != nullptr && cancelText != nullptr && cancelText->Length() > 0) + CancelButtonText->Text = cancelText; + } catch (...) {} +} + +} diff --git a/UI/Modals/ConfirmDialog.xaml.h b/UI/Modals/ConfirmDialog.xaml.h new file mode 100644 index 00000000..3ea05489 --- /dev/null +++ b/UI/Modals/ConfirmDialog.xaml.h @@ -0,0 +1,23 @@ +#pragma once + +#include "UI\Modals\ConfirmDialog.g.h" + +namespace moonlight_xbox_dx +{ + [Windows::Foundation::Metadata::WebHostHidden] + public ref class ConfirmDialog sealed + { + public: + ConfirmDialog(); + void Configure(Platform::String^ title, Platform::String^ message, + Windows::UI::Xaml::RoutedEventHandler^ onConfirm); + void Configure(Platform::String^ title, Platform::String^ message, Platform::String^ glyph, + Windows::UI::Xaml::RoutedEventHandler^ onConfirm); + void Configure(Platform::String^ title, Platform::String^ message, Platform::String^ glyph, + Platform::String^ confirmText, Platform::String^ cancelText, + Windows::UI::Xaml::RoutedEventHandler^ onConfirm); + + private: + Windows::UI::Xaml::RoutedEventHandler^ m_onConfirm; + }; +} diff --git a/UI/Modals/HostActionsDialog.xaml b/UI/Modals/HostActionsDialog.xaml new file mode 100644 index 00000000..aa3896a7 --- /dev/null +++ b/UI/Modals/HostActionsDialog.xaml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Modals/HostActionsDialog.xaml.cpp b/UI/Modals/HostActionsDialog.xaml.cpp new file mode 100644 index 00000000..61265d52 --- /dev/null +++ b/UI/Modals/HostActionsDialog.xaml.cpp @@ -0,0 +1,70 @@ +#include "pch.h" +#include "UI\Modals\HostActionsDialog.xaml.h" + +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; + +namespace moonlight_xbox_dx +{ + +namespace { + void HideIfOpen(ContentDialog^ dialog) { + try { if (dialog != nullptr) dialog->Hide(); } catch (...) {} + } +} + +HostActionsDialog::HostActionsDialog() +{ + InitializeComponent(); + + try { + this->HostSettingsButton->Click += ref new RoutedEventHandler([this](Platform::Object^ sender, RoutedEventArgs^ args) { + HideIfOpen(this); + if (m_onHostSettings != nullptr) m_onHostSettings->Invoke(sender, args); + }); + this->WakeHostButton->Click += ref new RoutedEventHandler([this](Platform::Object^ sender, RoutedEventArgs^ args) { + HideIfOpen(this); + if (m_onWakeHost != nullptr) m_onWakeHost->Invoke(sender, args); + }); + this->TestConnectionButton->Click += ref new RoutedEventHandler([this](Platform::Object^ sender, RoutedEventArgs^ args) { + HideIfOpen(this); + if (m_onTestConnection != nullptr) m_onTestConnection->Invoke(sender, args); + }); + this->RemoveHostButton->Click += ref new RoutedEventHandler([this](Platform::Object^ sender, RoutedEventArgs^ args) { + HideIfOpen(this); + if (m_onRemoveHost != nullptr) m_onRemoveHost->Invoke(sender, args); + }); + this->MoonlightSettingsButton->Click += ref new RoutedEventHandler([this](Platform::Object^ sender, RoutedEventArgs^ args) { + HideIfOpen(this); + if (m_onMoonlightSettings != nullptr) m_onMoonlightSettings->Invoke(sender, args); + }); + } catch (...) {} +} + +void HostActionsDialog::Configure( + Platform::String^ hostName, + bool showWake, + bool showTestConnection, + RoutedEventHandler^ onHostSettings, + RoutedEventHandler^ onWakeHost, + RoutedEventHandler^ onTestConnection, + RoutedEventHandler^ onRemoveHost, + RoutedEventHandler^ onMoonlightSettings) +{ + m_onHostSettings = onHostSettings; + m_onWakeHost = onWakeHost; + m_onTestConnection = onTestConnection; + m_onRemoveHost = onRemoveHost; + m_onMoonlightSettings = onMoonlightSettings; + + try { + if (this->HostNameHeader != nullptr) this->HostNameHeader->Text = hostName; + + if (this->WakeHostButton != nullptr) + this->WakeHostButton->Visibility = showWake ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; + if (this->TestConnectionButton != nullptr) + this->TestConnectionButton->Visibility = showTestConnection ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; + } catch (...) {} +} + +} diff --git a/UI/Modals/HostActionsDialog.xaml.h b/UI/Modals/HostActionsDialog.xaml.h new file mode 100644 index 00000000..0bc177c6 --- /dev/null +++ b/UI/Modals/HostActionsDialog.xaml.h @@ -0,0 +1,30 @@ +#pragma once + +#include "UI\Modals\HostActionsDialog.g.h" + +namespace moonlight_xbox_dx +{ + [Windows::Foundation::Metadata::WebHostHidden] + public ref class HostActionsDialog sealed + { + public: + HostActionsDialog(); + + void Configure( + Platform::String^ hostName, + bool showWake, + bool showTestConnection, + Windows::UI::Xaml::RoutedEventHandler^ onHostSettings, + Windows::UI::Xaml::RoutedEventHandler^ onWakeHost, + Windows::UI::Xaml::RoutedEventHandler^ onTestConnection, + Windows::UI::Xaml::RoutedEventHandler^ onRemoveHost, + Windows::UI::Xaml::RoutedEventHandler^ onMoonlightSettings); + + private: + Windows::UI::Xaml::RoutedEventHandler^ m_onHostSettings; + Windows::UI::Xaml::RoutedEventHandler^ m_onWakeHost; + Windows::UI::Xaml::RoutedEventHandler^ m_onTestConnection; + Windows::UI::Xaml::RoutedEventHandler^ m_onRemoveHost; + Windows::UI::Xaml::RoutedEventHandler^ m_onMoonlightSettings; + }; +} diff --git a/UI/Modals/PairDialog.xaml b/UI/Modals/PairDialog.xaml new file mode 100644 index 00000000..36b0be02 --- /dev/null +++ b/UI/Modals/PairDialog.xaml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Modals/PairDialog.xaml.cpp b/UI/Modals/PairDialog.xaml.cpp new file mode 100644 index 00000000..86cd1c4b --- /dev/null +++ b/UI/Modals/PairDialog.xaml.cpp @@ -0,0 +1,27 @@ +#include "pch.h" +#include "UI\Modals\PairDialog.xaml.h" + +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; + +namespace moonlight_xbox_dx +{ + +PairDialog::PairDialog() +{ + InitializeComponent(); +} + +void PairDialog::Configure(Platform::String^ pin) +{ + try { if (PinText != nullptr) PinText->Text = pin; } catch (...) {} + try { + if (OkButton != nullptr) { + OkButton->Click += ref new RoutedEventHandler([this](Platform::Object^, RoutedEventArgs^) { + try { this->Hide(); } catch (...) {} + }); + } + } catch (...) {} +} + +} diff --git a/UI/Modals/PairDialog.xaml.h b/UI/Modals/PairDialog.xaml.h new file mode 100644 index 00000000..194afb84 --- /dev/null +++ b/UI/Modals/PairDialog.xaml.h @@ -0,0 +1,14 @@ +#pragma once + +#include "UI\Modals\PairDialog.g.h" + +namespace moonlight_xbox_dx +{ + [Windows::Foundation::Metadata::WebHostHidden] + public ref class PairDialog sealed + { + public: + PairDialog(); + void Configure(Platform::String^ pin); + }; +} diff --git a/UI/Modals/StreamErrorDialog.xaml b/UI/Modals/StreamErrorDialog.xaml new file mode 100644 index 00000000..456876b6 --- /dev/null +++ b/UI/Modals/StreamErrorDialog.xaml @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Modals/StreamErrorDialog.xaml.cpp b/UI/Modals/StreamErrorDialog.xaml.cpp new file mode 100644 index 00000000..a83cd67d --- /dev/null +++ b/UI/Modals/StreamErrorDialog.xaml.cpp @@ -0,0 +1,55 @@ +#include "pch.h" +#include "UI\Modals\StreamErrorDialog.xaml.h" + +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; + +namespace moonlight_xbox_dx +{ + +StreamErrorDialog::StreamErrorDialog() +{ + InitializeComponent(); +} + +void StreamErrorDialog::Configure( + Platform::String^ title, + Platform::String^ message, + Platform::String^ headerGlyph, + Platform::String^ primaryText, + Platform::String^ primaryGlyph, + Platform::String^ secondaryText, + Platform::String^ secondaryGlyph, + RoutedEventHandler^ onPrimary, + RoutedEventHandler^ onSecondary) +{ + try { if (TitleHeader) TitleHeader->Text = title; } catch (...) {} + try { if (MessageText) MessageText->Text = message; } catch (...) {} + try { if (TitleIcon && headerGlyph && headerGlyph->Length() > 0) TitleIcon->Glyph = headerGlyph; } catch (...) {} + try { if (PrimaryButtonLabel && primaryText) PrimaryButtonLabel->Text = primaryText; } catch (...) {} + try { if (PrimaryButtonIcon && primaryGlyph && primaryGlyph->Length() > 0) PrimaryButtonIcon->Glyph = primaryGlyph; } catch (...) {} + try { if (SecondaryButtonLabel && secondaryText) SecondaryButtonLabel->Text = secondaryText; } catch (...) {} + try { if (SecondaryButtonIcon && secondaryGlyph && secondaryGlyph->Length() > 0) SecondaryButtonIcon->Glyph = secondaryGlyph; } catch (...) {} + + try { + if (PrimaryButton) { + PrimaryButton->Click += ref new RoutedEventHandler( + [this, onPrimary](Platform::Object^ sender, RoutedEventArgs^ args) { + try { this->Hide(); } catch (...) {} + if (onPrimary) onPrimary->Invoke(sender, args); + }); + } + } catch (...) {} + + try { + if (SecondaryButton) { + SecondaryButton->Click += ref new RoutedEventHandler( + [this, onSecondary](Platform::Object^ sender, RoutedEventArgs^ args) { + try { this->Hide(); } catch (...) {} + if (onSecondary) onSecondary->Invoke(sender, args); + }); + } + } catch (...) {} +} + +} diff --git a/UI/Modals/StreamErrorDialog.xaml.h b/UI/Modals/StreamErrorDialog.xaml.h new file mode 100644 index 00000000..c155b8c1 --- /dev/null +++ b/UI/Modals/StreamErrorDialog.xaml.h @@ -0,0 +1,22 @@ +#pragma once + +#include "UI\Modals\StreamErrorDialog.g.h" + +namespace moonlight_xbox_dx +{ + [Windows::Foundation::Metadata::WebHostHidden] + public ref class StreamErrorDialog sealed + { + public: + StreamErrorDialog(); + void Configure(Platform::String^ title, + Platform::String^ message, + Platform::String^ headerGlyph, + Platform::String^ primaryText, + Platform::String^ primaryGlyph, + Platform::String^ secondaryText, + Platform::String^ secondaryGlyph, + Windows::UI::Xaml::RoutedEventHandler^ onPrimary, + Windows::UI::Xaml::RoutedEventHandler^ onSecondary); + }; +} diff --git a/UI/Modals/TestConnectionResultDialog.xaml b/UI/Modals/TestConnectionResultDialog.xaml new file mode 100644 index 00000000..d469be05 --- /dev/null +++ b/UI/Modals/TestConnectionResultDialog.xaml @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Modals/TestConnectionResultDialog.xaml.cpp b/UI/Modals/TestConnectionResultDialog.xaml.cpp new file mode 100644 index 00000000..8438b2ae --- /dev/null +++ b/UI/Modals/TestConnectionResultDialog.xaml.cpp @@ -0,0 +1,68 @@ +#include "pch.h" +#include "UI\Modals\TestConnectionResultDialog.xaml.h" +#include "Utils.hpp" + +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Media; + +namespace moonlight_xbox_dx +{ + +TestConnectionResultDialog::TestConnectionResultDialog() +{ + InitializeComponent(); +} + +void TestConnectionResultDialog::Configure(Platform::String^ hostname, Platform::String^ resultMsg) +{ + try { if (HostNameHeader != nullptr) HostNameHeader->Text = hostname; } catch (...) {} + + std::string msg = Utils::PlatformStringToStdString(resultMsg); + bool isSuccess = (msg.rfind("Connection OK", 0) == 0); + + std::string statusText = msg; + auto parenPos = msg.find(" (RTT:"); + if (parenPos != std::string::npos) statusText = msg.substr(0, parenPos); + + double rttMs = -1.0; + auto rttPos = msg.find("RTT: "); + if (rttPos != std::string::npos) { + try { rttMs = std::stod(msg.substr(rttPos + 5)); } catch (...) {} + } + + try { if (StatusText != nullptr) StatusText->Text = Utils::StringFromStdString(statusText); } catch (...) {} + + try { + if (ResultIcon != nullptr) { + if (isSuccess) { + ResultIcon->Glyph = L"\xE73E"; + ResultIcon->Foreground = ref new SolidColorBrush( + Windows::UI::ColorHelper::FromArgb(255, 76, 175, 80)); + } else { + ResultIcon->Glyph = L"\xE711"; + ResultIcon->Foreground = ref new SolidColorBrush( + Windows::UI::ColorHelper::FromArgb(255, 244, 67, 54)); + } + } + } catch (...) {} + + try { + if (rttMs >= 0 && RttBadge != nullptr && RttText != nullptr) { + char buf[32]; + snprintf(buf, sizeof(buf), "RTT: %.1f ms", rttMs); + RttText->Text = Utils::StringFromStdString(std::string(buf)); + RttBadge->Visibility = Windows::UI::Xaml::Visibility::Visible; + } + } catch (...) {} + + try { + if (OkButton != nullptr) { + OkButton->Click += ref new RoutedEventHandler([this](Platform::Object^, RoutedEventArgs^) { + try { this->Hide(); } catch (...) {} + }); + } + } catch (...) {} +} + +} diff --git a/UI/Modals/TestConnectionResultDialog.xaml.h b/UI/Modals/TestConnectionResultDialog.xaml.h new file mode 100644 index 00000000..3ea364b9 --- /dev/null +++ b/UI/Modals/TestConnectionResultDialog.xaml.h @@ -0,0 +1,14 @@ +#pragma once + +#include "UI\Modals\TestConnectionResultDialog.g.h" + +namespace moonlight_xbox_dx +{ + [Windows::Foundation::Metadata::WebHostHidden] + public ref class TestConnectionResultDialog sealed + { + public: + TestConnectionResultDialog(); + void Configure(Platform::String^ hostname, Platform::String^ resultMsg); + }; +} diff --git a/UI/Models/MenuItem.cpp b/UI/Models/MenuItem.cpp new file mode 100644 index 00000000..56d9b5f6 --- /dev/null +++ b/UI/Models/MenuItem.cpp @@ -0,0 +1,36 @@ +#include "pch.h" +#include "MenuItem.h" + +using namespace moonlight_xbox_dx; + +MenuItem::MenuItem() +{ + Title = ref new Platform::String(L""); + IconGlyph = ref new Platform::String(L""); + Tag = nullptr; +} + +MenuItem::MenuItem(Platform::String ^ title, Platform::String ^ iconGlyph) { + Title = title; + IconGlyph = iconGlyph; +} + +MenuItem::MenuItem(Platform::String ^ title, Platform::String ^ iconGlyph, Platform::Object ^ tag) { + Title = title; + IconGlyph = iconGlyph; + Tag = tag; +} + +MenuItem::MenuItem(Platform::String ^ title, Platform::String ^ iconGlyph, Windows::Foundation::EventHandler^ clickAction) { + Title = title; + IconGlyph = iconGlyph; + Tag = nullptr; + ClickAction = clickAction; +} + +MenuItem::MenuItem(Platform::String ^ title, Platform::String ^ iconGlyph, Platform::Object ^ tag, Windows::Foundation::EventHandler^ clickAction) { + Title = title; + IconGlyph = iconGlyph; + Tag = tag; + ClickAction = clickAction; +} diff --git a/UI/Models/MenuItem.h b/UI/Models/MenuItem.h new file mode 100644 index 00000000..92ef4151 --- /dev/null +++ b/UI/Models/MenuItem.h @@ -0,0 +1,21 @@ +#pragma once + +namespace moonlight_xbox_dx +{ + public ref class MenuItem sealed + { + public: + MenuItem(); + MenuItem(Platform::String ^ title, Platform::String ^ iconGlyph); + MenuItem(Platform::String ^ title, Platform::String ^ iconGlyph, Platform::Object ^ tag); + + MenuItem(Platform::String ^ title, Platform::String ^ iconGlyph, Windows::Foundation::EventHandler^ clickAction); + + MenuItem(Platform::String ^ title, Platform::String ^ iconGlyph, Platform::Object ^ tag, Windows::Foundation::EventHandler^ clickAction); + + property Platform::String^ Title; + property Platform::String^ IconGlyph; + property Platform::Object^ Tag; + property Windows::Foundation::EventHandler^ ClickAction; + }; +} diff --git a/UI/Models/UIPersonalization.h b/UI/Models/UIPersonalization.h new file mode 100644 index 00000000..5d2298a8 --- /dev/null +++ b/UI/Models/UIPersonalization.h @@ -0,0 +1,68 @@ +#pragma once +#include "pch.h" + +namespace moonlight_xbox_dx { + +public enum class AppHostView : int { + List = 0, + Grid = 1, +}; + +[Windows::UI::Xaml::Data::Bindable] +public ref class UIPersonalization sealed : Windows::UI::Xaml::Data::INotifyPropertyChanged +{ +private: + Platform::String^ background = ""; + AppHostView appView = AppHostView::List; + Windows::UI::Color accentColor = Windows::UI::Color{ 255, 0, 120, 215 }; + bool useSystemAccent = true; + +public: + virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged; + + void OnPropertyChanged(Platform::String^ propertyName) + { + PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(propertyName)); + } + + property Platform::String^ Background + { + Platform::String^ get() { return this->background; } + void set(Platform::String^ value) { + if (background == value) return; + this->background = value; + OnPropertyChanged("Background"); + } + } + + property AppHostView AppView + { + AppHostView get() { return this->appView; } + void set(AppHostView value) { + if (appView == value) return; + this->appView = value; + OnPropertyChanged("AppView"); + } + } + + property Windows::UI::Color AccentColor + { + Windows::UI::Color get() { return this->accentColor; } + void set(Windows::UI::Color value) { + this->accentColor = value; + OnPropertyChanged("AccentColor"); + } + } + + property bool UseSystemAccent + { + bool get() { return this->useSystemAccent; } + void set(bool value) { + if (useSystemAccent == value) return; + this->useSystemAccent = value; + OnPropertyChanged("UseSystemAccent"); + } + } +}; + +} diff --git a/UI/Models/ViewModels/AppPageViewModel.cpp b/UI/Models/ViewModels/AppPageViewModel.cpp new file mode 100644 index 00000000..306279a0 --- /dev/null +++ b/UI/Models/ViewModels/AppPageViewModel.cpp @@ -0,0 +1,100 @@ +#include "pch.h" +#include "AppPageViewModel.h" +#include + +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Data; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Media::Animation; +using namespace Windows::UI::Xaml::Media::Imaging; +using namespace moonlight_xbox_dx; + +AppPageViewModel::AppPageViewModel() + : currentBackgroundImage(nullptr), + pageBackgroundBorder(nullptr), + backgroundTransitionDurationMs(250.0), + backgroundOverlayOpacity(0.05) +{ +} + +void AppPageViewModel::OnPropertyChanged(Platform::String^ propertyName) +{ + PropertyChanged(this, ref new PropertyChangedEventArgs(propertyName)); +} + +void AppPageViewModel::SetPageBackgroundBorder(Border^ border) +{ + this->pageBackgroundBorder = border; + this->currentBackgroundImage = nullptr; +} + +void AppPageViewModel::SetBackgroundTransitionSettings(double durationMs, double overlayOpacity) +{ + if (durationMs > 0.0) { + this->backgroundTransitionDurationMs = durationMs; + } + + if (overlayOpacity < 0.0) overlayOpacity = 0.0; + if (overlayOpacity > 1.0) overlayOpacity = 1.0; + this->backgroundOverlayOpacity = overlayOpacity; +} + +void AppPageViewModel::TransitionToBlurredImage(BitmapImage^ newImage) +{ + if (newImage == nullptr || this->pageBackgroundBorder == nullptr) return; + if (newImage == this->currentBackgroundImage) return; + this->currentBackgroundImage = newImage; + + try { + auto durationTicks = (long long)std::llround(this->backgroundTransitionDurationMs * 10000.0); + if (durationTicks <= 0) durationTicks = 2500000LL; + + auto fadeOutAnimation = ref new DoubleAnimation(); + fadeOutAnimation->To = 0.0; + fadeOutAnimation->Duration = Duration(Windows::Foundation::TimeSpan{ durationTicks }); + + Storyboard::SetTarget(fadeOutAnimation, this->pageBackgroundBorder); + Storyboard::SetTargetProperty(fadeOutAnimation, "Opacity"); + + auto fadeOutSB = ref new Storyboard(); + fadeOutSB->Children->Append(fadeOutAnimation); + + Platform::WeakReference weakThis(this); + fadeOutSB->Completed += ref new EventHandler( + [weakThis, newImage](Platform::Object^ sender, Platform::Object^ e) { + try { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + + auto localTicks = (long long)std::llround(that->backgroundTransitionDurationMs * 10000.0); + if (localTicks <= 0) localTicks = 2500000LL; + + try { + auto brush = dynamic_cast(that->pageBackgroundBorder->Background); + if (brush != nullptr) { + brush->ImageSource = newImage; + } + } catch(...) {} + + that->OnPropertyChanged("CurrentBackgroundImage"); + + auto fadeInAnimation = ref new DoubleAnimation(); + fadeInAnimation->From = 0.0; + fadeInAnimation->To = that->backgroundOverlayOpacity; + fadeInAnimation->Duration = Duration(Windows::Foundation::TimeSpan{ localTicks }); + + Storyboard::SetTarget(fadeInAnimation, that->pageBackgroundBorder); + Storyboard::SetTargetProperty(fadeInAnimation, "Opacity"); + + auto fadeInSB = ref new Storyboard(); + fadeInSB->Children->Append(fadeInAnimation); + fadeInSB->Begin(); + } catch(...) {} + }); + + fadeOutSB->Begin(); + } catch(...) {} +} diff --git a/UI/Models/ViewModels/AppPageViewModel.h b/UI/Models/ViewModels/AppPageViewModel.h new file mode 100644 index 00000000..bbc2725d --- /dev/null +++ b/UI/Models/ViewModels/AppPageViewModel.h @@ -0,0 +1,32 @@ +#pragma once + +#include "pch.h" + +namespace moonlight_xbox_dx { + + [Windows::UI::Xaml::Data::Bindable] + public ref class AppPageViewModel sealed : Windows::UI::Xaml::Data::INotifyPropertyChanged + { + private: + Windows::UI::Xaml::Media::Imaging::BitmapImage^ currentBackgroundImage; + Windows::UI::Xaml::Controls::Border^ pageBackgroundBorder; + double backgroundTransitionDurationMs; + double backgroundOverlayOpacity; + + public: + AppPageViewModel(); + + virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged; + void OnPropertyChanged(Platform::String^ propertyName); + + property Windows::UI::Xaml::Media::Imaging::BitmapImage^ CurrentBackgroundImage + { + Windows::UI::Xaml::Media::Imaging::BitmapImage^ get() { return this->currentBackgroundImage; } + } + + void TransitionToBlurredImage(Windows::UI::Xaml::Media::Imaging::BitmapImage^ newImage); + + void SetPageBackgroundBorder(Windows::UI::Xaml::Controls::Border^ border); + void SetBackgroundTransitionSettings(double durationMs, double overlayOpacity); + }; +} diff --git a/UI/Pages/AppPage/AppPage.Resources.Grid.xaml b/UI/Pages/AppPage/AppPage.Resources.Grid.xaml new file mode 100644 index 00000000..5769e0ea --- /dev/null +++ b/UI/Pages/AppPage/AppPage.Resources.Grid.xaml @@ -0,0 +1,106 @@ + + + + + + + + + diff --git a/UI/Pages/AppPage/AppPage.Resources.List.xaml b/UI/Pages/AppPage/AppPage.Resources.List.xaml new file mode 100644 index 00000000..59d56e5b --- /dev/null +++ b/UI/Pages/AppPage/AppPage.Resources.List.xaml @@ -0,0 +1,164 @@ + + + + + + + + + diff --git a/UI/Pages/AppPage/AppPage.Resources.xaml b/UI/Pages/AppPage/AppPage.Resources.xaml new file mode 100644 index 00000000..68ed290a --- /dev/null +++ b/UI/Pages/AppPage/AppPage.Resources.xaml @@ -0,0 +1,102 @@ + + + + + + 0:0:0.125 + + + 1.5 + 0.2 + -150 + + + 0.66 + 16 + 0,0,0,0 + 32,0,32,0 + + 1.0 + 1.2 + 1.15 + 0.75 + 1.0 + + 0.4 + 0.0 + + 0.0 + 0.6 + + + + + + + + + + + diff --git a/UI/Pages/AppPage/AppPage.Selection.cpp b/UI/Pages/AppPage/AppPage.Selection.cpp new file mode 100644 index 00000000..ea5d6e33 --- /dev/null +++ b/UI/Pages/AppPage/AppPage.Selection.cpp @@ -0,0 +1,755 @@ +#include "pch.h" +#include +#include +#include +#include +#include "AppPage.xaml.h" +#include "UI\Utilities\ImageHelpers.h" +#define MLOG_TAG_OVERRIDE "AppPage" +#include "Utils.hpp" + +using namespace Platform; +using namespace Windows::ApplicationModel::Core; +using namespace Windows::Foundation; +using namespace Windows::UI::Core; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Hosting; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::Graphics::Imaging; +using namespace Windows::Graphics::Display; +using namespace Windows::UI::Xaml::Media::Imaging; +using namespace Windows::Storage; +using namespace Windows::Storage::Streams; +using namespace concurrency; + +namespace moonlight_xbox_dx { + +namespace { + +static Platform::String ^ BlurCachePath(Platform::String ^ imagePath, bool isGlow) { + if (imagePath == nullptr) return nullptr; + const wchar_t *s = imagePath->Data(); + int len = (int)wcslen(s); + int lastSlash = -1, lastDot = -1; + for (int i = len - 1; i >= 0; i--) { + if (s[i] == L'\\' && lastSlash < 0) lastSlash = i; + if (s[i] == L'.' && lastDot < 0) lastDot = i; + } + if (lastSlash < 0 || lastDot <= lastSlash) return nullptr; + auto dir = ref new Platform::String(s, lastSlash + 1); + auto base = ref new Platform::String(s + lastSlash + 1, lastDot - lastSlash - 1); + auto blurDir = Platform::String::Concat(dir, ref new Platform::String(L"blur\\")); + auto suffix = ref new Platform::String(isGlow ? L"_glow.png" : L"_bg.png"); + return Platform::String::Concat(blurDir, Platform::String::Concat(base, suffix)); +} + +static void SaveBlurStreamSync(IRandomAccessStream ^ stream, Platform::String ^ filePath) { + if (stream == nullptr || filePath == nullptr) return; + const wchar_t *s = filePath->Data(); + int len = (int)wcslen(s); + int lastSlash = -1; + for (int i = len - 1; i >= 0; i--) { + if (s[i] == L'\\') { + lastSlash = i; + break; + } + } + if (lastSlash < 0) return; + auto dirPath = ref new Platform::String(s, lastSlash); + auto fileName = ref new Platform::String(s + lastSlash + 1); + CreateDirectory(dirPath->Data(), nullptr); + auto folder = create_task(StorageFolder::GetFolderFromPathAsync(dirPath)).get(); + auto file = create_task(folder->CreateFileAsync(fileName, + CreationCollisionOption::ReplaceExisting)) + .get(); + auto fs = create_task(file->OpenAsync(FileAccessMode::ReadWrite)).get(); + create_task(RandomAccessStream::CopyAsync( + stream->GetInputStreamAt(0), fs->GetOutputStreamAt(0))) + .get(); + create_task(fs->FlushAsync()).get(); +} + +static concurrency::task OpenBlurCacheStreamAsync(Platform::String ^ filePath) { + if (filePath == nullptr || GetFileAttributes(filePath->Data()) == INVALID_FILE_ATTRIBUTES) + return task_from_result(nullptr); + return create_task(StorageFile::GetFileFromPathAsync(filePath)) + .then([](StorageFile ^ file) -> concurrency::task { + if (file == nullptr) return task_from_result(nullptr); + return create_task(file->OpenReadAsync()) + .then([](IRandomAccessStream ^ s) -> IRandomAccessStream ^ { return s; }); + }); +} + +} + +void AppPage::AppsGrid_SelectionChanged(Platform::Object ^ sender, SelectionChangedEventArgs ^ e) { + + if (m_suppressSelectionVisuals) return; + + auto lv = dynamic_cast(sender); + if (lv == nullptr || lv->SelectedIndex < 0) return; + + auto app = dynamic_cast(lv->SelectedItem); + if (app == nullptr) return; + if (app == nullptr) return; + + ApplySelectionVisuals(app, true); +} + +void AppPage::ApplySelectionVisuals(MoonlightApp ^ app, bool animate, bool centerImmediate) { + + if (app == nullptr) return; + + MoonlightApp ^ prev = m_selectedApp; + if (prev != nullptr && prev->Id != app->Id) { + try { + prev->IsSelected = false; + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "ApplySelectionVisuals: prev->IsSelected=false failed for app id=%d, stale selected visual may persist\n", prev->Id); + } + UpdateContainerSelectionState(prev, false, animate); + } + + m_selectedApp = app; + try { + app->IsSelected = true; + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "ApplySelectionVisuals: app->IsSelected=true failed for app id=%d, selection visual desynced\n", app->Id); + } + UpdateContainerSelectionState(app, true, animate); + + if (app->BlurredImage == nullptr) + BlurAppImage(app); + else + FadeInBlurIfSelected(app, app->BlurredImage); + + UpdateSelectedAppBox(app, prev, animate); + + if (centerImmediate) { + CenterSelectedItem(true); + return; + } + + CenterSelectedItem(false); + + try { + if (m_centerDebounceTimer == nullptr) { + m_centerDebounceTimer = ref new Windows::UI::Xaml::DispatcherTimer(); + m_centerDebounceTimer->Interval = Windows::Foundation::TimeSpan{800000LL}; + Platform::WeakReference weakThis(this); + m_centerDebounceTimer_token = m_centerDebounceTimer->Tick += + ref new Windows::Foundation::EventHandler([weakThis](Platform::Object ^, Platform::Object ^) { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + try { + that->m_centerDebounceTimer->Stop(); + } catch (...) { + } + try { + that->CenterSelectedItem(false); + } catch (...) { + } + }); + } + m_centerDebounceTimer->Stop(); + m_centerDebounceTimer->Start(); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "ApplySelectionVisuals failed to set up center debounce timer, debounced re-centering permanently disabled"); + } +} + +void AppPage::UpdateContainerSelectionState(MoonlightApp ^ app, bool isSelected, bool animate) { + if (app == nullptr) return; + Windows::UI::Xaml::Controls::ListView ^ grids[] = {this->AppsListView, this->AppsGridView}; + for (auto grid : grids) { + if (grid == nullptr) continue; + try { + auto container = dynamic_cast(grid->ContainerFromItem(app)); + if (container == nullptr) continue; + VisualStateManager::GoToState(container, isSelected ? "Selected" : "Unselected", animate); + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "UpdateContainerSelectionState: GoToState failed for app id=%d, isSelected=%d\n", app->Id, isSelected); + } + } +} + +void AppPage::UpdateSelectedAppBox(MoonlightApp ^ app, MoonlightApp ^ prev, bool animate) { + try { + if (this->SelectedAppText == nullptr || this->SelectedAppBox == nullptr) return; + Platform::String ^ newText = app->Name != nullptr ? app->Name : ref new Platform::String(L""); + this->SelectedAppBox->Visibility = Windows::UI::Xaml::Visibility::Visible; + this->SelectedAppText->Visibility = Windows::UI::Xaml::Visibility::Visible; + this->SelectedAppText->Foreground = ref new SolidColorBrush(Windows::UI::Colors::White); + + Windows::UI::Xaml::Media::Animation::Storyboard ^ showSb = nullptr; + Windows::UI::Xaml::Media::Animation::Storyboard ^ hideSb = nullptr; + try { + auto res = this->Resources; + if (res != nullptr) { + try { + showSb = dynamic_cast(res->Lookup(ref new Platform::String(L"ShowSelectedAppStoryboard"))); + } catch (...) { + } + try { + hideSb = dynamic_cast(res->Lookup(ref new Platform::String(L"HideSelectedAppStoryboard"))); + } catch (...) { + } + } + } catch (...) { + } + + const unsigned int animVer = ++m_appTextAnimVersion; + auto weakThis = WeakReference(this); + auto capturedText = newText; + AnimateCrossfadeText( + this->SelectedAppBox, showSb, hideSb, animate && prev != nullptr, + [weakThis, capturedText]() { + auto that = weakThis.Resolve(); + if (that != nullptr && that->SelectedAppText != nullptr) + that->SelectedAppText->Text = capturedText; + }, + [weakThis, animVer]() { + auto that = weakThis.Resolve(); + return that != nullptr && that->m_appTextAnimVersion == animVer; + }); + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "ApplySelectionVisuals: text overlay update failed for app id=%d\n", app->Id); + } +} + +void AppPage::CenterSelectedItem(bool immediate) { + + auto lv = this->ActiveAppsGrid(); + ListViewItem ^ container = nullptr; + auto status = ResolveSelectedContainer(lv, m_scrollViewer, container); + if (status == SelectedContainerStatus::NoSelection) return; + if (status == SelectedContainerStatus::ScrollViewerMissing) { + WaitForScrollViewerThenCenter(immediate); + return; + } + if (status == SelectedContainerStatus::ContainerMissing) { + WaitForContainerThenCenter(immediate); + return; + } + + try { + lv->UpdateLayout(); + } catch (...) { + } + if (container->ActualWidth <= 0.0 || container->ActualHeight <= 0.0) return; + + double viewport = m_isGridLayout ? m_scrollViewer->ViewportHeight : m_scrollViewer->ViewportWidth; + if (viewport <= 0.0) { + try { + lv->UpdateLayout(); + } catch (...) { + } + viewport = m_isGridLayout ? m_scrollViewer->ViewportHeight : m_scrollViewer->ViewportWidth; + } + if (viewport <= 0.0) return; + + try { + double desiredEdgePadding = m_isGridLayout + ? 0.0 + : std::max(0.0, (viewport - container->ActualWidth) * 0.5); + if (ApplyEdgeCenteringPadding(lv, desiredEdgePadding) == EdgeCenteringPaddingResult::Applied) { + try { + lv->UpdateLayout(); + } catch (...) { + } + } + } catch (...) { + } + + if (!m_initialFocusApplied) { + m_initialFocusApplied = true; + try { + if (container != nullptr) + container->Focus(Windows::UI::Xaml::FocusState::Programmatic); + else + lv->Focus(Windows::UI::Xaml::FocusState::Programmatic); + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "CenterSelectedItem: initial Focus() failed\n"); + } + } + + CenterContainerInScrollViewer(m_scrollViewer, container, m_isGridLayout, immediate); + + RevealActiveAppsGrid(); + + m_entranceAnimationsArmed = false; +} + +void AppPage::WaitForContainerThenCenter(bool immediate) { + auto lv = this->ActiveAppsGrid(); + if (lv == nullptr || m_centerWaitContainer_token.Value != 0) return; + m_centerWaitContainerTarget = lv; + Platform::WeakReference weakThis(this); + m_centerWaitContainer_token = ArmContainerRealizedWait(lv, [weakThis, immediate]() { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + that->m_centerWaitContainer_token.Value = 0; + that->m_centerWaitContainerTarget = nullptr; + that->CenterSelectedItem(immediate); + }); + if (m_centerWaitContainer_token.Value == 0) m_centerWaitContainerTarget = nullptr; +} + +void AppPage::WaitForScrollViewerThenCenter(bool immediate) { + auto lv = this->ActiveAppsGrid(); + if (lv == nullptr || m_centerWaitScrollViewer_token.Value != 0) return; + m_centerWaitScrollViewerTarget = lv; + Platform::WeakReference weakThis(this); + m_centerWaitScrollViewer_token = ArmLayoutUpdatedWait(lv, [weakThis, immediate]() { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + that->m_centerWaitScrollViewer_token.Value = 0; + that->m_centerWaitScrollViewerTarget = nullptr; + that->CenterSelectedItem(immediate); + }); +} + +void AppPage::UpdateItemHeights() { + try { + if (m_isGridLayout) return; + if (this->AppsListView == nullptr) return; + + for (unsigned int i = 0; i < this->AppsListView->Items->Size; ++i) { + auto container = dynamic_cast(this->AppsListView->ContainerFromIndex(i)); + if (container == nullptr) continue; + + std::function find = [&](DependencyObject ^ parent) -> DependencyObject ^ { + if (parent == nullptr) return nullptr; + int count = VisualTreeHelper::GetChildrenCount(parent); + for (int j = 0; j < count; ++j) { + auto child = VisualTreeHelper::GetChild(parent, j); + auto fe = dynamic_cast(child); + if (fe != nullptr && fe->GetType()->FullName == "moonlight_xbox_dx.AspectRatioBox") return child; + auto rec = find(child); + if (rec != nullptr) return rec; + } + return nullptr; + }; + + auto found = find(container); + if (found == nullptr) continue; + auto fe = dynamic_cast(found); + if (fe == nullptr) continue; + if (std::isnan(fe->Height)) continue; + + fe->Height = std::nan(""); + fe->InvalidateMeasure(); + fe->UpdateLayout(); + } + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "UpdateItemHeights: failed, stale Height may cause clipped artwork\n"); + } +} + +void AppPage::ArmEntranceAnimations() { + m_entranceAnimationsArmed = true; + m_gridEntranceAnimatedIndices.clear(); + m_listEntranceAnimatedIndices.clear(); +} + +void AppPage::AppsGrid_ContainerContentChanging( + Windows::UI::Xaml::Controls::ListViewBase ^ sender, + Windows::UI::Xaml::Controls::ContainerContentChangingEventArgs ^ args) { + auto container = dynamic_cast(args->ItemContainer); + if (container == nullptr) return; + + if (args->InRecycleQueue) { + try { + Windows::UI::Xaml::Thickness zero; + zero.Left = zero.Top = zero.Right = zero.Bottom = 0.0; + container->Margin = zero; + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "AppsGrid_ContainerContentChanging: Margin reset failed on recycle, stale margin may persist into reuse\n"); + } + return; + } + + auto &entranceAnimated = (sender == this->AppsGridView) ? m_gridEntranceAnimatedIndices : m_listEntranceAnimatedIndices; + + if (args->Phase == 0) { + try { + if (entranceAnimated.find(args->ItemIndex) == entranceAnimated.end()) { + entranceAnimated.insert(args->ItemIndex); + bool isCurrentSelection = sender->SelectedItem != nullptr && args->Item == sender->SelectedItem; + if (m_entranceAnimationsArmed && !isCurrentSelection) + PlayEntranceAnimation(container, args->ItemIndex); + } + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "AppsGrid_ContainerContentChanging: entrance animation failed\n"); + } + + Platform::WeakReference weakThis(this); + args->RegisterUpdateCallback( + ref new TypedEventHandler( + [weakThis](ListViewBase ^ s, ContainerContentChangingEventArgs ^ a) { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + try { + that->AppsGrid_ContainerContentChanging(s, a); + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "AppsGrid_ContainerContentChanging: Phase1 recursive dispatch failed\n"); + } + })); + return; + } + + try { + auto app = dynamic_cast(args->Item); + if (app != nullptr) { + VisualStateManager::GoToState(container, app->IsSelected ? "Selected" : "Unselected", false); + if (app->IsSelected && app->BlurredImage != nullptr) + FadeInBlurIfSelected(app, app->BlurredImage); + } + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "AppsGrid_ContainerContentChanging: Phase1 blur recovery failed\n"); + } +} + +void AppPage::PlayEntranceAnimation(Windows::UI::Xaml::Controls::ListViewItem ^ container, int index) { + using namespace Windows::UI::Xaml::Media; + using namespace Windows::UI::Xaml::Media::Animation; + + auto prevSb = dynamic_cast(container->Tag); + if (prevSb != nullptr) { + try { + prevSb->Stop(); + } catch (...) { + } + } + + auto scale = ref new ScaleTransform(); + scale->ScaleX = kEntranceStartScale; + scale->ScaleY = kEntranceStartScale; + container->RenderTransformOrigin = Windows::Foundation::Point(0.5f, 0.5f); + container->RenderTransform = scale; + container->Opacity = 0.0; + + int staggerIndex = index < kEntranceMaxStaggerItems ? index : kEntranceMaxStaggerItems; + Windows::UI::Xaml::Duration animDuration(TimeSpan{(long long)kEntranceDurationMs * 10000LL}); + TimeSpan beginTime{(long long)staggerIndex * kEntranceStaggerMs * 10000LL}; + + auto ease = ref new QuadraticEase(); + ease->EasingMode = EasingMode::EaseOut; + + auto sb = ref new Storyboard(); + + auto opacityAnim = ref new DoubleAnimation(); + opacityAnim->To = 1.0; + opacityAnim->Duration = animDuration; + opacityAnim->BeginTime = beginTime; + opacityAnim->EasingFunction = ease; + Storyboard::SetTarget(opacityAnim, container); + Storyboard::SetTargetProperty(opacityAnim, ref new Platform::String(L"Opacity")); + sb->Children->Append(opacityAnim); + + auto scaleXAnim = ref new DoubleAnimation(); + scaleXAnim->To = 1.0; + scaleXAnim->Duration = animDuration; + scaleXAnim->BeginTime = beginTime; + scaleXAnim->EasingFunction = ease; + scaleXAnim->EnableDependentAnimation = true; + Storyboard::SetTarget(scaleXAnim, container); + Storyboard::SetTargetProperty(scaleXAnim, ref new Platform::String(L"(UIElement.RenderTransform).(ScaleTransform.ScaleX)")); + sb->Children->Append(scaleXAnim); + + auto scaleYAnim = ref new DoubleAnimation(); + scaleYAnim->To = 1.0; + scaleYAnim->Duration = animDuration; + scaleYAnim->BeginTime = beginTime; + scaleYAnim->EasingFunction = ease; + scaleYAnim->EnableDependentAnimation = true; + Storyboard::SetTarget(scaleYAnim, container); + Storyboard::SetTargetProperty(scaleYAnim, ref new Platform::String(L"(UIElement.RenderTransform).(ScaleTransform.ScaleY)")); + sb->Children->Append(scaleYAnim); + + container->Tag = sb; + sb->Begin(); +} + +void AppPage::BlurAppImage(MoonlightApp ^ selApp) { + try { + if (selApp->BlurredImage != nullptr) return; + if (m_blurInProgressIds.count(selApp->Id)) return; + + static Platform::String ^ placeholderImagePath = ref new Platform::String(L"ms-appx:///Assets/gamepad.svg"); + if (selApp->ImagePath == nullptr || selApp->ImagePath->Equals(placeholderImagePath)) { + Platform::WeakReference weakThis(this); + Platform::WeakReference weakApp(selApp); + auto token = std::make_shared(); + *token = selApp->PropertyChanged += ref new Windows::UI::Xaml::Data::PropertyChangedEventHandler( + [weakThis, weakApp, token](Platform::Object ^, Windows::UI::Xaml::Data::PropertyChangedEventArgs ^ e) { + if (e->PropertyName != "ImagePath") return; + auto app = weakApp.Resolve(); + if (app == nullptr) return; + try { app->PropertyChanged -= *token; } catch (...) { } + auto that = weakThis.Resolve(); + if (that == nullptr) return; + try { that->BlurAppImage(app); } catch (...) { } + }); + return; + } + + m_blurInProgressIds.insert(selApp->Id); + bool isGrid = this->m_isGridLayout; + Platform::WeakReference weakThis(this); + + float glowBlurAmount = 16.0f; + try { + auto boxed = Resources->Lookup("BlurAmount"); + auto pv = dynamic_cast(boxed); + if (pv != nullptr) glowBlurAmount = (float)pv->GetDouble(); + } catch (...) { + } + + auto getOrComputeStream = [this, selApp](float blurDip, float padDip, + Platform::String ^ cachePath) + -> concurrency::task { + if (cachePath != nullptr && GetFileAttributes(cachePath->Data()) != INVALID_FILE_ATTRIBUTES) + return OpenBlurCacheStreamAsync(cachePath); + return ApplyBlur(selApp, blurDip, padDip) + .then([cachePath](IRandomAccessStream ^ stream) -> IRandomAccessStream ^ { + if (stream != nullptr && cachePath != nullptr) { + try { + SaveBlurStreamSync(stream, cachePath); + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "BlurAppImage: cache write failed, blur will be recomputed every launch\n"); + } + stream->Seek(0); + } + return stream; + }, + concurrency::task_continuation_context::use_arbitrary()); + }; + + auto bgCachePath = BlurCachePath(selApp->ImagePath, false); + try { + getOrComputeStream(kBlurAmountBackground, 0.0f, bgCachePath) + .then([selApp, weakThis](IRandomAccessStream ^ stream) { + try { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + that->HandleBlurStreamReady(selApp, weakThis, stream, true); + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "BlurAppImage: background RunAsync dispatch failed for app id=%d, m_blurInProgressIds entry stuck\n", selApp->Id); + } + }, + concurrency::task_continuation_context::use_current()); + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "BlurAppImage: background getOrComputeStream setup failed for app id=%d, m_blurInProgressIds entry stuck\n", selApp->Id); + } + + if (!isGrid) { + auto glowCachePath = BlurCachePath(selApp->ImagePath, true); + try { + getOrComputeStream(glowBlurAmount, kBlurGlowPaddingDip, glowCachePath) + .then([selApp, weakThis](IRandomAccessStream ^ stream) { + try { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + that->HandleBlurStreamReady(selApp, weakThis, stream, false); + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "BlurAppImage: glow RunAsync dispatch failed for app id=%d, GlowImage permanently null\n", selApp->Id); + } + }, + concurrency::task_continuation_context::use_current()); + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "BlurAppImage: glow getOrComputeStream setup failed for app id=%d, GlowImage permanently null\n", selApp->Id); + } + } + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "BlurAppImage: unhandled failure for app id=%d, m_blurInProgressIds entry may be stuck\n", selApp->Id); + } +} + +concurrency::task AppPage::ApplyBlur(MoonlightApp ^ app, float blurDip, float padDip) { + if (app == nullptr) return concurrency::task_from_result(nullptr); + + Platform::String ^ path = app->ImagePath; + + return concurrency::create_task(ImageHelpers::LoadSoftwareBitmapFromUriOrPathAsync(path)) + .then([this, app, blurDip, padDip](SoftwareBitmap ^ softwareBitmap) -> concurrency::task { + if (softwareBitmap == nullptr) return concurrency::task_from_result(nullptr); + + unsigned int ui_targetW = 0, ui_targetH = 0; + double ui_dpi = 96.0; + try { + auto activeGrid = this->ActiveAppsGrid(); + FrameworkElement ^ fe_for_size = dynamic_cast(this->FindName("AppImageRect")); + if (fe_for_size == nullptr && activeGrid != nullptr) { + auto container = dynamic_cast(activeGrid->ContainerFromItem(app)); + if (container != nullptr) { + auto found = FindChildByName(container, ref new Platform::String(L"AppImageRect")); + if (found == nullptr) found = FindChildByName(container, ref new Platform::String(L"AppImageBlurRect")); + fe_for_size = dynamic_cast(found); + } + } + + if (fe_for_size != nullptr) { + double aw = 0.0, ah = 0.0; + try { + aw = fe_for_size->ActualWidth; + } catch (...) { + } + try { + ah = fe_for_size->ActualHeight; + } catch (...) { + } + + if ((aw <= 0 || ah <= 0) && activeGrid != nullptr) { + try { + auto container = dynamic_cast(activeGrid->ContainerFromItem(app)); + if (container != nullptr) { + auto tryFE = [&](const wchar_t *name) { + auto fe = dynamic_cast(FindChildByName(container, ref new Platform::String(name))); + if (fe != nullptr) { + try { + if (aw <= 0) aw = fe->ActualWidth; + } catch (...) { + } + try { + if (ah <= 0) ah = fe->ActualHeight; + } catch (...) { + } + } + }; + tryFE(L"AppAspectRatioBox"); + tryFE(L"ItemGrid"); + tryFE(L"AppImageBlurRect"); + if (aw <= 0) { + try { + aw = container->ActualWidth; + } catch (...) { + } + } + } + if (aw <= 0) { + try { + aw = activeGrid->ActualWidth; + } catch (...) { + } + } + } catch (...) { + } + } + + if (aw > 0 && ah > 0) { + try { + auto di = DisplayInformation::GetForCurrentView(); + double dpi = di != nullptr ? di->LogicalDpi : 96.0; + ui_dpi = dpi; + ui_targetW = (unsigned int)std::max(1u, (unsigned int)std::round(aw * dpi / 96.0)); + ui_targetH = (unsigned int)std::max(1u, (unsigned int)std::round(ah * dpi / 96.0)); + } catch (...) { + ui_targetW = ui_targetH = 0; + ui_dpi = 96.0; + } + } + } + } catch (...) { + ui_targetW = ui_targetH = 0; + } + + unsigned int targetW = ui_targetW != 0 ? ui_targetW : softwareBitmap->PixelWidth; + unsigned int targetH = ui_targetH != 0 ? ui_targetH : softwareBitmap->PixelHeight; + + unsigned int glowTargetW = targetW, glowTargetH = targetH; + if (padDip > 0.0f && targetW > 0 && targetH > 0) { + unsigned int padPx = (unsigned int)std::round((double)padDip * ui_dpi / 96.0); + if (padPx > 0) { + glowTargetW = targetW + 2 * padPx; + glowTargetH = targetH + 2 * padPx; + } + } + + try { + ImageHelpers::AdjustSaturation(softwareBitmap, kBackgroundSaturation); + } catch (...) { + } + return ImageHelpers::CreateMaskedBlurredPngStreamAsync( + softwareBitmap, glowTargetW, glowTargetH, ui_dpi, blurDip); + }); +} + +void AppPage::HandleBlurStreamReady(MoonlightApp ^ selApp, Platform::WeakReference weakThis, + IRandomAccessStream ^ stream, bool isBackground) { + if (stream == nullptr) { + if (!isBackground) return; + MLOGF(Utils::LogLevel::Warning, "BlurAppImage: background stream null for app id=%d\n", selApp->Id); + m_blurInProgressIds.erase(selApp->Id); + return; + } + + try { + if (selApp == nullptr) return; + auto img = ref new BitmapImage(); + create_task(img->SetSourceAsync(stream)) + .then([weakThis, selApp, img, isBackground]() { + try { + auto thatCont = weakThis.Resolve(); + if (thatCont == nullptr) return; + if (isBackground) { + thatCont->m_blurInProgressIds.erase(selApp->Id); + selApp->BlurredImage = img; + if (thatCont->m_selectedApp != nullptr && + thatCont->m_selectedApp != selApp && + thatCont->m_selectedApp->Id == selApp->Id) + thatCont->m_selectedApp->BlurredImage = img; + try { + thatCont->FadeInBlurIfSelected(selApp, img); + } catch (...) { + } + } else { + selApp->GlowImage = img; + if (thatCont->m_selectedApp != nullptr && + thatCont->m_selectedApp != selApp && + thatCont->m_selectedApp->Id == selApp->Id) + thatCont->m_selectedApp->GlowImage = img; + } + } catch (...) { + if (isBackground) + MLOGF(Utils::LogLevel::Warning, "BlurAppImage: assign-BlurredImage step failed for app id=%d, m_blurInProgressIds entry stuck\n", selApp->Id); + else + MLOGF(Utils::LogLevel::Warning, "BlurAppImage: assign-GlowImage step failed for app id=%d, GlowImage permanently null (BlurredImage gate blocks retry)\n", selApp->Id); + } + }, + concurrency::task_continuation_context::use_current()); + } catch (...) { + if (isBackground) + MLOGF(Utils::LogLevel::Warning, "BlurAppImage: SetSourceAsync setup failed for app id=%d, m_blurInProgressIds entry stuck\n", selApp->Id); + else + MLOGF(Utils::LogLevel::Warning, "BlurAppImage: glow SetSourceAsync setup failed for app id=%d, GlowImage permanently null\n", selApp->Id); + } +} + +void AppPage::FadeInBlurIfSelected(MoonlightApp ^ app, BitmapImage ^ img) { + if (app == nullptr || img == nullptr) return; + + bool isSelected = false; + try { + auto activeGrid = this->ActiveAppsGrid(); + if (activeGrid != nullptr) { + auto selItem = dynamic_cast(activeGrid->SelectedItem); + if (selItem != nullptr && selItem->Id == app->Id) isSelected = true; + } + if (!isSelected && m_selectedApp != nullptr && m_selectedApp->Id == app->Id) + isSelected = true; + } catch (...) { + } + + if (!isSelected) return; + + try { + auto vm = this->ViewModel; + if (vm != nullptr) vm->TransitionToBlurredImage(img); + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "FadeInBlurIfSelected: TransitionToBlurredImage failed for app id=%d\n", app->Id); + } +} + +} diff --git a/UI/Pages/AppPage/AppPage.xaml b/UI/Pages/AppPage/AppPage.xaml new file mode 100644 index 0000000000000000000000000000000000000000..d26877d04d6ec63168812e5d88d93a297604d4c5 GIT binary patch literal 25728 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 +#include +#include +#include +#include "State\MoonlightClient.h" +#include "UI\Controls\SlidingMenu.xaml.h" +#include "UI\Modals\AlertDialog.xaml.h" +#include "UI\Modals\AppActionsDialog.xaml.h" +#include "UI\Modals\ConfirmDialog.xaml.h" +#include "UI\Models\ViewModels\AppPageViewModel.h" +#include "UI\Pages\HostSelectorPage.xaml.h" +#include "UI\Pages\HostSettingsPage.xaml.h" +#include "UI\Pages\MoonlightSettings.xaml.h" +#include "UI\Pages\StreamPage.xaml.h" +#define MLOG_TAG_OVERRIDE "AppPage" +#include "Utils.hpp" + +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::ApplicationModel::Core; +using namespace Windows::UI::Core; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Input; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Hosting; +using namespace Windows::UI::Xaml::Navigation; +using namespace Windows::Foundation::Numerics; +using namespace concurrency; + +namespace moonlight_xbox_dx { + +namespace { + +static double ResolveBackgroundOverlayOpacityFromPageResources(Page ^ page) { + double overlayOpacity = 0.05; + if (page == nullptr || page->Resources == nullptr) return overlayOpacity; + + try { + auto value = page->Resources->Lookup(ref new Platform::String(L"BackgroundOverlayOpacity")); + auto pv = dynamic_cast(value); + if (pv != nullptr) { + overlayOpacity = pv->GetDouble(); + } + } catch (...) { + } + + if (overlayOpacity < 0.0) overlayOpacity = 0.0; + if (overlayOpacity > 1.0) overlayOpacity = 1.0; + return overlayOpacity; +} + +static double ResolveSharedAnimationDurationMsFromPageResources(Page ^ page) { + if (page == nullptr || page->Resources == nullptr) return 250.0; + + try { + auto value = page->Resources->Lookup(ref new Platform::String(L"SharedAnimationDuration")); + auto durationValue = dynamic_cast(value); + return Utils::DurationStringToMs(durationValue); + } catch (...) { + } + + return 250.0; +} + +static void FindElementChildren(DependencyObject ^ container, + UIElement ^ &outDesaturator, UIElement ^ &outImage, UIElement ^ &outName, + UIElement ^ &outBlur, UIElement ^ &outPlay) { + outDesaturator = outImage = outName = outBlur = outPlay = nullptr; + if (container == nullptr) return; + try { + outDesaturator = dynamic_cast(FindChildByName(container, ref new Platform::String(L"Desaturator"))); + outImage = dynamic_cast(FindChildByName(container, ref new Platform::String(L"AppImageRect"))); + outName = dynamic_cast(FindChildByName(container, ref new Platform::String(L"AppName"))); + outBlur = dynamic_cast(FindChildByName(container, ref new Platform::String(L"AppImageBlurRect"))); + outPlay = dynamic_cast(FindChildByName(container, ref new Platform::String(L"Play"))); + } catch (...) { + outDesaturator = outImage = outName = outBlur = outPlay = nullptr; + } +} + +} + +Controls::SlidingMenu ^ AppPage::GetLeftMenu() { + try { + if (m_leftMenu == nullptr) m_leftMenu = ref new Controls::SlidingMenu(); + return m_leftMenu; + } catch (...) { + return nullptr; + } +} + +AppPage::AppPage() { + InitializeComponent(); + Windows::UI::ViewManagement::ApplicationView::GetForCurrentView() + ->SetDesiredBoundsMode(Windows::UI::ViewManagement::ApplicationViewBoundsMode::UseCoreWindow); + + m_apps_changed_token.Value = m_back_cookie.Value = m_keydown_cookie.Value = 0; + m_centerWaitContainer_token.Value = 0; + m_centerWaitScrollViewer_token.Value = 0; + m_appsListView_selection_token.Value = m_appsListView_itemclick_token.Value = 0; + m_appsListView_righttapped_token.Value = 0; + m_appsListView_loaded_token.Value = m_appsListView_ccc_token.Value = 0; + m_appsGridView_selection_token.Value = m_appsGridView_itemclick_token.Value = 0; + m_appsGridView_righttapped_token.Value = 0; + m_appsGridView_loaded_token.Value = m_appsGridView_ccc_token.Value = 0; + m_searchbox_gettingfocus_token.Value = 0; + m_centerDebounceTimer_token.Value = 0; + + { + auto weakThis = WeakReference(this); + this->Loaded += ref new RoutedEventHandler([weakThis](Platform::Object ^ s, RoutedEventArgs ^ e) { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + try { + that->OnLoaded(s, e); + } catch (...) { + MLOGF(Utils::LogLevel::Error, "OnLoaded failed, page lifecycle wiring may be incomplete\n"); + } + }); + } + { + auto weakThis = WeakReference(this); + this->Unloaded += ref new RoutedEventHandler([weakThis](Platform::Object ^ s, RoutedEventArgs ^ e) { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + try { + that->OnUnloaded(s, e); + } catch (...) { + MLOGF(Utils::LogLevel::Error, "OnUnloaded failed, cleanup may be incomplete\n"); + } + }); + } + try { + WireAppsGridEvents(this->AppsListView, + m_appsListView_selection_token, m_appsListView_itemclick_token, + m_appsListView_righttapped_token, m_appsListView_loaded_token, m_appsListView_ccc_token); + } catch (...) { + MLOGF(Utils::LogLevel::Error, "AppsListView event wiring failed, selection/click/layout events permanently unavailable\n"); + } + + try { + WireAppsGridEvents(this->AppsGridView, + m_appsGridView_selection_token, m_appsGridView_itemclick_token, + m_appsGridView_righttapped_token, m_appsGridView_loaded_token, m_appsGridView_ccc_token); + } catch (...) { + MLOGF(Utils::LogLevel::Error, "AppsGridView event wiring failed, selection/click/layout events permanently unavailable\n"); + } + + try { + if (this->SearchBox != nullptr) { + auto weakThis = WeakReference(this); + m_searchbox_gettingfocus_token = this->SearchBox->GettingFocus += + ref new Windows::Foundation::TypedEventHandler< + Windows::UI::Xaml::UIElement ^, + Windows::UI::Xaml::Input::GettingFocusEventArgs ^>( + [weakThis](Windows::UI::Xaml::UIElement ^, Windows::UI::Xaml::Input::GettingFocusEventArgs ^ args) { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + try { + if (that->m_searchIsOpen) return; + if (args->Direction == FocusNavigationDirection::Up) { + that->m_searchIsOpen = true; + return; + } + args->Cancel = true; + } catch (...) { + } + }); + } + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "SearchBox GettingFocus wiring failed, DPad-Up-to-search navigation permanently unavailable\n"); + } + + m_filteredApps = ref new Platform::Collections::Vector(); +} + +void AppPage::WireAppsGridEvents( + ListView ^ grid, + Windows::Foundation::EventRegistrationToken &selectionToken, + Windows::Foundation::EventRegistrationToken &itemClickToken, + Windows::Foundation::EventRegistrationToken &rightTappedToken, + Windows::Foundation::EventRegistrationToken &loadedToken, + Windows::Foundation::EventRegistrationToken &cccToken) { + if (grid == nullptr) return; + auto weakThis = WeakReference(this); + selectionToken = grid->SelectionChanged += + ref new SelectionChangedEventHandler([weakThis](Platform::Object ^ s, SelectionChangedEventArgs ^ e) { + auto that = weakThis.Resolve(); + if (that) try { + that->AppsGrid_SelectionChanged(s, e); + } catch (...) { + } + }); + itemClickToken = grid->ItemClick += + ref new ItemClickEventHandler([weakThis](Platform::Object ^ s, ItemClickEventArgs ^ e) { + auto that = weakThis.Resolve(); + if (that) try { + that->AppsGrid_ItemClick(s, e); + } catch (...) { + } + }); + rightTappedToken = grid->RightTapped += + ref new RightTappedEventHandler([weakThis](Platform::Object ^ s, RightTappedRoutedEventArgs ^ e) { + auto that = weakThis.Resolve(); + if (that) try { + that->AppsGrid_RightTapped(s, e); + } catch (...) { + } + }); + loadedToken = grid->Loaded += + ref new RoutedEventHandler([weakThis](Platform::Object ^ s, RoutedEventArgs ^ e) { + auto that = weakThis.Resolve(); + if (that) try { + that->AppsGrid_Loaded(s, e); + } catch (...) { + } + }); + cccToken = grid->ContainerContentChanging += + ref new TypedEventHandler( + [weakThis](ListViewBase ^ s, ContainerContentChangingEventArgs ^ args) { + auto that = weakThis.Resolve(); + if (that) try { + that->AppsGrid_ContainerContentChanging(s, args); + } catch (...) { + } + }); +} + +void AppPage::UnwireAppsGridEvents( + const char *gridName, + ListView ^ grid, + Windows::Foundation::EventRegistrationToken selectionToken, + Windows::Foundation::EventRegistrationToken itemClickToken, + Windows::Foundation::EventRegistrationToken rightTappedToken, + Windows::Foundation::EventRegistrationToken loadedToken, + Windows::Foundation::EventRegistrationToken cccToken) { + if (grid == nullptr) return; + try { + grid->SelectionChanged -= selectionToken; + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "OnUnloaded: %s SelectionChanged -= failed, handler leaked\n", gridName); + } + try { + grid->ItemClick -= itemClickToken; + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "OnUnloaded: %s ItemClick -= failed, handler leaked\n", gridName); + } + try { + grid->RightTapped -= rightTappedToken; + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "OnUnloaded: %s RightTapped -= failed, handler leaked\n", gridName); + } + try { + grid->Loaded -= loadedToken; + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "OnUnloaded: %s Loaded -= failed, handler leaked\n", gridName); + } + try { + grid->ContainerContentChanging -= cccToken; + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "OnUnloaded: %s ContainerContentChanging -= failed, handler leaked\n", gridName); + } +} + +void AppPage::OnLoaded(Platform::Object ^, RoutedEventArgs ^) { + Platform::WeakReference weakThis(this); + m_back_cookie = Windows::UI::Core::SystemNavigationManager::GetForCurrentView()->BackRequested += + ref new EventHandler([weakThis](Platform::Object ^ s, BackRequestedEventArgs ^ args) { + auto that = weakThis.Resolve(); + if (that) that->OnBackRequested(s, args); + }); + + try { + auto window = CoreApplication::MainView->CoreWindow; + if (window != nullptr) { + m_keydown_cookie = window->KeyDown += + ref new TypedEventHandler([weakThis](CoreWindow ^ s, KeyEventArgs ^ args) { + auto that = weakThis.Resolve(); + if (that) that->OnGamepadKeyDown(s, args); + }); + } + } catch (...) { + MLOGF(Utils::LogLevel::Error, "OnLoaded: KeyDown subscribe failed, gamepad input permanently unavailable this page instance\n"); + } + + try { + if (this->ViewModel != nullptr && this->PageBackgroundImage != nullptr) { + this->ViewModel->SetPageBackgroundBorder(this->PageBackgroundImage); + this->ViewModel->SetBackgroundTransitionSettings( + ResolveSharedAnimationDurationMsFromPageResources(this), + ResolveBackgroundOverlayOpacityFromPageResources(this)); + } + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "OnLoaded: ViewModel background setup failed, background transitions may not apply\n"); + } + + StartBgPanAnimation(); + StartBannerSlideInAnimation(); +} + +void AppPage::OnUnloaded(Platform::Object ^, RoutedEventArgs ^) { + if (m_selectedApp != nullptr) { + try { + m_selectedApp->IsSelected = false; + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "OnUnloaded: IsSelected=false failed for app id=%d\n", m_selectedApp->Id); + } + } + m_selectedApp = nullptr; + m_initialFocusApplied = false; + m_appsGridRevealed = false; + try { + Windows::UI::Core::SystemNavigationManager::GetForCurrentView()->BackRequested -= m_back_cookie; + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "OnUnloaded: BackRequested -= failed, handler leaked\n"); + } + try { + if (this->SearchBox != nullptr) this->SearchBox->GettingFocus -= m_searchbox_gettingfocus_token; + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "OnUnloaded: SearchBox GettingFocus -= failed, handler leaked\n"); + } + continueAppFetch.store(false); + try { + PollingIndicator->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "OnUnloaded: PollingIndicator visibility reset failed\n"); + } + + auto window = CoreApplication::MainView->CoreWindow; + if (window != nullptr) { + try { + window->KeyDown -= m_keydown_cookie; + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "OnUnloaded: KeyDown -= failed, handler leaked\n"); + } + } + + if (this->host != nullptr && this->host->Apps != nullptr) { + auto obs = dynamic_cast ^>(this->host->Apps); + if (obs != nullptr) { + try { + obs->VectorChanged -= m_apps_changed_token; + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "OnUnloaded: VectorChanged -= failed, handler leaked\n"); + } + } + } + + if (m_centerDebounceTimer != nullptr) { + try { + m_centerDebounceTimer->Stop(); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "OnUnloaded: m_centerDebounceTimer->Stop() failed, timer may keep firing after teardown"); + } + try { + m_centerDebounceTimer->Tick -= m_centerDebounceTimer_token; + } catch (...) { + MLOG(Utils::LogLevel::Warning, "OnUnloaded: m_centerDebounceTimer Tick -= failed, handler leaked"); + } + m_centerDebounceTimer = nullptr; + } + + if (m_bgPanStoryboard != nullptr) { + m_bgPanStoryboard->Stop(); + m_bgPanStoryboard = nullptr; + } + UnwireAppsGridEvents("AppsListView", this->AppsListView, + m_appsListView_selection_token, m_appsListView_itemclick_token, + m_appsListView_righttapped_token, m_appsListView_loaded_token, m_appsListView_ccc_token); + UnwireAppsGridEvents("AppsGridView", this->AppsGridView, + m_appsGridView_selection_token, m_appsGridView_itemclick_token, + m_appsGridView_righttapped_token, m_appsGridView_loaded_token, m_appsGridView_ccc_token); + CancelContainerRealizedWait(m_centerWaitContainerTarget, m_centerWaitContainer_token); + m_centerWaitContainerTarget = nullptr; + CancelLayoutUpdatedWait(m_centerWaitScrollViewerTarget, m_centerWaitScrollViewer_token); + m_centerWaitScrollViewerTarget = nullptr; +} + +void AppPage::OnNavigatedTo(NavigationEventArgs ^ e) { + MoonlightHost ^ mhost = dynamic_cast(e->Parameter); + if (mhost == nullptr) return; + host = mhost; + + bool willAutoStartApp = (host->AutostartID >= 0 && GetApplicationState()->shouldAutoConnect); + + continueAppFetch.store(true); + wasConnected.store(true); + if (!willAutoStartApp) FadeInPollingIndicator(); + + try { + if (this->ClosingOverlayText != nullptr) + this->ClosingOverlayText->Text = ref new Platform::String(L"Loading apps..."); + if (this->ClosingOverlay != nullptr) { + this->ClosingOverlay->Opacity = 1.0; + this->ClosingOverlay->Visibility = Windows::UI::Xaml::Visibility::Visible; + } + } catch (...) { + MLOG(Utils::LogLevel::Warning, "OnNavigatedTo: failed to configure closing overlay text/visibility"); + } + + { + Platform::WeakReference weakThis(this); + create_task([weakThis]() { + auto that = weakThis.Resolve(); + if (that == nullptr || !that->continueAppFetch.load()) return; + that->host->UpdateApps(); + }); + } + + if (!willAutoStartApp) { + Platform::WeakReference weakThis(this); + create_task([weakThis]() { + while (true) { + auto that = weakThis.Resolve(); + if (that == nullptr) break; + if (!that->continueAppFetch.load()) break; + CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + CoreDispatcherPriority::Normal, + ref new DispatchedHandler([weakThis]() { + auto ui = weakThis.Resolve(); + if (ui) try { + ui->FadeInPollingIndicator(); + } catch (...) { + } + })); + try { + that->PollAppRunningAndConnectivity(); + } catch (const std::exception &e) { + MLOGF(Utils::LogLevel::Error, "Failed to poll app and host running state. Exception: %s\n", e.what()); + } catch (...) { + MLOG(Utils::LogLevel::Error, "Failed to poll app and host running state. Unknown Exception.\n"); + } + Sleep(3000); + CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + CoreDispatcherPriority::Normal, + ref new DispatchedHandler([weakThis]() { + auto ui = weakThis.Resolve(); + if (ui) try { + ui->FadeOutPollingIndicator(); + } catch (...) { + } + })); + Sleep(7000); + } + }); + } + + try { + auto obs = dynamic_cast ^>(host->Apps); + if (obs != nullptr) { + Platform::WeakReference weakThis(this); + m_apps_changed_token = obs->VectorChanged += + ref new Windows::Foundation::Collections::VectorChangedEventHandler( + [weakThis](Windows::Foundation::Collections::IObservableVector ^ sender, + Windows::Foundation::Collections::IVectorChangedEventArgs ^ args) { + auto that = weakThis.Resolve(); + if (that) try { + that->OnHostAppsChanged(sender, args); + } catch (...) { + } + }); + } + } catch (...) { + MLOGF(Utils::LogLevel::Error, "OnNavigatedTo: VectorChanged subscribe failed, app-list updates permanently unavailable this session\n"); + } + + try { + bool savedGrid = (host->Personalization->AppView == AppHostView::Grid); + if (savedGrid != m_isGridLayout && LayoutToggleButton != nullptr) { + LayoutToggleButton->IsChecked = savedGrid; + LayoutToggleButton_Click(LayoutToggleButton, nullptr); + } + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "OnNavigatedTo: saved layout restore failed for host, defaulting to current layout\n"); + } + + ArmEntranceAnimations(); + + { + Windows::UI::Color accentColor = host->Personalization->UseSystemAccent + ? (ref new Windows::UI::ViewManagement::UISettings()) + ->GetColorValue(Windows::UI::ViewManagement::UIColorType::Accent) + : host->Personalization->AccentColor; + ApplyAccentColor(accentColor); + + auto cur = this->ActualTheme; + this->RequestedTheme = (cur != ElementTheme::Dark) ? ElementTheme::Dark : ElementTheme::Light; + this->RequestedTheme = ElementTheme::Default; + } + + if (willAutoStartApp) { + GetApplicationState()->shouldAutoConnect = false; + CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + CoreDispatcherPriority::High, ref new DispatchedHandler([this]() { + this->Connect(host->AutostartID); + })); + } + GetApplicationState()->shouldAutoConnect = false; +} + +void AppPage::OnBackRequested(Platform::Object ^, BackRequestedEventArgs ^ args) { + auto lm = this->GetLeftMenu(); + if (lm != nullptr && lm->IsOpen) { + lm->Close(); + args->Handled = true; + return; + } + + if (this->Frame->CanGoBack) { + this->Frame->GoBack(); + args->Handled = true; + } +} + +void AppPage::backButton_Click(Platform::Object ^, RoutedEventArgs ^) { + this->Frame->GoBack(); +} + +void AppPage::PollAppRunningAndConnectivity() { + if (this->host == nullptr) return; + this->host->UpdateAppRunningStates(); + if (this->wasConnected.load() && !this->host->Connected) { + this->wasConnected.store(false); + auto weakThis = WeakReference(this); + CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + CoreDispatcherPriority::Normal, + ref new DispatchedHandler([weakThis]() { + auto that = weakThis.Resolve(); + if (that != nullptr) that->ShowDisconnectedDialogAndNavigateBack(); + })); + } else if (!this->wasConnected.load() && this->host->Connected) { + this->wasConnected.store(true); + } +} + +void AppPage::ShowDisconnectedDialogAndNavigateBack() { + try { + auto dialog = ref new ::moonlight_xbox_dx::AlertDialog(); + dialog->Configure(L"Disconnected", L"Connection to host was lost."); + try { + dialog->XamlRoot = this->XamlRoot; + } catch (...) { + MLOG(Utils::LogLevel::Warning, "ShowDisconnectedDialogAndNavigateBack: failed to set dialog XamlRoot, dialog may not display correctly"); + } + Platform::WeakReference weakThis(this); + create_task(dialog->ShowAsync()).then([weakThis](ContentDialogResult result) { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + that->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, ref new DispatchedHandler([that]() { + try { + auto slideBack = ref new Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionInfo(); + slideBack->Effect = Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionEffect::FromLeft; + that->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(HostSelectorPage::typeid), nullptr, slideBack); + } catch (const std::exception &e) { + MLOGF(Utils::LogLevel::Error, "Failed to navigate to HostSelectorPage after disconnect. Exception: %s\n", e.what()); + } catch (...) { + MLOG(Utils::LogLevel::Error, "Failed to navigate to HostSelectorPage after disconnect. Unknown Exception.\n"); + } + })); + }); + } catch (const std::exception &e) { + MLOGF(Utils::LogLevel::Error, "Failed to show disconnect dialog. Exception: %s\n", e.what()); + } catch (...) { + MLOG(Utils::LogLevel::Error, "Failed to show disconnect dialog. Unknown Exception.\n"); + } +} + +bool AppPage::ApplyAppFilter(Platform::String ^ filter) { + auto host = this->Host; + auto vec = this->m_filteredApps; + if (vec == nullptr) return false; + + if (host == nullptr || host->Apps == nullptr) { + vec->Clear(); + return false; + } + + auto newResults = ref new Platform::Collections::Vector(); + bool empty = (filter == nullptr || filter->Length() == 0); + std::wstring fw; + if (!empty) { + std::string fstr = Utils::PlatformStringToStdString(filter); + fw = Utils::NarrowToWideString(fstr); + std::transform(fw.begin(), fw.end(), fw.begin(), ::towlower); + } + + std::vector emptyOrdered; + for (unsigned int i = 0; i < host->Apps->Size; ++i) { + auto app = host->Apps->GetAt(i); + if (app == nullptr) continue; + if (empty) { + emptyOrdered.push_back(app); + } else { + std::string nstr = Utils::PlatformStringToStdString(app->Name != nullptr ? app->Name : ref new Platform::String(L"")); + std::wstring namew = Utils::NarrowToWideString(nstr); + std::transform(namew.begin(), namew.end(), namew.begin(), ::towlower); + if (namew.find(fw) != std::wstring::npos) newResults->Append(app); + } + } + if (empty) { + + auto &favoriteIds = host->favoriteAppIds; + std::stable_sort(emptyOrdered.begin(), emptyOrdered.end(), [&favoriteIds](MoonlightApp ^ a, MoonlightApp ^ b) { + if (a->IsFavorite != b->IsFavorite) return a->IsFavorite; + if (!a->IsFavorite) return false; + auto itA = std::find(favoriteIds.begin(), favoriteIds.end(), a->Id); + auto itB = std::find(favoriteIds.begin(), favoriteIds.end(), b->Id); + return itA < itB; + }); + for (auto app : emptyOrdered) newResults->Append(app); + } + + bool identical = false; + try { + if (vec->Size == newResults->Size) { + identical = true; + for (unsigned int i = 0; i < vec->Size; ++i) { + auto a = vec->GetAt(i), b = newResults->GetAt(i); + if ((a != nullptr ? a->Id : -1) != (b != nullptr ? b->Id : -1)) { + identical = false; + break; + } + } + } + } catch (...) { + identical = false; + } + if (identical) { + try { + for (unsigned int i = 0; i < vec->Size; ++i) { + auto a = vec->GetAt(i), b = newResults->GetAt(i); + if (a != b) { + b->IsSelected = a->IsSelected; + b->BlurredImage = a->BlurredImage; + b->GlowImage = a->GlowImage; + if (a->Image != nullptr) b->Image = a->Image; + if (m_selectedApp != nullptr && m_selectedApp->Id == a->Id) + m_selectedApp = b; + + vec->SetAt(i, b); + } + } + } catch (...) { + } + } else { + vec->Clear(); + for (unsigned int i = 0; i < newResults->Size; ++i) vec->Append(newResults->GetAt(i)); + + try { + bool emptyResults = (vec->Size == 0); + auto weakThis = WeakReference(this); + this->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, + ref new DispatchedHandler([weakThis, emptyResults]() { + try { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + if (that->NoAppsMessage != nullptr) + that->NoAppsMessage->Visibility = emptyResults ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; + + if (emptyResults) try { + that->RevealActiveAppsGrid(); + } catch (...) { + } + if (emptyResults && that->SelectedAppText != nullptr && that->SelectedAppBox != nullptr) { + auto sb = dynamic_cast( + that->Resources->Lookup(ref new Platform::String(L"HideSelectedAppStoryboard"))); + if (sb != nullptr) sb->Begin(); + } + } catch (...) { + } + })); + } catch (...) { + } + } + + auto activeGrid = this->ActiveAppsGrid(); + if (vec->Size > 0 && activeGrid != nullptr && activeGrid->SelectedIndex < 0) + activeGrid->SelectedIndex = 0; + return identical; +} + +void AppPage::OnHostAppsChanged( + Windows::Foundation::Collections::IObservableVector ^, + Windows::Foundation::Collections::IVectorChangedEventArgs ^) { + try { + auto weakThis = WeakReference(this); + this->Dispatcher->RunAsync(CoreDispatcherPriority::Normal, + ref new DispatchedHandler([weakThis]() { + try { + auto that = weakThis.Resolve(); + if (that) that->ApplyAppFilter(that->SearchBox != nullptr ? that->SearchBox->Text : nullptr); + } catch (...) { + } + })); + } catch (...) { + } +} + +void AppPage::SearchBox_TextChanged(Platform::Object ^ sender, TextChangedEventArgs ^) { + try { + auto tb = dynamic_cast(sender); + if (tb == nullptr) return; + ArmEntranceAnimations(); + ApplyAppFilter(tb->Text); + } catch (...) { + } +} + +Windows::UI::Xaml::Controls::ListView ^ AppPage::ActiveAppsGrid() { + return m_isGridLayout ? this->AppsGridView : this->AppsListView; +} + +void AppPage::LayoutToggleButton_Click(Platform::Object ^ sender, RoutedEventArgs ^) { + try { + auto toggle = dynamic_cast(sender); + bool wantGrid = toggle != nullptr && toggle->IsChecked != nullptr && toggle->IsChecked->Value; + if (m_isGridLayout == wantGrid) return; + + CancelContainerRealizedWait(m_centerWaitContainerTarget, m_centerWaitContainer_token); + m_centerWaitContainerTarget = nullptr; + CancelLayoutUpdatedWait(m_centerWaitScrollViewerTarget, m_centerWaitScrollViewer_token); + m_centerWaitScrollViewerTarget = nullptr; + + auto oldGrid = this->ActiveAppsGrid(); + auto selectedItem = (oldGrid != nullptr) ? oldGrid->SelectedItem : nullptr; + + m_isGridLayout = wantGrid; + + if (host != nullptr) { + host->Personalization->AppView = wantGrid ? AppHostView::Grid : AppHostView::List; + GetApplicationState()->UpdateFile(); + } + + VisualStateManager::GoToState(this, wantGrid ? "GridLayout" : "ListLayout", false); + + if (this->AppsListView != nullptr) + this->AppsListView->Visibility = wantGrid ? Windows::UI::Xaml::Visibility::Collapsed : Windows::UI::Xaml::Visibility::Visible; + if (this->AppsGridView != nullptr) + this->AppsGridView->Visibility = wantGrid ? Windows::UI::Xaml::Visibility::Visible : Windows::UI::Xaml::Visibility::Collapsed; + + m_scrollViewer = nullptr; + + if (this->Host != nullptr && this->Host->Apps != nullptr) { + for (int i = 0; i < (int)this->Host->Apps->Size; ++i) { + auto app = this->Host->Apps->GetAt(i); + app->BlurredImage = nullptr; + app->GlowImage = nullptr; + this->Host->Apps->SetAt(i, app); + } + } + + auto newGrid = this->ActiveAppsGrid(); + if (newGrid == nullptr) return; + + m_suppressSelectionVisuals = true; + if (selectedItem != nullptr) { + try { + newGrid->SelectedItem = selectedItem; + } catch (...) { + } + try { + newGrid->ScrollIntoView(selectedItem); + } catch (...) { + } + } + m_suppressSelectionVisuals = false; + + try { + newGrid->UpdateLayout(); + } catch (...) { + } + try { + this->UpdateItemHeights(); + } catch (...) { + } + + try { + if (newGrid->SelectedItem != nullptr) { + auto c2 = dynamic_cast(newGrid->ContainerFromItem(newGrid->SelectedItem)); + if (c2 != nullptr) { + UIElement ^ des2 = nullptr, ^img2 = nullptr, ^nm2 = nullptr; + UIElement ^ bl2 = nullptr, ^pl2 = nullptr; + FindElementChildren(c2, des2, img2, nm2, bl2, pl2); + auto resetCP = [](UIElement ^ el) { + if (el == nullptr) return; + auto fe = dynamic_cast(el); + if (fe == nullptr || fe->ActualWidth <= 0 || fe->ActualHeight <= 0) return; + auto vis = ElementCompositionPreview::GetElementVisual(el); + if (vis == nullptr) return; + Windows::Foundation::Numerics::float3 cp; + cp.x = (float)fe->ActualWidth * 0.5f; + cp.y = (float)fe->ActualHeight * 0.5f; + cp.z = 0.0f; + vis->CenterPoint = cp; + }; + resetCP(img2); + resetCP(des2); + resetCP(pl2); + } + } + } catch (...) { + } + + try { + auto app = dynamic_cast(newGrid->SelectedItem); + if (app != nullptr) + this->ApplySelectionVisuals(app, false, true); + else + this->CenterSelectedItem(true); + } catch (...) { + } + + try { + if (newGrid->SelectedItem != nullptr) { + auto sel = dynamic_cast(newGrid->ContainerFromItem(newGrid->SelectedItem)); + if (sel != nullptr) + sel->Focus(Windows::UI::Xaml::FocusState::Programmatic); + else + newGrid->Focus(Windows::UI::Xaml::FocusState::Programmatic); + } else { + newGrid->Focus(Windows::UI::Xaml::FocusState::Programmatic); + } + } catch (...) { + } + } catch (...) { + MLOGF(Utils::LogLevel::Error, "LayoutToggleButton_Click failed, layout toggle may be left in an inconsistent visual state\n"); + } +} + +void AppPage::AppsGrid_Loaded(Platform::Object ^ sender, RoutedEventArgs ^) { + try { + auto grid = dynamic_cast(sender); + if (grid != nullptr && grid == this->ActiveAppsGrid() && m_scrollViewer == nullptr) + m_scrollViewer = FindScrollViewer(grid); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "AppsGrid_Loaded: FindScrollViewer failed, m_scrollViewer left unset"); + } +} + +void AppPage::OnGamepadKeyDown(CoreWindow ^, KeyEventArgs ^ args) { + try { + using namespace Windows::System; + auto key = args->VirtualKey; + + if (key == VirtualKey::GamepadY || key == VirtualKey::Y) { + try { + this->HandleYButtonPress(); + } catch (...) { + } + args->Handled = true; + } + + if (key == VirtualKey::B) { + try { + auto lm = this->GetLeftMenu(); + if (lm != nullptr && lm->IsOpen) { + lm->Close(); + args->Handled = true; + } else if (this->Frame->CanGoBack) { + this->Frame->GoBack(); + args->Handled = true; + } + } catch (...) { + } + } + + if (key == VirtualKey::GamepadX || key == VirtualKey::X) { + try { + this->HandleXButtonPress(); + } catch (...) { + } + args->Handled = true; + } + + bool searchHasFocus = this->SearchBox != nullptr && + this->SearchBox->FocusState != Windows::UI::Xaml::FocusState::Unfocused; + + if (key == VirtualKey::GamepadDPadUp && !searchHasFocus && !m_isGridLayout) { + m_searchIsOpen = true; + try { + if (this->SearchBox != nullptr && + this->SearchBox->FocusState == Windows::UI::Xaml::FocusState::Unfocused) + this->SearchBox->Focus(Windows::UI::Xaml::FocusState::Programmatic); + } catch (...) { + } + } + + if (key == VirtualKey::GamepadDPadDown && searchHasFocus) { + m_searchIsOpen = false; + try { + this->FocusSelectedContainerOrGrid(); + } catch (...) { + } + args->Handled = true; + } + + } catch (...) { + } +} + +void AppPage::HandleYButtonPress() { + bool searchHasFocus = this->SearchBox != nullptr && + this->SearchBox->FocusState != Windows::UI::Xaml::FocusState::Unfocused; + if (!searchHasFocus) { + this->m_searchIsOpen = true; + this->SearchBox->Focus(Windows::UI::Xaml::FocusState::Programmatic); + return; + } + this->m_searchIsOpen = false; + this->FocusSelectedContainerOrGrid(); +} + +void AppPage::HandleXButtonPress() { + bool newState = !this->m_isGridLayout; + if (this->LayoutToggleButton) this->LayoutToggleButton->IsChecked = newState; + this->LayoutToggleButton_Click(this->LayoutToggleButton, nullptr); +} + +void AppPage::FocusSelectedContainerOrGrid() { + auto lv = this->ActiveAppsGrid(); + if (lv == nullptr) return; + if (lv->SelectedItem != nullptr) { + auto c = dynamic_cast(lv->ContainerFromItem(lv->SelectedItem)); + if (c != nullptr) { + c->Focus(Windows::UI::Xaml::FocusState::Programmatic); + return; + } + } + lv->Focus(Windows::UI::Xaml::FocusState::Programmatic); +} + +void AppPage::AppsGrid_ItemClick(Platform::Object ^ sender, ItemClickEventArgs ^ e) { + MoonlightApp ^ app = (MoonlightApp ^) e->ClickedItem; + this->currentApp = app; + + if (this->host != nullptr) { + for (unsigned int i = 0; i < this->host->Apps->Size; ++i) { + auto candidate = this->host->Apps->GetAt(i); + if (candidate != nullptr && candidate->CurrentlyRunning && candidate->Id != app->Id) { + this->closeAndStartButton_Click(nullptr, nullptr); + return; + } + } + } + this->Connect(app->Id); +} + +void AppPage::Connect(int appId) { + StreamConfiguration ^ config = ref new StreamConfiguration(); + config->hostname = host->LastHostname; + config->appID = appId; + config->width = host->Resolution->Width; + config->height = host->Resolution->Height; + config->bitrate = host->Bitrate; + config->FPS = host->FPS; + config->audioConfig = host->AudioConfig; + config->videoCodec = host->VideoCodec; + config->playAudioOnPC = host->PlayAudioOnPC; + config->enableHDR = host->EnableHDR; + config->enableSOPS = host->EnableSOPS; + config->enableStats = host->EnableStats; + config->enableGraphs = host->EnableGraphs; + if (config->enableHDR) host->VideoCodec = "HEVC (H.265)"; + config->backgroundImage = (this->currentApp != nullptr) ? this->currentApp->BlurredImage : nullptr; + config->appName = (this->currentApp != nullptr) ? this->currentApp->Name : nullptr; + this->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(StreamPage::typeid), config, ref new Windows::UI::Xaml::Media::Animation::DrillInNavigationTransitionInfo()); +} + +void AppPage::AppsGrid_RightTapped(Platform::Object ^ sender, Windows::UI::Xaml::Input::RightTappedRoutedEventArgs ^ e) { + FrameworkElement ^ senderElement = dynamic_cast(e != nullptr ? e->OriginalSource : nullptr); + if (senderElement != nullptr) { + if (senderElement->GetType()->FullName->Equals(ListViewItem::typeid->FullName)) { + currentApp = (MoonlightApp ^)((ListViewItem ^) senderElement)->Content; + } else { + currentApp = dynamic_cast(senderElement->DataContext); + if (currentApp == nullptr) { + auto activeGrid = this->ActiveAppsGrid(); + if (activeGrid != nullptr && activeGrid->SelectedIndex >= 0) + currentApp = (MoonlightApp ^) activeGrid->SelectedItem; + } + } + } + + bool anyRunning = false; + if (this->host != nullptr) { + for (unsigned int i = 0; i < this->host->Apps->Size; ++i) { + auto c = this->host->Apps->GetAt(i); + if (c != nullptr && c->CurrentlyRunning) { + anyRunning = true; + break; + } + } + } + + if (this->currentApp == nullptr) { + if (e != nullptr) e->Handled = false; + return; + } + + try { + Platform::WeakReference weakThis(this); + auto dialog = ref new AppActionsDialog(); + dialog->Configure( + (this->currentApp->Name != nullptr) ? this->currentApp->Name : ref new Platform::String(L""), + this->currentApp->CurrentlyRunning, + !this->currentApp->CurrentlyRunning && anyRunning, + !this->currentApp->CurrentlyRunning && !anyRunning, + this->currentApp->IsFavorite, + ref new RoutedEventHandler([weakThis](Platform::Object ^, RoutedEventArgs ^) { + auto that = weakThis.Resolve(); + if (that != nullptr) try { + that->Connect(that->currentApp->Id); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "AppActionsDialog Resume handler failed to Connect"); + } + }), + ref new RoutedEventHandler([weakThis, dialog](Platform::Object ^, RoutedEventArgs ^) { + auto that = weakThis.Resolve(); + if (that != nullptr) try { + that->closeAppButton_Click(dialog, nullptr); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "AppActionsDialog Close handler failed to close running app"); + } + }), + ref new RoutedEventHandler([weakThis, dialog](Platform::Object ^, RoutedEventArgs ^) { + auto that = weakThis.Resolve(); + if (that != nullptr) try { + that->closeAndStartButton_Click(dialog, nullptr); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "AppActionsDialog CloseAndStart handler failed to close and start app"); + } + }), + ref new RoutedEventHandler([weakThis](Platform::Object ^, RoutedEventArgs ^) { + auto that = weakThis.Resolve(); + if (that != nullptr && that->currentApp != nullptr) try { + that->Connect(that->currentApp->Id); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "AppActionsDialog Start handler failed to Connect"); + } + }), + ref new RoutedEventHandler([weakThis](Platform::Object ^, RoutedEventArgs ^) { + auto that = weakThis.Resolve(); + if (that != nullptr) try { + auto t = ref new Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionInfo(); + t->Effect = Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionEffect::FromRight; + that->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(MoonlightSettings::typeid), nullptr, t); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "AppActionsDialog MoonlightSettings handler failed to navigate"); + } + }), + ref new RoutedEventHandler([weakThis](Platform::Object ^, RoutedEventArgs ^) { + auto that = weakThis.Resolve(); + if (that != nullptr) try { + auto t = ref new Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionInfo(); + t->Effect = Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionEffect::FromRight; + that->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(HostSettingsPage::typeid), that->Host, t); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "AppActionsDialog HostSettings handler failed to navigate"); + } + }), + ref new RoutedEventHandler([weakThis](Platform::Object ^, RoutedEventArgs ^) { + auto that = weakThis.Resolve(); + if (that != nullptr && that->currentApp != nullptr && that->host != nullptr) try { + that->host->ToggleFavorite(that->currentApp->Id); + GetApplicationState()->UpdateFile(); + that->ApplyAppFilter(that->SearchBox != nullptr ? that->SearchBox->Text : nullptr); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "AppActionsDialog ToggleFavorite handler failed"); + } + })); + + create_task(dialog->ShowAsync()); + if (e != nullptr) e->Handled = true; + } catch (...) { + if (e != nullptr) e->Handled = false; + } +} + +Platform::String ^ AppPage::FindRunningAppName() { + if (this->host == nullptr) return nullptr; + for (unsigned int i = 0; i < this->host->Apps->Size; ++i) { + auto candidate = this->host->Apps->GetAt(i); + if (candidate != nullptr && candidate->CurrentlyRunning && + (this->currentApp == nullptr || candidate->Id != this->currentApp->Id)) { + return candidate->Name; + } + } + return nullptr; +} + +void AppPage::closeAndStartButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { + if (this->currentApp == nullptr) return; + if (sender != nullptr) { + this->ExecuteCloseAndStart(); + return; + } + + Platform::String ^ runningName = this->FindRunningAppName(); + + auto startName = (this->currentApp->Name != nullptr && this->currentApp->Name->Length() > 0) + ? std::wstring(this->currentApp->Name->Data()) + : std::wstring(L"this app"); + auto closePart = (runningName != nullptr && runningName->Length() > 0) + ? std::wstring(L"Close '") + runningName->Data() + L"'" + : std::wstring(L"Close the currently running app"); + Platform::String ^ message = ref new Platform::String((closePart + L" and start '" + startName + L"'?").c_str()); + + Platform::WeakReference weakThis(this); + auto dialog = ref new ConfirmDialog(); + dialog->Configure( + ref new Platform::String(L"Close & Start"), + message, + ref new RoutedEventHandler([weakThis](Platform::Object ^, RoutedEventArgs ^) { + auto that = weakThis.Resolve(); + if (that) that->ExecuteCloseAndStart(); + })); + create_task(dialog->ShowAsync()); +} + +void AppPage::CloseRunningAppOnHostAsync( + const char *callerName, + CoreDispatcherPriority uiPriority, + std::function onClosedUIThread) { + Platform::WeakReference weakThis(this); + create_task(create_async([weakThis, callerName, uiPriority, onClosedUIThread]() { + try { + auto thatLocal = weakThis.Resolve(); + if (thatLocal == nullptr) return; + MoonlightClient client; + auto ipAddr = Utils::PlatformStringToStdString(thatLocal->host->LastHostname); + if (client.Connect(ipAddr.c_str()) == 0) { + client.StopApp(); + Sleep(1000); + } + } catch (...) { + MLOGF(Utils::LogLevel::Error, "%s: StopApp on host failed, running app may not actually be closed\n", callerName); + } + auto thatLocal2 = weakThis.Resolve(); + if (thatLocal2 == nullptr) return; + thatLocal2->Dispatcher->RunAsync(uiPriority, + ref new DispatchedHandler([weakThis, callerName, onClosedUIThread]() { + auto thatUI = weakThis.Resolve(); + try { + if (thatUI != nullptr) onClosedUIThread(thatUI); + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "%s: post-close UI update failed, ClosingOverlay may stay visible\n", callerName); + } + })); + })).then([](concurrency::task t) { + try { + t.get(); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "CloseRunningAppOnHostAsync background task faulted"); + } + }); +} + +void AppPage::ExecuteCloseAndStart() { + auto name = this->FindRunningAppName(); + this->ClosingOverlayText->Text = (name != nullptr && name->Length() > 0) + ? ref new Platform::String((std::wstring(L"Closing ") + name->Data() + L"...").c_str()) + : ref new Platform::String(L"Closing..."); + this->ClosingOverlay->Visibility = Windows::UI::Xaml::Visibility::Visible; + + CloseRunningAppOnHostAsync("ExecuteCloseAndStart", CoreDispatcherPriority::High, [](AppPage ^ thatUI) { + thatUI->ClosingOverlay->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + if (thatUI->currentApp != nullptr) + thatUI->Connect(thatUI->currentApp->Id); + }); +} + +void AppPage::closeAppButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { + auto name = (this->currentApp != nullptr && this->currentApp->Name != nullptr) ? this->currentApp->Name : nullptr; + this->ClosingOverlayText->Text = (name != nullptr && name->Length() > 0) + ? ref new Platform::String((std::wstring(L"Closing ") + name->Data() + L"...").c_str()) + : ref new Platform::String(L"Closing..."); + this->ClosingOverlay->Visibility = Windows::UI::Xaml::Visibility::Visible; + + CloseRunningAppOnHostAsync("closeAppButton_Click", CoreDispatcherPriority::Normal, [](AppPage ^ thatUI) { + thatUI->host->UpdateHostInfo(true); + thatUI->host->UpdateAppRunningStates(); + thatUI->ClosingOverlay->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + }); +} + +void AppPage::FadeInPollingIndicator() { + try { + using namespace Windows::UI::Xaml::Media::Animation; + PollingIndicator->Opacity = 0.0; + PollingIndicator->Visibility = Windows::UI::Xaml::Visibility::Visible; + auto anim = ref new DoubleAnimation(); + anim->To = ref new Platform::Box(0.3); + TimeSpan ts; + ts.Duration = 1500000LL; + anim->Duration = DurationHelper::FromTimeSpan(ts); + auto sb = ref new Storyboard(); + sb->Children->Append(anim); + Storyboard::SetTarget(anim, PollingIndicator); + Storyboard::SetTargetProperty(anim, ref new Platform::String(L"(UIElement.Opacity)")); + sb->Begin(); + } catch (...) { + } +} + +void AppPage::FadeOutPollingIndicator() { + try { + using namespace Windows::UI::Xaml::Media::Animation; + auto anim = ref new DoubleAnimation(); + anim->To = ref new Platform::Box(0.0); + TimeSpan ts; + ts.Duration = 1500000LL; + anim->Duration = DurationHelper::FromTimeSpan(ts); + auto sb = ref new Storyboard(); + sb->Children->Append(anim); + Storyboard::SetTarget(anim, PollingIndicator); + Storyboard::SetTargetProperty(anim, ref new Platform::String(L"(UIElement.Opacity)")); + Platform::WeakReference weakThis(this); + sb->Completed += ref new EventHandler([weakThis](Platform::Object ^, Platform::Object ^) { + auto that = weakThis.Resolve(); + if (that) try { + that->PollingIndicator->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + } catch (...) { + } + }); + sb->Begin(); + } catch (...) { + } +} + +void AppPage::RevealActiveAppsGrid() { + if (m_appsGridRevealed) return; + m_appsGridRevealed = true; + try { + using namespace Windows::UI::Xaml::Media::Animation; + if (this->ClosingOverlay == nullptr) return; + auto anim = ref new DoubleAnimation(); + anim->To = ref new Platform::Box(0.0); + TimeSpan ts; + ts.Duration = 1500000LL; + anim->Duration = DurationHelper::FromTimeSpan(ts); + auto sb = ref new Storyboard(); + sb->Children->Append(anim); + Storyboard::SetTarget(anim, this->ClosingOverlay); + Storyboard::SetTargetProperty(anim, ref new Platform::String(L"(UIElement.Opacity)")); + Platform::WeakReference weakThis(this); + sb->Completed += ref new EventHandler([weakThis](Platform::Object ^, Platform::Object ^) { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + try { + that->ClosingOverlay->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + that->ClosingOverlay->Opacity = 1.0; + } catch (...) { + MLOG(Utils::LogLevel::Warning, "RevealActiveAppsGrid: failed to reset ClosingOverlay after reveal, m_appsGridRevealed already set so this will not retry"); + } + }); + sb->Begin(); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "RevealActiveAppsGrid: reveal animation failed to start, m_appsGridRevealed already set so this will not retry"); + } +} + +void AppPage::StartBgPanAnimation() { + using namespace Windows::UI::Xaml::Media; + using namespace Windows::UI::Xaml::Media::Animation; + + try { + if (PageBackgroundImage == nullptr) return; + + auto transform = ref new TranslateTransform(); + PageBackgroundImage->RenderTransform = transform; + + auto sb = ref new Storyboard(); + sb->RepeatBehavior = RepeatBehaviorHelper::Forever; + sb->AutoReverse = true; + + Windows::UI::Xaml::Duration dur(TimeSpan{kBgPanDurationSec * 10000000LL}); + + auto ease = ref new SineEase(); + ease->EasingMode = EasingMode::EaseInOut; + + auto animY = ref new DoubleAnimation(); + animY->To = ref new Platform::Box(120.0); + animY->Duration = dur; + animY->EasingFunction = ease; + Storyboard::SetTarget(animY, transform); + Storyboard::SetTargetProperty(animY, ref new Platform::String(L"Y")); + sb->Children->Append(animY); + + sb->Begin(); + m_bgPanStoryboard = sb; + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "StartBgPanAnimation failed, background pan animation will not run\n"); + } +} + +void AppPage::StartBannerSlideInAnimation() { + using namespace Windows::UI::Xaml::Media; + using namespace Windows::UI::Xaml::Media::Animation; + + try { + if (ComputerNameBannerContainer == nullptr) return; + + auto transform = ref new TranslateTransform(); + ComputerNameBannerContainer->RenderTransform = transform; + + auto ease = ref new CubicEase(); + ease->EasingMode = EasingMode::EaseInOut; + + auto anim = ref new DoubleAnimation(); + anim->From = ref new Platform::Box(-200.0); + anim->To = ref new Platform::Box(0.0); + TimeSpan ts; + ts.Duration = 15000000LL; + anim->Duration = DurationHelper::FromTimeSpan(ts); + anim->EasingFunction = ease; + + auto sb = ref new Storyboard(); + sb->Children->Append(anim); + Storyboard::SetTarget(anim, transform); + Storyboard::SetTargetProperty(anim, ref new Platform::String(L"X")); + sb->Begin(); + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "StartBannerSlideInAnimation failed, banner slide-in animation will not run\n"); + } +} + +} diff --git a/UI/Pages/AppPage/AppPage.xaml.h b/UI/Pages/AppPage/AppPage.xaml.h new file mode 100644 index 00000000..3c981ec5 --- /dev/null +++ b/UI/Pages/AppPage/AppPage.xaml.h @@ -0,0 +1,165 @@ +#pragma once + +#include "UI\Pages\AppPage\AppPage.g.h" +#include "State\MoonlightApp.h" +#include "UI\Models\ViewModels\AppPageViewModel.h" +#include "UI\Utilities\XamlHelpers.h" +#include +#include +#include +#include + +namespace moonlight_xbox_dx +{ + +static constexpr float kBackgroundSaturation = 1.25f; +static constexpr int kBgPanDurationSec = 10; +static constexpr float kBlurAmountBackground = 2.0f; +static constexpr float kBlurGlowPaddingDip = 60.0f; + +static constexpr double kEntranceStartScale = 0.25; +static constexpr int kEntranceDurationMs = 500; +static constexpr int kEntranceStaggerMs = 100; +static constexpr int kEntranceMaxStaggerItems = 100; + + namespace Controls { ref class SlidingMenu; } + [Windows::Foundation::Metadata::WebHostHidden] + public ref class AppPage sealed + { + private: + moonlight_xbox_dx::Controls::SlidingMenu^ m_leftMenu = nullptr; + moonlight_xbox_dx::Controls::SlidingMenu^ GetLeftMenu(); + AppPageViewModel^ m_viewModel = nullptr; + MoonlightHost^ host; + MoonlightApp^ currentApp; + Platform::Collections::Vector^ m_filteredApps; + bool ApplyAppFilter(Platform::String^ filter); + Windows::Foundation::EventRegistrationToken m_apps_changed_token; + void OnHostAppsChanged(Windows::Foundation::Collections::IObservableVector^ sender, Windows::Foundation::Collections::IVectorChangedEventArgs^ args); + Windows::Foundation::EventRegistrationToken m_back_cookie; + std::atomic continueAppFetch{ false }; + std::atomic wasConnected{ false }; + MoonlightApp^ m_selectedApp = nullptr; + protected: + virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override; + void Connect(int app); + public: + AppPage(); + + property Windows::Foundation::Collections::IObservableVector^ FilteredApps { + Windows::Foundation::Collections::IObservableVector^ get() { + if (m_filteredApps == nullptr) m_filteredApps = ref new Platform::Collections::Vector(); + return m_filteredApps; + } + } + property MoonlightHost^ Host { + MoonlightHost^ get() { return this->host; } + } + property AppPageViewModel^ ViewModel { + AppPageViewModel^ get() { + if (m_viewModel == nullptr) m_viewModel = ref new AppPageViewModel(); + return m_viewModel; + } + } + void OnBackRequested(Platform::Object^ e, Windows::UI::Core::BackRequestedEventArgs^ args); + + private: + void AppsGrid_ItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ e); + void BlurAppImage(MoonlightApp ^ selApp); + void HandleBlurStreamReady(MoonlightApp^ selApp, Platform::WeakReference weakThis, + Windows::Storage::Streams::IRandomAccessStream^ stream, bool isBackground); + void AppsGrid_Loaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void SearchBox_TextChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::TextChangedEventArgs^ e); + void AppsGrid_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e); + void ApplySelectionVisuals(MoonlightApp^ app, bool animate, bool centerImmediate = false); + void UpdateContainerSelectionState(MoonlightApp^ app, bool isSelected, bool animate); + bool m_suppressSelectionVisuals = false; + Windows::UI::Xaml::DispatcherTimer^ m_centerDebounceTimer = nullptr; + Windows::Foundation::EventRegistrationToken m_centerDebounceTimer_token; + void AppsGrid_RightTapped(Platform::Object^ sender, Windows::UI::Xaml::Input::RightTappedRoutedEventArgs^ e); + void AppsGrid_ContainerContentChanging(Windows::UI::Xaml::Controls::ListViewBase^ sender, Windows::UI::Xaml::Controls::ContainerContentChangingEventArgs^ args); + void PlayEntranceAnimation(Windows::UI::Xaml::Controls::ListViewItem^ container, int index); + void ArmEntranceAnimations(); + std::unordered_set m_listEntranceAnimatedIndices; + std::unordered_set m_gridEntranceAnimatedIndices; + bool m_entranceAnimationsArmed = false; + void closeAppButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void closeAndStartButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void ExecuteCloseAndStart(); + Platform::String^ FindRunningAppName(); + void CloseRunningAppOnHostAsync( + const char* callerName, + Windows::UI::Core::CoreDispatcherPriority uiPriority, + std::function onClosedUIThread); + void backButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void OnUnloaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void UpdateItemHeights(); + void WireAppsGridEvents( + Windows::UI::Xaml::Controls::ListView^ grid, + Windows::Foundation::EventRegistrationToken& selectionToken, + Windows::Foundation::EventRegistrationToken& itemClickToken, + Windows::Foundation::EventRegistrationToken& rightTappedToken, + Windows::Foundation::EventRegistrationToken& loadedToken, + Windows::Foundation::EventRegistrationToken& cccToken); + void UnwireAppsGridEvents( + const char* gridName, + Windows::UI::Xaml::Controls::ListView^ grid, + Windows::Foundation::EventRegistrationToken selectionToken, + Windows::Foundation::EventRegistrationToken itemClickToken, + Windows::Foundation::EventRegistrationToken rightTappedToken, + Windows::Foundation::EventRegistrationToken loadedToken, + Windows::Foundation::EventRegistrationToken cccToken); + + Windows::UI::Xaml::Controls::ScrollViewer^ m_scrollViewer; + Windows::UI::Xaml::Media::Animation::Storyboard^ m_bgPanStoryboard = nullptr; + void StartBgPanAnimation(); + void StartBannerSlideInAnimation(); + + bool m_isGridLayout = false; + + void LayoutToggleButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void OnGamepadKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ args); + void HandleYButtonPress(); + void HandleXButtonPress(); + void FocusSelectedContainerOrGrid(); + Windows::UI::Xaml::Controls::ListView^ ActiveAppsGrid(); + void RevealActiveAppsGrid(); + bool m_appsGridRevealed = false; + void CenterSelectedItem(bool immediate = false); + void WaitForContainerThenCenter(bool immediate); + void WaitForScrollViewerThenCenter(bool immediate); + void UpdateSelectedAppBox(MoonlightApp^ app, MoonlightApp^ prev, bool animate); + + Windows::Foundation::EventRegistrationToken m_keydown_cookie; + + Windows::Foundation::EventRegistrationToken m_appsListView_selection_token; + Windows::Foundation::EventRegistrationToken m_appsListView_itemclick_token; + Windows::Foundation::EventRegistrationToken m_appsListView_righttapped_token; + Windows::Foundation::EventRegistrationToken m_appsListView_loaded_token; + Windows::Foundation::EventRegistrationToken m_appsListView_ccc_token; + Windows::Foundation::EventRegistrationToken m_appsGridView_selection_token; + Windows::Foundation::EventRegistrationToken m_appsGridView_itemclick_token; + Windows::Foundation::EventRegistrationToken m_appsGridView_righttapped_token; + Windows::Foundation::EventRegistrationToken m_appsGridView_loaded_token; + Windows::Foundation::EventRegistrationToken m_appsGridView_ccc_token; + Windows::Foundation::EventRegistrationToken m_searchbox_gettingfocus_token; + Windows::Foundation::EventRegistrationToken m_centerWaitContainer_token; + Windows::UI::Xaml::Controls::ListViewBase^ m_centerWaitContainerTarget = nullptr; + Windows::Foundation::EventRegistrationToken m_centerWaitScrollViewer_token; + Windows::UI::Xaml::Controls::ListViewBase^ m_centerWaitScrollViewerTarget = nullptr; + bool m_searchIsOpen = false; + std::unordered_set m_blurInProgressIds; + + bool m_initialFocusApplied = false; + unsigned int m_appTextAnimVersion = 0; + + concurrency::task ApplyBlur(MoonlightApp^ app, float blurDip, float padDip = 0.0f); + + void FadeInBlurIfSelected(MoonlightApp^ app, Windows::UI::Xaml::Media::Imaging::BitmapImage^ img); + void FadeInPollingIndicator(); + void FadeOutPollingIndicator(); + void PollAppRunningAndConnectivity(); + void ShowDisconnectedDialogAndNavigateBack(); + }; +} diff --git a/UI/Pages/HostSelectorPage.xaml b/UI/Pages/HostSelectorPage.xaml new file mode 100644 index 00000000..c36d7879 --- /dev/null +++ b/UI/Pages/HostSelectorPage.xaml @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MOONLIGHT + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UI/Pages/HostSelectorPage.xaml.cpp b/UI/Pages/HostSelectorPage.xaml.cpp new file mode 100644 index 00000000..084d0a3c --- /dev/null +++ b/UI/Pages/HostSelectorPage.xaml.cpp @@ -0,0 +1,1040 @@ +#include "pch.h" +#include "HostSelectorPage.xaml.h" +#include "UI\Backgrounds\DynamicBackgroundHost.xaml.h" +#include "UI\Controls\LunarPhaseControl.xaml.h" +#include "UI\Pages\AppPage\AppPage.xaml.h" +#include +#include "UI\Pages\HostSettingsPage.xaml.h" +#define MLOG_TAG_OVERRIDE "HostSelectorPage" +#include "Utils.hpp" +#include "UI\Utilities\XamlHelpers.h" +#include "UI\Utilities\ToastService.h" +#include "UI\Pages\MoonlightSettings.xaml.h" +#include "State\MDNSHandler.h" +#include "UI\Pages\MoonlightWelcome.xaml.h" +#include "UI\Modals\AlertDialog.xaml.h" +#include "UI\Modals\PairDialog.xaml.h" +#include "UI\Modals\ConfirmDialog.xaml.h" +#include "UI\Modals\HostActionsDialog.xaml.h" +#include "UI\Modals\TestConnectionResultDialog.xaml.h" +#include +#include +#include +#include +#include +#include + +using namespace moonlight_xbox_dx; + +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::Foundation::Collections; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Controls::Primitives; +using namespace Windows::UI::Xaml::Data; +using namespace Windows::UI::Xaml::Input; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Navigation; +using namespace Windows::UI::ViewManagement::Core; + +HostSelectorPage::HostSelectorPage() +{ + state = GetApplicationState(); + InitializeComponent(); + m_hostsScrollViewer = nullptr; +} + +void HostSelectorPage::OnStateLoaded() { + if (GetApplicationState()->FirstTime) { + this->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(MoonlightWelcome::typeid), nullptr, ref new Windows::UI::Xaml::Media::Animation::EntranceNavigationTransitionInfo()); + return; + } + + { + auto weakThis = WeakReference(this); + try { + this->Dispatcher->RunAsync( + Windows::UI::Core::CoreDispatcherPriority::Low, + ref new Windows::UI::Core::DispatchedHandler([weakThis]() { + auto that = weakThis.Resolve(); + if (that != nullptr) that->UpdateAllMoonPhases(true, 4); + })); + } catch (...) {} + } + + { + auto weakThis = WeakReference(this); + try { + this->Dispatcher->RunAsync( + Windows::UI::Core::CoreDispatcherPriority::Low, + ref new Windows::UI::Core::DispatchedHandler([weakThis]() { + auto that = weakThis.Resolve(); + if (that != nullptr) that->FocusFirstHostItem(4); + })); + } catch (...) {} + } + + Concurrency::create_task([this]() { + std::vector hostsSnapshot; + for (auto a : GetApplicationState()->SavedHosts) { + hostsSnapshot.push_back(a); + } + Concurrency::parallel_for_each(hostsSnapshot.begin(), hostsSnapshot.end(), [](MoonlightHost^ a) { + try { + a->UpdateHostInfo(true); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "OnStateLoaded UpdateHostInfo failed for a host"); + } + }); + }).then([this]() { + bool willAutoConnect = false; + if (GetApplicationState()->autostartInstance.size() > 0) { + auto pii = Utils::StringFromStdString(GetApplicationState()->autostartInstance); + for (unsigned int i = 0; i < GetApplicationState()->SavedHosts->Size; i++) { + auto host = GetApplicationState()->SavedHosts->GetAt(i); + if (host->InstanceId->Equals(pii)) { + willAutoConnect = true; + auto that = this; + Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + Windows::UI::Core::CoreDispatcherPriority::High, + ref new Windows::UI::Core::DispatchedHandler([that, host]() { + that->Connect(host); + }) + ); + break; + } + } + } + if (!willAutoConnect) { + auto that = this; + Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + Windows::UI::Core::CoreDispatcherPriority::Normal, + ref new Windows::UI::Core::DispatchedHandler([that]() { + that->StartPollTimerIfNeeded(); + }) + ); + } + }).then([this](concurrency::task t) { + try { + t.get(); + } + catch (const std::exception &e) { + MLOGF(Utils::LogLevel::Error, "OnStateLoaded task exception: %s", e.what()); + } + catch (...) { + MLOG(Utils::LogLevel::Error, "OnStateLoaded task unknown exception"); + } + }); +} + +void HostSelectorPage::OnKeyDown(Platform::Object ^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs ^ e) { + if (e->Key == Windows::System::VirtualKey::Enter) { + CoreInputView::GetForCurrentView()->TryHide(); + } +} + +void HostSelectorPage::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) { + Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->SetDesiredBoundsMode(Windows::UI::ViewManagement::ApplicationViewBoundsMode::UseCoreWindow); + continueFetch.store(true); + m_isNavigatedAway.store(false); + + try { + if (BackgroundHost != nullptr) { + BackgroundHost->SetHosts(State->SavedHosts); + BackgroundHost->Refresh(); + BackgroundHost->StartAnimations(); + } + } catch (...) { + MLOG(Utils::LogLevel::Warning, "OnNavigatedTo failed to refresh background/animations"); + } + + if (GetApplicationState()->IsStateLoaded) { + StartPollTimerIfNeeded(); + } +} + +void HostSelectorPage::StartPollTimerIfNeeded() +{ + if (m_isNavigatedAway.load()) return; + if (m_pollTimer != nullptr) return; + + using namespace Windows::System::Threading; + using namespace Windows::Foundation; + + try { + TimeSpan period; + period.Duration = 5000 * 10000LL; + Platform::WeakReference weakThis(this); + auto callback = ref new TimerElapsedHandler([weakThis](ThreadPoolTimer^ timer) { + auto that = weakThis.Resolve(); + if (that == nullptr) { + try { + if (timer != nullptr) timer->Cancel(); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "failed to cancel poll timer for destroyed page"); + } + return; + } + that->PollTick(timer); + }); + + m_pollTimer = ThreadPoolTimer::CreatePeriodicTimer(callback, period); + } catch (...) { + MLOG(Utils::LogLevel::Error, "failed to start poll timer"); + } +} + +void HostSelectorPage::OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs ^ e) { + try { + if (BackgroundHost != nullptr) BackgroundHost->StopAnimations(); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "OnNavigatedFrom failed to stop background animations"); + } + + try { + if (m_hostsGrid_ccc_token.Value != 0 && HostsGrid != nullptr) { + HostsGrid->LayoutUpdated -= m_hostsGrid_ccc_token; + m_hostsGrid_ccc_token.Value = 0; + } + } catch (...) { + MLOG(Utils::LogLevel::Warning, "failed to unsubscribe HostsGrid.LayoutUpdated on navigate-away"); + } + + CancelContainerRealizedWait(HostsGrid, m_hostsContainerWait_token); + CancelLayoutUpdatedWait(HostsGrid, m_hostsScrollViewerWait_token); + + m_isNavigatedAway.store(true); + continueFetch.store(false); + try { + if (m_pollTimer != nullptr) { + m_pollTimer->Cancel(); + m_pollTimer = nullptr; + } + } catch (...) { + MLOG(Utils::LogLevel::Warning, "failed to cancel poll timer on navigate-away"); + } + + for (int i = 0; i < 50 && m_pollActiveCount.load() > 0; ++i) { + Sleep(20); + } + + try { + __super::OnNavigatedFrom(e); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "OnNavigatedFrom base class call failed"); + } +} + +static const double kSelectedHostContainerWidth = 230; + +bool HostSelectorPage::EnsureCenteringPadding() +{ + try { + if (m_adjustingCenterPadding) return false; + + auto grid = this->HostsGrid; + ListViewItem^ container = nullptr; + auto status = ResolveSelectedContainer(grid, m_hostsScrollViewer, container); + if (status != SelectedContainerStatus::Ready) return false; + + double viewport = m_hostsScrollViewer->ViewportWidth; + double width = kSelectedHostContainerWidth; + if (!std::isfinite(viewport) || viewport <= 0.0) return false; + + double desired = std::max(0.0, (viewport - width) * 0.5); + if (!std::isfinite(desired)) return false; + if (m_lastCenterPadding >= 0.0 && std::fabs(m_lastCenterPadding - desired) < 0.5) return false; + + m_adjustingCenterPadding = true; + auto result = ApplyEdgeCenteringPadding(grid, desired); + m_lastCenterPadding = desired; + m_adjustingCenterPadding = false; + bool applied = result == EdgeCenteringPaddingResult::Applied; + if (applied) m_paddingDirty = true; + return applied; + } catch (...) { + m_adjustingCenterPadding = false; + return false; + } +} + +void HostSelectorPage::CenterSelectedHost(bool immediate) +{ + try { + auto grid = this->HostsGrid; + ListViewItem^ container = nullptr; + auto status = ResolveSelectedContainer(grid, m_hostsScrollViewer, container); + if (status == SelectedContainerStatus::NoSelection) return; + if (status == SelectedContainerStatus::ScrollViewerMissing) { + WaitForHostScrollViewerThenCenter(immediate); + return; + } + if (status == SelectedContainerStatus::ContainerMissing) { + WaitForHostContainerThenCenter(immediate); + return; + } + + EnsureCenteringPadding(); + if (m_paddingDirty) { + m_paddingDirty = false; + WaitForHostScrollViewerThenCenter(immediate); + return; + } + + try { grid->UpdateLayout(); } catch (...) {} + if (container->ActualWidth <= 0.0 || container->ActualHeight <= 0.0 || + std::fabs(container->ActualWidth - kSelectedHostContainerWidth) > 1.0) { + WaitForHostScrollViewerThenCenter(immediate); + return; + } + + double viewport = m_hostsScrollViewer->ViewportWidth; + if (!std::isfinite(viewport) || viewport <= 0.0) { + try { grid->UpdateLayout(); } catch (...) {} + viewport = m_hostsScrollViewer->ViewportWidth; + } + if (!std::isfinite(viewport) || viewport <= 0.0) return; + + CenterContainerInScrollViewer(m_hostsScrollViewer, container, false, immediate); + } catch (...) {} +} + +void HostSelectorPage::WaitForHostContainerThenCenter(bool immediate) +{ + auto grid = this->HostsGrid; + if (grid == nullptr || m_hostsContainerWait_token.Value != 0) return; + Platform::WeakReference weakThis(this); + m_hostsContainerWait_token = ArmContainerRealizedWait(grid, [weakThis, immediate]() { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + that->m_hostsContainerWait_token.Value = 0; + that->CenterSelectedHost(immediate); + }); +} + +void HostSelectorPage::WaitForHostScrollViewerThenCenter(bool immediate) +{ + auto grid = this->HostsGrid; + if (grid == nullptr || m_hostsScrollViewerWait_token.Value != 0) return; + Platform::WeakReference weakThis(this); + m_hostsScrollViewerWait_token = ArmLayoutUpdatedWait(grid, [weakThis, immediate]() { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + that->m_hostsScrollViewerWait_token.Value = 0; + that->CenterSelectedHost(immediate); + }); +} + +void HostSelectorPage::PreRealizeAllHostContainers() +{ + if (m_hostsPreRealized) return; + auto grid = this->HostsGrid; + if (grid == nullptr || grid->Items == nullptr || grid->Items->Size == 0) return; + + m_hostsPreRealized = true; + unsigned int count = grid->Items->Size; + int selectedIndex = grid->SelectedIndex; + try { + for (unsigned int i = 0; i < count; ++i) { + grid->ScrollIntoView(grid->Items->GetAt(i)); + grid->UpdateLayout(); + } + if (selectedIndex >= 0) { + grid->ScrollIntoView(grid->Items->GetAt(selectedIndex)); + grid->UpdateLayout(); + } + } catch (...) { + MLOG(Utils::LogLevel::Warning, "PreRealizeAllHostContainers failed"); + } +} + +void HostSelectorPage::HostsGrid_Loaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^) +{ + try { + auto grid = dynamic_cast(sender); + if (grid == nullptr) return; + if (m_hostsScrollViewer == nullptr) m_hostsScrollViewer = FindScrollViewer(grid); + + try { + if (grid->Items != nullptr && grid->Items->Size > 0 && grid->SelectedIndex < 0) { + grid->SelectedIndex = 0; + } + if (grid->Items != nullptr && grid->Items->Size > 0) { + Platform::WeakReference weakThis(this); + this->Dispatcher->RunAsync( + Windows::UI::Core::CoreDispatcherPriority::Low, + ref new Windows::UI::Core::DispatchedHandler([weakThis]() { + auto that = weakThis.Resolve(); + if (that != nullptr) that->FocusFirstHostItem(4); + })); + } + } catch (...) { + MLOG(Utils::LogLevel::Warning, "HostsGrid_Loaded initial-selection/focus dispatch failed"); + } + + PreRealizeAllHostContainers(); + SubscribeMoonPhaseRefreshOnNextLayout(true); + EnsureCenteringPadding(); + CenterSelectedHost(true); + UpdateAllMoonPhases(false, 4); + } catch (...) { + MLOG(Utils::LogLevel::Error, "HostsGrid_Loaded setup failed"); + } +} + +void HostSelectorPage::HostsGrid_SizeChanged(Platform::Object^, Windows::UI::Xaml::SizeChangedEventArgs^) +{ + if (m_adjustingCenterPadding) return; + EnsureCenteringPadding(); + CenterSelectedHost(true); +} + +void HostSelectorPage::FocusFirstHostItem(int attempts) +{ + try { + auto grid = HostsGrid; + if (grid == nullptr || grid->Items == nullptr || grid->Items->Size == 0) return; + if (grid->SelectedIndex < 0) grid->SelectedIndex = 0; + int idx = grid->SelectedIndex >= 0 ? grid->SelectedIndex : 0; + auto container = dynamic_cast(grid->ContainerFromIndex(idx)); + if (container != nullptr) { + container->Focus(Windows::UI::Xaml::FocusState::Programmatic); + return; + } + if (attempts <= 0 || Dispatcher == nullptr) return; + Platform::WeakReference weakThis(this); + try { + Dispatcher->RunAsync( + Windows::UI::Core::CoreDispatcherPriority::Normal, + ref new Windows::UI::Core::DispatchedHandler([weakThis, attempts]() { + auto that = weakThis.Resolve(); + if (that != nullptr) that->FocusFirstHostItem(attempts - 1); + })); + } catch (...) {} + } catch (...) {} +} + +void HostSelectorPage::HostsGrid_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^) +{ + try { + auto grid = dynamic_cast(sender); + if (grid == nullptr || grid->SelectedIndex < 0) return; + CenterSelectedHost(false); + UpdateAllMoonPhases(true); + } catch (...) {} + + try { + auto grid = dynamic_cast(sender); + auto selectedHost = (grid != nullptr && grid->SelectedItem != nullptr) + ? dynamic_cast(grid->SelectedItem) + : nullptr; + + auto bgKey = (selectedHost != nullptr) ? selectedHost->Personalization->Background : nullptr; + auto ls = Windows::Storage::ApplicationData::Current->LocalSettings->Values; + if (bgKey != nullptr && !bgKey->IsEmpty()) { + ls->Insert("background", bgKey); + } else { + ls->Insert("background", DynamicBackgroundHost::DefaultKey); + } + + if (BackgroundHost != nullptr) { + auto singleHost = ref new Platform::Collections::Vector(); + if (selectedHost != nullptr) singleHost->Append(selectedHost); + BackgroundHost->SetHosts(singleHost->Size > 0 ? (IVector^)singleHost : State->SavedHosts); + BackgroundHost->Refresh(); + BackgroundHost->StartAnimations(); + } + + Windows::UI::Color accentColor = (selectedHost != nullptr && !selectedHost->Personalization->UseSystemAccent) + ? selectedHost->Personalization->AccentColor + : (ref new Windows::UI::ViewManagement::UISettings()) + ->GetColorValue(Windows::UI::ViewManagement::UIColorType::Accent); + ApplyAccentColor(accentColor); + } catch (...) {} + + try { + auto grid = dynamic_cast(sender); + auto selectedHost = (grid != nullptr && grid->SelectedItem != nullptr) + ? dynamic_cast(grid->SelectedItem) + : nullptr; + + if (this->SelectedHostBanner != nullptr && this->SelectedHostText != nullptr) { + Platform::String^ newName = (selectedHost != nullptr && selectedHost->ComputerName != nullptr) + ? selectedHost->ComputerName + : ref new Platform::String(L""); + + Windows::UI::Xaml::Media::Animation::Storyboard^ showSb = nullptr; + Windows::UI::Xaml::Media::Animation::Storyboard^ hideSb = nullptr; + auto res = this->Resources; + if (res != nullptr) { + try { showSb = dynamic_cast(res->Lookup(ref new Platform::String(L"ShowSelectedHostStoryboard"))); } catch (...) {} + try { hideSb = dynamic_cast(res->Lookup(ref new Platform::String(L"HideSelectedHostStoryboard"))); } catch (...) {} + } + + bool alreadyVisible = this->SelectedHostBanner->Opacity > 0.01; + if (selectedHost == nullptr) { + if (alreadyVisible && hideSb != nullptr) hideSb->Begin(); + this->SelectedHostText->Text = ref new Platform::String(L""); + } else { + const unsigned int animVer = ++m_hostTextAnimVersion; + Platform::WeakReference weakThis(this); + auto capturedName = newName; + AnimateCrossfadeText( + this->SelectedHostBanner, showSb, hideSb, true, + [weakThis, capturedName]() { + auto that = weakThis.Resolve(); + if (that != nullptr && that->SelectedHostText != nullptr) + that->SelectedHostText->Text = capturedName; + }, + [weakThis, animVer]() { + auto that = weakThis.Resolve(); + return that != nullptr && that->m_hostTextAnimVersion == animVer; + }); + } + } + } catch (...) {} +} + +void HostSelectorPage::SubscribeMoonPhaseRefreshOnNextLayout(bool alsoFocusFirst) +{ + if (HostsGrid == nullptr || m_hostsGrid_ccc_token.Value != 0) return; + Platform::WeakReference weakThis(this); + m_hostsGrid_ccc_token = HostsGrid->LayoutUpdated += + ref new EventHandler([weakThis, alsoFocusFirst](Platform::Object^, Platform::Object^) { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + if (that->HostsGrid == nullptr || + that->HostsGrid->Items == nullptr || + that->HostsGrid->Items->Size == 0) return; + + try { that->HostsGrid->LayoutUpdated -= that->m_hostsGrid_ccc_token; } + catch (...) { MLOG(Utils::LogLevel::Warning, "failed to unsubscribe HostsGrid.LayoutUpdated"); } + that->m_hostsGrid_ccc_token.Value = 0; + that->UpdateAllMoonPhases(false, 0); + if (alsoFocusFirst) that->FocusFirstHostItem(4); + }); +} + +void HostSelectorPage::UpdateAllMoonPhases(bool animated, int attempts) +{ + try { + auto grid = HostsGrid; + if (grid == nullptr) return; + int selectedIdx = grid->SelectedIndex; + if (selectedIdx < 0) selectedIdx = 0; + unsigned int count = grid->Items->Size; + bool anyMissing = false; + for (unsigned int i = 0; i < count; ++i) { + try { + auto container = dynamic_cast( + grid->ContainerFromIndex(i)); + if (container == nullptr) { anyMissing = true; continue; } + auto ctrl = FindLunarControl(container); + if (ctrl == nullptr) { anyMissing = true; continue; } + int dist = (int)i - selectedIdx; + double fillAmount = dist < 0 ? -dist * 0.4 : dist * 0.4; + if (fillAmount > 1.0) fillAmount = 1.0; + int side = dist < 0 ? 1 : dist > 0 ? -1 : 0; + ctrl->UpdatePhase(fillAmount, side, animated); + ctrl->SetSelected(i == (unsigned int)selectedIdx, animated); + } catch (...) { + MLOGF(Utils::LogLevel::Warning, "UpdateAllMoonPhases failed for item %u", i); + } + } + if (anyMissing && attempts > 0) { + Platform::WeakReference weakThis(this); + bool capturedAnimated = animated; + int next = attempts - 1; + try { + this->Dispatcher->RunAsync( + Windows::UI::Core::CoreDispatcherPriority::Normal, + ref new Windows::UI::Core::DispatchedHandler([weakThis, capturedAnimated, next]() { + auto that = weakThis.Resolve(); + if (that != nullptr) that->UpdateAllMoonPhases(capturedAnimated, next); + })); + } catch (...) {} + } + } catch (...) {} +} + +LunarPhaseControl^ HostSelectorPage::FindLunarControl(Windows::UI::Xaml::DependencyObject^ root) +{ + if (root == nullptr) return nullptr; + if (auto ctrl = dynamic_cast(root)) return ctrl; + try { + int count = VisualTreeHelper::GetChildrenCount(root); + for (int i = 0; i < count; ++i) { + auto found = FindLunarControl(VisualTreeHelper::GetChild(root, i)); + if (found != nullptr) return found; + } + } catch (...) {} + return nullptr; +} + +void HostSelectorPage::NewHostButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) +{ + auto dialog = ref new AddHostDialog(); + Platform::WeakReference weakThis(this); + + dialog->Configure( + ref new Windows::UI::Xaml::RoutedEventHandler([weakThis, dialog](Platform::Object^, Windows::UI::Xaml::RoutedEventArgs^) { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + Platform::String^ hostname = dialog->GetHostname(); + if (hostname == nullptr || hostname->Length() == 0) return; + dialog->SetAddButtonEnabled(false); + Concurrency::create_task([that, dialog, hostname]() { + bool status = that->state->AddHost(hostname); + Platform::WeakReference weakPage(that); + Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + Windows::UI::Core::CoreDispatcherPriority::High, + ref new Windows::UI::Core::DispatchedHandler([weakPage, dialog, status, hostname]() { + if (!status) { + dialog->ShowError("Failed to connect to " + hostname); + dialog->SetAddButtonEnabled(true); + } else { + try { dialog->Hide(); } + catch (...) { MLOG(Utils::LogLevel::Warning, "failed to hide AddHostDialog after successful add"); } + auto page = weakPage.Resolve(); + if (page != nullptr) { + try { + if (page->HostsGrid != nullptr && page->HostsGrid->SelectedIndex < 0 && + page->HostsGrid->Items != nullptr && page->HostsGrid->Items->Size > 0) { + page->HostsGrid->SelectedIndex = 0; + } + } catch (...) { + MLOG(Utils::LogLevel::Warning, "failed to select newly added host"); + } + try { + page->SubscribeMoonPhaseRefreshOnNextLayout(false); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "failed to subscribe HostsGrid.LayoutUpdated for new host"); + } + } + } + })); + }); + }), + nullptr + ); + + dialog->SetRecentHostnames(state->RecentHostnames, state->RecentHostDisplayNames); + try { dialog->XamlRoot = this->XamlRoot; } catch (...) {} + concurrency::create_task(dialog->ShowAsync()); +} + +void HostSelectorPage::GridView_ItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ e) +{ + MoonlightHost^ host = (MoonlightHost^)e->ClickedItem; + + if (host->Connected && host->Paired) { + this->Connect(host); + return; + } + + if (host->Connected && !host->Paired) { + this->StartPairing(host); + return; + } + + this->ShowHostActions(host); +} + +void HostSelectorPage::ShowHostActions(MoonlightHost^ host) +{ + currentHost = host; + if (currentHost == nullptr) return; + + bool showWake = !(currentHost->Connected || currentHost->WolPolling); + bool showTest = !showWake; + + auto dialog = ref new HostActionsDialog(); + m_hostActionsDialog = dialog; + + Platform::WeakReference weakThis(this); + + dialog->Configure( + currentHost->ComputerName, + showWake, + showTest, + ref new Windows::UI::Xaml::RoutedEventHandler([weakThis](Platform::Object^, Windows::UI::Xaml::RoutedEventArgs^) { + auto that = weakThis.Resolve(); + if (that == nullptr || that->currentHost == nullptr) return; + auto t = ref new Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionInfo(); + t->Effect = Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionEffect::FromRight; + that->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(HostSettingsPage::typeid), that->currentHost, t); + }), + ref new Windows::UI::Xaml::RoutedEventHandler([weakThis](Platform::Object^, Windows::UI::Xaml::RoutedEventArgs^) { + auto that = weakThis.Resolve(); + if (that == nullptr || that->currentHost == nullptr) return; + that->wakeHostButton_Click(nullptr, nullptr); + }), + ref new Windows::UI::Xaml::RoutedEventHandler([weakThis](Platform::Object^, Windows::UI::Xaml::RoutedEventArgs^) { + auto that = weakThis.Resolve(); + if (that == nullptr || that->currentHost == nullptr) return; + that->testConnectionButton_Click(nullptr, nullptr); + }), + ref new Windows::UI::Xaml::RoutedEventHandler([weakThis](Platform::Object^, Windows::UI::Xaml::RoutedEventArgs^) { + auto that = weakThis.Resolve(); + if (that == nullptr || that->currentHost == nullptr) return; + auto confirmDialog = ref new ConfirmDialog(); + Platform::String^ hostName = that->currentHost->ComputerName; + Platform::String^ message = "Remove '" + hostName + "' from your saved hosts?"; + confirmDialog->Configure( + "Remove Host", + message, + L"\xE74D", + "Remove", + "Cancel", + ref new Windows::UI::Xaml::RoutedEventHandler([weakThis](Platform::Object^, Windows::UI::Xaml::RoutedEventArgs^) { + auto that = weakThis.Resolve(); + if (that == nullptr || that->currentHost == nullptr) return; + int removedIdx = that->HostsGrid->SelectedIndex; + that->State->RemoveHost(that->currentHost); + that->currentHost = nullptr; + int newSize = (int)that->State->SavedHosts->Size; + if (newSize > 0) { + that->HostsGrid->SelectedIndex = removedIdx < newSize ? removedIdx : newSize - 1; + } else if (that->BackgroundHost != nullptr) { + that->BackgroundHost->ResetBackground(); + } + }) + ); + try { confirmDialog->XamlRoot = that->XamlRoot; } catch (...) {} + concurrency::create_task(confirmDialog->ShowAsync()); + }), + ref new Windows::UI::Xaml::RoutedEventHandler([weakThis](Platform::Object^, Windows::UI::Xaml::RoutedEventArgs^) { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + auto t = ref new Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionInfo(); + t->Effect = Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionEffect::FromRight; + that->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(MoonlightSettings::typeid), nullptr, t); + }) + ); + + try { + dialog->XamlRoot = this->XamlRoot; + } catch (...) {} + + concurrency::create_task(dialog->ShowAsync()); +} + +void HostSelectorPage::HostsGrid_RightTapped(Platform::Object^ sender, Windows::UI::Xaml::Input::RightTappedRoutedEventArgs^ e) +{ + FrameworkElement^ senderElement = dynamic_cast(e->OriginalSource); + if (senderElement == nullptr) return; + + auto itemContainer = dynamic_cast(senderElement); + MoonlightHost^ host = nullptr; + if (itemContainer != nullptr) { + host = dynamic_cast(itemContainer->Content); + } + else { + host = dynamic_cast(senderElement->DataContext); + } + + this->ShowHostActions(host); +} + +void HostSelectorPage::SettingsButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) +{ + auto slideForward = ref new Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionInfo(); + slideForward->Effect = Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionEffect::FromRight; + this->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(MoonlightSettings::typeid), nullptr, slideForward); +} + +void HostSelectorPage::StartPairing(MoonlightHost^ host) { + MoonlightClient* client = new MoonlightClient(); + char ipAddressStr[2048]; + wcstombs_s(NULL, ipAddressStr, host->LastHostname->Data(), 2047); + int status = client->Connect(ipAddressStr); + if (status != 0)return; + char* pin = client->GeneratePIN(); + auto dialog = ref new ::moonlight_xbox_dx::PairDialog(); + wchar_t wpin[32]; + mbstowcs_s(NULL, wpin, pin, 31); + dialog->Configure(ref new Platform::String(wpin)); + try { dialog->XamlRoot = this->XamlRoot; } catch (...) {} + concurrency::create_task(dialog->ShowAsync()); + Concurrency::create_task([dialog, host, client, pin]() { + int a = client->Pair(); + Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High, ref new Windows::UI::Core::DispatchedHandler([a, dialog, host]() + { + if (a == 0) { + try { dialog->Hide(); } + catch (...) { MLOG(Utils::LogLevel::Warning, "failed to hide PairDialog after successful pairing"); } + } + host->UpdateHostInfo(true); + } + )); + }) .then([](concurrency::task t) { + try { + t.get(); + } + catch (const std::exception &e) { + MLOGF(Utils::LogLevel::Error, "StartPairing task exception: %s", e.what()); + } + catch (...) { + MLOG(Utils::LogLevel::Error, "StartPairing task unknown exception"); + } + }); +} + +static bool TryQuickTcpConnect(const std::string& hostOnly) +{ + bool reachable = false; + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) == 0) { + struct addrinfo hints = {}, *res = nullptr; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + if (getaddrinfo(hostOnly.c_str(), "47989", &hints, &res) == 0) { + SOCKET s = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (s != INVALID_SOCKET) { + u_long mode = 1; + ioctlsocket(s, FIONBIO, &mode); + connect(s, res->ai_addr, (int)res->ai_addrlen); + fd_set writeSet; FD_ZERO(&writeSet); FD_SET(s, &writeSet); + timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; + reachable = (select(0, NULL, &writeSet, NULL, &tv) > 0 && FD_ISSET(s, &writeSet)); + closesocket(s); + } + freeaddrinfo(res); + } + WSACleanup(); + } + return reachable; +} + +void HostSelectorPage::Connect(MoonlightHost^ host) { + if (!host->Connected)return; + if (!host->Paired) { + StartPairing(host); + return; + } + state->shouldAutoConnect = true; + continueFetch.store(false); + + Platform::WeakReference weakThis(this); + std::string hostOnly = Utils::PlatformStringToStdString(host->LastHostname); + auto colonPos = hostOnly.find(':'); + if (colonPos != std::string::npos) hostOnly = hostOnly.substr(0, colonPos); + + Concurrency::create_task([weakThis, host, hostOnly]() { + bool reachable = TryQuickTcpConnect(hostOnly); + + Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync( + Windows::UI::Core::CoreDispatcherPriority::High, + ref new Windows::UI::Core::DispatchedHandler([weakThis, host, reachable]() { + auto that = weakThis.Resolve(); + if (that == nullptr) return; + if (!reachable) { + that->continueFetch.store(true); + auto dialog = ref new ::moonlight_xbox_dx::AlertDialog(); + dialog->Configure(L"Host Offline", L"The host is not reachable. It may have gone offline."); + try { dialog->XamlRoot = that->XamlRoot; } catch (...) {} + concurrency::create_task(dialog->ShowAsync()); + return; + } + auto slideForward = ref new Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionInfo(); + slideForward->Effect = Windows::UI::Xaml::Media::Animation::SlideNavigationTransitionEffect::FromRight; + that->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(AppPage::typeid), host, slideForward); + })); + }); +} + +void HostSelectorPage::PollTick(Windows::System::Threading::ThreadPoolTimer^ timer) +{ + if (m_isNavigatedAway.load()) { + try { + if (timer != nullptr) timer->Cancel(); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "PollTick failed to cancel timer for destroyed page"); + } + return; + } + + m_pollActiveCount.fetch_add(1); + if (continueFetch.load()) { + try { + try { + mdns_send_query(); + } catch (...) { + } + query_mdns(); + std::vector hostsSnapshot; + { + auto savedHosts = GetApplicationState()->SavedHosts; + for (unsigned int i = 0; i < savedHosts->Size; i++) { + try { + hostsSnapshot.push_back(savedHosts->GetAt(i)); + } catch (...) { + break; + } + } + } + Concurrency::parallel_for_each(hostsSnapshot.begin(), hostsSnapshot.end(), [](MoonlightHost^ a) { + try { + a->UpdateHostInfo(true); + } catch (...) { + } + }); + } catch (const std::exception &ex) { + MLOGF(Utils::LogLevel::Error, "poll exception: %s", ex.what()); + } catch (...) { + MLOG(Utils::LogLevel::Error, "poll unknown exception"); + } + } + m_pollActiveCount.fetch_sub(1); +} + +void moonlight_xbox_dx::HostSelectorPage::wakeHostButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { + if (currentHost == nullptr) { + return; + } + + try { + bool success = State->WakeHost(currentHost); + if (!success) { + auto fail = ref new ::moonlight_xbox_dx::AlertDialog(); + fail->Configure("Wake Host Failed", "Failed to send Wake-on-LAN packet.\n\nPlease check if Wake-on-LAN is enabled on the host."); + try { fail->XamlRoot = this->XamlRoot; } catch (...) {} + concurrency::create_task(fail->ShowAsync()); + } + + if (success) { + ShowToast(L"Wake on LAN sent"); + auto host = currentHost; + host->WolPolling = true; + PollHostAfterWake(host); + } + } catch (std::exception ex) { + auto errDlg = ref new ::moonlight_xbox_dx::AlertDialog(); + errDlg->Configure("Wake Host Error", "An error occurred while trying to wake the host:\n\n" + Utils::StringFromChars((char *)ex.what())); + try { errDlg->XamlRoot = this->XamlRoot; } catch (...) {} + concurrency::create_task(errDlg->ShowAsync()); + } +} + +void HostSelectorPage::PollHostAfterWake(MoonlightHost^ host) +{ + concurrency::create_task(concurrency::create_async([host]() { + int consecutiveSuccess = 0; + for (int i = 0; i < 240; ++i) { + try { + host->UpdateHostInfo(false); + if (host->Connected) { + consecutiveSuccess++; + if (consecutiveSuccess >= 2) { + host->WolPolling = false; + break; + } + } else { + consecutiveSuccess = 0; + } + } catch (...) { + consecutiveSuccess = 0; + } + Sleep(250); + } + })).then([host](concurrency::task t) { + try { + t.get(); + } catch (...) { + MLOG(Utils::LogLevel::Error, "PollHostAfterWake task unknown exception"); + } + Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::Normal, ref new Windows::UI::Core::DispatchedHandler([host]() { + host->WolPolling = false; + })); + }); +} + +static std::string ProbeHostConnection(const std::string& hostOnly) +{ + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) return "WSAStartup failed"; + + struct addrinfo hints; + struct addrinfo *res = nullptr; + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_protocol = IPPROTO_TCP; + + int gai = getaddrinfo(hostOnly.c_str(), "47989", &hints, &res); + if (gai != 0) { + WSACleanup(); + return std::string("DNS lookup failed: ") + std::to_string(gai); + } + + bool ok = false; + double bestRttMs = -1.0; + for (struct addrinfo *p = res; p != nullptr; p = p->ai_next) { + SOCKET s = socket(p->ai_family, p->ai_socktype, p->ai_protocol); + if (s == INVALID_SOCKET) continue; + u_long mode = 1; + ioctlsocket(s, FIONBIO, &mode); + + using namespace std::chrono; + auto start = high_resolution_clock::now(); + int rc = connect(s, p->ai_addr, (int)p->ai_addrlen); + if (rc == 0) { + auto end = high_resolution_clock::now(); + double ms = duration_cast(end - start).count() / 1000.0; + if (bestRttMs < 0 || ms < bestRttMs) bestRttMs = ms; + ok = true; + closesocket(s); + break; + } + fd_set writeSet; + FD_ZERO(&writeSet); + FD_SET(s, &writeSet); + timeval tv; tv.tv_sec = 3; tv.tv_usec = 0; + int sel = select(0, NULL, &writeSet, NULL, &tv); + if (sel > 0 && FD_ISSET(s, &writeSet)) { + auto end = high_resolution_clock::now(); + double ms = duration_cast(end - start).count() / 1000.0; + if (bestRttMs < 0 || ms < bestRttMs) bestRttMs = ms; + ok = true; + closesocket(s); + break; + } + closesocket(s); + } + freeaddrinfo(res); + WSACleanup(); + + if (!ok) return "Connection failed"; + if (bestRttMs < 0) return "Connection OK"; + char buf[64]; + snprintf(buf, sizeof(buf), "Connection OK (RTT: %.1f ms)", bestRttMs); + return buf; +} + +void HostSelectorPage::testConnectionButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { + if (currentHost == nullptr) return; + + std::string hostname = Utils::PlatformStringToStdString(currentHost->LastHostname); + auto pos = hostname.find(':'); + std::string hostOnly = (pos == std::string::npos) ? hostname : hostname.substr(0, pos); + + Platform::WeakReference weakThis(this); + concurrency::create_task([hostOnly, weakThis]() { + std::string resultMsg = ProbeHostConnection(hostOnly); + + Windows::ApplicationModel::Core::CoreApplication::MainView->CoreWindow->Dispatcher->RunAsync(Windows::UI::Core::CoreDispatcherPriority::High, ref new Windows::UI::Core::DispatchedHandler([resultMsg, hostOnly, weakThis]() { + auto dialog = ref new TestConnectionResultDialog(); + dialog->Configure( + Utils::StringFromStdString(hostOnly), + Utils::StringFromStdString(resultMsg) + ); + auto page = weakThis.Resolve(); + if (page != nullptr) { + try { dialog->XamlRoot = page->XamlRoot; } catch (...) {} + } + concurrency::create_task(dialog->ShowAsync()); + })); + }); +} diff --git a/UI/Pages/HostSelectorPage.xaml.h b/UI/Pages/HostSelectorPage.xaml.h new file mode 100644 index 00000000..cc7ee0a8 --- /dev/null +++ b/UI/Pages/HostSelectorPage.xaml.h @@ -0,0 +1,73 @@ + +#pragma once + +#include "UI\Pages\HostSelectorPage.g.h" +#include "UI\Backgrounds\DynamicBackgroundHost.xaml.h" +#include "UI\Modals\HostActionsDialog.xaml.h" +#include "UI\Modals\AddHostDialog.xaml.h" +#include "State\ApplicationState.h" +#include "UI\Controls\LunarPhaseControl.xaml.h" + +#include + +using namespace Windows::UI::Core; +namespace moonlight_xbox_dx +{ + [Windows::Foundation::Metadata::WebHostHidden] + public ref class HostSelectorPage sealed + { + public: + HostSelectorPage(); + property ApplicationState^ State { + ApplicationState^ get() { + return this->state; + } + } + void OnStateLoaded(); + void Connect(MoonlightHost^ host); + protected: + virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override; + virtual void OnNavigatedFrom(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override; + private: + ApplicationState ^state; + void NewHostButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void GridView_ItemClick(Platform::Object^ sender, Windows::UI::Xaml::Controls::ItemClickEventArgs^ e); + void HostsGrid_Loaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void HostsGrid_SizeChanged(Platform::Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e); + void HostsGrid_SelectionChanged(Platform::Object^ sender, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^ e); + void CenterSelectedHost(bool immediate = false); + bool EnsureCenteringPadding(); + void WaitForHostContainerThenCenter(bool immediate); + void WaitForHostScrollViewerThenCenter(bool immediate); + void PreRealizeAllHostContainers(); + void StartPairing(MoonlightHost^ host); + void HostsGrid_RightTapped(Platform::Object^ sender, Windows::UI::Xaml::Input::RightTappedRoutedEventArgs^ e); + MoonlightHost^ currentHost; + Windows::UI::Xaml::Controls::ScrollViewer^ m_hostsScrollViewer; + bool m_adjustingCenterPadding = false; + double m_lastCenterPadding = -1.0; + bool m_paddingDirty = false; + bool m_hostsPreRealized = false; + void SettingsButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + std::atomic continueFetch; + std::atomic m_isNavigatedAway; + std::atomic m_pollActiveCount; + Windows::System::Threading::ThreadPoolTimer^ m_pollTimer; + void OnKeyDown(Platform::Object^ sender, Windows::UI::Xaml::Input::KeyRoutedEventArgs^ e); + void ShowHostActions(MoonlightHost^ host); + HostActionsDialog^ m_hostActionsDialog; + void wakeHostButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void testConnectionButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void UpdateAllMoonPhases(bool animated, int attempts = 0); + void FocusFirstHostItem(int attempts = 4); + LunarPhaseControl^ FindLunarControl(Windows::UI::Xaml::DependencyObject^ root); + void SubscribeMoonPhaseRefreshOnNextLayout(bool alsoFocusFirst); + void PollTick(Windows::System::Threading::ThreadPoolTimer^ timer); + void PollHostAfterWake(MoonlightHost^ host); + void StartPollTimerIfNeeded(); + unsigned int m_hostTextAnimVersion = 0; + Windows::Foundation::EventRegistrationToken m_hostsGrid_ccc_token; + Windows::Foundation::EventRegistrationToken m_hostsContainerWait_token; + Windows::Foundation::EventRegistrationToken m_hostsScrollViewerWait_token; + }; +} diff --git a/UI/Pages/HostSettingsPage.xaml b/UI/Pages/HostSettingsPage.xaml new file mode 100644 index 00000000..34ec25f9 --- /dev/null +++ b/UI/Pages/HostSettingsPage.xaml @@ -0,0 +1,440 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + HDR requires Xbox system resolution set to 4K. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +   + In-stream menu + + + + + + + + + + + + + + + + + + + Left click + + Right click + + Show Keyboard + + Guide / Xbox button + + + + + + + + + + + + + + + + + + + + Close + + Left Click + + Right Click + + Move / Drag + + Press to Scroll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Servers + To make Moonlight work, you need a Server. You can use one of the following options: + + + + + + + + + + + Sunshine + + The recommended open source solution. Compatible with Windows/macOS/Linux and with any GPU. + + + https://github.com/LizardByte/Sunshine + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Hints + + + + After installing your server, wait or press '+' for adding your PC manually + + + + + During the stream, you can press ' + ' + to show more options and enable mouse mode + + + + + + + In mouse mode, you can move and scroll using your sticks, ' ' Left click + ' ' Right click ' ' Guide/Xbox button + + + + + + + In mouse mode, you can press ' ' to show Keyboard. You can also use a USB Keyboard + + + + + + + + Is your screen a quarter of its size? Change the global composition scale inside the 'Moonlight Settings' of the App + + + + + + + + + + + Support + Need some Help? + + + + + + + + + + GitHub + + Check the Wiki for Support Links, documentation and more! + + + https://github.com/TheElixZammuto/moonlight-xbox/wiki + + + + + + + + + + + diff --git a/MoonlightWelcome.xaml.cpp b/UI/Pages/MoonlightWelcome.xaml.cpp similarity index 88% rename from MoonlightWelcome.xaml.cpp rename to UI/Pages/MoonlightWelcome.xaml.cpp index f795a222..aeb44e9e 100644 --- a/MoonlightWelcome.xaml.cpp +++ b/UI/Pages/MoonlightWelcome.xaml.cpp @@ -1,7 +1,3 @@ -// -// MoonlightWelcome.xaml.cpp -// Implementazione della classe MoonlightWelcome -// #include "pch.h" #include "MoonlightWelcome.xaml.h" @@ -19,12 +15,10 @@ using namespace Windows::UI::Xaml::Input; using namespace Windows::UI::Xaml::Media; using namespace Windows::UI::Xaml::Navigation; -// Il modello di elemento Pagina vuota è documentato all'indirizzo https://go.microsoft.com/fwlink/?LinkId=234238 - MoonlightWelcome::MoonlightWelcome() { InitializeComponent(); - Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->SetDesiredBoundsMode(Windows::UI::ViewManagement::ApplicationViewBoundsMode::UseVisible); + Windows::UI::ViewManagement::ApplicationView::GetForCurrentView()->SetDesiredBoundsMode(Windows::UI::ViewManagement::ApplicationViewBoundsMode::UseCoreWindow); this->Loaded += ref new Windows::UI::Xaml::RoutedEventHandler(this, &MoonlightWelcome::OnLoaded); this->Unloaded += ref new Windows::UI::Xaml::RoutedEventHandler(this, &MoonlightWelcome::OnUnloaded); @@ -32,9 +26,7 @@ MoonlightWelcome::MoonlightWelcome() void MoonlightWelcome::OnBackRequested(Platform::Object^ e, Windows::UI::Core::BackRequestedEventArgs^ args) { - // UWP on Xbox One triggers a back request whenever the B - // button is pressed which can result in the app being - // suspended if unhandled + if (this->FlipView->SelectedIndex > 0) { args->Handled = true; this->FlipView->SelectedIndex = this->FlipView->SelectedIndex - 1; @@ -81,10 +73,12 @@ void MoonlightWelcome::OnLoaded(Platform::Object^ sender, Windows::UI::Xaml::Rou { auto navigation = Windows::UI::Core::SystemNavigationManager::GetForCurrentView(); m_back_token = navigation->BackRequested += ref new EventHandler(this, &MoonlightWelcome::OnBackRequested); + BackgroundControl->StartAnimations(); } void MoonlightWelcome::OnUnloaded(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { auto navigation = Windows::UI::Core::SystemNavigationManager::GetForCurrentView(); navigation->BackRequested -= m_back_token; + BackgroundControl->StopAnimations(); } diff --git a/MoonlightWelcome.xaml.h b/UI/Pages/MoonlightWelcome.xaml.h similarity index 77% rename from MoonlightWelcome.xaml.h rename to UI/Pages/MoonlightWelcome.xaml.h index b09d53d4..e8db28af 100644 --- a/MoonlightWelcome.xaml.h +++ b/UI/Pages/MoonlightWelcome.xaml.h @@ -1,17 +1,12 @@ -// -// MoonlightWelcome.xaml.h -// Dichiarazione della classe MoonlightWelcome -// #pragma once -#include "MoonlightWelcome.g.h" +#include "UI\Pages\MoonlightWelcome.g.h" +#include "UI\Backgrounds\Streaks\StreaksBackground.xaml.h" namespace moonlight_xbox_dx { - /// - /// Pagina vuota che può essere usata autonomamente oppure per l'esplorazione all'interno di un frame. - /// + [Windows::Foundation::Metadata::WebHostHidden] public ref class MoonlightWelcome sealed { diff --git a/UI/Pages/StreamPage.xaml b/UI/Pages/StreamPage.xaml new file mode 100644 index 00000000..a1fe1a6a --- /dev/null +++ b/UI/Pages/StreamPage.xaml @@ -0,0 +1,389 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pages/StreamPage.xaml.cpp b/UI/Pages/StreamPage.xaml.cpp similarity index 58% rename from Pages/StreamPage.xaml.cpp rename to UI/Pages/StreamPage.xaml.cpp index b4a04b0d..0cd94837 100644 --- a/Pages/StreamPage.xaml.cpp +++ b/UI/Pages/StreamPage.xaml.cpp @@ -1,14 +1,10 @@ -// -// DirectXPage.xaml.cpp -// Implementation of the DirectXPage class. -// #include "pch.h" #include "StreamPage.xaml.h" #include "../Streaming/FFMpegDecoder.h" +#define MLOG_TAG_OVERRIDE "StreamPage" #include #include -#include "../Common/ModalDialog.xaml.h" using namespace moonlight_xbox_dx; @@ -29,6 +25,8 @@ using namespace Windows::UI::Xaml::Controls::Primitives; using namespace Windows::UI::Xaml::Data; using namespace Windows::UI::Xaml::Input; using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Media::Animation; +using namespace Windows::UI::Xaml::Media::Imaging; using namespace Windows::UI::Xaml::Navigation; using namespace concurrency; @@ -43,22 +41,32 @@ StreamPage::StreamPage(): swapChainPanel->SizeChanged += ref new SizeChangedEventHandler(this, &StreamPage::OnSwapChainPanelSizeChanged); m_deviceResources = std::make_shared(); -} - + m_subMenuCloseTimer = ref new Windows::UI::Xaml::DispatcherTimer(); + m_subMenuCloseTimer->Interval = Windows::Foundation::TimeSpan{ 1000000LL }; + m_subMenuCloseTimer->Tick += ref new Windows::Foundation::EventHandler(this, &StreamPage::OnSubMenuCloseTimer_Tick); +} void StreamPage::OnBackRequested(Platform::Object^ e,Windows::UI::Core::BackRequestedEventArgs^ args) { - // UWP on Xbox One triggers a back request whenever the B - // button is pressed which can result in the app being - // suspended if unhandled + args->Handled = true; } void StreamPage::Page_Loaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { this->m_progressView->Visibility = Windows::UI::Xaml::Visibility::Visible; - this->m_progressRing->IsActive = true; + + m_streamMenuVisible = false; + this->MenuShowStoryboard->Stop(); + this->MenuHideStoryboard->Stop(); + this->StreamMenuGrid->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + InstantHideSubMenu(); + + if (m_backgroundImage != nullptr) { + StartBgPanAnimation(); + FadeInBackground(); + } auto navigation = Windows::UI::Core::SystemNavigationManager::GetForCurrentView(); m_back_cookie = navigation->BackRequested += ref new EventHandler(this, &StreamPage::OnBackRequested); @@ -68,14 +76,13 @@ void StreamPage::Page_Loaded(Platform::Object ^ sender, Windows::UI::Xaml::Route keyDownHandler = (Windows::UI::Core::CoreWindow::GetForCurrentThread()->KeyDown += ref new Windows::Foundation::TypedEventHandler(this, &StreamPage::OnKeyDown)); keyUpHandler = (Windows::UI::Core::CoreWindow::GetForCurrentThread()->KeyUp += ref new Windows::Foundation::TypedEventHandler(this, &StreamPage::OnKeyUp)); - // Detect gamepad connection and disconnection events gamepadAddedHandler = Gamepad::GamepadAdded += ref new EventHandler(this, &StreamPage::OnGamepadAdded); gamepadRemovedHandler = Gamepad::GamepadRemoved += ref new EventHandler(this, &StreamPage::OnGamepadRemoved); try { m_deviceResources->SetSwapChainPanel(swapChainPanel); } catch (...) { - Utils::Log("StreamPage::Page_Loaded: SetSwapChainPanel failed\n"); + MLOG(Utils::LogLevel::Error, "Page_Loaded: SetSwapChainPanel failed\n"); } Platform::WeakReference weakThis(this); @@ -88,21 +95,23 @@ void StreamPage::Page_Loaded(Platform::Object ^ sender, Windows::UI::Xaml::Route that->m_main->CreateWindowSizeDependentResources(); that->m_main->StartRenderLoop(); } catch (const std::exception &ex) { - Utils::Logf("StreamPage::Page_Loaded: Exception when starting stream. Exception: %s", ex.what()); + MLOGF(Utils::LogLevel::Error, "Page_Loaded: Exception when starting stream. Exception: %s", ex.what()); } catch (const std::string &string) { - Utils::Logf("StreamPage::Page_Loaded: Exception when starting stream. Exception: %s", string); + MLOGF(Utils::LogLevel::Error, "Page_Loaded: Exception when starting stream. Exception: %s", string); } catch (Platform::Exception ^ e) { Platform::String ^ errorMsg = ref new Platform::String(); errorMsg = errorMsg->Concat(L"Exception: ", e->Message); errorMsg = errorMsg->Concat(errorMsg, Utils::StringPrintf("%x", e->HResult)); - Utils::Logf("StreamPage::Page_Loaded: Exception when starting stream. Exception: %s", Utils::PlatformStringToStdString(errorMsg)); + MLOGF(Utils::LogLevel::Error, "Page_Loaded: Exception when starting stream. Exception: %s", Utils::PlatformStringToStdString(errorMsg)); } catch (...) { - Utils::Log("StreamPage::Page_Loaded: Exception when starting stream. Exception: Generic Exception"); + MLOG(Utils::LogLevel::Error, "Page_Loaded: Exception when starting stream. Exception: Generic Exception"); } }); } void StreamPage::Page_Unloaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { + try { if (m_bgPanStoryboard != nullptr) { m_bgPanStoryboard->Stop(); m_bgPanStoryboard = nullptr; } } catch(...) {} + auto navigation = Windows::UI::Core::SystemNavigationManager::GetForCurrentView(); navigation->BackRequested -= m_back_cookie; @@ -111,18 +120,18 @@ void StreamPage::Page_Unloaded(Platform::Object ^ sender, Windows::UI::Xaml::Rou if (this->m_main) { - Utils::Log("StreamPage::Page_Unloaded stopping m_main render loop\n"); + MLOG(Utils::LogLevel::Info, "Page_Unloaded stopping m_main render loop\n"); try { this->m_main->StopRenderLoop(); this->m_main.reset(); } catch (std::exception &ex) { - Utils::Logf("StreamPage::Page_Unloaded m_main threw an exception: %s\n", ex.what()); + MLOGF(Utils::LogLevel::Error, "Page_Unloaded m_main threw an exception: %s\n", ex.what()); } catch (...) { - Utils::Log("StreamPage::Page_Unloaded m_main threw an exception\n"); + MLOG(Utils::LogLevel::Error, "Page_Unloaded m_main threw an exception\n"); } - Utils::Log("StreamPage::Page_Unloaded m_main reset\n"); + MLOG(Utils::LogLevel::Info, "Page_Unloaded m_main reset\n"); } Windows::UI::Core::CoreWindow::GetForCurrentThread()->KeyDown -= keyDownHandler; @@ -136,24 +145,80 @@ StreamPage::~StreamPage() void StreamPage::OnSwapChainPanelSizeChanged(Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e) { if (m_main == nullptr || m_deviceResources == nullptr)return; - Utils::Logf("StreamPage::OnSwapChainPanelSizeChanged( NewSize: %f x %f )\n", e->NewSize.Width, e->NewSize.Height); + MLOGF(Utils::LogLevel::Debug, "OnSwapChainPanelSizeChanged( NewSize: %f x %f )\n", e->NewSize.Width, e->NewSize.Height); critical_section::scoped_lock lock(m_main->GetCriticalSection()); m_deviceResources->SetLogicalSize(e->NewSize); m_main->CreateDeviceDependentResources(); m_main->CreateWindowSizeDependentResources(); } +void StreamPage::SetStreamMenuVisible(bool visible) { + m_streamMenuVisible = visible; + if (visible) { + this->MenuHideStoryboard->Stop(); + this->StreamMenuGrid->Visibility = Windows::UI::Xaml::Visibility::Visible; + this->MenuShowStoryboard->Begin(); + this->FirstMenuButton->Focus(Windows::UI::Xaml::FocusState::Programmatic); + } else { + InstantHideSubMenu(); + this->MenuShowStoryboard->Stop(); + this->MenuHideStoryboard->Begin(); -void StreamPage::flyoutButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) -{ - Windows::UI::Xaml::Controls::Flyout::ShowAttachedFlyout((FrameworkElement^)sender); - m_main->SetFlyoutOpened(true); + } + if (m_main) m_main->SetMenuVisible(visible); +} + +void StreamPage::MenuHideStoryboard_Completed(Platform::Object^ sender, Platform::Object^ e) { + this->StreamMenuGrid->Visibility = Windows::UI::Xaml::Visibility::Collapsed; } +void StreamPage::ShowSubMenu() { + if (m_subMenuVisible) return; + m_subMenuVisible = true; + this->SubMenuHideStoryboard->Stop(); + this->OtherSubMenuPanel->Visibility = Windows::UI::Xaml::Visibility::Visible; + this->SubMenuShowStoryboard->Begin(); +} -void StreamPage::ActionsFlyout_Closed(Platform::Object^ sender, Platform::Object^ e) -{ - if(m_main != nullptr) m_main->SetFlyoutOpened(false); +void StreamPage::HideSubMenu() { + if (!m_subMenuVisible) return; + m_subMenuVisible = false; + this->SubMenuShowStoryboard->Stop(); + this->SubMenuHideStoryboard->Begin(); +} + +void StreamPage::InstantHideSubMenu() { + if (m_subMenuCloseTimer) m_subMenuCloseTimer->Stop(); + m_subMenuVisible = false; + this->SubMenuShowStoryboard->Stop(); + this->SubMenuHideStoryboard->Stop(); + this->OtherSubMenuPanel->Visibility = Windows::UI::Xaml::Visibility::Collapsed; +} + +void StreamPage::SubMenuHideStoryboard_Completed(Platform::Object^ sender, Platform::Object^ e) { + this->OtherSubMenuPanel->Visibility = Windows::UI::Xaml::Visibility::Collapsed; +} + +void StreamPage::OtherButton_GotFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { + m_subMenuCloseTimer->Stop(); + ShowSubMenu(); +} + +void StreamPage::OtherButton_LostFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { + m_subMenuCloseTimer->Start(); +} + +void StreamPage::SubMenuButton_GotFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { + m_subMenuCloseTimer->Stop(); +} + +void StreamPage::SubMenuButton_LostFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { + m_subMenuCloseTimer->Start(); +} + +void StreamPage::OnSubMenuCloseTimer_Tick(Platform::Object^ sender, Platform::Object^ e) { + m_subMenuCloseTimer->Stop(); + HideSubMenu(); } void StreamPage::toggleMouseButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) @@ -163,13 +228,24 @@ void StreamPage::toggleMouseButton_Click(Platform::Object^ sender, Windows::UI:: void StreamPage::SetMouseMode(bool enabled) { - this->MouseMode = enabled; - if (m_main) m_main->mouseMode = this->MouseMode; + this->MouseMode = enabled; + if (m_main) m_main->mouseMode = this->MouseMode; + + if (this->MouseModeText != nullptr) { + this->MouseModeText->Text = this->MouseMode ? ref new Platform::String(L"Mouse On") : ref new Platform::String(L"Mouse Off"); + } + + if (enabled) { + this->StreamMenuGrid->Visibility = Windows::UI::Xaml::Visibility::Visible; + this->FirstMenuButton->Focus(Windows::UI::Xaml::FocusState::Programmatic); + this->StreamMenuGrid->Visibility = Windows::UI::Xaml::Visibility::Collapsed; + } } void StreamPage::showKeyboardButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { if (!m_main) return; + SetStreamMenuVisible(false); if (GetApplicationState()->EnableKeyboard) { m_main->keyboardMode = true; @@ -190,7 +266,10 @@ void StreamPage::toggleLogsButton_Click(Platform::Object^ sender, Windows::UI::X } void StreamPage::SetShowLogs(bool enabled) { - this->ShowLogs = enabled; + this->ShowLogs = enabled; + if (this->ShowLogsText != nullptr) { + this->ShowLogsText->Text = this->ShowLogs ? ref new Platform::String(L"Logs On") : ref new Platform::String(L"Logs Off"); + } } void StreamPage::toggleStatsButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { @@ -199,7 +278,60 @@ void StreamPage::toggleStatsButton_Click(Platform::Object ^ sender, Windows::UI: } void StreamPage::SetShowStats(bool enabled) { - this->ShowStats = enabled; + this->ShowStats = enabled; + if (this->ShowStatsText != nullptr) { + this->ShowStatsText->Text = this->ShowStats ? ref new Platform::String(L"Stats On") : ref new Platform::String(L"Stats Off"); + } +} + +void StreamPage::StartBgPanAnimation() { + try { + if (PageBackgroundImage == nullptr) return; + + auto transform = ref new TranslateTransform(); + PageBackgroundImage->RenderTransform = transform; + + auto sb = ref new Storyboard(); + sb->RepeatBehavior = RepeatBehaviorHelper::Forever; + sb->AutoReverse = true; + + Windows::UI::Xaml::Duration dur(TimeSpan{ 10LL * 10000000LL }); + + auto ease = ref new SineEase(); + ease->EasingMode = EasingMode::EaseInOut; + + auto animY = ref new DoubleAnimation(); + animY->To = ref new Platform::Box(120.0); + animY->Duration = dur; + animY->EasingFunction = ease; + Storyboard::SetTarget(animY, transform); + Storyboard::SetTargetProperty(animY, ref new Platform::String(L"Y")); + sb->Children->Append(animY); + + sb->Begin(); + m_bgPanStoryboard = sb; + } catch(...) {} +} + +void StreamPage::FadeInBackground() { + try { + if (PageBackgroundImage == nullptr || m_backgroundImage == nullptr) return; + + auto brush = dynamic_cast(PageBackgroundImage->Background); + if (brush == nullptr) return; + brush->ImageSource = m_backgroundImage; + + auto anim = ref new DoubleAnimation(); + anim->From = 0.0; + anim->To = 0.2; + anim->Duration = Windows::UI::Xaml::Duration(TimeSpan{ 2500000LL }); + + auto sb = ref new Storyboard(); + sb->Children->Append(anim); + Storyboard::SetTarget(anim, PageBackgroundImage); + Storyboard::SetTargetProperty(anim, ref new Platform::String(L"Opacity")); + sb->Begin(); + } catch(...) {} } void StreamPage::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) { @@ -209,6 +341,8 @@ void StreamPage::OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArg if (configuration == nullptr)return; + m_backgroundImage = configuration->backgroundImage; + SetMouseMode(false); SetShowLogs(false); SetShowStats(configuration->enableStats); @@ -220,16 +354,16 @@ void StreamPage::disonnectButton_Click(Platform::Object^ sender, Windows::UI::Xa Windows::UI::Core::CoreWindow::GetForCurrentThread()->KeyDown -= keyDownHandler; Windows::UI::Core::CoreWindow::GetForCurrentThread()->KeyUp -= keyUpHandler; - // trigger the server disconnected flow this->m_main->moonlightClient->SetConnectionTerminated(); } void StreamPage::OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ e) { - //Ignore Gamepad input + if (e->VirtualKey >= Windows::System::VirtualKey::GamepadA && e->VirtualKey <= Windows::System::VirtualKey::GamepadRightThumbstickLeft) { return; } + if (!m_main) return; char modifiers = 0; modifiers |= CoreWindow::GetForCurrentThread()->GetKeyState(Windows::System::VirtualKey::Control) == (CoreVirtualKeyStates::Down) ? MODIFIER_CTRL : 0; modifiers |= CoreWindow::GetForCurrentThread()->GetKeyState(Windows::System::VirtualKey::Menu) == (CoreVirtualKeyStates::Down) ? MODIFIER_ALT : 0; @@ -237,10 +371,9 @@ void StreamPage::OnKeyDown(Windows::UI::Core::CoreWindow^ sender, Windows::UI::C this->m_main->OnKeyDown((unsigned short)e->VirtualKey,modifiers); } - void StreamPage::OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Core::KeyEventArgs^ e) { - //Ignore Gamepad input + if (e->VirtualKey >= Windows::System::VirtualKey::GamepadA && e->VirtualKey <= Windows::System::VirtualKey::GamepadRightThumbstickLeft) { return; } @@ -249,37 +382,37 @@ void StreamPage::OnKeyUp(Windows::UI::Core::CoreWindow^ sender, Windows::UI::Cor modifiers |= CoreWindow::GetForCurrentThread()->GetKeyState(Windows::System::VirtualKey::Menu) == (CoreVirtualKeyStates::Down) ? MODIFIER_ALT : 0; modifiers |= CoreWindow::GetForCurrentThread()->GetKeyState(Windows::System::VirtualKey::Shift) == (CoreVirtualKeyStates::Down) ? MODIFIER_SHIFT : 0; this->m_main->OnKeyUp((unsigned short) e->VirtualKey, modifiers); - } void StreamPage::disconnectAndCloseButton_Click(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e) { Windows::UI::Core::CoreWindow::GetForCurrentThread()->KeyDown -= keyDownHandler; Windows::UI::Core::CoreWindow::GetForCurrentThread()->KeyUp -= keyUpHandler; if (this->m_main) { - // trigger the server disconnected flow which will cleanly exit the loop and call StopRenderLoop() + this->m_main->moonlightClient->SetConnectionTerminated(); } auto that = this; - auto progressToken = ::moonlight_xbox_dx::ModalDialog::ShowProgressDialogToken(nullptr, Utils::StringFromStdString("Closing...")); + auto name = (configuration != nullptr && configuration->appName != nullptr) ? configuration->appName : nullptr; + this->ClosingOverlayText->Text = (name != nullptr && name->Length() > 0) + ? ref new Platform::String((std::wstring(L"Closing ") + name->Data() + L"...").c_str()) + : ref new Platform::String(L"Closing..."); + this->ClosingOverlay->Visibility = Windows::UI::Xaml::Visibility::Visible; - concurrency::create_task(concurrency::create_async([that, progressToken]() { + concurrency::create_task(concurrency::create_async([that]() { try { if (that->m_main) { that->m_main->CloseApp(); } } catch (...) { } - })).then([that, progressToken](concurrency::task t) { + })).then([that](concurrency::task t) { try { t.get(); - // UI is sent back to HostSelectorPage in StartRenderLoop(), after the loop exits - // All we need to do is close the progress dialog - - DISPATCH_UI([progressToken] { - ::moonlight_xbox_dx::ModalDialog::HideDialogByToken(progressToken); + DISPATCH_UI([that] { + that->ClosingOverlay->Visibility = Windows::UI::Xaml::Visibility::Collapsed; }); } catch (...) { } @@ -291,25 +424,23 @@ void StreamPage::Keyboard_OnKeyDown(KeyboardControl^ sender, KeyEvent^ e) this->m_main->OnKeyDown(e->VirtualKey, e->Modifiers); } - void StreamPage::Keyboard_OnKeyUp(KeyboardControl^ sender, KeyEvent^ e) { this->m_main->OnKeyUp(e->VirtualKey, e->Modifiers); } - void StreamPage::guideButtonShort_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { + SetStreamMenuVisible(false); this->m_main->SendGuideButton(500); } - void StreamPage::guideButtonLong_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { + SetStreamMenuVisible(false); this->m_main->SendGuideButton(3000); } - void StreamPage::toggleHDR_WinAltB_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { this->m_main->SendWinAltB(); @@ -322,14 +453,14 @@ void StreamPage::resetDecoder_Click(Platform::Object^ sender, Windows::UI::Xaml: void StreamPage::toggleFramePacing_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e) { - // thread safe atomic bool + bool isImmediate = Pacer::instance().getPacingImmediate(); Pacer::instance().setPacingImmediate(isImmediate ? false : true); } void StreamPage::OnPropertyChanged(Platform::String^ propertyName) { - PropertyChanged(this, ref new Windows::UI::Xaml::Data::PropertyChangedEventArgs(propertyName)); + } void StreamPage::OnGamepadAdded(Platform::Object^ sender, Gamepad^ gamepad) @@ -349,4 +480,3 @@ bool StreamPage::ShouldRefreshGamepads() { void StreamPage::RequestRefreshGamepads() { m_refreshGamepads.store(true, std::memory_order_release); } - diff --git a/Pages/StreamPage.xaml.h b/UI/Pages/StreamPage.xaml.h similarity index 75% rename from Pages/StreamPage.xaml.h rename to UI/Pages/StreamPage.xaml.h index dc1862a0..36193956 100644 --- a/Pages/StreamPage.xaml.h +++ b/UI/Pages/StreamPage.xaml.h @@ -1,36 +1,28 @@ -// -// DirectXPage.xaml.h -// Declaration of the DirectXPage class. -// #pragma once -#include "Pages\StreamPage.g.h" +#include "UI\Pages\StreamPage.g.h" #include "Common\DeviceResources.h" #include "Streaming\moonlight_xbox_dxMain.h" #include "KeyboardControl.xaml.h" -#include "Converters\BoolToTextConverter.h" +#include "UI\Converters\BoolToTextConverter.h" using namespace Microsoft::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Controls; namespace moonlight_xbox_dx { - /// - /// A page that hosts a DirectX SwapChainPanel. - /// - class moonlight_xbox_dxMain; - public ref class StreamPage sealed : Windows::UI::Xaml::Data::INotifyPropertyChanged + class moonlight_xbox_dxMain; + public ref class StreamPage sealed { public: StreamPage(); virtual ~StreamPage(); - virtual event Windows::UI::Xaml::Data::PropertyChangedEventHandler^ PropertyChanged; - void OnPropertyChanged(Platform::String^ propertyName); + void OnPropertyChanged(Platform::String^ propertyName); bool ShouldRefreshGamepads(); void RequestRefreshGamepads(); - + void SetMouseMode(bool enabled); property bool MouseMode { bool get() { return m_mouseMode; } void set(bool value) { @@ -71,17 +63,13 @@ namespace moonlight_xbox_dx return GetApplicationState(); } } - property MenuFlyout^ m_flyout { - MenuFlyout^ get() { - return this->ActionsFlyout; + property Grid^ m_streamMenuGrid { + Grid^ get() { + return this->StreamMenuGrid; } } - property Button^ m_flyoutButton { - Button^ get() { - return this->flyoutButton; - } - } + void SetStreamMenuVisible(bool visible); property Grid^ m_progressView { Grid^ get() { @@ -89,12 +77,6 @@ namespace moonlight_xbox_dx } } - property Microsoft::UI::Xaml::Controls::ProgressRing^ m_progressRing { - Microsoft::UI::Xaml::Controls::ProgressRing ^ get() { - return this->MainProgressRing; - } - } - property TextBlock^ m_statusText { TextBlock^ get() { return this->StatusText; @@ -122,12 +104,11 @@ namespace moonlight_xbox_dx protected: virtual void OnNavigatedTo(Windows::UI::Xaml::Navigation::NavigationEventArgs^ e) override; private: - // Track our independent input on a background worker thread. + Windows::Foundation::IAsyncAction^ m_inputLoopWorker; Windows::UI::Core::CoreIndependentInputSource^ m_coreInput; Windows::Foundation::EventRegistrationToken m_back_cookie; - // Resources used to render the DirectX content in the XAML page background. std::shared_ptr m_deviceResources; std::unique_ptr m_main; bool m_windowVisible; @@ -135,14 +116,24 @@ namespace moonlight_xbox_dx void Page_Unloaded(Platform::Object ^ sender, Windows::UI::Xaml::RoutedEventArgs ^ e); void OnSwapChainPanelSizeChanged(Object^ sender, Windows::UI::Xaml::SizeChangedEventArgs^ e); - void flyoutButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void ActionsFlyout_Closed(Platform::Object^ sender, Platform::Object^ e); void toggleMouseButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); - void SetMouseMode(bool enabled); + void OtherButton_GotFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void OtherButton_LostFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void SubMenuButton_GotFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void SubMenuButton_LostFocus(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); + void SubMenuHideStoryboard_Completed(Platform::Object^ sender, Platform::Object^ e); + void OnSubMenuCloseTimer_Tick(Platform::Object^ sender, Platform::Object^ e); + void ShowSubMenu(); + void HideSubMenu(); + void InstantHideSubMenu(); void showKeyboardButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); void toggleLogsButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); void SetShowLogs(bool show); StreamConfiguration^ configuration; + Windows::UI::Xaml::Media::Animation::Storyboard^ m_bgPanStoryboard = nullptr; + Windows::UI::Xaml::Media::Imaging::BitmapImage^ m_backgroundImage = nullptr; + void StartBgPanAnimation(); + void FadeInBackground(); void toggleStatsButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); void SetShowStats(bool show); void disonnectButton_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e); @@ -166,6 +157,9 @@ namespace moonlight_xbox_dx bool m_mouseMode = false; bool m_showLogs = false; bool m_showStats = false; + bool m_streamMenuVisible = false; + bool m_subMenuVisible = false; + Windows::UI::Xaml::DispatcherTimer^ m_subMenuCloseTimer = nullptr; + void MenuHideStoryboard_Completed(Platform::Object^ sender, Platform::Object^ e); }; } - diff --git a/UI/Styles/Colors.xaml b/UI/Styles/Colors.xaml new file mode 100644 index 00000000..d4a59392 --- /dev/null +++ b/UI/Styles/Colors.xaml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UI/Styles/Controls.xaml b/UI/Styles/Controls.xaml new file mode 100644 index 00000000..e7e23590 --- /dev/null +++ b/UI/Styles/Controls.xaml @@ -0,0 +1,914 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/UI/Utilities/EffectsLibrary.cpp b/UI/Utilities/EffectsLibrary.cpp new file mode 100644 index 00000000..69eda4ed --- /dev/null +++ b/UI/Utilities/EffectsLibrary.cpp @@ -0,0 +1,628 @@ +#include "pch.h" +#include "UI\Utilities\EffectsLibrary.h" +#include +#include +#include +#define MLOG_TAG_OVERRIDE "EffectsLibrary" +#include "../Utils.hpp" +#include "Common\DirectXHelper.h" +#include +#include +#include "UI\Utilities\ImageHelpers.h" +using namespace Windows::Graphics::Imaging; + +struct DECLSPEC_UUID("5B0D3235-4DBA-4D44-865E-8F1D0ED9F3E4") IMemoryBufferByteAccess : IUnknown { + virtual HRESULT STDMETHODCALLTYPE GetBuffer(BYTE** value, UINT32* capacity) = 0; +}; + +ID3D11Device* EffectsLibrary::m_device = nullptr; +ID3D11DeviceContext* EffectsLibrary::m_context = nullptr; +ID3D11Multithread* EffectsLibrary::m_multithread = nullptr; +ID3D11VertexShader* EffectsLibrary::m_vs = nullptr; +ID3D11PixelShader* EffectsLibrary::m_blurPS = nullptr; +ID3D11Buffer* EffectsLibrary::m_cb = nullptr; +ID3D11SamplerState* EffectsLibrary::m_sampler = nullptr; +std::mutex EffectsLibrary::m_mutex; +bool EffectsLibrary::m_ownsDevice = false; + +void EffectsLibrary::Initialize(ID3D11Device* device, ID3D11DeviceContext* context) +{ + std::lock_guard lock(m_mutex); + + if (m_device == device && m_context == context) + return; + + if (m_vs) { m_vs->Release(); m_vs = nullptr; } + if (m_blurPS) { m_blurPS->Release(); m_blurPS = nullptr; } + if (m_cb) { m_cb->Release(); m_cb = nullptr; } + if (m_sampler) { m_sampler->Release(); m_sampler = nullptr; } + + if (m_ownsDevice) { + if (m_context) { m_context->Release(); m_context = nullptr; } + if (m_device) { m_device->Release(); m_device = nullptr; } + if (m_multithread) { m_multithread->SetMultithreadProtected(FALSE); m_multithread->Release(); m_multithread = nullptr; } + m_ownsDevice = false; + } + + m_device = device; + m_context = context; + + m_ownsDevice = false; + if (m_multithread) { m_multithread->SetMultithreadProtected(FALSE); m_multithread->Release(); m_multithread = nullptr; } +} + +bool EffectsLibrary::EnsureDeviceInitialized() +{ + + if (m_device != nullptr && m_context != nullptr) return true; + + std::lock_guard lock(m_mutex); + if (m_device != nullptr && m_context != nullptr) return true; + + D3D_FEATURE_LEVEL featureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0 }; + ID3D11Device* dev = nullptr; + ID3D11DeviceContext* ctx = nullptr; + D3D_FEATURE_LEVEL createdFL = D3D_FEATURE_LEVEL_11_0; + UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; +#if defined(_DEBUG) + flags |= D3D11_CREATE_DEVICE_DEBUG; +#endif + HRESULT hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags, featureLevels, _countof(featureLevels), + D3D11_SDK_VERSION, &dev, &createdFL, &ctx); + if (FAILED(hr) || dev == nullptr || ctx == nullptr) { + MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "lazy D3D11CreateDevice failed hr=0x%08x\n", hr); + + return false; + } + + m_device = dev; + m_context = ctx; + + Microsoft::WRL::ComPtr mt; + if (SUCCEEDED(m_device->QueryInterface(__uuidof(ID3D11Multithread), reinterpret_cast(mt.GetAddressOf())))) { + mt->SetMultithreadProtected(TRUE); + + mt.Get()->AddRef(); + m_multithread = mt.Get(); + } + m_ownsDevice = true; + return true; +} + +bool EffectsLibrary::EnsureBlurShadersCompiled() +{ + std::lock_guard lock(m_mutex); + if (m_vs != nullptr && m_blurPS != nullptr) return true; + + const char* vsSrc = "struct VSOut { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; };\n" + "VSOut VS(uint vid : SV_VertexID) { VSOut o; float2 pos[3] = { float2(-1,-1), float2(-1,3), float2(3,-1) }; o.pos = float4(pos[vid], 0.0f, 1.0f); o.uv = pos[vid] * 0.5f + 0.5f; return o; }\n"; + const char* psSrc = + "Texture2D srcTex : register(t0); SamplerState samp : register(s0); cbuffer BlurCB : register(b0) { float2 texSize; float sigma; int direction; };\n" + "struct VSOut { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; };\n" + "float4 PS(VSOut i) : SV_TARGET {\n" + " float s = max(sigma, 0.0001f);\n" + " int radius = (int)ceil(3.0f * s);\n" + " float2 texel = float2(1.0/texSize.x, 1.0/texSize.y);\n" + " float2 step = (direction==0) ? float2(texel.x,0) : float2(0,texel.y);\n" + " float4 sum = float4(0,0,0,0);\n" + " float wsum = 0.0f;\n" + " float twoSigmaSq = 2.0f * s * s;\n" + " for (int k = -radius; k <= radius; ++k) {\n" + " float wk = exp(-((float)(k*k)) / twoSigmaSq);\n" + " sum += srcTex.SampleLevel(samp, i.uv + step * k, 0) * wk;\n" + " wsum += wk;\n" + " }\n" + " return sum / wsum;\n" + "}\n"; + + Microsoft::WRL::ComPtr vsBlob, psBlob, errBlob; + UINT flags = D3DCOMPILE_OPTIMIZATION_LEVEL3; + HRESULT hr = D3DCompile(vsSrc, strlen(vsSrc), nullptr, nullptr, nullptr, "VS", "vs_4_0", flags, 0, vsBlob.GetAddressOf(), errBlob.GetAddressOf()); + if (FAILED(hr)) { + if (errBlob) MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "EnsureBlurShadersCompiled: VS compile error: %s\n", (const char*)errBlob->GetBufferPointer()); + return false; + } + hr = m_device->CreateVertexShader(vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), nullptr, &m_vs); + if (FAILED(hr)) { MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "EnsureBlurShadersCompiled: CreateVertexShader failed hr=0x%08x\n", hr); return false; } + + hr = D3DCompile(psSrc, strlen(psSrc), nullptr, nullptr, nullptr, "PS", "ps_4_0", flags, 0, psBlob.GetAddressOf(), errBlob.GetAddressOf()); + if (FAILED(hr)) { + if (errBlob) MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "EnsureBlurShadersCompiled: PS compile error: %s\n", (const char*)errBlob->GetBufferPointer()); + return false; + } + hr = m_device->CreatePixelShader(psBlob->GetBufferPointer(), psBlob->GetBufferSize(), nullptr, &m_blurPS); + if (FAILED(hr)) { MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "EnsureBlurShadersCompiled: CreatePixelShader failed hr=0x%08x\n", hr); return false; } + + if (m_sampler == nullptr) { + D3D11_SAMPLER_DESC sd = {}; + + sd.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR; + + sd.AddressU = D3D11_TEXTURE_ADDRESS_BORDER; + sd.AddressV = D3D11_TEXTURE_ADDRESS_BORDER; + sd.AddressW = D3D11_TEXTURE_ADDRESS_BORDER; + sd.ComparisonFunc = D3D11_COMPARISON_NEVER; + sd.MinLOD = 0; + sd.MaxLOD = D3D11_FLOAT32_MAX; + sd.BorderColor[0] = 0.0f; + sd.BorderColor[1] = 0.0f; + sd.BorderColor[2] = 0.0f; + sd.BorderColor[3] = 0.0f; + m_device->CreateSamplerState(&sd, &m_sampler); + } + + if (m_cb == nullptr) { + D3D11_BUFFER_DESC cbd = {}; + cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + cbd.Usage = D3D11_USAGE_DYNAMIC; + cbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + cbd.ByteWidth = sizeof(BlurCB); + m_device->CreateBuffer(&cbd, nullptr, &m_cb); + } + return true; +} + +static SoftwareBitmap^ EnsureBgra8Premultiplied(Windows::Graphics::Imaging::SoftwareBitmap^ bmp) +{ + if (bmp == nullptr) return nullptr; + if (bmp->BitmapPixelFormat == BitmapPixelFormat::Bgra8 && bmp->BitmapAlphaMode == BitmapAlphaMode::Premultiplied) + return bmp; + return ImageHelpers::EnsureBgra8Premultiplied(bmp); +} + +bool EffectsLibrary::BoxBlurSoftwareBitmap(Windows::Graphics::Imaging::SoftwareBitmap^ bitmap, int radius) { + using namespace Windows::Graphics::Imaging; + if (bitmap == nullptr) return false; + try { + auto buffer = bitmap->LockBuffer(BitmapBufferAccessMode::ReadWrite); + auto reference = buffer->CreateReference(); + Microsoft::WRL::ComPtr bufferByteAccess; + HRESULT hr = S_OK; + IUnknown* unk = reinterpret_cast(reference); + if (unk == nullptr) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "BoxBlurSoftwareBitmap: CreateReference returned null IUnknown\n"); + return false; + } + hr = unk->QueryInterface(IID_PPV_ARGS(&bufferByteAccess)); + BYTE* data = nullptr; UINT32 capacity = 0; + bool usedFastPath = false; + if (!FAILED(hr) && bufferByteAccess != nullptr) { + hr = bufferByteAccess->GetBuffer(&data, &capacity); + if (!FAILED(hr) && data != nullptr) { + usedFastPath = true; + } else { + MLOGF(moonlight_xbox_dx::Utils::LogLevel::Warning, "BoxBlurSoftwareBitmap: GetBuffer failed hr=0x%08x\n", hr); + } + } else { + MLOGF(moonlight_xbox_dx::Utils::LogLevel::Warning, "BoxBlurSoftwareBitmap: QueryInterface failed hr=0x%08x\n", hr); + } + auto desc = buffer->GetPlaneDescription(0); + int width = desc.Width; + int height = desc.Height; + int stride = desc.Stride; + int start = desc.StartIndex; + if (width <= 0 || height <= 0 || stride <= 0) return false; + size_t planeSize = (size_t)height * (size_t)stride; + std::vector src(planeSize); + if (usedFastPath) { + memcpy(src.data(), data + start, planeSize); + } else { + + reference = nullptr; + buffer = nullptr; + try { + auto ibuf = ref new Windows::Storage::Streams::Buffer((unsigned int)planeSize); + bitmap->CopyToBuffer(ibuf); + auto reader = Windows::Storage::Streams::DataReader::FromBuffer(ibuf); + reader->ReadBytes(Platform::ArrayReference(src.data(), (unsigned int)planeSize)); + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "BoxBlurSoftwareBitmap: fallback CopyToBuffer/DataReader failed\n"); + return false; + } + } + + std::vector tmp(src.size()); + std::vector dst(src.size()); + + for (int y = 0; y < height; ++y) { + const uint8_t* row = src.data() + y * stride; + std::vector prefB(width + 1, 0), prefG(width + 1, 0), prefR(width + 1, 0), prefA(width + 1, 0); + for (int x = 0; x < width; ++x) { + uint8_t b = row[x * 4 + 0]; + uint8_t g = row[x * 4 + 1]; + uint8_t r = row[x * 4 + 2]; + uint8_t a = row[x * 4 + 3]; + prefB[x + 1] = prefB[x] + b; + prefG[x + 1] = prefG[x] + g; + prefR[x + 1] = prefR[x] + r; + prefA[x + 1] = prefA[x] + a; + } + for (int x = 0; x < width; ++x) { + int x0 = std::max(0, x - radius); + int x1 = std::min(width - 1, x + radius); + int count = x1 - x0 + 1; + uint32_t sumB = prefB[x1 + 1] - prefB[x0]; + uint32_t sumG = prefG[x1 + 1] - prefG[x0]; + uint32_t sumR = prefR[x1 + 1] - prefR[x0]; + uint32_t sumA = prefA[x1 + 1] - prefA[x0]; + uint8_t* outPx = tmp.data() + y * stride + x * 4; + outPx[0] = (uint8_t)(sumB / count); + outPx[1] = (uint8_t)(sumG / count); + outPx[2] = (uint8_t)(sumR / count); + outPx[3] = (uint8_t)(sumA / count); + } + } + + for (int x = 0; x < width; ++x) { + std::vector prefB(height + 1, 0), prefG(height + 1, 0), prefR(height + 1, 0), prefA(height + 1, 0); + for (int y = 0; y < height; ++y) { + uint8_t* px = tmp.data() + y * stride + x * 4; + prefB[y + 1] = prefB[y] + px[0]; + prefG[y + 1] = prefG[y] + px[1]; + prefR[y + 1] = prefR[y] + px[2]; + prefA[y + 1] = prefA[y] + px[3]; + } + for (int y = 0; y < height; ++y) { + int y0 = std::max(0, y - radius); + int y1 = std::min(height - 1, y + radius); + int count = y1 - y0 + 1; + uint32_t sumB = prefB[y1 + 1] - prefB[y0]; + uint32_t sumG = prefG[y1 + 1] - prefG[y0]; + uint32_t sumR = prefR[y1 + 1] - prefR[y0]; + uint32_t sumA = prefA[y1 + 1] - prefA[y0]; + uint8_t* outPx = dst.data() + y * stride + x * 4; + outPx[0] = (uint8_t)(sumB / count); + outPx[1] = (uint8_t)(sumG / count); + outPx[2] = (uint8_t)(sumR / count); + outPx[3] = (uint8_t)(sumA / count); + } + } + if (usedFastPath) { + memcpy(data + start, dst.data(), planeSize); + } else { + try { + auto writer = ref new Windows::Storage::Streams::DataWriter(); + writer->WriteBytes(Platform::ArrayReference(dst.data(), (unsigned int)planeSize)); + auto outBuf = writer->DetachBuffer(); + bitmap->CopyFromBuffer(outBuf); + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "BoxBlurSoftwareBitmap: fallback CopyFromBuffer failed\n"); + return false; + } + } + + return true; + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "BoxBlurSoftwareBitmap: exception while blurring\n"); + return false; + } +} + +static void CopyBgraRegion(uint8_t* dst, size_t dstRowPitch, const uint8_t* src, size_t srcRowPitch, int pad, bool returnPadded, int copyWidth, int copyHeight) +{ + size_t srcOffsetRow = returnPadded ? 0 : (size_t)pad * srcRowPitch; + size_t srcOffsetCol = returnPadded ? 0 : (size_t)pad * 4; + for (int y = 0; y < copyHeight; ++y) { + const uint8_t* srcRow = src + srcOffsetRow + (size_t)y * srcRowPitch + srcOffsetCol; + size_t copyBytes = std::min((size_t)copyWidth * 4, dstRowPitch); + memcpy(dst + (size_t)y * dstRowPitch, srcRow, copyBytes); + if (dstRowPitch > copyBytes) memset(dst + (size_t)y * dstRowPitch + copyBytes, 0, dstRowPitch - copyBytes); + } +} + +SoftwareBitmap^ EffectsLibrary::GpuBoxBlurSoftwareBitmap(SoftwareBitmap^ bitmap, int radius, bool returnPadded) +{ + using namespace Windows::Graphics::Imaging; + if (bitmap == nullptr) return nullptr; + + if ((m_device == nullptr || m_context == nullptr) && !EnsureDeviceInitialized()) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: D3D device/context not initialized\n"); + return nullptr; + } + + bitmap = EnsureBgra8Premultiplied(bitmap); + + try { + auto buffer = bitmap->LockBuffer(BitmapBufferAccessMode::Read); + auto reference = buffer->CreateReference(); + Microsoft::WRL::ComPtr bufferByteAccess; + HRESULT hr = S_OK; + IUnknown* unk = reinterpret_cast(reference); + BYTE* data = nullptr; UINT32 capacity = 0; + bool usedFastPath = false; + if (unk != nullptr) hr = unk->QueryInterface(IID_PPV_ARGS(&bufferByteAccess)); + if (!FAILED(hr) && bufferByteAccess != nullptr) { + hr = bufferByteAccess->GetBuffer(&data, &capacity); + if (!FAILED(hr) && data != nullptr) usedFastPath = true; + } + + auto desc = buffer->GetPlaneDescription(0); + int width = desc.Width; + int height = desc.Height; + int stride = desc.Stride; + int start = desc.StartIndex; + if (width <= 0 || height <= 0 || stride <= 0) return nullptr; + size_t planeSize = (size_t)height * (size_t)stride; + std::vector src(planeSize); + if (usedFastPath) { + memcpy(src.data(), data + start, planeSize); + } else { + + reference = nullptr; + buffer = nullptr; + try { + auto ibuf = ref new Windows::Storage::Streams::Buffer((unsigned int)planeSize); + bitmap->CopyToBuffer(ibuf); + auto reader = Windows::Storage::Streams::DataReader::FromBuffer(ibuf); + reader->ReadBytes(Platform::ArrayReference(src.data(), (unsigned int)planeSize)); + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: fallback CopyToBuffer failed\n"); + return nullptr; + } + } + + Microsoft::WRL::ComPtr srcTex; + D3D11_TEXTURE2D_DESC texDesc = {}; + + int pad = 54; + int paddedW = width + pad * 2; + int paddedH = height + pad * 2; + int paddedStride = paddedW * 4; + texDesc.Width = paddedW; + texDesc.Height = paddedH; + texDesc.MipLevels = 1; + texDesc.ArraySize = 1; + texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + texDesc.SampleDesc.Count = 1; + texDesc.Usage = D3D11_USAGE_DEFAULT; + texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET; + texDesc.CPUAccessFlags = 0; + + std::vector paddedSrc((size_t)paddedH * (size_t)paddedStride); + + memset(paddedSrc.data(), 0, paddedSrc.size()); + + for (int y = 0; y < height; ++y) { + uint8_t* dstRow = paddedSrc.data() + ((size_t)(y + pad) * (size_t)paddedStride); + uint8_t* srcRow = src.data() + ((size_t)y * (size_t)stride); + + memcpy(dstRow + pad * 4, srcRow, width * 4); + } + + D3D11_SUBRESOURCE_DATA initData = {}; + initData.pSysMem = paddedSrc.data(); + initData.SysMemPitch = paddedStride; + + hr = m_device->CreateTexture2D(&texDesc, &initData, srcTex.GetAddressOf()); + if (FAILED(hr) || srcTex == nullptr) { + MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: CreateTexture2D(src padded) failed hr=0x%08x\n", hr); + return nullptr; + } + + Microsoft::WRL::ComPtr srcSRV; + hr = m_device->CreateShaderResourceView(srcTex.Get(), nullptr, srcSRV.GetAddressOf()); + if (FAILED(hr) || srcSRV == nullptr) { + MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: CreateShaderResourceView(src) failed hr=0x%08x\n", hr); + return nullptr; + } + + Microsoft::WRL::ComPtr rtA, rtB; + Microsoft::WRL::ComPtr rtvA, rtvB; + Microsoft::WRL::ComPtr srvB; + + D3D11_TEXTURE2D_DESC rtDesc = texDesc; + rtDesc.Width = paddedW; + rtDesc.Height = paddedH; + rtDesc.BindFlags = D3D11_BIND_RENDER_TARGET | D3D11_BIND_SHADER_RESOURCE; + rtDesc.Usage = D3D11_USAGE_DEFAULT; + rtDesc.CPUAccessFlags = 0; + + hr = m_device->CreateTexture2D(&rtDesc, nullptr, rtA.GetAddressOf()); + if (FAILED(hr)) { MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: CreateTexture2D(rtA) failed hr=0x%08x\n", hr); return nullptr; } + hr = m_device->CreateTexture2D(&rtDesc, nullptr, rtB.GetAddressOf()); + if (FAILED(hr)) { MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: CreateTexture2D(rtB) failed hr=0x%08x\n", hr); return nullptr; } + hr = m_device->CreateRenderTargetView(rtA.Get(), nullptr, rtvA.GetAddressOf()); + if (FAILED(hr)) { MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: CreateRenderTargetView(rtA) failed hr=0x%08x\n", hr); return nullptr; } + hr = m_device->CreateRenderTargetView(rtB.Get(), nullptr, rtvB.GetAddressOf()); + if (FAILED(hr)) { MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: CreateRenderTargetView(rtB) failed hr=0x%08x\n", hr); return nullptr; } + hr = m_device->CreateShaderResourceView(rtB.Get(), nullptr, srvB.GetAddressOf()); + if (FAILED(hr)) { MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: CreateSRV(rtB) failed hr=0x%08x\n", hr); return nullptr; } + + if (!EnsureBlurShadersCompiled()) return nullptr; + + D3D11_VIEWPORT vp = {}; + vp.TopLeftX = 0.f; + vp.TopLeftY = 0.f; + vp.Width = (float)paddedW; + vp.Height = (float)paddedH; + vp.MinDepth = 0.f; + vp.MaxDepth = 1.f; + + { + std::lock_guard lock(m_mutex); + m_context->IASetInputLayout(nullptr); + m_context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + m_context->VSSetShader(m_vs, nullptr, 0); + m_context->PSSetShader(m_blurPS, nullptr, 0); + ID3D11SamplerState* samps[1] = { m_sampler }; + m_context->PSSetSamplers(0, 1, samps); + + BlurCB cbdata; + cbdata.texSize = DirectX::XMFLOAT2((float)paddedW, (float)paddedH); + cbdata.sigma = (float)radius; + + cbdata.direction = 0; + D3D11_MAPPED_SUBRESOURCE mapped = {}; + if (m_cb) { + if (SUCCEEDED(m_context->Map(m_cb, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped))) { + memcpy(mapped.pData, &cbdata, sizeof(cbdata)); + m_context->Unmap(m_cb, 0); + } + ID3D11Buffer* cbs[1] = { m_cb }; + m_context->PSSetConstantBuffers(0, 1, cbs); + } + + ID3D11ShaderResourceView* nullSRV[1] = { nullptr }; + ID3D11ShaderResourceView* srvSrcPad = srcSRV.Get(); + m_context->PSSetShaderResources(0, 1, &srvSrcPad); + ID3D11RenderTargetView* rtvBptr = rtvB.Get(); + m_context->OMSetRenderTargets(1, &rtvBptr, nullptr); + m_context->RSSetViewports(1, &vp); + m_context->Draw(3, 0); + + ID3D11RenderTargetView* nullRTVArr[1] = { nullptr }; + m_context->OMSetRenderTargets(1, nullRTVArr, nullptr); + + m_context->PSSetShaderResources(0, 1, nullSRV); + cbdata.direction = 1; + if (m_cb) { + if (SUCCEEDED(m_context->Map(m_cb, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped))) { + memcpy(mapped.pData, &cbdata, sizeof(cbdata)); + m_context->Unmap(m_cb, 0); + } + ID3D11Buffer* cbs2[1] = { m_cb }; + m_context->PSSetConstantBuffers(0, 1, cbs2); + } + ID3D11ShaderResourceView* srvBptr = srvB.Get(); + m_context->PSSetShaderResources(0, 1, &srvBptr); + ID3D11RenderTargetView* rtvAptr = rtvA.Get(); + m_context->OMSetRenderTargets(1, &rtvAptr, nullptr); + m_context->RSSetViewports(1, &vp); + m_context->Draw(3, 0); + + m_context->PSSetShaderResources(0, 1, nullSRV); + + ID3D11RenderTargetView* nullRTV[1] = { nullptr }; + m_context->OMSetRenderTargets(1, nullRTV, nullptr); + } + + Microsoft::WRL::ComPtr staging; + D3D11_TEXTURE2D_DESC stagingDesc = rtDesc; + stagingDesc.Usage = D3D11_USAGE_STAGING; + stagingDesc.BindFlags = 0; + stagingDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + stagingDesc.MiscFlags = 0; + hr = m_device->CreateTexture2D(&stagingDesc, nullptr, staging.GetAddressOf()); + if (FAILED(hr)) { MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: CreateTexture2D(staging) failed hr=0x%08x\n", hr); return nullptr; } + { + std::lock_guard lock(m_mutex); + + m_context->CopyResource(staging.Get(), rtA.Get()); + + D3D11_MAPPED_SUBRESOURCE mapSR = {}; + hr = m_context->Map(staging.Get(), 0, D3D11_MAP_READ, 0, &mapSR); + if (FAILED(hr)) { + MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: Map staging failed hr=0x%08x\n", hr); + return nullptr; + } + bool stagingMapped = true; + + int copyWidth = returnPadded ? paddedW : width; + int copyHeight = returnPadded ? paddedH : height; + uint8_t* srcPtr = (uint8_t*)mapSR.pData; + size_t srcRowPitch = mapSR.RowPitch; + + try { + auto outBmp = ref new SoftwareBitmap(BitmapPixelFormat::Bgra8, copyWidth, copyHeight, BitmapAlphaMode::Premultiplied); + auto outBuf = outBmp->LockBuffer(BitmapBufferAccessMode::Write); + auto outRef = outBuf->CreateReference(); + Microsoft::WRL::ComPtr outAccess; + IUnknown* outUnk = reinterpret_cast(outRef); + + static std::atomic s_outAccessAvailable(-1); + HRESULT outQiHr = E_FAIL; + BYTE* outData = nullptr; UINT32 outCap = 0; + + if (s_outAccessAvailable.load() == 1) { + if (outUnk != nullptr) outQiHr = outUnk->QueryInterface(IID_PPV_ARGS(&outAccess)); + if (SUCCEEDED(outQiHr) && outAccess) outAccess->GetBuffer(&outData, &outCap); + } else if (s_outAccessAvailable.load() == 0) { + outQiHr = E_NOINTERFACE; + } else { + try { + if (outUnk != nullptr) outQiHr = outUnk->QueryInterface(IID_PPV_ARGS(&outAccess)); + } catch (...) { + outQiHr = RPC_E_DISCONNECTED; + } + if (SUCCEEDED(outQiHr) && outAccess) { + outAccess->GetBuffer(&outData, &outCap); + s_outAccessAvailable.store(1); + } else { + s_outAccessAvailable.store(0); + } + } + + if (SUCCEEDED(outQiHr) && outData != nullptr) { + auto outDesc = outBuf->GetPlaneDescription(0); + uint8_t* dstPtr = outData + outDesc.StartIndex; + size_t dstRowPitch = outDesc.Stride; + if (srcPtr == nullptr) { + if (stagingMapped) { m_context->Unmap(staging.Get(), 0); stagingMapped = false; } + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: invalid mapped source pointer (null)\n"); + return nullptr; + } + if (srcRowPitch < (size_t)width * 4) { + if (stagingMapped) { m_context->Unmap(staging.Get(), 0); stagingMapped = false; } + MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: unexpected srcRowPitch=%u (width*4=%u)\n", (unsigned)srcRowPitch, (unsigned)(width*4)); + return nullptr; + } + if (outCap < outDesc.StartIndex + dstRowPitch * (size_t)height) { + if (stagingMapped) { m_context->Unmap(staging.Get(), 0); stagingMapped = false; } + MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: output buffer capacity too small (cap=%u required=%zu)\n", outCap, outDesc.StartIndex + dstRowPitch * (size_t)height); + return nullptr; + } + CopyBgraRegion(dstPtr, dstRowPitch, srcPtr, srcRowPitch, pad, returnPadded, copyWidth, copyHeight); + if (stagingMapped) { m_context->Unmap(staging.Get(), 0); stagingMapped = false; } + return outBmp; + } + + try { + if (srcPtr == nullptr) { + if (stagingMapped) { m_context->Unmap(staging.Get(), 0); stagingMapped = false; } + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: invalid mapped source pointer (null) in fallback\n"); + return nullptr; + } + if (srcRowPitch < (size_t)4) { + if (stagingMapped) { m_context->Unmap(staging.Get(), 0); stagingMapped = false; } + MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: unexpected small srcRowPitch=%u in fallback\n", (unsigned)srcRowPitch); + return nullptr; + } + auto outDesc = outBuf->GetPlaneDescription(0); + size_t dstRowPitch = outDesc.Stride; + size_t allocSize = (size_t)copyHeight * dstRowPitch; + if (allocSize == 0 || allocSize / (size_t)copyHeight != dstRowPitch) { + if (stagingMapped) { m_context->Unmap(staging.Get(), 0); stagingMapped = false; } + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: integer overflow or zero detected allocating tmpBuf\n"); + return nullptr; + } + std::vector tmpBuf(allocSize); + CopyBgraRegion(tmpBuf.data(), dstRowPitch, srcPtr, srcRowPitch, pad, returnPadded, copyWidth, copyHeight); + if (stagingMapped) { m_context->Unmap(staging.Get(), 0); stagingMapped = false; } + + auto writer = ref new Windows::Storage::Streams::DataWriter(); + writer->WriteBytes(Platform::ArrayReference(tmpBuf.data(), (unsigned int)tmpBuf.size())); + auto outIBuf = writer->DetachBuffer(); + return SoftwareBitmap::CreateCopyFromBuffer(outIBuf, BitmapPixelFormat::Bgra8, copyWidth, copyHeight, BitmapAlphaMode::Premultiplied); + } catch(...) { + if (stagingMapped) { m_context->Unmap(staging.Get(), 0); stagingMapped = false; } + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: fallback CopyFromBuffer failed\n"); + return nullptr; + } + } catch(...) { + if (stagingMapped) { m_context->Unmap(staging.Get(), 0); stagingMapped = false; } + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: exception while creating output bitmap\n"); + return nullptr; + } + } + + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "GpuBoxBlurSoftwareBitmap: unexpected exception\n"); + return nullptr; + } +} + +ID3D11ShaderResourceView* EffectsLibrary::Blur(ID3D11ShaderResourceView* src, float sigma) +{ + + return src; +} diff --git a/UI/Utilities/EffectsLibrary.h b/UI/Utilities/EffectsLibrary.h new file mode 100644 index 00000000..509f579c --- /dev/null +++ b/UI/Utilities/EffectsLibrary.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include + +#include +#include + +class EffectsLibrary { + +public: + + static void Initialize(ID3D11Device* device, ID3D11DeviceContext* context); + + static ID3D11ShaderResourceView* Blur(ID3D11ShaderResourceView* src, float sigma); + + static bool BoxBlurSoftwareBitmap(Windows::Graphics::Imaging::SoftwareBitmap^ bitmap, int radius); + + static Windows::Graphics::Imaging::SoftwareBitmap^ GpuBoxBlurSoftwareBitmap(Windows::Graphics::Imaging::SoftwareBitmap^ bitmap, int radius, bool returnPadded = false); + + static ID3D11ShaderResourceView* Glow(ID3D11ShaderResourceView* src, float radius) { + + return Blur(src, radius); + + } + + static ID3D11Device* GetDevice() { return m_device; } + + static ID3D11DeviceContext* GetContext() { return m_context; } + +private: + + static ID3D11Device* m_device; + + static ID3D11DeviceContext* m_context; + static ID3D11Multithread* m_multithread; + + static bool m_ownsDevice; + + static ID3D11VertexShader* m_vs; + + static ID3D11PixelShader* m_blurPS; + + static ID3D11Buffer* m_cb; + + static ID3D11SamplerState* m_sampler; + + static std::mutex m_mutex; + + struct BlurCB { + + DirectX::XMFLOAT2 texSize; + + float sigma; + + int direction; + + }; + + static bool EnsureDeviceInitialized(); + + static bool EnsureBlurShadersCompiled(); + +}; \ No newline at end of file diff --git a/UI/Utilities/ImageHelpers.cpp b/UI/Utilities/ImageHelpers.cpp new file mode 100644 index 00000000..3313a59d --- /dev/null +++ b/UI/Utilities/ImageHelpers.cpp @@ -0,0 +1,646 @@ +#include "pch.h" +#include "ImageHelpers.h" +#define MLOG_TAG_OVERRIDE "ImageHelpers" +#include "../Utils.hpp" +#include "UI\Utilities\EffectsLibrary.h" + +struct DECLSPEC_UUID("5B0D3235-4DBA-4D44-865E-8F1D0ED9F3E4") IMemoryBufferByteAccess : IUnknown { + virtual HRESULT STDMETHODCALLTYPE GetBuffer(BYTE** value, UINT32* capacity) = 0; +}; + +using namespace Platform; +using namespace Windows::Storage; +using namespace Windows::Storage::Streams; +using namespace Windows::Graphics::Imaging; +using namespace concurrency; + +static concurrency::task DecodeStreamToSoftwareBitmapAsync(concurrency::task streamTask, const char* context) { + return streamTask.then([context](task st) -> task { + try { + auto stream = st.get(); + if (stream == nullptr || stream->Size == 0) return task_from_result(nullptr); + return create_task(BitmapDecoder::CreateAsync(stream)).then([](BitmapDecoder^ decoder) -> task { + if (decoder == nullptr) return task_from_result(nullptr); + return create_task(decoder->GetSoftwareBitmapAsync()).then([](SoftwareBitmap^ sb) -> SoftwareBitmap^ { + try { return ImageHelpers::EnsureBgra8Premultiplied(sb); } catch(...) { MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "EnsureBgra8Premultiplied failed\n"); return nullptr; } + }); + }).then([](task sbTask) -> SoftwareBitmap^ { + try { return sbTask.get(); } + catch (Platform::COMException^ ex) { MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "BitmapDecoder/GetSoftwareBitmap failed hr=0x%08x\n", ex->HResult); return nullptr; } + catch(...) { MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "BitmapDecoder/GetSoftwareBitmap unknown error\n"); return nullptr; } + }); + } catch(...) { + MLOGF(moonlight_xbox_dx::Utils::LogLevel::Error, "LoadSoftwareBitmapFromUriOrPathAsync: stream task failed (%s)\n", context); + return task_from_result(nullptr); + } + }); +} + +concurrency::task ImageHelpers::LoadSoftwareBitmapFromUriOrPathAsync(String^ path) { + if (path == nullptr) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Warning, "LoadSoftwareBitmapFromUriOrPathAsync: path is null\n"); + return task_from_result(nullptr); + } + const wchar_t* raw = path->Data(); + try { + + size_t rawLen = wcslen(raw); + if (rawLen >= 4 && _wcsicmp(raw + rawLen - 4, L".svg") == 0) { + return task_from_result(nullptr); + } + + if (wcsncmp(raw, L"ms-appx://", 10) == 0 || wcsncmp(raw, L"ms-appdata://", 12) == 0) { + auto uri = ref new Windows::Foundation::Uri(path); + + concurrency::task streamTask = create_task(StorageFile::GetFileFromApplicationUriAsync(uri)) + .then([](task fileTask) -> task { + try { + StorageFile^ file = fileTask.get(); + return create_task(file->OpenReadAsync()).then([](task sTask) -> IRandomAccessStream^ { + try { auto s = sTask.get(); return safe_cast(s); } catch(...) { return nullptr; } + }); + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "LoadSoftwareBitmapFromUriOrPathAsync: GetFileFromApplicationUriAsync failed\n"); + return task_from_result(nullptr); + } + }); + return DecodeStreamToSoftwareBitmapAsync(streamTask, "ms-appx/ms-appdata"); + } + + if ((wcslen(raw) >= 2 && raw[1] == L':') || (wcslen(raw) >= 2 && raw[0] == L'\\' && raw[1] == L'\\')) { + + try { + auto localPath = Windows::Storage::ApplicationData::Current->LocalFolder->Path->Data(); + size_t lpLen = wcslen(localPath); + if (_wcsnicmp(raw, localPath, lpLen) == 0) { + + const wchar_t* rel = raw + lpLen; + if (*rel == L'\\' || *rel == L'/') ++rel; + Platform::String^ relStr = ref new Platform::String(rel); + concurrency::task streamTask = create_task(Windows::Storage::ApplicationData::Current->LocalFolder->GetFileAsync(relStr)).then([](StorageFile^ file) -> task { + if (file == nullptr) return task_from_result(nullptr); + return create_task(file->OpenReadAsync()).then([](IRandomAccessStreamWithContentType^ s) -> IRandomAccessStream^ { return safe_cast(s); }); + }); + return DecodeStreamToSoftwareBitmapAsync(streamTask, "LocalFolder-relative path"); + } + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Warning, "LoadSoftwareBitmapFromUriOrPathAsync: LocalFolder-relative path attempt failed, falling back\n"); + } + + concurrency::task streamTask = create_task(StorageFile::GetFileFromPathAsync(path)).then([](task fileTask) -> task { + try { + StorageFile^ file = fileTask.get(); + return create_task(file->OpenReadAsync()).then([](task sTask) -> IRandomAccessStream^ { + try { auto s = sTask.get(); return safe_cast(s); } catch(...) { return nullptr; } + }); + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "LoadSoftwareBitmapFromUriOrPathAsync: GetFileFromPathAsync failed\n"); + return task_from_result(nullptr); + } + }); + return DecodeStreamToSoftwareBitmapAsync(streamTask, "file path"); + } + + MLOGF(moonlight_xbox_dx::Utils::LogLevel::Warning, "LoadSoftwareBitmapFromUriOrPathAsync: unsupported path='%S'\n", raw); + return task_from_result(nullptr); + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "LoadSoftwareBitmapFromUriOrPathAsync: exception during load\n"); + return task_from_result(nullptr); + } +} + +static void UnpremultiplyBgraPixel(uint8_t pb, uint8_t pg, uint8_t pr, uint8_t pa, + uint8_t& outB, uint8_t& outG, uint8_t& outR, uint8_t& outA) { + if (pa == 0) { outB = outG = outR = outA = 0; return; } + uint32_t r = (uint32_t)pr * 255 + (pa / 2); + uint32_t g = (uint32_t)pg * 255 + (pa / 2); + uint32_t b = (uint32_t)pb * 255 + (pa / 2); + outR = (uint8_t)std::min(255, r / pa); + outG = (uint8_t)std::min(255, g / pa); + outB = (uint8_t)std::min(255, b / pa); + outA = pa; +} + +static SoftwareBitmap^ BuildStraightAlphaBitmap(SoftwareBitmap^ bitmap) { + try { + unsigned int w = bitmap->PixelWidth; + unsigned int h = bitmap->PixelHeight; + + SoftwareBitmap^ src = ImageHelpers::EnsureBgra8Premultiplied(bitmap); + if (src == nullptr) src = bitmap; + + auto out = ref new SoftwareBitmap(BitmapPixelFormat::Bgra8, (int)w, (int)h, BitmapAlphaMode::Straight); + + bool fastPath = false; + try { + auto srcBuf = src->LockBuffer(BitmapBufferAccessMode::Read); + auto outBuf = out->LockBuffer(BitmapBufferAccessMode::Write); + auto srcRef = srcBuf->CreateReference(); + auto outRef = outBuf->CreateReference(); + Microsoft::WRL::ComPtr srcAccess; + Microsoft::WRL::ComPtr outAccess; + IUnknown* srcUnk = reinterpret_cast(srcRef); + IUnknown* outUnk = reinterpret_cast(outRef); + BYTE* srcData = nullptr; UINT32 srcCap = 0; + BYTE* outData = nullptr; UINT32 outCap = 0; + if (srcUnk != nullptr && outUnk != nullptr && SUCCEEDED(srcUnk->QueryInterface(IID_PPV_ARGS(&srcAccess))) && SUCCEEDED(outUnk->QueryInterface(IID_PPV_ARGS(&outAccess))) + && SUCCEEDED(srcAccess->GetBuffer(&srcData, &srcCap)) && SUCCEEDED(outAccess->GetBuffer(&outData, &outCap))) { + auto srcDesc = srcBuf->GetPlaneDescription(0); + auto outDesc = outBuf->GetPlaneDescription(0); + for (unsigned int y = 0; y < h; ++y) { + uint8_t* srow = srcData + srcDesc.StartIndex + (size_t)y * srcDesc.Stride; + uint8_t* orow = outData + outDesc.StartIndex + (size_t)y * outDesc.Stride; + for (unsigned int x = 0; x < w; ++x) { + UnpremultiplyBgraPixel(srow[x*4 + 0], srow[x*4 + 1], srow[x*4 + 2], srow[x*4 + 3], + orow[x*4 + 0], orow[x*4 + 1], orow[x*4 + 2], orow[x*4 + 3]); + } + } + fastPath = true; + } + } catch(...) { fastPath = false; } + + if (!fastPath) { + + try { + unsigned int sh = src->PixelHeight; + auto srcBuf2 = src->LockBuffer(BitmapBufferAccessMode::Read); + auto srcDesc2 = srcBuf2->GetPlaneDescription(0); + std::vector srcTmp((size_t)sh * (size_t)srcDesc2.Stride); + try { + auto ib = ref new Windows::Storage::Streams::Buffer((unsigned int)srcTmp.size()); + src->CopyToBuffer(ib); + auto reader = Windows::Storage::Streams::DataReader::FromBuffer(ib); + reader->ReadBytes(Platform::ArrayReference(srcTmp.data(), (unsigned int)srcTmp.size())); + } catch(...) { + + MLOG(moonlight_xbox_dx::Utils::LogLevel::Warning, "BuildStraightAlphaBitmap: failed to read src buffer (fallback path)\n"); + } + + auto outDesc = out->LockBuffer(BitmapBufferAccessMode::Write)->GetPlaneDescription(0); + std::vector outTmp((size_t)h * (size_t)outDesc.Stride); + for (unsigned int y = 0; y < h; ++y) { + uint8_t* srow = srcTmp.data() + (size_t)y * srcDesc2.Stride; + uint8_t* orow = outTmp.data() + (size_t)y * outDesc.Stride; + for (unsigned int x = 0; x < w; ++x) { + UnpremultiplyBgraPixel(srow[x*4 + 0], srow[x*4 + 1], srow[x*4 + 2], srow[x*4 + 3], + orow[x*4 + 0], orow[x*4 + 1], orow[x*4 + 2], orow[x*4 + 3]); + } + } + + try { + auto writer = ref new Windows::Storage::Streams::DataWriter(); + writer->WriteBytes(Platform::ArrayReference(outTmp.data(), (unsigned int)outTmp.size())); + auto buf = writer->DetachBuffer(); + out->CopyFromBuffer(buf); + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "BuildStraightAlphaBitmap: failed to write unpremultiplied buffer into out bitmap\n"); + } + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "BuildStraightAlphaBitmap: fallback unpremultiply path failed\n"); + } + } + + return out; + } catch(...) { + + return bitmap; + } +} + +static Platform::Array^ ExtractStraightAlphaPixels(SoftwareBitmap^ bitmap, unsigned int outW, unsigned int outH) { + try { + SoftwareBitmap^ src = ImageHelpers::EnsureBgra8Premultiplied(bitmap); + auto buf = src->LockBuffer(BitmapBufferAccessMode::Read); + auto desc = buf->GetPlaneDescription(0); + std::vector srcBytes((size_t)outH * (size_t)desc.Stride); + try { + auto ib = ref new Windows::Storage::Streams::Buffer((unsigned int)srcBytes.size()); + src->CopyToBuffer(ib); + auto reader = Windows::Storage::Streams::DataReader::FromBuffer(ib); + reader->ReadBytes(Platform::ArrayReference(srcBytes.data(), (unsigned int)srcBytes.size())); + } catch(...) { srcBytes.clear(); } + + if (srcBytes.empty()) return nullptr; + + size_t pixCount = (size_t)outW * (size_t)outH; + std::vector outPixels(pixCount * 4); + for (unsigned int y = 0; y < outH; ++y) { + uint8_t* srow = srcBytes.data() + (size_t)y * desc.Stride; + for (unsigned int x = 0; x < outW; ++x) { + size_t idx = ((size_t)y * outW + x) * 4; + UnpremultiplyBgraPixel(srow[x*4 + 0], srow[x*4 + 1], srow[x*4 + 2], srow[x*4 + 3], + outPixels[idx + 0], outPixels[idx + 1], outPixels[idx + 2], outPixels[idx + 3]); + } + } + auto pixelArr = ref new Platform::Array((unsigned int)outPixels.size()); + memcpy(pixelArr->Data, outPixels.data(), outPixels.size()); + return pixelArr; + } catch(...) { + return nullptr; + } +} + +concurrency::task ImageHelpers::EncodeSoftwareBitmapToPngStreamAsync(SoftwareBitmap^ bitmap) { + if (bitmap == nullptr) return task_from_result(nullptr); + try { + auto stream = ref new InMemoryRandomAccessStream(); + + SoftwareBitmap^ encodeBitmap = BuildStraightAlphaBitmap(bitmap); + + unsigned int outW = bitmap->PixelWidth; + unsigned int outH = bitmap->PixelHeight; + Platform::Array^ pixelArr = ExtractStraightAlphaPixels(encodeBitmap != nullptr ? encodeBitmap : bitmap, outW, outH); + + return concurrency::create_task(BitmapEncoder::CreateAsync(BitmapEncoder::PngEncoderId, stream)).then([pixelArr, outW, outH, stream, encodeBitmap](BitmapEncoder^ encoder) -> concurrency::task { + bool usedSetPixelData = false; + try { + if (pixelArr != nullptr) { + encoder->SetPixelData(BitmapPixelFormat::Bgra8, BitmapAlphaMode::Straight, outW, outH, 96.0, 96.0, pixelArr); + usedSetPixelData = true; + } + } catch(...) { usedSetPixelData = false; } + + if (!usedSetPixelData) { + + try { + + if (encodeBitmap != nullptr) encoder->SetSoftwareBitmap(encodeBitmap); + else encoder->SetSoftwareBitmap(EnsureBgra8Premultiplied(ref new SoftwareBitmap(BitmapPixelFormat::Bgra8, outW, outH, BitmapAlphaMode::Premultiplied))); + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "EncodeSoftwareBitmapToPngStreamAsync: SetSoftwareBitmap fallback failed, encoder has no pixel data\n"); + } + } + return concurrency::create_task(encoder->FlushAsync()); + }).then([stream]() -> IRandomAccessStream^ { + try { + stream->Seek(0); + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Warning, "EncodeSoftwareBitmapToPngStreamAsync: exception seeking stream\n"); + } + return stream; + }); + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "EncodeSoftwareBitmapToPngStreamAsync: exception encoding\n"); + return task_from_result(nullptr); + } +} + +SoftwareBitmap^ ImageHelpers::EnsureBgra8Premultiplied(SoftwareBitmap^ bitmap) { + if (bitmap == nullptr) return nullptr; + if (bitmap->BitmapPixelFormat != BitmapPixelFormat::Bgra8 || bitmap->BitmapAlphaMode != BitmapAlphaMode::Premultiplied) { + try { + auto conv = SoftwareBitmap::Convert(bitmap, BitmapPixelFormat::Bgra8, BitmapAlphaMode::Premultiplied); + return conv; + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Warning, "EnsureBgra8Premultiplied: conversion failed\n"); + return bitmap; + } + } + return bitmap; +} + +SoftwareBitmap^ ImageHelpers::ResizeSoftwareBitmap(SoftwareBitmap^ src, unsigned int width, unsigned int height) { + if (src == nullptr) return nullptr; + try { + src = EnsureBgra8Premultiplied(src); + unsigned int srcW = src->PixelWidth; + unsigned int srcH = src->PixelHeight; + if (srcW == width && srcH == height) return src; + + auto outBmp = ref new SoftwareBitmap(BitmapPixelFormat::Bgra8, width, height, BitmapAlphaMode::Premultiplied); + + auto srcBuf = src->LockBuffer(BitmapBufferAccessMode::Read); + auto srcDesc = srcBuf->GetPlaneDescription(0); + auto dstBuf = outBmp->LockBuffer(BitmapBufferAccessMode::Write); + auto dstDesc = dstBuf->GetPlaneDescription(0); + + auto srcRef = srcBuf->CreateReference(); + auto dstRef = dstBuf->CreateReference(); + Microsoft::WRL::ComPtr srcAccess; + Microsoft::WRL::ComPtr dstAccess; + IUnknown* srcUnk = reinterpret_cast(srcRef); + IUnknown* dstUnk = reinterpret_cast(dstRef); + BYTE* srcData = nullptr; UINT32 srcCap = 0; + BYTE* dstData = nullptr; UINT32 dstCap = 0; + bool haveFast = srcUnk != nullptr && dstUnk != nullptr + && SUCCEEDED(srcUnk->QueryInterface(IID_PPV_ARGS(&srcAccess))) && SUCCEEDED(dstUnk->QueryInterface(IID_PPV_ARGS(&dstAccess))) + && SUCCEEDED(srcAccess->GetBuffer(&srcData, &srcCap)) && SUCCEEDED(dstAccess->GetBuffer(&dstData, &dstCap)); + + if (haveFast) { + for (unsigned int y = 0; y < height; ++y) { + unsigned int sy = (unsigned int)((uint64_t)y * srcH / height); + uint8_t* srcRow = srcData + srcDesc.StartIndex + sy * srcDesc.Stride; + uint8_t* dstRow = dstData + dstDesc.StartIndex + y * dstDesc.Stride; + for (unsigned int x = 0; x < width; ++x) { + unsigned int sx = (unsigned int)((uint64_t)x * srcW / width); + uint8_t* pSrc = srcRow + sx * 4; + uint8_t* pDst = dstRow + x * 4; + pDst[0] = pSrc[0]; pDst[1] = pSrc[1]; pDst[2] = pSrc[2]; pDst[3] = pSrc[3]; + } + } + return outBmp; + } + + std::vector srcBufData((size_t)srcH * (size_t)srcDesc.Stride); + try { + auto ib = ref new Windows::Storage::Streams::Buffer((unsigned int)srcBufData.size()); + src->CopyToBuffer(ib); + auto reader = Windows::Storage::Streams::DataReader::FromBuffer(ib); + reader->ReadBytes(Platform::ArrayReference(srcBufData.data(), (unsigned int)srcBufData.size())); + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "ResizeSoftwareBitmap: failed to read src buffer\n"); + return nullptr; + } + + std::vector dstTmp((size_t)height * (size_t)dstDesc.Stride); + for (unsigned int y = 0; y < height; ++y) { + unsigned int sy = (unsigned int)((uint64_t)y * srcH / height); + uint8_t* srcRow = srcBufData.data() + sy * srcDesc.Stride; + uint8_t* dstRow = dstTmp.data() + y * dstDesc.Stride; + for (unsigned int x = 0; x < width; ++x) { + unsigned int sx = (unsigned int)((uint64_t)x * srcW / width); + uint8_t* pSrc = srcRow + sx * 4; + uint8_t* pDst = dstRow + x * 4; + pDst[0] = pSrc[0]; pDst[1] = pSrc[1]; pDst[2] = pSrc[2]; pDst[3] = pSrc[3]; + } + } + + try { + auto writer = ref new Windows::Storage::Streams::DataWriter(); + writer->WriteBytes(Platform::ArrayReference(dstTmp.data(), (unsigned int)dstTmp.size())); + auto buf = writer->DetachBuffer(); + + dstRef = nullptr; dstBuf = nullptr; + outBmp->CopyFromBuffer(buf); + return outBmp; + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "ResizeSoftwareBitmap: failed to write dst buffer\n"); + return nullptr; + } + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "ResizeSoftwareBitmap: exception during resize\n"); + return nullptr; + } +} + +SoftwareBitmap^ ImageHelpers::ResizeSoftwareBitmapUniformToFill(SoftwareBitmap^ src, unsigned int width, unsigned int height) { + if (src == nullptr) return nullptr; + try { + unsigned int srcW = src->PixelWidth; + unsigned int srcH = src->PixelHeight; + if (srcW == 0 || srcH == 0) return nullptr; + double srcAspect = (double)srcW / (double)srcH; + double dstAspect = (double)width / (double)height; + + unsigned int cropX = 0, cropY = 0, cropW = srcW, cropH = srcH; + if (srcAspect > dstAspect) { + + cropH = srcH; + cropW = (unsigned int)std::ceil(dstAspect * (double)cropH); + if (cropW > srcW) cropW = srcW; + cropX = (srcW - cropW) / 2; + cropY = 0; + } else if (srcAspect < dstAspect) { + + cropW = srcW; + cropH = (unsigned int)std::ceil((double)cropW / dstAspect); + if (cropH > srcH) cropH = srcH; + cropY = (srcH - cropH) / 2; + cropX = 0; + } else { + + } + + if (cropX == 0 && cropY == 0 && cropW == srcW && cropH == srcH) { + return ResizeSoftwareBitmap(src, width, height); + } + + auto cropped = ref new SoftwareBitmap(BitmapPixelFormat::Bgra8, cropW, cropH, BitmapAlphaMode::Premultiplied); + + auto srcBuf = src->LockBuffer(BitmapBufferAccessMode::Read); + auto srcDesc = srcBuf->GetPlaneDescription(0); + auto dstBuf = cropped->LockBuffer(BitmapBufferAccessMode::Write); + auto dstDesc = dstBuf->GetPlaneDescription(0); + + try { + + std::vector srcData((size_t)srcH * (size_t)srcDesc.Stride); + try { + auto ib = ref new Windows::Storage::Streams::Buffer((unsigned int)srcData.size()); + src->CopyToBuffer(ib); + auto reader = Windows::Storage::Streams::DataReader::FromBuffer(ib); + reader->ReadBytes(Platform::ArrayReference(srcData.data(), (unsigned int)srcData.size())); + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "ResizeSoftwareBitmapUniformToFill: failed to read src buffer\n"); + return nullptr; + } + + std::vector dstTmp((size_t)cropH * (size_t)dstDesc.Stride); + for (unsigned int y = 0; y < cropH; ++y) { + uint8_t* srcRow = srcData.data() + (size_t)(cropY + y) * srcDesc.Stride; + uint8_t* dstRow = dstTmp.data() + (size_t)y * dstDesc.Stride; + for (unsigned int x = 0; x < cropW; ++x) { + uint8_t* pSrc = srcRow + (size_t)(cropX + x) * 4; + uint8_t* pDst = dstRow + (size_t)x * 4; + pDst[0] = pSrc[0]; pDst[1] = pSrc[1]; pDst[2] = pSrc[2]; pDst[3] = pSrc[3]; + } + } + + try { + auto writer = ref new Windows::Storage::Streams::DataWriter(); + writer->WriteBytes(Platform::ArrayReference(dstTmp.data(), (unsigned int)dstTmp.size())); + auto buf = writer->DetachBuffer(); + dstBuf = nullptr; + cropped->CopyFromBuffer(buf); + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "ResizeSoftwareBitmapUniformToFill: failed to write cropped buffer\n"); + return nullptr; + } + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "ResizeSoftwareBitmapUniformToFill: unexpected exception\n"); + return nullptr; + } + + return ResizeSoftwareBitmap(cropped, width, height); + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "ResizeSoftwareBitmapUniformToFill: exception during operation\n"); + return nullptr; + } +} + +bool ImageHelpers::AdjustSaturation(SoftwareBitmap^ bmp, float saturation) { + if (bmp == nullptr) return false; + try { + auto b = EnsureBgra8Premultiplied(bmp); + if (b == nullptr) return false; + auto buf = b->LockBuffer(BitmapBufferAccessMode::ReadWrite); + auto desc = buf->GetPlaneDescription(0); + unsigned int w = b->PixelWidth; unsigned int h = b->PixelHeight; + + if (saturation == 1.0f) return true; + + const float satf = saturation; + + try { + auto ref = buf->CreateReference(); + Microsoft::WRL::ComPtr access; + IUnknown* unk = reinterpret_cast(ref); + BYTE* raw = nullptr; UINT32 cap = 0; + if (unk != nullptr && SUCCEEDED(unk->QueryInterface(IID_PPV_ARGS(&access))) + && SUCCEEDED(access->GetBuffer(&raw, &cap)) && raw != nullptr) { + for (unsigned int y = 0; y < h; ++y) { + uint8_t* row = raw + desc.StartIndex + (size_t)y * desc.Stride; + for (unsigned int x = 0; x < w; ++x) { + uint8_t b_px = row[x*4 + 0]; + uint8_t g_px = row[x*4 + 1]; + uint8_t r_px = row[x*4 + 2]; + uint8_t a_px = row[x*4 + 3]; + if (a_px == 0) continue; + + unsigned int invA = std::max(1, a_px); + float ur = (float)((uint32_t)r_px * 255u) / (float)invA; + float ug = (float)((uint32_t)g_px * 255u) / (float)invA; + float ub = (float)((uint32_t)b_px * 255u) / (float)invA; + + float gray = ur * 0.299f + ug * 0.587f + ub * 0.114f; + + float nrf = gray + satf * (ur - gray); + float ngf = gray + satf * (ug - gray); + float nbf = gray + satf * (ub - gray); + + int nri = (int)std::round(nrf); + int ngi = (int)std::round(ngf); + int nbi = (int)std::round(nbf); + nri = nri < 0 ? 0 : (nri > 255 ? 255 : nri); + ngi = ngi < 0 ? 0 : (ngi > 255 ? 255 : ngi); + nbi = nbi < 0 ? 0 : (nbi > 255 ? 255 : nbi); + + uint8_t pr = (uint8_t)((uint32_t)nri * a_px / 255u); + uint8_t pg = (uint8_t)((uint32_t)ngi * a_px / 255u); + uint8_t pb = (uint8_t)((uint32_t)nbi * a_px / 255u); + + row[x*4 + 0] = pb; row[x*4 + 1] = pg; row[x*4 + 2] = pr; row[x*4 + 3] = a_px; + } + } + return true; + } + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Warning, "AdjustSaturation: fast-path buffer access failed, falling back\n"); + } + + try { + buf = nullptr; + std::vector data((size_t)h * desc.Stride); + auto ib = ref new Windows::Storage::Streams::Buffer((unsigned int)data.size()); + b->CopyToBuffer(ib); + auto reader = Windows::Storage::Streams::DataReader::FromBuffer(ib); + reader->ReadBytes(Platform::ArrayReference(data.data(), (unsigned int)data.size())); + + for (unsigned int y = 0; y < h; ++y) { + uint8_t* row = data.data() + (size_t)y * desc.Stride; + for (unsigned int x = 0; x < w; ++x) { + uint8_t b_px = row[x*4 + 0]; + uint8_t g_px = row[x*4 + 1]; + uint8_t r_px = row[x*4 + 2]; + uint8_t a_px = row[x*4 + 3]; + if (a_px == 0) continue; + + unsigned int invA = std::max(1, a_px); + float ur = (float)((uint32_t)r_px * 255u) / (float)invA; + float ug = (float)((uint32_t)g_px * 255u) / (float)invA; + float ub = (float)((uint32_t)b_px * 255u) / (float)invA; + + float gray = ur * 0.299f + ug * 0.587f + ub * 0.114f; + + float nrf = gray + satf * (ur - gray); + float ngf = gray + satf * (ug - gray); + float nbf = gray + satf * (ub - gray); + + int nri = (int)std::round(nrf); + int ngi = (int)std::round(ngf); + int nbi = (int)std::round(nbf); + nri = nri < 0 ? 0 : (nri > 255 ? 255 : nri); + ngi = ngi < 0 ? 0 : (ngi > 255 ? 255 : ngi); + nbi = nbi < 0 ? 0 : (nbi > 255 ? 255 : nbi); + + uint8_t pr = (uint8_t)((uint32_t)nri * a_px / 255u); + uint8_t pg = (uint8_t)((uint32_t)ngi * a_px / 255u); + uint8_t pb = (uint8_t)((uint32_t)nbi * a_px / 255u); + + row[x*4 + 0] = pb; row[x*4 + 1] = pg; row[x*4 + 2] = pr; row[x*4 + 3] = a_px; + } + } + + auto writer = ref new Windows::Storage::Streams::DataWriter(); + writer->WriteBytes(Platform::ArrayReference(data.data(), (unsigned int)data.size())); + auto outBuf = writer->DetachBuffer(); + b->CopyFromBuffer(outBuf); + return true; + } catch(...) { return false; } + } catch(...) { return false; } +} + +concurrency::task ImageHelpers::CreateMaskedBlurredPngStreamAsync( + SoftwareBitmap^ src, + unsigned int targetW, + unsigned int targetH, + double dpi, + float blurDip) { + if (src == nullptr) return task_from_result(nullptr); + + try { + + unsigned int tW = targetW != 0 ? targetW : src->PixelWidth; + unsigned int tH = targetH != 0 ? targetH : src->PixelHeight; + + auto raster = ResizeSoftwareBitmapUniformToFill(src, tW, tH); + if (raster != nullptr) src = raster; + + unsigned int radiusPx = 0; + try { radiusPx = (unsigned int)std::round((double)blurDip * dpi / 96.0); } catch(...) { radiusPx = (unsigned int)std::round((double)blurDip); } + + SoftwareBitmap^ preBlurBitmap = src; + + try { + auto gpuResult = ::EffectsLibrary::GpuBoxBlurSoftwareBitmap(preBlurBitmap, (int)radiusPx, true); + if (gpuResult != nullptr) { + + try { AdjustSaturation(gpuResult, 1.0f); } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Warning, "CreateMaskedBlurredPngStreamAsync: AdjustSaturation on GPU result failed\n"); + } + return create_task(EncodeSoftwareBitmapToPngStreamAsync(gpuResult)).then([](IRandomAccessStream^ s) -> IRandomAccessStream^ { + if (s != nullptr) { + try { s->Seek(0); } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Warning, "CreateMaskedBlurredPngStreamAsync: failed to seek GPU-path stream\n"); + } + } + return s; + }); + } + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Warning, "CreateMaskedBlurredPngStreamAsync: GPU blur failed, falling back to CPU\n"); + } + + try { + auto cpuTarget = preBlurBitmap; + ::EffectsLibrary::BoxBlurSoftwareBitmap(cpuTarget, (int)radiusPx); + try { AdjustSaturation(cpuTarget, 1.0f); } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Warning, "CreateMaskedBlurredPngStreamAsync: AdjustSaturation on CPU result failed\n"); + } + return create_task(EncodeSoftwareBitmapToPngStreamAsync(cpuTarget)).then([](IRandomAccessStream^ s) -> IRandomAccessStream^ { + if (s != nullptr) { + try { s->Seek(0); } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Warning, "CreateMaskedBlurredPngStreamAsync: failed to seek CPU-path stream\n"); + } + } + return s; + }); + } catch(...) { + MLOG(moonlight_xbox_dx::Utils::LogLevel::Error, "CreateMaskedBlurredPngStreamAsync: CPU blur fallback also failed\n"); + } + + return task_from_result(nullptr); + } catch(...) { + return task_from_result(nullptr); + } +} diff --git a/UI/Utilities/ImageHelpers.h b/UI/Utilities/ImageHelpers.h new file mode 100644 index 00000000..058eb03e --- /dev/null +++ b/UI/Utilities/ImageHelpers.h @@ -0,0 +1,29 @@ + +#pragma once +#include "pch.h" +#include +#include +#include + +namespace ImageHelpers { + +concurrency::task LoadSoftwareBitmapFromUriOrPathAsync(Platform::String^ path); + +concurrency::task EncodeSoftwareBitmapToPngStreamAsync(Windows::Graphics::Imaging::SoftwareBitmap^ bitmap); + +Windows::Graphics::Imaging::SoftwareBitmap^ EnsureBgra8Premultiplied(Windows::Graphics::Imaging::SoftwareBitmap^ bitmap); + +Windows::Graphics::Imaging::SoftwareBitmap^ ResizeSoftwareBitmap(Windows::Graphics::Imaging::SoftwareBitmap^ src, unsigned int width, unsigned int height); + +Windows::Graphics::Imaging::SoftwareBitmap^ ResizeSoftwareBitmapUniformToFill(Windows::Graphics::Imaging::SoftwareBitmap^ src, unsigned int width, unsigned int height); + +bool AdjustSaturation(Windows::Graphics::Imaging::SoftwareBitmap^ bmp, float saturation); + +concurrency::task CreateMaskedBlurredPngStreamAsync( + Windows::Graphics::Imaging::SoftwareBitmap^ src, + unsigned int targetW, + unsigned int targetH, + double dpi, + float blurDip); + +} diff --git a/UI/Utilities/ToastService.cpp b/UI/Utilities/ToastService.cpp new file mode 100644 index 00000000..5a3ba38b --- /dev/null +++ b/UI/Utilities/ToastService.cpp @@ -0,0 +1,127 @@ +#include "pch.h" +#include "ToastService.h" +#include "XamlHelpers.h" + +using namespace Platform; +using namespace Windows::Foundation; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Media; +using namespace Windows::UI::Xaml::Media::Animation; + +static Border^ s_toastBorder = nullptr; +static TextBlock^ s_toastText = nullptr; +static Storyboard^ s_toastStoryboard = nullptr; +static AcrylicBrush^ s_toastBrush = nullptr; + +namespace moonlight_xbox_dx { + +void InitializeToastService(Grid^ rootGrid) { + s_toastText = ref new TextBlock(); + s_toastText->Foreground = ref new SolidColorBrush(Windows::UI::Colors::White); + s_toastText->FontSize = 12; + + s_toastBorder = ref new Border(); + s_toastBorder->HorizontalAlignment = HorizontalAlignment::Right; + s_toastBorder->VerticalAlignment = VerticalAlignment::Bottom; + s_toastBorder->Margin = Thickness{ 0, 0, 0, 48 }; + s_toastBorder->Padding = Thickness{ 16, 10, 48, 10 }; + s_toastBorder->CornerRadius = CornerRadius{ 8, 0, 0, 8 }; + s_toastBorder->Visibility = Visibility::Collapsed; + s_toastBorder->IsHitTestVisible = false; + s_toastBorder->Child = s_toastText; + + s_toastBrush = ref new AcrylicBrush(); + s_toastBrush->TintOpacity = 0.75; + s_toastBrush->TintLuminosityOpacity = 0.5; + s_toastBorder->Background = s_toastBrush; + + auto translate = ref new TranslateTransform(); + s_toastBorder->RenderTransform = translate; + + s_toastStoryboard = ref new Storyboard(); + + auto xAnim = ref new DoubleAnimationUsingKeyFrames(); + Storyboard::SetTarget(xAnim, translate); + Storyboard::SetTargetProperty(xAnim, "X"); + + auto kfX0 = ref new DiscreteDoubleKeyFrame(); + kfX0->KeyTime = KeyTime{ TimeSpan{ 0LL } }; + kfX0->Value = 400.0; + + auto kfX1 = ref new EasingDoubleKeyFrame(); + kfX1->KeyTime = KeyTime{ TimeSpan{ 3000000LL } }; + kfX1->Value = 0.0; + auto easeIn = ref new CubicEase(); + easeIn->EasingMode = EasingMode::EaseOut; + kfX1->EasingFunction = easeIn; + + auto kfX2 = ref new DiscreteDoubleKeyFrame(); + kfX2->KeyTime = KeyTime{ TimeSpan{ 23000000LL } }; + kfX2->Value = 0.0; + + auto kfX3 = ref new EasingDoubleKeyFrame(); + kfX3->KeyTime = KeyTime{ TimeSpan{ 28000000LL } }; + kfX3->Value = 400.0; + auto easeOut = ref new CubicEase(); + easeOut->EasingMode = EasingMode::EaseIn; + kfX3->EasingFunction = easeOut; + + xAnim->KeyFrames->Append(kfX0); + xAnim->KeyFrames->Append(kfX1); + xAnim->KeyFrames->Append(kfX2); + xAnim->KeyFrames->Append(kfX3); + + auto opAnim = ref new DoubleAnimationUsingKeyFrames(); + Storyboard::SetTarget(opAnim, s_toastBorder); + Storyboard::SetTargetProperty(opAnim, "Opacity"); + + auto kfOp0 = ref new DiscreteDoubleKeyFrame(); + kfOp0->KeyTime = KeyTime{ TimeSpan{ 0LL } }; + kfOp0->Value = 0.0; + + auto kfOp1 = ref new EasingDoubleKeyFrame(); + kfOp1->KeyTime = KeyTime{ TimeSpan{ 2500000LL } }; + kfOp1->Value = 1.0; + auto opEaseIn = ref new CubicEase(); + opEaseIn->EasingMode = EasingMode::EaseOut; + kfOp1->EasingFunction = opEaseIn; + + auto kfOp2 = ref new DiscreteDoubleKeyFrame(); + kfOp2->KeyTime = KeyTime{ TimeSpan{ 23000000LL } }; + kfOp2->Value = 1.0; + + auto kfOp3 = ref new EasingDoubleKeyFrame(); + kfOp3->KeyTime = KeyTime{ TimeSpan{ 28000000LL } }; + kfOp3->Value = 0.0; + auto opEaseOut = ref new CubicEase(); + opEaseOut->EasingMode = EasingMode::EaseIn; + kfOp3->EasingFunction = opEaseOut; + + opAnim->KeyFrames->Append(kfOp0); + opAnim->KeyFrames->Append(kfOp1); + opAnim->KeyFrames->Append(kfOp2); + opAnim->KeyFrames->Append(kfOp3); + + s_toastStoryboard->Children->Append(xAnim); + s_toastStoryboard->Children->Append(opAnim); + + s_toastStoryboard->Completed += ref new EventHandler([](Object^, Object^) { + s_toastBorder->Visibility = Visibility::Collapsed; + }); + + rootGrid->Children->Append(s_toastBorder); +} + +void ShowToast(Platform::String^ message) { + if (s_toastBorder == nullptr || s_toastText == nullptr || s_toastStoryboard == nullptr) + return; + if (s_toastBrush != nullptr) + s_toastBrush->TintColor = GetAppliedAccentColor(); + s_toastStoryboard->Stop(); + s_toastText->Text = message; + s_toastBorder->Visibility = Visibility::Visible; + s_toastStoryboard->Begin(); +} + +} diff --git a/UI/Utilities/ToastService.h b/UI/Utilities/ToastService.h new file mode 100644 index 00000000..5987a139 --- /dev/null +++ b/UI/Utilities/ToastService.h @@ -0,0 +1,9 @@ +#pragma once +#include "pch.h" + +namespace moonlight_xbox_dx { + +void InitializeToastService(Windows::UI::Xaml::Controls::Grid^ rootGrid); +void ShowToast(Platform::String^ message); + +} diff --git a/UI/Utilities/XamlHelpers.cpp b/UI/Utilities/XamlHelpers.cpp new file mode 100644 index 00000000..d4cd5094 --- /dev/null +++ b/UI/Utilities/XamlHelpers.cpp @@ -0,0 +1,253 @@ +#include "pch.h" +#include "XamlHelpers.h" +#define MLOG_TAG_OVERRIDE "XamlHelpers" +#include "Utils.hpp" +#include +#include +#include + +using namespace Windows::Foundation; +using namespace Windows::UI::Xaml; +using namespace Windows::UI::Xaml::Controls; +using namespace Windows::UI::Xaml::Media; + +namespace moonlight_xbox_dx { + +FrameworkElement^ FindChildByName(DependencyObject^ parent, Platform::String^ name) { + if (parent == nullptr) return nullptr; + int count = VisualTreeHelper::GetChildrenCount(parent); + for (int i = 0; i < count; ++i) { + auto child = VisualTreeHelper::GetChild(parent, i); + auto fe = dynamic_cast(child); + if (fe != nullptr && fe->Name == name) return fe; + auto rec = FindChildByName(child, name); + if (rec != nullptr) return rec; + } + return nullptr; +} + +ScrollViewer^ FindScrollViewer(DependencyObject^ parent) { + if (parent == nullptr) return nullptr; + int count = VisualTreeHelper::GetChildrenCount(parent); + for (int i = 0; i < count; ++i) { + auto child = VisualTreeHelper::GetChild(parent, i); + auto sv = dynamic_cast(child); + if (sv != nullptr) return sv; + auto rec = FindScrollViewer(child); + if (rec != nullptr) return rec; + } + return nullptr; +} + +static Windows::UI::Color s_appliedAccentColor = Windows::UI::Color{ 255, 0, 120, 215 }; + +Windows::UI::Color GetAppliedAccentColor() { + return s_appliedAccentColor; +} + +void ApplyAccentColor(Windows::UI::Color color) { + s_appliedAccentColor = color; + + wchar_t buf[16]; + swprintf_s(buf, L"#%02X%02X%02X%02X", color.A, color.R, color.G, color.B); + Windows::UI::Xaml::Interop::TypeName colorType; + colorType.Name = "Windows.UI.Color"; + colorType.Kind = Windows::UI::Xaml::Interop::TypeKind::Metadata; + auto boxed = Windows::UI::Xaml::Markup::XamlBindingHelper::ConvertValue( + colorType, ref new Platform::String(buf)); + + static const wchar_t* themeKeys[] = { L"Default", L"Dark", L"Light" }; + auto appThemes = Windows::UI::Xaml::Application::Current->Resources->ThemeDictionaries; + for (auto themeKey : themeKeys) { + auto key = ref new Platform::String(themeKey); + Windows::UI::Xaml::ResourceDictionary^ dict; + if (appThemes->HasKey(key)) { + dict = dynamic_cast(appThemes->Lookup(key)); + } else { + dict = ref new Windows::UI::Xaml::ResourceDictionary(); + appThemes->Insert(key, dict); + } + if (dict != nullptr) { + dict->Insert("SystemAccentColor", boxed); + + auto brush = ref new Windows::UI::Xaml::Media::SolidColorBrush(color); + dict->Insert("SystemControlHighlightAccentBrush", brush); + dict->Insert("SystemControlBackgroundAccentBrush", brush); + } + } +} + +SelectedContainerStatus ResolveSelectedContainer( + ListViewBase^ grid, ScrollViewer^& scrollViewer, ListViewItem^& container) +{ + container = nullptr; + if (grid == nullptr || grid->SelectedIndex < 0 || grid->SelectedItem == nullptr) + return SelectedContainerStatus::NoSelection; + + if (scrollViewer == nullptr) scrollViewer = FindScrollViewer(grid); + if (scrollViewer == nullptr) return SelectedContainerStatus::ScrollViewerMissing; + + auto item = grid->SelectedItem; + try { + container = dynamic_cast(grid->ContainerFromItem(item)); + } catch (...) {} + if (container == nullptr) { + try { + grid->ScrollIntoView(item); + grid->UpdateLayout(); + container = dynamic_cast(grid->ContainerFromItem(item)); + } catch (...) {} + } + if (container == nullptr) { + return SelectedContainerStatus::ContainerMissing; + } + + return SelectedContainerStatus::Ready; +} + +CenterContainerOutcome CenterContainerInScrollViewer( + ScrollViewer^ scrollViewer, UIElement^ container, bool vertical, bool immediate) +{ + if (scrollViewer == nullptr || container == nullptr) return CenterContainerOutcome::NotReady; + + auto fe = dynamic_cast(container); + double containerWidth = fe != nullptr ? fe->ActualWidth : 0.0; + double containerHeight = fe != nullptr ? fe->ActualHeight : 0.0; + if (containerWidth <= 0.0 || containerHeight <= 0.0) return CenterContainerOutcome::NotReady; + + double viewport = vertical ? scrollViewer->ViewportHeight : scrollViewer->ViewportWidth; + if (!std::isfinite(viewport) || viewport <= 0.0) return CenterContainerOutcome::NotReady; + + auto contentElement = dynamic_cast(scrollViewer->Content); + if (contentElement == nullptr) return CenterContainerOutcome::NotReady; + + Point origin; origin.X = 0.0f; origin.Y = 0.0f; + Point inContent; + try { + inContent = container->TransformToVisual(contentElement)->TransformPoint(origin); + } catch (...) { + return CenterContainerOutcome::NotReady; + } + + double itemCenter = vertical + ? (inContent.Y + (containerHeight * 0.5)) + : (inContent.X + (containerWidth * 0.5)); + + double scrollable = vertical ? scrollViewer->ScrollableHeight : scrollViewer->ScrollableWidth; + double current = vertical ? scrollViewer->VerticalOffset : scrollViewer->HorizontalOffset; + if (!std::isfinite(scrollable) || !std::isfinite(current)) return CenterContainerOutcome::NotReady; + + double target = itemCenter - (viewport * 0.5); + target = std::max(0.0, std::min(target, std::max(0.0, scrollable))); + + if (std::fabs(target - current) < 0.5) { + return CenterContainerOutcome::Applied; + } + + try { + if (vertical) scrollViewer->ChangeView(nullptr, target, nullptr, immediate); + else scrollViewer->ChangeView(target, nullptr, nullptr, immediate); + } catch (...) {} + + return CenterContainerOutcome::Applied; +} + +EdgeCenteringPaddingResult ApplyEdgeCenteringPadding(ListViewBase^ grid, double desiredPadding) +{ + if (grid == nullptr || !std::isfinite(desiredPadding)) return EdgeCenteringPaddingResult::Unchanged; + + auto padding = grid->Padding; + if (std::fabs(padding.Left - desiredPadding) < 0.5 && std::fabs(padding.Right - desiredPadding) < 0.5) + return EdgeCenteringPaddingResult::Unchanged; + + padding.Left = desiredPadding; + padding.Right = desiredPadding; + grid->Padding = padding; + return EdgeCenteringPaddingResult::Applied; +} + +Windows::Foundation::EventRegistrationToken ArmContainerRealizedWait(ListViewBase^ grid, std::function onFire) +{ + Windows::Foundation::EventRegistrationToken empty{ 0 }; + if (grid == nullptr) return empty; + auto selectedItem = grid->SelectedItem; + if (selectedItem == nullptr) return empty; + + auto tokenHolder = std::make_shared(); + tokenHolder->Value = 0; + *tokenHolder = grid->ContainerContentChanging += + ref new TypedEventHandler( + [grid, selectedItem, tokenHolder, onFire](ListViewBase^, ContainerContentChangingEventArgs^ args) { + if (args->Item != selectedItem) return; + try { grid->ContainerContentChanging -= *tokenHolder; } + catch (...) { MLOG(Utils::LogLevel::Error, "ArmContainerRealizedWait failed to unsubscribe"); } + tokenHolder->Value = 0; + onFire(); + }); + return *tokenHolder; +} + +void CancelContainerRealizedWait(ListViewBase^ grid, Windows::Foundation::EventRegistrationToken& token) +{ + if (grid == nullptr || token.Value == 0) return; + try { grid->ContainerContentChanging -= token; } + catch (...) { MLOG(Utils::LogLevel::Error, "CancelContainerRealizedWait failed to unsubscribe"); } + token.Value = 0; +} + +Windows::Foundation::EventRegistrationToken ArmLayoutUpdatedWait(ListViewBase^ grid, std::function onFire) +{ + Windows::Foundation::EventRegistrationToken empty{ 0 }; + if (grid == nullptr) return empty; + + auto tokenHolder = std::make_shared(); + tokenHolder->Value = 0; + *tokenHolder = grid->LayoutUpdated += + ref new EventHandler( + [grid, tokenHolder, onFire](Platform::Object^, Platform::Object^) { + try { grid->LayoutUpdated -= *tokenHolder; } + catch (...) { MLOG(Utils::LogLevel::Error, "ArmLayoutUpdatedWait failed to unsubscribe"); } + tokenHolder->Value = 0; + onFire(); + }); + return *tokenHolder; +} + +void CancelLayoutUpdatedWait(ListViewBase^ grid, Windows::Foundation::EventRegistrationToken& token) +{ + if (grid == nullptr || token.Value == 0) return; + try { grid->LayoutUpdated -= token; } + catch (...) { MLOG(Utils::LogLevel::Error, "CancelLayoutUpdatedWait failed to unsubscribe"); } + token.Value = 0; +} + +void AnimateCrossfadeText( + FrameworkElement^ visibilityAnchor, + Windows::UI::Xaml::Media::Animation::Storyboard^ showSb, + Windows::UI::Xaml::Media::Animation::Storyboard^ hideSb, + bool wantAnimate, + std::function applyNewText, + std::function isStillCurrent) +{ + bool alreadyVisible = visibilityAnchor != nullptr && visibilityAnchor->Opacity > 0.01; + if (wantAnimate && alreadyVisible && hideSb != nullptr && showSb != nullptr) { + auto token = std::make_shared(); + *token = hideSb->Completed += ref new EventHandler( + [hideSb, showSb, token, applyNewText, isStillCurrent](Platform::Object^, Platform::Object^) mutable { + try { hideSb->Completed -= *token; } + catch (...) { MLOG(Utils::LogLevel::Error, "AnimateCrossfadeText failed to unsubscribe hideSb.Completed"); } + if (!isStillCurrent()) return; + try { + applyNewText(); + showSb->Begin(); + } catch (...) { MLOG(Utils::LogLevel::Error, "AnimateCrossfadeText post-hide apply failed"); } + }); + hideSb->Begin(); + } else { + try { applyNewText(); } + catch (...) { MLOG(Utils::LogLevel::Error, "AnimateCrossfadeText direct apply failed"); } + if (showSb != nullptr) showSb->Begin(); + } +} + +} diff --git a/UI/Utilities/XamlHelpers.h b/UI/Utilities/XamlHelpers.h new file mode 100644 index 00000000..95d51233 --- /dev/null +++ b/UI/Utilities/XamlHelpers.h @@ -0,0 +1,72 @@ +#pragma once +#include "pch.h" +#include + +namespace moonlight_xbox_dx { + +Windows::UI::Xaml::Controls::ScrollViewer^ + FindScrollViewer(Windows::UI::Xaml::DependencyObject^ parent); + +Windows::UI::Xaml::FrameworkElement^ + FindChildByName(Windows::UI::Xaml::DependencyObject^ parent, Platform::String^ name); + +void ApplyAccentColor(Windows::UI::Color color); +Windows::UI::Color GetAppliedAccentColor(); + +enum class SelectedContainerStatus { + NoSelection, + ScrollViewerMissing, + ContainerMissing, + Ready, +}; + +SelectedContainerStatus ResolveSelectedContainer( + Windows::UI::Xaml::Controls::ListViewBase^ grid, + Windows::UI::Xaml::Controls::ScrollViewer^& scrollViewer, + Windows::UI::Xaml::Controls::ListViewItem^& container); + +enum class CenterContainerOutcome { + NotReady, + Applied, +}; + +CenterContainerOutcome CenterContainerInScrollViewer( + Windows::UI::Xaml::Controls::ScrollViewer^ scrollViewer, + Windows::UI::Xaml::UIElement^ container, + bool vertical, + bool immediate); + +enum class EdgeCenteringPaddingResult { + Unchanged, + Applied, +}; + +EdgeCenteringPaddingResult ApplyEdgeCenteringPadding( + Windows::UI::Xaml::Controls::ListViewBase^ grid, + double desiredPadding); + +Windows::Foundation::EventRegistrationToken ArmContainerRealizedWait( + Windows::UI::Xaml::Controls::ListViewBase^ grid, + std::function onFire); + +void CancelContainerRealizedWait( + Windows::UI::Xaml::Controls::ListViewBase^ grid, + Windows::Foundation::EventRegistrationToken& token); + +Windows::Foundation::EventRegistrationToken ArmLayoutUpdatedWait( + Windows::UI::Xaml::Controls::ListViewBase^ grid, + std::function onFire); + +void CancelLayoutUpdatedWait( + Windows::UI::Xaml::Controls::ListViewBase^ grid, + Windows::Foundation::EventRegistrationToken& token); + +void AnimateCrossfadeText( + Windows::UI::Xaml::FrameworkElement^ visibilityAnchor, + Windows::UI::Xaml::Media::Animation::Storyboard^ showSb, + Windows::UI::Xaml::Media::Animation::Storyboard^ hideSb, + bool wantAnimate, + std::function applyNewText, + std::function isStillCurrent); + +} diff --git a/Utils.cpp b/Utils.cpp index 365aa2a9..aec98e58 100644 --- a/Utils.cpp +++ b/Utils.cpp @@ -4,12 +4,18 @@ #include #include +#include +#include +#include +#include +#include #include #include #include using namespace std::chrono; constexpr auto LOG_LINES = 70; +constexpr size_t MAX_LOG_READ_BYTES = 512 * 1024; namespace moonlight_xbox_dx { namespace Utils { @@ -18,6 +24,117 @@ namespace moonlight_xbox_dx { bool showStats = false; std::mutex logMutex; + namespace { + HANDLE g_currentLogHandle = INVALID_HANDLE_VALUE; + std::wstring g_currentLogPath; + std::wstring g_lastLogPath; + + Platform::String^ ReadLogFileText(const std::wstring& path) { + if (path.empty()) { + return ref new Platform::String(L"(log unavailable)"); + } + HANDLE file = CreateFile2(path.c_str(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, OPEN_EXISTING, nullptr); + if (file == INVALID_HANDLE_VALUE) { + return ref new Platform::String(L"(no log available)"); + } + LARGE_INTEGER size{}; + if (!GetFileSizeEx(file, &size) || size.QuadPart <= 0) { + CloseHandle(file); + return ref new Platform::String(L"(log is empty)"); + } + + bool truncated = false; + LARGE_INTEGER readSize = size; + if (static_cast(size.QuadPart) > MAX_LOG_READ_BYTES) { + truncated = true; + readSize.QuadPart = static_cast(MAX_LOG_READ_BYTES); + LARGE_INTEGER seekTo{}; + seekTo.QuadPart = size.QuadPart - readSize.QuadPart; + SetFilePointerEx(file, seekTo, nullptr, FILE_BEGIN); + } + + std::string buffer(static_cast(readSize.QuadPart), '\0'); + DWORD bytesRead = 0; + BOOL ok = ReadFile(file, buffer.data(), static_cast(buffer.size()), &bytesRead, nullptr); + CloseHandle(file); + if (!ok) { + return ref new Platform::String(L"(failed to read log)"); + } + buffer.resize(bytesRead); + + std::string result = truncated ? "(...truncated, showing tail...)\n" + buffer : buffer; + return StringFromStdString(result); + } + + void WriteCrashLine(const char* msg) { + if (g_currentLogHandle == INVALID_HANDLE_VALUE || msg == nullptr) return; + std::string line(msg); + line += '\n'; + DWORD written = 0; + WriteFile(g_currentLogHandle, line.data(), static_cast(line.size()), &written, nullptr); + FlushFileBuffers(g_currentLogHandle); + } + + const char* ExceptionCodeName(DWORD code) { + switch (code) { + case EXCEPTION_ACCESS_VIOLATION: return "ACCESS_VIOLATION"; + case EXCEPTION_STACK_OVERFLOW: return "STACK_OVERFLOW"; + case EXCEPTION_ILLEGAL_INSTRUCTION: return "ILLEGAL_INSTRUCTION"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: return "INT_DIVIDE_BY_ZERO"; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "ARRAY_BOUNDS_EXCEEDED"; + case EXCEPTION_DATATYPE_MISALIGNMENT: return "DATATYPE_MISALIGNMENT"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "FLT_DIVIDE_BY_ZERO"; + case EXCEPTION_FLT_INVALID_OPERATION: return "FLT_INVALID_OPERATION"; + case EXCEPTION_PRIV_INSTRUCTION: return "PRIV_INSTRUCTION"; + case EXCEPTION_IN_PAGE_ERROR: return "IN_PAGE_ERROR"; + case EXCEPTION_BREAKPOINT: return "BREAKPOINT"; + default: return "UNKNOWN"; + } + } + + LONG WINAPI OnUnhandledSEHException(EXCEPTION_POINTERS* info) { + DWORD code = (info && info->ExceptionRecord) ? info->ExceptionRecord->ExceptionCode : 0; + void* addr = (info && info->ExceptionRecord) ? info->ExceptionRecord->ExceptionAddress : nullptr; + char buf[256]; + snprintf(buf, sizeof(buf), "*** CRASH: unhandled exception 0x%08lX (%s) at address 0x%p ***", + code, ExceptionCodeName(code), addr); + WriteCrashLine(buf); + return EXCEPTION_CONTINUE_SEARCH; + } + + void OnTerminate() { + try { + if (auto ex = std::current_exception()) { + std::rethrow_exception(ex); + } + WriteCrashLine("*** CRASH: std::terminate called (no active exception) ***"); + } catch (Platform::Exception^ ex) { + char buf[512]; + std::wstring msg = ex->Message != nullptr ? std::wstring(ex->Message->Data()) : L""; + snprintf(buf, sizeof(buf), "*** CRASH: unhandled Platform::Exception (HRESULT 0x%08X): %S ***", + ex->HResult, msg.c_str()); + WriteCrashLine(buf); + } catch (const std::exception& ex) { + char buf[512]; + snprintf(buf, sizeof(buf), "*** CRASH: unhandled C++ exception: %s ***", ex.what()); + WriteCrashLine(buf); + } catch (...) { + WriteCrashLine("*** CRASH: unhandled exception of unknown type ***"); + } + std::abort(); + } + + void OnPureCall() { + WriteCrashLine("*** CRASH: pure virtual function call ***"); + std::abort(); + } + + void OnInvalidParameter(const wchar_t*, const wchar_t*, const wchar_t*, unsigned int, uintptr_t) { + WriteCrashLine("*** CRASH: invalid parameter passed to a CRT function ***"); + std::abort(); + } + } + Platform::String^ StringPrintf(const char* fmt, ...) { va_list args; va_start(args, fmt); @@ -56,16 +173,46 @@ namespace moonlight_xbox_dx { return std::wstring(buffer); } - void Log(const std::string_view& msg) { + const wchar_t* LogLevelTag(LogLevel level) { + switch (level) { + case LogLevel::Verbose: return L"VRB"; + case LogLevel::Debug: return L"DBG"; + case LogLevel::Info: return L"INF"; + case LogLevel::Warning: return L"WRN"; + case LogLevel::Error: return L"ERR"; + } + return L"INF"; + } + + LogLevel ParseLogLevelFromLine(const std::wstring& line) { + + if (line.find(L"[ERR]") != std::wstring::npos || line.find(L"{ERR}") != std::wstring::npos) return LogLevel::Error; + if (line.find(L"[WRN]") != std::wstring::npos || line.find(L"{WRN}") != std::wstring::npos) return LogLevel::Warning; + if (line.find(L"[INF]") != std::wstring::npos || line.find(L"{INF}") != std::wstring::npos) return LogLevel::Info; + if (line.find(L"[DBG]") != std::wstring::npos || line.find(L"{DBG}") != std::wstring::npos) return LogLevel::Debug; + if (line.find(L"[VRB]") != std::wstring::npos || line.find(L"{VRB}") != std::wstring::npos) return LogLevel::Verbose; + + return LogLevel::Error; + } + + void Log(LogLevel level, const std::string_view& msg) { try { - std::wstring string = GetCurrentTimestamp() + NarrowToWideString(msg); + std::wstring tag = std::wstring(L"[") + LogLevelTag(level) + L"] "; + std::wstring string = GetCurrentTimestamp() + tag + NarrowToWideString(msg); OutputDebugString(string.c_str()); { std::unique_lock lk(logMutex); + if (g_currentLogHandle != INVALID_HANDLE_VALUE) { + std::string line = WideToNarrowString(string); + if (line.empty() || line.back() != '\n') line += '\n'; + DWORD written = 0; + WriteFile(g_currentLogHandle, line.data(), static_cast(line.size()), &written, nullptr); + } if (logLines.size() == LOG_LINES) { logLines.erase(logLines.begin()); } - for (auto& ch : string) { + std::wstring displayString = string; + for (auto& ch : displayString) { // ModeSeven renders [ ] as left and right arrows, so we replace them // with { } which render as brackets if (ch == L'[') { @@ -75,7 +222,7 @@ namespace moonlight_xbox_dx { ch = L'}'; } } - logLines.push_back(string); + logLines.push_back(displayString); } } catch (...) { @@ -83,13 +230,13 @@ namespace moonlight_xbox_dx { } } - void Log(const char* msg) { + void Log(LogLevel level, const char* msg) { if (msg) { - Log(std::string_view(msg)); + Log(level, std::string_view(msg)); } } - void Logf(const char* format, ...) { + void Logf(LogLevel level, const char* format, ...) { va_list args; va_start(args, format); @@ -97,13 +244,89 @@ namespace moonlight_xbox_dx { std::vsnprintf(buf, sizeof(buf) - 1, format, args); va_end(args); - Log(std::string_view(buf)); + Log(level, std::string_view(buf)); + } + + std::string TagFromFunction(const char* function) { + if (!function) return std::string(); + std::string_view sv(function); + size_t lastSep = sv.rfind("::"); + if (lastSep == std::string_view::npos) { + return std::string(sv); + } + std::string_view withoutMethod = sv.substr(0, lastSep); + size_t prevSep = withoutMethod.rfind("::"); + std::string_view scope = (prevSep == std::string_view::npos) + ? withoutMethod + : withoutMethod.substr(prevSep + 2); + return std::string(scope); + } + + void LogTagged(LogLevel level, const std::string& tag, const char* msg) { + Log(level, "[" + tag + "] " + (msg ? msg : "")); + } + + void LogfTagged(LogLevel level, const std::string& tag, const char* format, ...) { + va_list args; + va_start(args, format); + + char buf[1024]; + std::vsnprintf(buf, sizeof(buf) - 1, format, args); + va_end(args); + + LogTagged(level, tag, buf); } std::vector GetLogLines() { return logLines; } + void InitFileLogging() { + try { + std::wstring folder(Windows::Storage::ApplicationData::Current->LocalFolder->Path->Data()); + + std::unique_lock lk(logMutex); + g_currentLogPath = folder + L"\\current_run.log"; + g_lastLogPath = folder + L"\\last_run.log"; + + DeleteFileW(g_lastLogPath.c_str()); + MoveFileExW(g_currentLogPath.c_str(), g_lastLogPath.c_str(), MOVEFILE_REPLACE_EXISTING); + + if (g_currentLogHandle != INVALID_HANDLE_VALUE) { + CloseHandle(g_currentLogHandle); + } + g_currentLogHandle = CreateFile2(g_currentLogPath.c_str(), GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS, nullptr); + } + catch (...) { + + } + } + + void InstallCrashHandlers() { + SetUnhandledExceptionFilter(&OnUnhandledSEHException); + std::set_terminate(&OnTerminate); + _set_purecall_handler(&OnPureCall); + _set_invalid_parameter_handler(&OnInvalidParameter); + } + + Platform::String^ ReadCurrentRunLogText() { + std::wstring path; + { + std::unique_lock lk(logMutex); + path = g_currentLogPath; + } + return ReadLogFileText(path); + } + + Platform::String^ ReadLastRunLogText() { + std::wstring path; + { + std::unique_lock lk(logMutex); + path = g_lastLogPath; + } + return ReadLogFileText(path); + } + Platform::String^ StringFromChars(const char* chars) { if (chars == nullptr) { @@ -159,5 +382,50 @@ namespace moonlight_xbox_dx { return result; } + + std::string Join(const std::vector& parts, const std::string& separator) { + std::string result; + for (size_t i = 0; i < parts.size(); i++) { + if (i > 0) result += separator; + result += parts[i]; + } + return result; + } + + double DurationStringToMs(Platform::String^ durationValue) { + if (durationValue == nullptr || durationValue->IsEmpty()) return 250.0; + + std::wstring text(durationValue->Data()); + std::wstringstream ss(text); + std::wstring segment; + std::vector parts; + + while (std::getline(ss, segment, L':')) { + if (segment.empty()) return 250.0; + try { + size_t idx = 0; + double value = std::stod(segment, &idx); + if (idx != segment.size()) return 250.0; + parts.push_back(value); + } catch (...) { + return 250.0; + } + } + + double totalSeconds = 0.0; + if (parts.size() == 3) { + totalSeconds = (parts[0] * 3600.0) + (parts[1] * 60.0) + parts[2]; + } else if (parts.size() == 2) { + totalSeconds = (parts[0] * 60.0) + parts[1]; + } else if (parts.size() == 1) { + totalSeconds = parts[0]; + } else { + return 250.0; + } + + if (!std::isfinite(totalSeconds) || totalSeconds <= 0.0) return 250.0; + return totalSeconds * 1000.0; + } + } } diff --git a/Utils.hpp b/Utils.hpp index 9f8f5c35..9224218f 100644 --- a/Utils.hpp +++ b/Utils.hpp @@ -3,6 +3,14 @@ namespace moonlight_xbox_dx { namespace Utils { + enum class LogLevel { + Verbose = 0, + Debug = 1, + Info = 2, + Warning = 3, + Error = 4 + }; + extern std::vector logLines; extern bool showLogs; extern bool showStats; @@ -10,14 +18,39 @@ namespace moonlight_xbox_dx { Platform::String^ StringPrintf(const char* fmt, ...); - void Log(const char* msg); - void Log(const std::string_view& msg); - void Logf(const char* msg, ...); + void Log(LogLevel level, const char* msg); + void Log(LogLevel level, const std::string_view& msg); + void Logf(LogLevel level, const char* msg, ...); + + std::string TagFromFunction(const char* function); + void LogTagged(LogLevel level, const std::string& tag, const char* msg); + void LogfTagged(LogLevel level, const std::string& tag, const char* format, ...); + + const wchar_t* LogLevelTag(LogLevel level); + LogLevel ParseLogLevelFromLine(const std::wstring& line); + + void InitFileLogging(); + void InstallCrashHandlers(); + Platform::String^ ReadCurrentRunLogText(); + Platform::String^ ReadLastRunLogText(); std::vector GetLogLines(); Platform::String^ StringFromChars(const char* chars); Platform::String^ StringFromStdString(std::string st); std::string PlatformStringToStdString(Platform::String^ input); std::string WideToNarrowString(const std::wstring_view& str); - std::wstring NarrowToWideString(const std::string_view& str); } + std::wstring NarrowToWideString(const std::string_view& str); + std::string Join(const std::vector& parts, const std::string& separator); + + double DurationStringToMs(Platform::String^ durationValue); + } } + +#ifdef MLOG_TAG_OVERRIDE +#define MLOG_TAG (MLOG_TAG_OVERRIDE) +#else +#define MLOG_TAG (moonlight_xbox_dx::Utils::TagFromFunction(__FUNCTION__)) +#endif + +#define MLOG(level, msg) moonlight_xbox_dx::Utils::LogTagged((level), MLOG_TAG, (msg)) +#define MLOGF(level, fmt, ...) moonlight_xbox_dx::Utils::LogfTagged((level), MLOG_TAG, (fmt), ##__VA_ARGS__) diff --git a/Utils/FloatBuffer.cpp b/Utils/FloatBuffer.cpp index b5c6239b..10268fcd 100644 --- a/Utils/FloatBuffer.cpp +++ b/Utils/FloatBuffer.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "FloatBuffer.h" +#define MLOG_TAG_OVERRIDE "FloatBuffer" #include "../Utils.hpp" #include @@ -155,7 +156,7 @@ void FloatBuffer::dump() const noexcept std::lock_guard lock(mtx_); if (count_ == 0) { - Utils::Logf("[FloatBuffer empty]\n"); + MLOGF(Utils::LogLevel::Debug, "[FloatBuffer empty]\n"); return; } @@ -177,5 +178,5 @@ void FloatBuffer::dump() const noexcept oss << ',' << buffer_[i]; } - Utils::Logf("%s\n", oss.str().c_str()); + MLOGF(Utils::LogLevel::Debug, "%s\n", oss.str().c_str()); } diff --git a/libgamestream/client.c b/libgamestream/client.c index 93808fa9..5dcb3843 100644 --- a/libgamestream/client.c +++ b/libgamestream/client.c @@ -33,6 +33,7 @@ #include #ifdef _WIN32 #define PATH_MAX 4096 +#include #include "winrt.h" #else #include @@ -834,16 +835,32 @@ int gs_quit_app(PSERVER_DATA server) { return ret; } +static bool identityLoaded = false; +static SRWLOCK identityLock = SRWLOCK_INIT; + +static int ensure_identity_loaded(const char* keyDirectory, int log_level) { + int ret = GS_OK; + AcquireSRWLockExclusive(&identityLock); + if (!identityLoaded) { + if (load_unique_id(keyDirectory) != GS_OK) { + ret = GS_FAILED; + } else if (load_cert(keyDirectory)) { + ret = GS_FAILED; + } else { + http_init(keyDirectory, log_level); + identityLoaded = true; + } + } + ReleaseSRWLockExclusive(&identityLock); + return ret; +} + int gs_init(PSERVER_DATA server, char *address, unsigned short httpPort, const char *keyDirectory, int log_level, bool unsupported) { mkdirtree(keyDirectory); - if (load_unique_id(keyDirectory) != GS_OK) - return GS_FAILED; - if (load_cert(keyDirectory)) + if (ensure_identity_loaded(keyDirectory, log_level) != GS_OK) return GS_FAILED; - http_init(keyDirectory, log_level); - LiInitializeServerInformation(&server->serverInfo); server->serverInfo.address = address; server->unsupported = unsupported; @@ -854,21 +871,19 @@ int gs_init(PSERVER_DATA server, char *address, unsigned short httpPort, const c int gs_appasset(PSERVER_DATA server, const char *keyDirectory, int appId) { int ret = GS_OK; - char url[4096]; - char* result = NULL; + char url[4096]; snprintf(url, sizeof(url), "https://%s:%u/appasset?appid=%d&AssetType=2&AssetIdx=0", server->serverInfo.address, server->httpsPort, appId); char uniqueFilePath[PATH_MAX]; snprintf(uniqueFilePath, PATH_MAX, "%s%d.png", keyDirectory, appId); FILE* fd = fopen(uniqueFilePath, "wb"); CURL* curl = get_curl_handle(); - if ((ret = http_request_binary(curl, url, fd)) != GS_OK) - goto cleanup; + ret = http_request_binary(curl, url, fd); fclose(fd); + if (ret != GS_OK) + remove(uniqueFilePath); cleanup: - if (result != NULL) - free(result); http_cleanup(curl); return ret; } diff --git a/libgamestream/http.c b/libgamestream/http.c index c308b603..14c0b50a 100644 --- a/libgamestream/http.c +++ b/libgamestream/http.c @@ -182,6 +182,14 @@ int http_request_binary(CURL *curl, char* url, FILE *data) { gs_error = curl_easy_strerror(res); return GS_FAILED; } + + long http_code = 0; + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); + if (http_code != 200) { + gs_error = "HTTP error response"; + return GS_FAILED; + } + return GS_OK; } diff --git a/moonlight-xbox-dx.vcxproj b/moonlight-xbox-dx.vcxproj index d3bb8bd6..57a7a799 100644 --- a/moonlight-xbox-dx.vcxproj +++ b/moonlight-xbox-dx.vcxproj @@ -272,12 +272,23 @@ + + + + + + + + + + + @@ -328,70 +339,170 @@ App.xaml + + + - - - MoonlightWelcome.xaml + + UI\Controls\AspectRatioBox.xaml + + + UI\Controls\SwatchPicker.xaml + + + UI\Controls\LunarPhaseControl.xaml + + + UI\Controls\TabsLayout.xaml + + + UI\Controls\SlidingMenu.xaml + + + + + + UI\Pages\MoonlightWelcome.xaml KeyboardControl.xaml - - Common\ModalDialog.xaml - + + UI\Modals\AddHostDialog.xaml + + + UI\Modals\HostActionsDialog.xaml + + + UI\Modals\TestConnectionResultDialog.xaml + + + UI\Modals\AlertDialog.xaml + + + UI\Modals\PairDialog.xaml + + + UI\Modals\ConfirmDialog.xaml + + + UI\Modals\StreamErrorDialog.xaml + + + + - - Pages\HostSettingsPage.xaml + + UI\Pages\HostSettingsPage.xaml - - Pages\MoonlightSettings.xaml + + UI\Pages\MoonlightSettings.xaml - - Pages\AppPage.xaml + + UI\Pages\AppPage\AppPage.xaml - - Pages\HostSelectorPage.xaml + + UI\Modals\AppActionsDialog.xaml - - Pages\StreamPage.xaml + + UI\Pages\HostSelectorPage.xaml + + + UI\Pages\StreamPage.xaml + + + + + UI\Backgrounds\Particles\ParticleBackground.xaml + + + UI\Backgrounds\Particles\ParticleSettingsControl.xaml + + + UI\Backgrounds\Spheres\SpheresBackground.xaml + + + UI\Backgrounds\Spheres\SpheresSettingsControl.xaml + + + UI\Backgrounds\Streaks\StreaksBackground.xaml + + + UI\Backgrounds\Streaks\StreaksSettingsControl.xaml + + + UI\Backgrounds\SwipeReveal\SwipeRevealBackground.xaml + + + UI\Backgrounds\GlobeGrid\GlobeGridBackground.xaml + + + UI\Backgrounds\Orbs\OrbsBackground.xaml + + + UI\Backgrounds\Orbs\OrbsSettingsControl.xaml + + + UI\Backgrounds\Blobs\BlobsBackground.xaml + + + UI\Backgrounds\Blobs\BlobsSettingsControl.xaml + + + UI\Backgrounds\DynamicBackgroundHost.xaml + + App.xaml - + + UI\Controls\AspectRatioBox.xaml + + + UI\Controls\SwatchPicker.xaml + + + UI\Controls\LunarPhaseControl.xaml + + + UI\Controls\TabsLayout.xaml + + + @@ -479,11 +590,68 @@ KeyboardControl.xaml - - MoonlightWelcome.xaml + + UI\Pages\MoonlightWelcome.xaml + + + UI\Backgrounds\Particles\ParticleBackground.xaml + + + UI\Backgrounds\Particles\ParticleSettingsControl.xaml - - Common\ModalDialog.xaml + + UI\Backgrounds\Spheres\SpheresBackground.xaml + + + UI\Backgrounds\Spheres\SpheresSettingsControl.xaml + + + UI\Backgrounds\Streaks\StreaksBackground.xaml + + + UI\Backgrounds\Streaks\StreaksSettingsControl.xaml + + + UI\Backgrounds\SwipeReveal\SwipeRevealBackground.xaml + + + UI\Backgrounds\GlobeGrid\GlobeGridBackground.xaml + + + UI\Backgrounds\Orbs\OrbsBackground.xaml + + + UI\Backgrounds\Orbs\OrbsSettingsControl.xaml + + + UI\Backgrounds\Blobs\BlobsBackground.xaml + + + UI\Backgrounds\Blobs\BlobsSettingsControl.xaml + + + UI\Backgrounds\DynamicBackgroundHost.xaml + + + UI\Modals\AddHostDialog.xaml + + + UI\Modals\HostActionsDialog.xaml + + + UI\Modals\TestConnectionResultDialog.xaml + + + UI\Modals\AlertDialog.xaml + + + UI\Modals\PairDialog.xaml + + + UI\Modals\ConfirmDialog.xaml + + + UI\Modals\StreamErrorDialog.xaml @@ -491,28 +659,40 @@ - - + + + + + + - - Pages\HostSettingsPage.xaml + + UI\Pages\HostSettingsPage.xaml - - Pages\MoonlightSettings.xaml + + UI\Pages\MoonlightSettings.xaml - - Pages\AppPage.xaml + + UI\Pages\AppPage\AppPage.xaml - - Pages\HostSelectorPage.xaml + + UI\Modals\AppActionsDialog.xaml - - Pages\StreamPage.xaml + + + UI\Controls\SlidingMenu.xaml + + + + UI\Pages\HostSelectorPage.xaml + + + UI\Pages\StreamPage.xaml Create @@ -560,12 +740,21 @@ + Designer + + + Pixel + + + Vertex + + Designer @@ -618,11 +807,11 @@ true - + true Document - + true Document @@ -634,24 +823,58 @@ - + + + + + + + + + + + + + + + + + + Designer - + + + + + + Designer + + + + + + + + + + Designer + + Designer - + Designer - + + + Designer - + Designer - - diff --git a/moonlight-xbox-dx.vcxproj.filters b/moonlight-xbox-dx.vcxproj.filters index a1df5136..49768934 100644 --- a/moonlight-xbox-dx.vcxproj.filters +++ b/moonlight-xbox-dx.vcxproj.filters @@ -1,4 +1,4 @@ - + @@ -6,11 +6,8 @@ svg;png - {3fbf8cfc-2833-4477-9705-aa2b53149e20} - dll;pdb - - {c71dcdb3-0f05-4f2e-b87d-d392921f9242} + dll;pdb {4FC737F1-C7A5-4376-A066-2A32D752A2FF} @@ -20,473 +17,952 @@ {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx;hm;inl;inc;ipp;xsd - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - {93995380-89BD-4b04-88EB-625FBE52EBFB} + {93995380-89BD-4b04-88EB-625FBE52EBFC} hlsl {c5fa3ea7-182b-41f7-91ef-1c48589370b7} + + {2aace108-c3a8-52cf-826b-8c9cdea480f8} + + + {b8f174b0-5e31-566d-9201-73fcfb6e5f37} + + + {0d2cc763-ad0c-5756-b153-b595e4c93cfa} + + + {f4bb19a3-bfc0-55d3-a4a9-30d4a6a0db1e} + + + {f34ed542-7efa-50f9-99ef-bc1852f15a37} + + + {efe1c19e-a719-51bc-85e3-3305867278a4} + + + {e6cb9d4e-bbb2-5dfa-8e47-e6b2609544bc} + + + {6930fa43-8b19-56bd-b5c9-56afd0d688e3} + + + {c72fd6cf-9169-5619-a367-2a04d22259f9} + + + {5704adc6-b65f-5cd2-a5d7-2e3b8800e124} + + + {889a1e1a-f914-5a13-9ccf-f5be1130e6ef} + + + {6bac72a9-5993-5866-9970-6ecd54286ecd} + + + {4dcf855c-eec4-571b-b638-9e2b296931ee} + + + {116f9d2d-c1c6-5b6c-878b-87eca5788f07} + + + {45ee189f-14af-5f42-a1a2-ddc9c842f88f} + + + {26cb9df7-7ce8-5aeb-8c04-872b0f6a4c7c} + + + {40ce30a4-9150-5a67-93e8-bf57dd8055b7} + + + {890be602-8fe3-55ca-91d5-4a3f3e359c6e} + + + {847bb4eb-d94e-52df-9a09-8820dde367b9} + + + {4cafd1ae-b0cd-5927-85a2-f819ef06180c} + + + {bd62e9e7-ae66-5165-a7e2-2e48e023afe5} + + + {8b742987-262d-5ecd-9d2b-2ca577cdb354} + + + {522e4255-b673-5a87-a302-e4b083e14599} + + + {280fdf19-7ced-5d3b-9fd7-658388d53b10} + + + {b48b8cb7-a2b9-5642-9186-ca7ab781ac9c} + + + {1e23cefe-35ac-57b4-87a8-0a5a13fcb316} + + + {b6666f08-8ced-5907-9bd7-d8266c1fca2a} + + + {29f95cb9-c5b4-5b22-ad2c-8b1282efe5a1} + + + {eae1029b-01a6-547e-9715-1f13dbbeaace} + + + {e7b6af5a-4e97-579b-b40b-70fc2ba709c3} + + + {291aa1fc-74e0-5aa0-9930-3bed8801c11a} + + + {1ea73efe-bdbb-5566-889b-d3b42ef30e85} + + + {62add804-f1de-56ed-8aca-a16851b8cb65} + + + {d8480659-c2cd-58e5-b866-1ec230594064} + - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - + + UI\Backgrounds\Particles + + + UI\Backgrounds\Particles + + + UI\Backgrounds\Spheres + + + UI\Backgrounds\Spheres + + + UI\Backgrounds\Streaks + + + UI\Backgrounds\Streaks + + + UI\Backgrounds\SwipeReveal + + + UI\Backgrounds\GlobeGrid + + + UI\Backgrounds\Orbs + + + UI\Backgrounds\Orbs + + + UI\Backgrounds\Blobs + + + UI\Backgrounds\Blobs + + + UI\Backgrounds + + + UI\Controls + + + UI\Controls + + + UI\Controls + + + UI\Controls + + + UI\Pages + + + + UI\Modals + + + UI\Pages\AppPage + + + UI\Pages\AppPage + + + UI\Pages\AppPage + + + UI\Pages\AppPage + + + UI\Modals + + + UI\Modals + + + UI\Modals + + + UI\Modals + + + UI\Modals + + + UI\Modals + + + UI\Modals + + + UI\Pages + + + UI\Pages + + + UI\Pages + + + UI\Pages + + + UI\Controls + + + UI\Styles + + + UI\Styles + + + + Source Files - - Source Files + + Common - - Source Files + + UI\Controls - - Source Files + + UI\Controls - - Source Files + + UI\Controls - - Source Files + + UI\Controls - - Source Files + + UI\Converters - - Source Files + + UI\Converters - - Source Files - - - Source Files - - - Source Files - - - Source Files + + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard - Source Files + Keyboard + Keyboard + + Source Files - + Source Files + + UI\Pages + + + UI\Backgrounds\Particles + + + UI\Backgrounds\Particles + + + UI\Backgrounds\Spheres + + + UI\Backgrounds\Spheres + + + UI\Backgrounds\Streaks + + + UI\Backgrounds\Streaks + + + UI\Backgrounds\SwipeReveal + + + UI\Backgrounds\GlobeGrid + + + UI\Backgrounds\Orbs + + + UI\Backgrounds\Orbs + + + UI\Backgrounds\Blobs + + + UI\Backgrounds\Blobs + + + UI\Backgrounds + + + UI\Modals + + + UI\Modals + + + UI\Modals + + + UI\Modals + + + UI\Modals + + + UI\Modals + + + UI\Modals + + + Plot + - Source Files + State + + + State + State + + + State + + + Common + + + UI\Utilities + + + UI\Utilities + + + UI\Utilities + + + UI\Utilities + + + Streaming + + + Streaming + + + Streaming + + + Streaming + + + Streaming + + + State + + + UI\Pages + + + UI\Pages + + + State + + + State + + + UI\Pages\AppPage + + + UI\Modals + + + UI\Pages\AppPage + + + UI\Controls + + + UI\Models + + + UI\Pages + + + UI\Pages + + Source Files - - ImGui + + Streaming - - ImGui + + State - - ImGui + + State - - ImGui + + Streaming + + + Streaming + + + Streaming - ImGui + third_party\imgui-uwp\backends - - ImGui + + third_party\imgui\backends - ImGui + third_party\imgui - - Source Files + + third_party\imgui - - Source Files + + third_party\imgui - - Source Files + + third_party\imgui - - Source Files + + third_party\imgui - + Source Files - - Source Files + + Utils + + + UI\Models\ViewModels - + Header Files - + + UI\Utilities + + + UI\Utilities + + + UI\Utilities + + + Common + + + Common + + + UI\Controls + + + UI\Controls + + + UI\Controls + + + UI\Controls + + + UI\Controls + + + UI\Models + + + UI\Converters + + + UI\Converters + + + UI\Pages + + Header Files + + Keyboard + + + UI\Modals + + + UI\Modals + + + UI\Modals + + + UI\Modals + + + UI\Modals + + + UI\Modals + + + UI\Modals + + + Plot + + + Plot + + + State + + + State + + + State + + + State + + + State + + + State + - Header Files + Common - Header Files + Common + + + UI\Utilities - Header Files + Common + + + Streaming + + + Streaming + + + Streaming + + + Streaming + + + Streaming - Header Files + Streaming - Header Files + Streaming - Header Files + Streaming - Header Files + State + + + UI\Pages + + + UI\Pages - Header Files + State - Header Files + State + + + UI\Pages\AppPage + + + UI\Modals + + + UI\Pages + + + UI\Pages Header Files - Header Files + Streaming - Header Files + State - Header Files + State + + + UI\Models - Header Files + Streaming - Header Files + Streaming - Header Files + Streaming Header Files - - Header Files + + UI\Backgrounds - - Header Files + + UI\Backgrounds - - Header Files + + UI\Backgrounds\Particles - - Header Files + + UI\Backgrounds\Particles - - Header Files + + UI\Backgrounds\Spheres - - Header Files + + UI\Backgrounds\Spheres - - Header Files + + UI\Backgrounds\Streaks - - Header Files + + UI\Backgrounds\Streaks - - Header Files + + UI\Backgrounds\SwipeReveal - - Header Files + + UI\Backgrounds\GlobeGrid - - Header Files + + UI\Backgrounds\Orbs - - Header Files + + UI\Backgrounds\Orbs - - Header Files + + UI\Backgrounds\Blobs - - Header Files + + UI\Backgrounds\Blobs + + + UI\Backgrounds + + + Utils + + + UI\Models\ViewModels + + Assets + Assets @@ -502,9 +978,39 @@ Assets + + Assets + Assets + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + Assets @@ -641,14 +1147,6 @@ Assets - - - - - - DLLs - - Assets @@ -656,6 +1154,12 @@ Assets + + Assets\Font + + + Assets\Font + Assets @@ -665,33 +1169,37 @@ Assets - - - - Assets Assets - - Assets + + Assets\Shader - - Assets + + Assets\Shader - - + + + + - - - - - - - - + + + + + Streaming + + + Streaming + + + + + vcpkg_installed\x64-uwp\bin + diff --git a/pch.h b/pch.h index fb755620..416212a0 100644 --- a/pch.h +++ b/pch.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,8 @@ #include #include #include "App.xaml.h" +#include "UI\Converters\BoolToVisibilityConverter.h" +#include "UI\Converters\BoolToTextConverter.h" #define IMGUI_USER_CONFIG "Common\imconfig.moonlight.h" #include @@ -106,7 +109,7 @@ static inline int64_t MsToQpc(double ms) { do { \ static std::once_flag CONCAT(_onceFlag_, __LINE__); \ std::call_once(CONCAT(_onceFlag_, __LINE__), [&] { \ - Utils::Logf(fmt, ##__VA_ARGS__); \ + Utils::Logf(Utils::LogLevel::Debug, fmt, ##__VA_ARGS__); \ }); \ } while (0) @@ -118,14 +121,14 @@ static inline int64_t MsToQpc(double ms) { #ifdef FRAME_QUEUE_VERBOSE #define FQLog(fmt, ...) \ - moonlight_xbox_dx::Utils::Logf("[%lu] " fmt, ::GetCurrentThreadId(), ##__VA_ARGS__) + moonlight_xbox_dx::Utils::Logf(moonlight_xbox_dx::Utils::LogLevel::Verbose, "[%lu] " fmt, ::GetCurrentThreadId(), ##__VA_ARGS__) #else # ifdef FRAME_QUEUE_VERBOSE_LIMITED #include static std::atomic g_fqlog_counter{0}; #define FQLog(fmt, ...) \ if (++g_fqlog_counter > 200 && g_fqlog_counter < 1000) \ - moonlight_xbox_dx::Utils::Logf("[%lu] " fmt, ::GetCurrentThreadId(), ##__VA_ARGS__) + moonlight_xbox_dx::Utils::Logf(moonlight_xbox_dx::Utils::LogLevel::Verbose, "[%lu] " fmt, ::GetCurrentThreadId(), ##__VA_ARGS__) # else #if defined(_MSC_VER) #define FQLog(...) __noop From ba62432a882a57bb1048d24afcd4b3ad5408f154 Mon Sep 17 00:00:00 2001 From: mpotrykus <42190680+mpotrykus@users.noreply.github.com> Date: Wed, 29 Jul 2026 13:36:59 -0500 Subject: [PATCH 2/2] fix: respect enableHDR setting, add audio encryption flag, report LiStartConnection failures, remove dead EffectsLibrary blur/glow code --- State/ApplicationState.cpp | 6 ++++- State/MoonlightClient.cpp | 16 ++++++++++-- State/MoonlightClient.h | 3 ++- Streaming/YUVRGBPixelShader.hlsl | 43 ------------------------------- Streaming/YUVRGBVertexShader.hlsl | 19 -------------- UI/Utilities/EffectsLibrary.cpp | 5 ---- UI/Utilities/EffectsLibrary.h | 8 ------ moonlight-xbox-dx.vcxproj | 8 ------ moonlight-xbox-dx.vcxproj.filters | 8 ------ 9 files changed, 21 insertions(+), 95 deletions(-) delete mode 100644 Streaming/YUVRGBPixelShader.hlsl delete mode 100644 Streaming/YUVRGBVertexShader.hlsl diff --git a/State/ApplicationState.cpp b/State/ApplicationState.cpp index 3fdf809f..4fa9a09a 100644 --- a/State/ApplicationState.cpp +++ b/State/ApplicationState.cpp @@ -241,7 +241,11 @@ void moonlight_xbox_dx::ApplicationState::RemoveHost(MoonlightHost^ host) { if (host->Connected) { host->Unpair(); } - } catch (...) {} + } catch (Platform::Exception^ ex) { + MLOGF(Utils::LogLevel::Warning, "RemoveHost: failed to unpair before removal: %ws\n", ex->Message->Data()); + } catch (...) { + MLOG(Utils::LogLevel::Warning, "RemoveHost: failed to unpair before removal (unknown exception)\n"); + } }); } diff --git a/State/MoonlightClient.cpp b/State/MoonlightClient.cpp index 0b56321c..71209d19 100644 --- a/State/MoonlightClient.cpp +++ b/State/MoonlightClient.cpp @@ -258,12 +258,14 @@ int MoonlightClient::StartStreaming(std::shared_ptr res, St } config.colorRange = this->IsRGBFull() ? COLOR_RANGE_FULL : COLOR_RANGE_LIMITED; config.colorSpace = COLORSPACE_REC_601; - config.encryptionFlags = 0; + config.encryptionFlags = ENCFLG_AUDIO; config.packetSize = 1024; config.supportedVideoFormats = VIDEO_FORMAT_H264; if (!IsXboxOneVCR()) { config.supportedVideoFormats |= VIDEO_FORMAT_H265; - config.supportedVideoFormats |= VIDEO_FORMAT_H265_MAIN10; + if (sConfig->enableHDR) { + config.supportedVideoFormats |= VIDEO_FORMAT_H265_MAIN10; + } } config.audioConfiguration = AUDIO_CONFIGURATION_STEREO; @@ -316,9 +318,14 @@ int MoonlightClient::StartStreaming(std::shared_ptr res, St DECODER_RENDERER_CALLBACKS rCallbacks = FFMpegDecoder::getDecoder(); AUDIO_RENDERER_CALLBACKS aCallbacks = AudioPlayer::getDecoder(); + m_stageFailureReported.store(false); int k = LiStartConnection(&serverData.serverInfo, &config, &callbacks, &rCallbacks, &aCallbacks, NULL, 0, NULL, 0); sprintf(message, "LiStartConnection %d\n", k); MLOG(Utils::LogLevel::Debug, message); + + if (k != 0 && !m_stageFailureReported.load() && this->OnFailed != nullptr) { + this->OnFailed(0, k, "Connection failed"); + } return k; } @@ -381,6 +388,7 @@ void stage_failed(int stage, int err) { LiStringifyPortFlags(portFlags, ", ", failingPorts, sizeof(failingPorts)); sprintf(message, "%s failed with error %d.\n Check Firewall and Connections to port: %s\n", LiGetStageName(stage), err, failingPorts); MLOG(Utils::LogLevel::Error, message); + connectedInstance->SetStageFailureReported(); if (connectedInstance->OnFailed != nullptr) { connectedInstance->OnFailed(stage, err, message); } @@ -457,6 +465,10 @@ void MoonlightClient::SetConnectionTerminated() { m_connectionTerminated.store(true); } +void MoonlightClient::SetStageFailureReported() { + m_stageFailureReported.store(true); +} + bool MoonlightClient::IsPaired() { return serverData.paired; } diff --git a/State/MoonlightClient.h b/State/MoonlightClient.h index 013ac415..1926cee2 100644 --- a/State/MoonlightClient.h +++ b/State/MoonlightClient.h @@ -28,6 +28,7 @@ namespace moonlight_xbox_dx { bool IsRGBFull(); bool IsConnectionTerminated(); void SetConnectionTerminated(); + void SetStageFailureReported(); int Pair(); char *GeneratePIN(); std::vector GetApplications(bool fetchAssets = true); @@ -54,7 +55,6 @@ namespace moonlight_xbox_dx { std::function OnCompleted; std::function SetHDR; std::function OnFailed; - std::function OnStartFailed; std::function OnRumble; std::function OnTriggerRumble; private: @@ -68,6 +68,7 @@ namespace moonlight_xbox_dx { bool m_isHDR; bool m_isRGBFull; std::atomic m_connectionTerminated{ false }; + std::atomic m_stageFailureReported{ false }; Windows::Gaming::Input::GamepadReading m_lastGamepadReading[16]; }; } diff --git a/Streaming/YUVRGBPixelShader.hlsl b/Streaming/YUVRGBPixelShader.hlsl deleted file mode 100644 index cfbcdfcf..00000000 --- a/Streaming/YUVRGBPixelShader.hlsl +++ /dev/null @@ -1,43 +0,0 @@ -Texture2D shaderTexture; -SamplerState SampleType; -struct PixelShaderInput -{ - float4 pos : SV_POSITION; - float2 texCoord : TEXCOORD0; -}; - -Texture2D luminanceChannel : t0; -Texture2D chrominanceChannel : t1; -SamplerState defaultSampler : s0; - -// Derived from https://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx -// Section: Converting 8-bit YUV to RGB888 -static const float3x3 YUVtoRGBCoeffMatrix = -{ - 1.164383f, 1.164383f, 1.164383f, - 0.000000f, -0.391762f, 2.017232f, - 1.596027f, -0.812968f, 0.000000f -}; - -float3 ConvertYUVtoRGB(float3 yuv) -{ - // Derived from https://msdn.microsoft.com/en-us/library/windows/desktop/dd206750(v=vs.85).aspx - // Section: Converting 8-bit YUV to RGB888 - - // These values are calculated from (16 / 255) and (128 / 255) - yuv -= float3(0.062745f, 0.501960f, 0.501960f); - yuv = mul(yuv, YUVtoRGBCoeffMatrix); - - return saturate(yuv); -} - -// A pass-through function for the (interpolated) color data. -float4 main(PixelShaderInput input) : SV_TARGET -{ - float y = luminanceChannel.Sample(defaultSampler, input.texCoord); - float2 uv = chrominanceChannel.Sample(defaultSampler, input.texCoord); - /*float r = uv.r; - uv.r = uv.g; - uv.g = r;*/ - return float4(ConvertYUVtoRGB(float3(y, uv)), 1.f); -} diff --git a/Streaming/YUVRGBVertexShader.hlsl b/Streaming/YUVRGBVertexShader.hlsl deleted file mode 100644 index bbcfc59d..00000000 --- a/Streaming/YUVRGBVertexShader.hlsl +++ /dev/null @@ -1,19 +0,0 @@ -// Per-vertex data used as input to the vertex shader. -struct VertexShaderInput -{ - float4 Pos : POSITION; - float2 Tex : TEXCOORD; -}; - -// Per-pixel color data passed through the pixel shader. -struct PixelShaderInput -{ - float4 Pos : SV_POSITION; - float2 Tex : TEXCOORD; -}; - -// Simple shader to do vertex processing on the GPU. -PixelShaderInput main(VertexShaderInput input) -{ - return input; -} diff --git a/UI/Utilities/EffectsLibrary.cpp b/UI/Utilities/EffectsLibrary.cpp index 69eda4ed..80186492 100644 --- a/UI/Utilities/EffectsLibrary.cpp +++ b/UI/Utilities/EffectsLibrary.cpp @@ -621,8 +621,3 @@ SoftwareBitmap^ EffectsLibrary::GpuBoxBlurSoftwareBitmap(SoftwareBitmap^ bitmap, } } -ID3D11ShaderResourceView* EffectsLibrary::Blur(ID3D11ShaderResourceView* src, float sigma) -{ - - return src; -} diff --git a/UI/Utilities/EffectsLibrary.h b/UI/Utilities/EffectsLibrary.h index 509f579c..9e3ad09f 100644 --- a/UI/Utilities/EffectsLibrary.h +++ b/UI/Utilities/EffectsLibrary.h @@ -13,18 +13,10 @@ class EffectsLibrary { static void Initialize(ID3D11Device* device, ID3D11DeviceContext* context); - static ID3D11ShaderResourceView* Blur(ID3D11ShaderResourceView* src, float sigma); - static bool BoxBlurSoftwareBitmap(Windows::Graphics::Imaging::SoftwareBitmap^ bitmap, int radius); static Windows::Graphics::Imaging::SoftwareBitmap^ GpuBoxBlurSoftwareBitmap(Windows::Graphics::Imaging::SoftwareBitmap^ bitmap, int radius, bool returnPadded = false); - static ID3D11ShaderResourceView* Glow(ID3D11ShaderResourceView* src, float radius) { - - return Blur(src, radius); - - } - static ID3D11Device* GetDevice() { return m_device; } static ID3D11DeviceContext* GetContext() { return m_context; } diff --git a/moonlight-xbox-dx.vcxproj b/moonlight-xbox-dx.vcxproj index 57a7a799..6286b5eb 100644 --- a/moonlight-xbox-dx.vcxproj +++ b/moonlight-xbox-dx.vcxproj @@ -747,14 +747,6 @@ Designer - - - Pixel - - - Vertex - - Designer diff --git a/moonlight-xbox-dx.vcxproj.filters b/moonlight-xbox-dx.vcxproj.filters index 49768934..6fa027d8 100644 --- a/moonlight-xbox-dx.vcxproj.filters +++ b/moonlight-xbox-dx.vcxproj.filters @@ -1189,14 +1189,6 @@ - - - Streaming - - - Streaming - - vcpkg_installed\x64-uwp\bin