From d527d9576d1564fecf294d58a87c7e16eec319db Mon Sep 17 00:00:00 2001 From: Laszlo Nemeth <57342539+donlaci@users.noreply.github.com> Date: Mon, 30 Sep 2024 15:37:21 +0200 Subject: [PATCH 01/95] [Workspaces]Fix snapshot tool: update capturing windows settings app (#35140) * [Workspaces] fix snapshot tool: update capturing windows settings app * spell checker * spell checker * minor change in app utils, when the system settings app is captured --- .github/actions/spell-check/expect.txt | 2 +- src/modules/Workspaces/WorkspacesLib/AppUtils.cpp | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 1fca6be137..47662c3766 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1590,7 +1590,7 @@ SYSKEYUP SYSLIB SYSMENU SYSTEMAPPS -systemsettings +SYSTEMSETTINGS SYSTEMTIME SYSTEMWOW tapp diff --git a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp index 7c9bfe332d..389e54eae5 100644 --- a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp +++ b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp @@ -26,6 +26,7 @@ namespace Utils constexpr const wchar_t* FileExplorerName = L"File Explorer"; constexpr const wchar_t* FileExplorerPath = L"C:\\WINDOWS\\EXPLORER.EXE"; + constexpr const wchar_t* SystemSettingsPath = L"SYSTEMSETTINGS.EXE"; constexpr const wchar_t* PowerToys = L"PowerToys.exe"; constexpr const wchar_t* PowerToysSettingsUpper = L"POWERTOYS.SETTINGS.EXE"; constexpr const wchar_t* PowerToysSettings = L"PowerToys.Settings.exe"; @@ -257,6 +258,14 @@ namespace Utils if (appPathUpper.contains(installPathUpper)) { + // check if the found app is the System Settings. If yes, update the install path to the exe path + if (appPathUpper.ends_with(NonLocalizable::SystemSettingsPath)) + { + auto settingsAppData = appData; + settingsAppData.installPath = appPath; + return settingsAppData; + } + return appData; } From 9b41786c57adde72f5f107a0d6c9bc5f7cacfc81 Mon Sep 17 00:00:00 2001 From: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Date: Mon, 30 Sep 2024 16:00:50 +0200 Subject: [PATCH 02/95] [Deps]Revert CommunityToolkit update to fix ARM settings app crash (#35139) --- Directory.Packages.props | 16 ++++++++-------- NOTICE.md | 16 ++++++++-------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 9ec1d03bd9..0967532dc3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -6,14 +6,14 @@ - - - - - - - - + + + + + + + + diff --git a/NOTICE.md b/NOTICE.md index ca957e45ec..d4328bbfc4 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1299,14 +1299,14 @@ EXHIBIT A -Mozilla Public License. - Appium.WebDriver 4.4.5 - Azure.AI.OpenAI 1.0.0-beta.12 - CommunityToolkit.Mvvm 8.2.2 -- CommunityToolkit.WinUI.Animations 8.1.240821 -- CommunityToolkit.WinUI.Collections 8.1.240821 -- CommunityToolkit.WinUI.Controls.Primitives 8.1.240821 -- CommunityToolkit.WinUI.Controls.Segmented 8.1.240821 -- CommunityToolkit.WinUI.Controls.SettingsControls 8.1.240821 -- CommunityToolkit.WinUI.Controls.Sizers 8.1.240821 -- CommunityToolkit.WinUI.Converters 8.1.240821 -- CommunityToolkit.WinUI.Extensions 8.1.240821 +- CommunityToolkit.WinUI.Animations 8.0.240109 +- CommunityToolkit.WinUI.Collections 8.0.240109 +- CommunityToolkit.WinUI.Controls.Primitives 8.0.240109 +- CommunityToolkit.WinUI.Controls.Segmented 8.0.240109 +- CommunityToolkit.WinUI.Controls.SettingsControls 8.0.240109 +- CommunityToolkit.WinUI.Controls.Sizers 8.0.240109 +- CommunityToolkit.WinUI.Converters 8.0.240109 +- CommunityToolkit.WinUI.Extensions 8.0.240109 - CommunityToolkit.WinUI.UI.Controls.DataGrid 7.1.2 - CommunityToolkit.WinUI.UI.Controls.Markdown 7.1.2 - ControlzEx 6.0.0 From 77de44fdb4d73c53d9eb6897759fdf4d841072f8 Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Mon, 30 Sep 2024 16:54:07 +0100 Subject: [PATCH 03/95] [NewPlus]Normalize settings infrastructure (#35145) --- .../powertoys_module.cpp | 21 +++++-- .../NewShellExtensionContextMenu/settings.cpp | 62 ++++++++----------- .../Settings.UI.Library/NewPlusProperties.cs | 34 ++++++++++ .../Settings.UI.Library/NewPlusSettings.cs | 29 ++++----- .../ViewModels/DashboardViewModel.cs | 2 +- .../ViewModels/NewPlusViewModel.cs | 27 +++----- 6 files changed, 94 insertions(+), 81 deletions(-) create mode 100644 src/settings-ui/Settings.UI.Library/NewPlusProperties.cs diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/powertoys_module.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu/powertoys_module.cpp index 2962f236fc..2e6dccfdee 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/powertoys_module.cpp +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/powertoys_module.cpp @@ -51,21 +51,30 @@ public: return true; } - virtual void set_config(PCWSTR config) override + virtual void set_config(const wchar_t* config) override { // The following just checks to see if the Template Location was changed for metrics purposes // Note: We are not saving the settings here and instead relying on read/write of json in Settings App .cs code paths try { - json::JsonObject config_as_json = json::JsonValue::Parse(winrt::to_hstring(config)).GetObjectW(); + // Parse the input JSON string. + PowerToysSettings::PowerToyValues values = + PowerToysSettings::PowerToyValues::from_json_string(config, get_key()); - const auto latest_location_value = config_as_json.GetNamedString(newplus::constants::non_localizable::settings_json_key_template_location).data(); - const auto existing_location_value = NewSettingsInstance().GetTemplateLocation(); + values.save_to_settings_file(); + NewSettingsInstance().Load(); - if (!newplus::utilities::wstring_same_when_comparing_ignore_case(latest_location_value, existing_location_value)) + auto templateValue = values.get_string_value(newplus::constants::non_localizable::settings_json_key_template_location); + if (templateValue.has_value()) { - Trace::EventChangedTemplateLocation(); + const auto latest_location_value = templateValue.value(); + const auto existing_location_value = NewSettingsInstance().GetTemplateLocation(); + if (!newplus::utilities::wstring_same_when_comparing_ignore_case(latest_location_value, existing_location_value)) + { + Trace::EventChangedTemplateLocation(); + } } + } catch (std::exception& e) { diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/settings.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu/settings.cpp index f1322caf1f..7dd5042c4d 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/settings.cpp +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/settings.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include "settings.h" #include "constants.h" @@ -38,18 +39,13 @@ NewSettings::NewSettings() void NewSettings::Save() { - json::JsonObject new_settings_json_data; + PowerToysSettings::PowerToyValues values(newplus::constants::non_localizable::powertoy_key, newplus::constants::non_localizable::powertoy_key); - new_settings_json_data.SetNamedValue(newplus::constants::non_localizable::settings_json_key_hide_file_extension, - json::value(new_settings.hide_file_extension)); + values.add_property(newplus::constants::non_localizable::settings_json_key_hide_file_extension, new_settings.hide_file_extension); + values.add_property(newplus::constants::non_localizable::settings_json_key_hide_starting_digits, new_settings.hide_starting_digits); + values.add_property(newplus::constants::non_localizable::settings_json_key_template_location, new_settings.template_location); - new_settings_json_data.SetNamedValue(newplus::constants::non_localizable::settings_json_key_hide_starting_digits, - json::value(new_settings.hide_starting_digits)); - - new_settings_json_data.SetNamedValue(newplus::constants::non_localizable::settings_json_key_template_location, - json::value(new_settings.template_location)); - - json::to_file(new_settings_json_file_path, new_settings_json_data); + values.save_to_settings_file(); GetSystemTimeAsFileTime(&new_settings_last_loaded_timestamp); } @@ -122,35 +118,27 @@ void NewSettings::Reload() void NewSettings::ParseJson() { - auto json = json::from_file(new_settings_json_file_path); - if (json) + PowerToysSettings::PowerToyValues settings = + PowerToysSettings::PowerToyValues::load_from_settings_file(newplus::constants::non_localizable::powertoy_key); + + auto templateValue = settings.get_string_value(newplus::constants::non_localizable::settings_json_key_template_location); + if (templateValue.has_value()) { - try - { - const json::JsonObject& new_settings_json = json.value(); - - if (json::has(new_settings_json, newplus::constants::non_localizable::settings_json_key_hide_file_extension, json::JsonValueType::Boolean)) - { - new_settings.hide_file_extension = new_settings_json.GetNamedBoolean( - newplus::constants::non_localizable::settings_json_key_hide_file_extension); - } - - if (json::has(new_settings_json, newplus::constants::non_localizable::settings_json_key_hide_starting_digits, json::JsonValueType::Boolean)) - { - new_settings.hide_starting_digits = new_settings_json.GetNamedBoolean( - newplus::constants::non_localizable::settings_json_key_hide_starting_digits); - } - - if (json::has(new_settings_json, newplus::constants::non_localizable::settings_json_key_template_location, json::JsonValueType::String)) - { - new_settings.template_location = new_settings_json.GetNamedString( - newplus::constants::non_localizable::settings_json_key_template_location); - } - } - catch (const winrt::hresult_error&) - { - } + new_settings.template_location = templateValue.value(); } + + auto hideFileExtensionValue = settings.get_bool_value(newplus::constants::non_localizable::settings_json_key_hide_file_extension); + if (hideFileExtensionValue.has_value()) + { + new_settings.hide_file_extension = hideFileExtensionValue.value(); + } + + auto hideStartingDigitsValue = settings.get_bool_value(newplus::constants::non_localizable::settings_json_key_hide_starting_digits); + if (hideStartingDigitsValue.has_value()) + { + new_settings.hide_starting_digits = hideStartingDigitsValue.value(); + } + GetSystemTimeAsFileTime(&new_settings_last_loaded_timestamp); } diff --git a/src/settings-ui/Settings.UI.Library/NewPlusProperties.cs b/src/settings-ui/Settings.UI.Library/NewPlusProperties.cs new file mode 100644 index 0000000000..2a4970c690 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/NewPlusProperties.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class NewPlusProperties + { + public const string ModuleName = "NewPlus"; + + public NewPlusProperties() + { + HideFileExtension = new BoolProperty(true); + HideStartingDigits = new BoolProperty(true); + TemplateLocation = new StringProperty(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "PowerToys", "NewPlus", "Templates")); + } + + [JsonPropertyName("HideFileExtension")] + public BoolProperty HideFileExtension { get; set; } + + [JsonPropertyName("HideStartingDigits")] + public BoolProperty HideStartingDigits { get; set; } + + [JsonPropertyName("TemplateLocation")] + public StringProperty TemplateLocation { get; set; } + + public override string ToString() => JsonSerializer.Serialize(this); + } +} diff --git a/src/settings-ui/Settings.UI.Library/NewPlusSettings.cs b/src/settings-ui/Settings.UI.Library/NewPlusSettings.cs index 6010185f8c..2450898684 100644 --- a/src/settings-ui/Settings.UI.Library/NewPlusSettings.cs +++ b/src/settings-ui/Settings.UI.Library/NewPlusSettings.cs @@ -12,34 +12,27 @@ using Settings.UI.Library.Resources; namespace Microsoft.PowerToys.Settings.UI.Library { - public class NewPlusSettings : ISettingsConfig + public class NewPlusSettings : BasePTModuleSettings, ISettingsConfig { public const string ModuleName = "NewPlus"; + public const string ModuleVersion = "1.0"; - public void InitializeWithDefaultSettings() + [JsonPropertyName("properties")] + public NewPlusProperties Properties { get; set; } + + public NewPlusSettings() { - // This code path should never happen + Name = ModuleName; + Version = ModuleVersion; + Properties = new NewPlusProperties(); } - public string ToJsonString() - { - return JsonSerializer.Serialize(this); - } - - [JsonPropertyName("HideFileExtension")] - public bool HideFileExtension { get; set; } - - [JsonPropertyName("HideStartingDigits")] - public bool HideStartingDigits { get; set; } - - [JsonPropertyName("TemplateLocation")] - public string TemplateLocation { get; set; } - public string GetModuleName() { - return ModuleName; + return Name; } + // This can be utilized in the future if the settings.json file is to be modified/deleted. public bool UpgradeSettingsConfiguration() { return false; diff --git a/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs index c72a52c7f7..7884a0994f 100644 --- a/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs @@ -130,7 +130,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels { var settingsUtils = new SettingsUtils(); var settings = NewPlusViewModel.LoadSettings(settingsUtils); - NewPlusViewModel.CopyTemplateExamples(settings.TemplateLocation); + NewPlusViewModel.CopyTemplateExamples(settings.Properties.TemplateLocation.Value); } } diff --git a/src/settings-ui/Settings.UI/ViewModels/NewPlusViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/NewPlusViewModel.cs index b1cceb8afb..cab1a9df77 100644 --- a/src/settings-ui/Settings.UI/ViewModels/NewPlusViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/NewPlusViewModel.cs @@ -47,9 +47,9 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels Settings = LoadSettings(settingsUtils); // Initialize properties - _hideFileExtension = Settings.HideFileExtension; - _hideStartingDigits = Settings.HideStartingDigits; - _templateLocation = Settings.TemplateLocation; + _hideFileExtension = Settings.Properties.HideFileExtension.Value; + _hideStartingDigits = Settings.Properties.HideStartingDigits.Value; + _templateLocation = Settings.Properties.TemplateLocation.Value; InitializeEnabledValue(); InitializeGpoValues(); @@ -119,12 +119,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels if (_templateLocation != value) { _templateLocation = value; - Settings.TemplateLocation = value; + Settings.Properties.TemplateLocation.Value = value; OnPropertyChanged(nameof(TemplateLocation)); NotifySettingsChanged(); - - SaveSettingsToJson(); } } } @@ -146,12 +144,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels if (_hideFileExtension != value && !_hideFileExtensionIsGPOConfigured) { _hideFileExtension = value; - Settings.HideFileExtension = value; + Settings.Properties.HideFileExtension.Value = value; OnPropertyChanged(nameof(HideFileExtension)); NotifySettingsChanged(); - - SaveSettingsToJson(); } } } @@ -168,12 +164,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels if (_hideStartingDigits != value) { _hideStartingDigits = value; - Settings.HideStartingDigits = value; + Settings.Properties.HideStartingDigits.Value = value; OnPropertyChanged(nameof(HideStartingDigits)); NotifySettingsChanged(); - - SaveSettingsToJson(); } } } @@ -208,10 +202,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels { settings = settingsUtils.GetSettingsOrDefault(NewPlusSettings.ModuleName); - if (string.IsNullOrEmpty(settings.TemplateLocation)) + if (string.IsNullOrEmpty(settings.Properties.TemplateLocation.Value)) { // This can happen when running the DEBUG Settings application without first letting the runner create the default settings file. - settings.TemplateLocation = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "PowerToys", "NewPlus", "Templates"); + settings.Properties.TemplateLocation.Value = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "PowerToys", "NewPlus", "Templates"); } } catch (Exception e) @@ -278,10 +272,5 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.GetSettingsWindow()); return await Task.FromResult(GetFolderDialogWithFlags(hwnd, FolderDialogFlags._BIF_NEWDIALOGSTYLE)); } - - private void SaveSettingsToJson() - { - _settingsUtils.SaveSettings(Settings.ToJsonString(), ModuleName); - } } } From 28b90d5f4917c65183e7b65648254ef18bc6cc46 Mon Sep 17 00:00:00 2001 From: Seraphima Zykova Date: Mon, 30 Sep 2024 19:23:21 +0300 Subject: [PATCH 04/95] [Workspaces]Fix application icons on ARM (#35146) fixed app path --- src/modules/Workspaces/WorkspacesLib/AppUtils.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp index 389e54eae5..2d69d6f86f 100644 --- a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp +++ b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp @@ -26,11 +26,11 @@ namespace Utils constexpr const wchar_t* FileExplorerName = L"File Explorer"; constexpr const wchar_t* FileExplorerPath = L"C:\\WINDOWS\\EXPLORER.EXE"; - constexpr const wchar_t* SystemSettingsPath = L"SYSTEMSETTINGS.EXE"; constexpr const wchar_t* PowerToys = L"PowerToys.exe"; constexpr const wchar_t* PowerToysSettingsUpper = L"POWERTOYS.SETTINGS.EXE"; constexpr const wchar_t* PowerToysSettings = L"PowerToys.Settings.exe"; constexpr const wchar_t* ApplicationFrameHost = L"APPLICATIONFRAMEHOST.EXE"; + constexpr const wchar_t* Exe = L".EXE"; } AppList IterateAppsFolder() @@ -258,8 +258,8 @@ namespace Utils if (appPathUpper.contains(installPathUpper)) { - // check if the found app is the System Settings. If yes, update the install path to the exe path - if (appPathUpper.ends_with(NonLocalizable::SystemSettingsPath)) + // Update the install path to keep .exe in the path + if (!installPathUpper.ends_with(NonLocalizable::Exe)) { auto settingsAppData = appData; settingsAppData.installPath = appPath; From 6c17fae14855ec5f18f7fbe0253b5d3e7307b477 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Mon, 30 Sep 2024 11:04:49 -0700 Subject: [PATCH 05/95] Removed build status from readme (#35113) Removed build status --- README.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.md b/README.md index 7bdc0becf0..c178872a61 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,6 @@ [How to use PowerToys][usingPowerToys-docs-link] | [Downloads & Release notes][github-release-link] | [Contributing to PowerToys](#contributing) | [What's Happening](#whats-happening) | [Roadmap](#powertoys-roadmap) -## Build status - -| Architecture | Solution (Main) | Solution (Stable) | Installer (Main) | -|--------------|-----------------|-------------------|------------------| -| x64 | [![Build Status for Main](https://dev.azure.com/shine-oss/PowerToys/_apis/build/status%2FPowerToys%20CI?branchName=main&jobName=Build%20x64%20Release)](https://dev.azure.com/shine-oss/PowerToys/_build/latest?definitionId=3&branchName=main) | [![Build Status for Stable](https://dev.azure.com/shine-oss/PowerToys/_apis/build/status%2FPowerToys%20CI?branchName=stable&jobName=Build%20x64%20Release)](https://dev.azure.com/shine-oss/PowerToys/_build/latest?definitionId=3&branchName=stable) | [![Build Status Installer pipeline](https://dev.azure.com/microsoft/Dart/_apis/build/status/PowerToys/PowerToys%20Signed%20YAML%20Release%20Build?branchName=main&jobName=Build&configuration=Build%20Release_x64)](https://dev.azure.com/microsoft/Dart/_build/latest?definitionId=76541&branchName=main) | -| ARM64 | [![Build Status for Main](https://dev.azure.com/shine-oss/PowerToys/_apis/build/status%2FPowerToys%20CI?branchName=main&jobName=Build%20arm64%20Release)](https://dev.azure.com/shine-oss/PowerToys/_build/latest?definitionId=3&branchName=main) | [![Build Status for Stable](https://dev.azure.com/shine-oss/PowerToys/_apis/build/status%2FPowerToys%20CI?branchName=main&jobName=Build%20arm64%20Release)](https://dev.azure.com/shine-oss/PowerToys/_build/latest?definitionId=3&branchName=main) | [![Build Status Installer pipeline](https://dev.azure.com/microsoft/Dart/_apis/build/status/PowerToys/PowerToys%20Signed%20YAML%20Release%20Build?branchName=main&jobName=Build&configuration=Build%20Release_arm64)](https://dev.azure.com/microsoft/Dart/_build/latest?definitionId=76541&branchName=main) | - ## About Microsoft PowerToys is a set of utilities for power users to tune and streamline their Windows experience for greater productivity. For more info on [PowerToys overviews and how to use the utilities][usingPowerToys-docs-link], or any other tools and resources for [Windows development environments](https://learn.microsoft.com/windows/dev-environment/overview), head over to [learn.microsoft.com][usingPowerToys-docs-link]! From ccdfbb0f685c42700e7ac0381edd5972cc645892 Mon Sep 17 00:00:00 2001 From: Laszlo Nemeth <57342539+donlaci@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:52:49 +0200 Subject: [PATCH 06/95] [Workspaces]Fix Workspace icon when installed, move to assets (#35163) * [Workspaces] fix defaulticon.ico, move it to the Assets directory * Move assets to single source of truth. Installer logic --------- Co-authored-by: Stefan Markovic --- .../PowerToysSetup/PowerToysInstaller.wixproj | 2 ++ installer/PowerToysSetup/Product.wxs | 1 + installer/PowerToysSetup/Workspaces.wxs | 31 ++++++++++++++++++ .../generateAllFileComponents.ps1 | 4 +++ .../PowerToysSetupCustomActions.vcxproj | 1 + .../Workspaces}/DefaultIcon.ico | Bin .../Assets/{ => Workspaces}/Workspaces.ico | Bin .../WorkspacesEditor/Models/Application.cs | 2 +- .../WorkspacesEditor/WorkspacesEditor.csproj | 15 +++------ .../WorkspacesEditor/images/Workspaces.ico | Bin 467385 -> 0 bytes .../WorkspacesLauncherResource.base.rc | Bin 2754 -> 2776 bytes .../Models/AppLaunching.cs | 2 +- .../WorkspacesLauncherUI.csproj | 18 ++++------ .../images/DefaultIcon.ico | Bin 112730 -> 0 bytes .../images/Workspaces.ico | Bin 467385 -> 0 bytes .../WorkspacesSnapshotToolResources.base.rc | Bin 2754 -> 2776 bytes .../WorkspacesWindowArrangerResource.base.rc | Bin 2754 -> 2776 bytes 17 files changed, 52 insertions(+), 24 deletions(-) create mode 100644 installer/PowerToysSetup/Workspaces.wxs rename src/modules/Workspaces/{WorkspacesEditor/images => Assets/Workspaces}/DefaultIcon.ico (100%) rename src/modules/Workspaces/Assets/{ => Workspaces}/Workspaces.ico (100%) delete mode 100644 src/modules/Workspaces/WorkspacesEditor/images/Workspaces.ico delete mode 100644 src/modules/Workspaces/WorkspacesLauncherUI/images/DefaultIcon.ico delete mode 100644 src/modules/Workspaces/WorkspacesLauncherUI/images/Workspaces.ico diff --git a/installer/PowerToysSetup/PowerToysInstaller.wixproj b/installer/PowerToysSetup/PowerToysInstaller.wixproj index 6d9ca4a6c5..2826a68493 100644 --- a/installer/PowerToysSetup/PowerToysInstaller.wixproj +++ b/installer/PowerToysSetup/PowerToysInstaller.wixproj @@ -54,6 +54,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil call move /Y ..\..\..\VideoConference.wxs.bk ..\..\..\VideoConference.wxs call move /Y ..\..\..\WinAppSDK.wxs.bk ..\..\..\WinAppSDK.wxs call move /Y ..\..\..\WinUI3Applications.wxs.bk ..\..\..\WinUI3Applications.wxs + call move /Y ..\..\..\Workspaces.wxs.bk ..\..\..\Workspaces.wxs @@ -125,6 +126,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil + diff --git a/installer/PowerToysSetup/Product.wxs b/installer/PowerToysSetup/Product.wxs index 3a30002a37..7a3a6ee697 100644 --- a/installer/PowerToysSetup/Product.wxs +++ b/installer/PowerToysSetup/Product.wxs @@ -79,6 +79,7 @@ + diff --git a/installer/PowerToysSetup/Workspaces.wxs b/installer/PowerToysSetup/Workspaces.wxs new file mode 100644 index 0000000000..4237aab945 --- /dev/null +++ b/installer/PowerToysSetup/Workspaces.wxs @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/installer/PowerToysSetup/generateAllFileComponents.ps1 b/installer/PowerToysSetup/generateAllFileComponents.ps1 index 42864c392c..50ce0f2dd6 100644 --- a/installer/PowerToysSetup/generateAllFileComponents.ps1 +++ b/installer/PowerToysSetup/generateAllFileComponents.ps1 @@ -317,3 +317,7 @@ Generate-FileComponents -fileListName "SettingsV2AssetsFiles" -wxsFilePath $PSSc Generate-FileComponents -fileListName "SettingsV2AssetsModulesFiles" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot Generate-FileComponents -fileListName "SettingsV2OOBEAssetsModulesFiles" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot Generate-FileComponents -fileListName "SettingsV2OOBEAssetsFluentIconsFiles" -wxsFilePath $PSScriptRoot\Settings.wxs -regroot $registryroot + +#Workspaces +Generate-FileList -fileDepsJson "" -fileListName WorkspacesImagesComponentFiles -wxsFilePath $PSScriptRoot\Workspaces.wxs -depsPath "$PSScriptRoot..\..\..\$platform\Release\Assets\Workspaces\" +Generate-FileComponents -fileListName "WorkspacesImagesComponentFiles" -wxsFilePath $PSScriptRoot\Workspaces.wxs -regroot $registryroot diff --git a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj index af6a0f184b..959a600706 100644 --- a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj +++ b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj @@ -76,6 +76,7 @@ call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\VideoConference.wxs"" ""$(ProjectDir)..\PowerToysSetup\VideoConference.wxs.bk"""" call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\WinAppSDK.wxs"" ""$(ProjectDir)..\PowerToysSetup\WinAppSDK.wxs.bk"""" call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\WinUI3Applications.wxs"" ""$(ProjectDir)..\PowerToysSetup\WinUI3Applications.wxs.bk"""" + call cmd /C "copy ""$(ProjectDir)..\PowerToysSetup\Workspaces.wxs"" ""$(ProjectDir)..\PowerToysSetup\Workspaces.wxs.bk"""" if not "$(NormalizedPerUserValue)" == "true" call powershell.exe -NonInteractive -executionpolicy Unrestricted -File ..\PowerToysSetup\generateAllFileComponents.ps1 -platform $(Platform) if "$(NormalizedPerUserValue)" == "true" call powershell.exe -NonInteractive -executionpolicy Unrestricted -File ..\PowerToysSetup\generateAllFileComponents.ps1 -platform $(Platform) -installscopeperuser $(NormalizedPerUserValue) diff --git a/src/modules/Workspaces/WorkspacesEditor/images/DefaultIcon.ico b/src/modules/Workspaces/Assets/Workspaces/DefaultIcon.ico similarity index 100% rename from src/modules/Workspaces/WorkspacesEditor/images/DefaultIcon.ico rename to src/modules/Workspaces/Assets/Workspaces/DefaultIcon.ico diff --git a/src/modules/Workspaces/Assets/Workspaces.ico b/src/modules/Workspaces/Assets/Workspaces/Workspaces.ico similarity index 100% rename from src/modules/Workspaces/Assets/Workspaces.ico rename to src/modules/Workspaces/Assets/Workspaces/Workspaces.ico diff --git a/src/modules/Workspaces/WorkspacesEditor/Models/Application.cs b/src/modules/Workspaces/WorkspacesEditor/Models/Application.cs index 9f7be13742..c31087c20c 100644 --- a/src/modules/Workspaces/WorkspacesEditor/Models/Application.cs +++ b/src/modules/Workspaces/WorkspacesEditor/Models/Application.cs @@ -247,7 +247,7 @@ namespace WorkspacesEditor.Models { Logger.LogWarning($"Icon not found on app path: {AppPath}. Using default icon"); IsNotFound = true; - _icon = new Icon(@"images\DefaultIcon.ico"); + _icon = new Icon(@"Assets\Workspaces\DefaultIcon.ico"); } } diff --git a/src/modules/Workspaces/WorkspacesEditor/WorkspacesEditor.csproj b/src/modules/Workspaces/WorkspacesEditor/WorkspacesEditor.csproj index 50ec21ea11..aa359291eb 100644 --- a/src/modules/Workspaces/WorkspacesEditor/WorkspacesEditor.csproj +++ b/src/modules/Workspaces/WorkspacesEditor/WorkspacesEditor.csproj @@ -23,16 +23,12 @@ - images\Workspaces.ico + ..\Assets\Workspaces\Workspaces.ico app.manifest PowerToys.WorkspacesEditor - - - - tlbimp @@ -54,10 +50,10 @@ - - Always + + Assets\Workspaces\%(Filename)%(Extension) + PreserveNewest - @@ -100,7 +96,4 @@ Settings.Designer.cs - - <_DeploymentManifestIconFile Remove="images\Workspaces.ico" /> - \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesEditor/images/Workspaces.ico b/src/modules/Workspaces/WorkspacesEditor/images/Workspaces.ico deleted file mode 100644 index 14292758fa2a98ddd72db0cfa95c2cfa63997e1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 467385 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 delta 11 Scmca1dPsD`7Ou&sxcC4cr39z| diff --git a/src/modules/Workspaces/WorkspacesLauncherUI/Models/AppLaunching.cs b/src/modules/Workspaces/WorkspacesLauncherUI/Models/AppLaunching.cs index bd0f50b467..f3878a3847 100644 --- a/src/modules/Workspaces/WorkspacesLauncherUI/Models/AppLaunching.cs +++ b/src/modules/Workspaces/WorkspacesLauncherUI/Models/AppLaunching.cs @@ -58,7 +58,7 @@ namespace WorkspacesLauncherUI.Models { Logger.LogWarning($"Icon not found on app path: {Application.ApplicationPath}. Using default icon"); IsNotFound = true; - _icon = new Icon(@"images\DefaultIcon.ico"); + _icon = new Icon(@"Assets\Workspaces\DefaultIcon.ico"); } } diff --git a/src/modules/Workspaces/WorkspacesLauncherUI/WorkspacesLauncherUI.csproj b/src/modules/Workspaces/WorkspacesLauncherUI/WorkspacesLauncherUI.csproj index 146716b201..a915e0f0a3 100644 --- a/src/modules/Workspaces/WorkspacesLauncherUI/WorkspacesLauncherUI.csproj +++ b/src/modules/Workspaces/WorkspacesLauncherUI/WorkspacesLauncherUI.csproj @@ -23,16 +23,20 @@ - images\Workspaces.ico + ..\Assets\Workspaces\Workspaces.ico app.manifest PowerToys.WorkspacesLauncherUI + - - + + Assets\Workspaces\%(Filename)%(Extension) + PreserveNewest + + tlbimp @@ -53,14 +57,6 @@ true - - - Always - - - Always - - diff --git a/src/modules/Workspaces/WorkspacesLauncherUI/images/DefaultIcon.ico b/src/modules/Workspaces/WorkspacesLauncherUI/images/DefaultIcon.ico deleted file mode 100644 index 70279d6a4fc391900c32f61e8e124d6c070f6b2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 112730 zcmeFa2UHbHw=TK}5|j)|&OyWk2m(r!B&aAzFaRQ=n2;orVUZUZ$vH^|0Rt+afRY6S zB*(SLl7r+_uNt@i+3&t__dai&t@rLRVEB!yu3p_$^ZVwUHM?01Q7AkV5eoUiLvf&{ zgi)wFu#ZOnIOZq8Lus5qp=4$MI6i|y*`3Bi2@3u=&frC%k{#id9Df{Bp-`&N@K98+ zMecW(5QSQJg@^LKq^3;1muWA&HMz=J1r6jZ0up3uU5gAjr|>Hk1vyQd?&-LO0G_Mi z9bioVgmqwGea`2s+(&lbqMj+k2Laqe&AGdk+}*_gH4Qn zM0%M{=$V@STWKlc(hVJe8CuR;(24<4`4~b}={J zzB-`5L;22U^@RrB*CkLKdABSArT9H>O0wC9p}wld>!&+Mlq&LMMJzQm*7k(TXq$@p4V{_MQ%;^b6|l}E zOV@^uqCOKje0SM|@cr20k|=esR)TWBvJ{@#hrpXG+)J+C7LU8w*jzk8*@ksA6hPmM z6@+(=&EAX;+lz~>KP@EuwL3vZ<#w1gZG)?)kf`Ed>ZYVPpJ1IOO?(!i0oVJREEFMK zD7JN+X*WqdFqPczQ<+^+AQ-lBUAI>x$aST`=^)xaghTbFbOF7ALt>&^fdmzw3`6za zXBGKod%cLq#&|BE3aTGsBdMDN)grf4QOx@!zi(x4f4Ct;MknHzj`zTh1&<(vLyC95 zflbM3r={=`)+THCy8SR8y{JL-kl_9@K?=|D%l#`y@aEbl?LK9{!c$(!?LTaepGL+| zeeuBj;fwSqxGh3g_@W8U;|u!H-pQjunKI(p8uSKLcx@g>)vkcfazlm3p&8?3^#{N&C9jRejc9Y4^xad5-}UeGz3CwVtA2z4HypAJB7GpbyQRayC z9%}j_#q7veulPk%g^s#en~q`3>$I?0QcjNh0qX*}OK~<@;`i*^|<=MW> znH8}u?JG3m*;j3a+wJ_N7fhJy(v=7b@#lIBH8usgF%&0?Kb(0wrKZ3*^1O8O zqrFqYlodY4O6x|VoyEERiR(&O7FRpxEbWxbm+&JugpWyFHOh@)X!4S%5;l}e;Bu`i zACC)r1$=>}=)CQ&8YkIzv^ zFv~J7etnUUL2hN%ibE-U(?FV6s?r!ke6LJw&~y&Y^20o{c;bv{)d&cHg89>t%UX4y=?AJL!5du zP4j4FGrlw^F3n$FcU=-J?(bJrG>J!)lXFQP+KU2`ZEHF+>iI?9x!`1 zU9TrG9(!5uwCoU2MSH-~E6<4!+x~^|0{7vCBQB+P+Ib!VjT5Z`w8W8^S&$k(l=JvtoY`A zX+BBeD>2vMq`p@@jaaynLiaKGv4py5oaC*-z2Ay3ORB9tR`ks)y!`y^2h(qzBRxi# zC+rkA-8IHWrFhi9Y-Y7$bJ2L|+Mct9rSfe@?WsQAgDIcE{?1D~RM$1VgBzNM6>bw8 z>6su*d_CdNB<#3JMIC#t_7t8TRWsWbGn%m-Q1>Oo7}pGW@f7Zag{GgtXVX=0 zqI90QC$lL`M0V!uv+Vq4erZP?i56zc*GE4}d@?*1VJUsE-uR_+Pu9MKAkp(GM5;wP z)QTaJ;tyDpi5*Y}EW5-_3A87!a;R=7=XY}jopJqWrq$xRqNghD^1$}n`3N$Rm;TC+ z@VeLQmp9U7scn3+>)%qKo|%x)k1ZJJ>z@>t3DNBReqycHK`k=V9e1dUf`nz!B81=_ z&-^g`{qv89h7vl)nThzv);Z?2yC1RY?$kgmC6EcblQ6y}5)JcU9M?f$Zaz&H(Cf zZrkrlvbts*-6_Aoj&irsB=>2`J3ex{aCMM7&q{$il2^d2IbNkneV=YkBd+qQ+lVm} zv6&L4-sbJz)tUQyP&VK1cq|E^qw=q%&E^D$hZv*6lu7utek$hdnin7(d!$K!>i_l@(#1of2xRX^`&DN}= z?<*cj&sR6&d1~tMw2|U)#Ch{2_WC#X539eZ-NQ6cHh57nvgT-!F|%wOdCfIi*O{%? z4gp!ZNz?flc@dW88X1LB=0_}#9T<%yv&=8Bli=TMuh|&e7dRHN5ahUK;aTPD1kR+` zisUlvk;wgSda$`C?95(?b{TE8Fx`AN!;_L#m$X(6T@cJW9T<9;&OWgC zwgNj>e=p`5mAGM-*Fub4_<3peu{#F!`SG$+In1Pnom3&!@nB1T`C_uR+r<(Z*X@ zg_G35E^3@n>36<|_ikFpNgSjswM^b1NIsQLEO!0-J@!-ItM1n^_vCNAIi@|*$%2bo zcyXFIE_PVgd-l+%#f`=E7uL(MWSTUREU~#JA|iGV2|xJw-m}!=f3?LKPBu0nX^A;_ z02Mp@{egC1v;**=uA9%e_g2P*nM(8%^*5vrIMnr zAA>gDu$neUjtANmktRBQJ?ti3YD~m1;dbkC#m!5;$z54}r^GFii|)3uUc~G7-aEH> zh7Vo$N}^<4_mhe9tg*594I6R%W2tuZ@9T~&a;={TDhNG=sAnaE-7TBEo`O%Xw z{e&~#QnhO~l$+;=sP%R0^RI=(Jk39y52vU<-?c4so=8Lf;_cN#Rx|1i{$- zsTvgQ&k$PocE9hYWEVch%!x1L(;(S5+q?2>O|z22(&DYBJ!JBwPJ0}bbWsU$S*Z1A zz@r_imG!byX%RBLh{_~8b=kXMKECO0l*#Pmnc_Cp^c$DS5@Xz6Q%e*s8G>gcivrXs z?awyfGjX4EwG4LZ$$o^TZc3L-VSLTcHlxS-W{3bwDf2khmQGdp(xFWuGRv6g+EWC3 z+8GWeYwRHkb1v;RJbBMjC1=^l zlIiwNEf@u#8qHty>1PV$vAfLsIMfDx)6wJeowu~3_mX|de8$(8P!-7+pFiA3NO)Ay zX+H}-Z96Jglj&ROrSdzdIqc_(s4v`EDcr1>!FF@Mk$TEHMV9st!u)%$?IB3tt3vH} z_VxzdkLN1V5SJi-yL;{4^8_1XAX`Fm?O2|IuEjv=iNIX_Ywa;o+*$Pw#o?!%;{fJGYjqkB_3XAkw-WQ|GQa31P0=OSNKI715_06zC03Z9{tKr?$ zS3=HZM6wKagi8Br2BpNle!Nu8-#Rc?XP z^|1Q#Egzir5zbGqT4(ce295QVlA1fp1S=xWh$_|Yuh#UgFHSl~LyV_Bv_IFoNiJur zv?Scb6o1G+jo?PHrO>MVr-?HG#;gRc?TTj$NFr53EivkTXf1L)omvrd1%kwn7cB3F z7)!jl$s$|dV;H^(pJT~xHAV^gRJEb!v&BJT3&oGK=@W?+rj=i+LfUN<)x|H4Sg z;M0TY>o&BcZ=+qzs@*f%;x!7GEhzlg4`HnzN(zRWbc8UZ`0H4ACiNX;&+(WesTN71 z7F5h7mx{f9>d~}B=OPzQ_7;8!UQgKG5`zZ9ha_=#W^&F{=%qIZa}r5eP~`64pEMgZ zaw9BOPRLb*IiEwO&F(WTO1BWRL>Q!BxQ^G;TxPZIR)>1u{(Wsdx7wtUgkw{Ch%5Zd z&CICt*0SUKo=)r)q3|chF=VS=sHLFK=)KbLL9@mY~(sK&_2=E;0-M}Wn{jy|sJzHdhf_Q)CsvMVqO~lV$okwuI`nwgIYLE#R4a zU|8Bkw_DmK!Q+7h&pIrR3sHm)u^H~aJ~`(i8jj6Im6ia)WMn;2g z*L*Fn(wc9UJA6{Wnb@RbQB1-sN&9BudS;m)-5s+!nvBN;u1uRRJl(fnZa*)PNSN)M z6j2+m%DaBM@Pz{?GP)9PlWjWkLgS^Ea@@Y4*{F#k$?@dGq5bOYn|r*B`wC~pl@nJs zH{`t?3!JWCW!2dizFHNw&YlZal6v^4;)AMgZ{CM(Z6jRV_E#Tt7#oh&2>(iw!V#sj zivt;B2Uc{2r7{}ULcB~tYm&6|0+$5qh87-?Q={fw#p80zxaL1hgn8vQRUVviAR1K0!LKnkJ7fD^op{KcIRcppqd0f zPEK?wT+nx2ox{UL-&VXquxjNqY)Rd5!m&^6lpNLim9Qh)p4k!c8&Yk$5}%!G!O>cm zRJ(gc##KdQez&PRUhsN7c}AAKKWs80K-fYhF(zyEIU^qVkbBe-#&0Fgro2)QUlSRQ z5(;BxLV z6`rkC75m^@7FGfT6pmcwL$iz6&qtHIU5~NcV!c@Akp9d|?rm=@iTk|0!Q-Acx5f=t zFR{qlO2sABX`;%-Z!Y&TX1rJP%$hbz960}W%_+?$n<5|wpQ@af*8ZiEiPDR{;~yw= zpPbM+HIUNyZ~A zYEnBN9kD-#o4YF9dJq3nVv)jX`qNKt1UgpanitJc4g#@PJLFmBPbi(ARkar%xmKk+ zAr($2Y<4od+B?9}LJXJrqHc2a(uXJK(GCgO5Onv@exn4U$+%-{7=--yPAfZ~c$T^J!@Qj`{WIx;hT40H9p5+DgG8pR9yW0bhmUETN4H$Sx2M>{ zRuaD489GTwHi#mkwkV($yc+A3aGFQy=$p(~EtYi$_RnU)y)16Y3}ilYX+%+4l^l8Z zJ%(tAsl1yf1I|4kC_N*@r8G7oR!Aq2+TEA$RUBfuDi*fkslv^2kLp;A^mU!nOkav0 z2hAInOT5NxEfwS(bU7r*od{pl!dRK;Z)G-&eV?eR%67<0D;?kLC@0H-aly&1&>8Be zx0>@xxMac`fy>a9L?bTqnKca_Vc(G+E2S>!GZhmUa$(z<8%5(S*-L+gj0{bmJ`8ZNUZdnNvS!iE%RN`iA!;v zE-!=e%DEz}j5)Km*?RY2&o&(Gxk9cn;=pbb^2R68#oTqZO8D zp$uOU-vEDo@wJyBl|$Oac}Bcz*|cNsIr#NfJ)&HXM2vxnncawPTPg0cm)Yr7G3^O| z_H*uo>$&{p@u}%<=ZFMCJrgs6hkT5v4i?olklcl@mQ!kFXRmL2-uqY(80yPrlh|ZH zbiA5;)TPHq&MlecDqWr*SIkpaE3ZCH*%sQSiB_qDpL0t*W?B<5^At5sAf-GH(>!V2 zzdE=v`)c05DL-S+jd2=q_3^hWk0SD(@tEYv+Qe|py^D3`Ck~W5ah@s5)GZQFyB(jj zP)kgTnU%VIJ4DkqW97VhxsqB`Fr=yt?8QZOHh1p=6QFkoWDSd_bcv6 z|9PjU#`+juM%M)do)a)%!54z=*alnO0E9!-A zZ0m?>+kcdx;N6EPrKpsvzzl$Ove`1H_&E9E>062o{up+cf%$B z>tZ+Vva1w@aK7E0W`s5q_SEhwZQ zE(Fe97Q=sPa1Bxo#nIU+hxT)4QB$D4@5`a>v5~A`nd6IzssBD%xx=!W<8X}R*B0D zg}0ie!d#|;*nVu`J4MF$%==TE+02>xlr=XJhSo>C1(pbviB#93x%^S&xlt(QXH}NilvCn>+Re#JZ?HGb&y~lxAa5`E%3Iu zJI_hRG~1$#(qu0tiN`V}rstze%|{6@?YNnv>TkZhRqu8aLyaC{Y81U+K+JS>d`Rz5 z#vxCM_ThLMCY?vC$!a4fhwi<>39CB_$PI zoNNTMi|V}-0cBP>G=?YHdI#PN;J+mOzJYbt&5C+Q6GFuMap6HWi4pRuH(xAqTQSPY zuz>4!bKyEFFrzl`(y6U-aYm&vmRL52sUTt7ro7YzeO=lszH<%9k5})JzB?Jx=y|TQQ1G-lCaFxNmq_BB?wi9#`@NoxW>iKROBK!~W5w=! zo2r-+8FCa*^<2Uu8cMkp>W4eztL7Kj=3l|fc~{w3H}j>6=q;+c3%%MW?0w`hx#v+T z&8hsBjwu2|=2S=ZrWne-jOUDpYCO#ey(+eDp6i*F@g~KU$i0&Zm>gP`p_}j)sJ614 zcz9Z}+vxd|%+rEdUUwfpoKSh)PKG^U6DTBIv7T=kmnC`rlB$x1&g?TF^>`%vm5MS} zC$Q{eCSd;<>QyrKMOO2JT51-_i${CnWM0J0-k>DZnq}`Um@Y_F^W&s{y(pCUSb_!= zTOVa|CPH0#&2^>`^WrR#%#2)-Gs(nCuAz}-b?oQvvtPyzCf^%25BU@w8hAA9<^yXU zd@`Td33Hph6R8GA-gOM|DlDOooaI3WxM*K2Unvt%9lV^bcUGQ6%Q98?-lA31qQFx= ze`OWE?4h$1el7~S6}^|}B`aSm%KgZ%Mv8L21n2bYHVBg5*qJiylf{ugoyiYH$HvU^ z>-3**v$qp`G!u?OvpYS+iKZ@B=6G=997g9VJ{_U?_X=A*Jm%}DRz2-0l)jx$bDT$>-a zYz?|4@_nzd(rSr0_*qaBoOm=_u?W`5Gwo3}(x=_HacgU2M{5bqO899}c3Y#W7qcu! z<)cwo4sv7>_^7I18hbJ;p`!17iqDOEgey0ck88m!RN(1n+r55r2G&X3v^4qJ5a>lBPdz>^uXnO#O+P0Q-lE=e2?RN$hBzEbXVV>3(ES?yV%I!E()Pt-e z>CtGyvPr?y5%z1(Mg3Rw#^y$~TO6@RUR+s@w8cd^&8#W9*>G-%(H}dwd`V;B7Imho zzM^+pmsgL-9=^6lP}HK3K(cAW9b6MLi5BY+W|Ps+YxctA?z@sZf;nS`N-xV4@V`Gq zL)ll^gyi{;u~`Mh)%AwMHjQwBGxS{7z^1P;WJjI4`$j{P z%_00#4}v+rjLe@GB2g&!`B2REs_G!+p4;WaZLIk-w=3cAFG4zoD6pj^1gQ5;TWSm6 z8NX`L$(}nJ%Oo3eJT_xu^#p&6=)zgJSSB8V>D3Brl8#2%{RL54C|?5jx&wP&hb~n0 zB}rCt;5cor@F3ZzGh&8@%Dys^!V=f=RNo&L#eYZlK1Fp)*lhOeaA`?ix2Zo6EtW_s%0l3Jht>On#vpBHtuzb& z+kQ`_X%`8d6NaCg_h@aoYTR9%R&|oeMzK*M~_wWg&+A%IRrlyrzCx7&cZo#Qg zkrP}q8iUNXY*6M_txi?W$f>+s5k~jc;~&Z;vl)dm!ff>nHjxF zM|#yQW@YKDN78A%2hG~fsrze< z4wcU{MYN53P>YTV>vmmIwEy85@yF-wEy^uwTa4yqC1jenKZRVMJ5!b}ifU zMf~pWF0+EyeTRk_YJ$E6-% zzg5Wf-j2I&&95Kl^@^w;k0pg=%(!uMbr8Pa``|D-AXV;?-<16!06p4YCUMm}xqXZP zJ$agZaGbkjLtEEQCojS#o6`xq&Hu!Agvln|s&s?urBOtPM=Wy%KW0>&NA^%ka1nca zmDPpTM)@vr6FLq-TP*`6J%ZFn;Sx1M?Bwg2w}{oyn*F}#Ja1y_2&v>{OM^*qGo!56 z8qEUo+f8J^#2#(+Ns3t8de$bI1wyk96^UcA_vum+&b|4VZC#c{tbMpD!TD%XovD+9 z*IU!u`0q+?UcgbGMqOev;Bx&gU3!d2=1a$$^QhE4qe)@2-D14s#wRE^4()9r!|ePf zVyaNe=ITdfUr}w&Kr5-@LE@4||5b4}QQrDP`qHDqz52I?nXm>YY0ZJ2ZZ>D&e7f@! za+J+-15?6Q>Xq5;ZHA_t#^q&EL_sqm6_y@ z*6TDjV)+Ns_tic*F2@!K2`nnvon8r}jsH=KK#XDty|KUM< z$>}h+kKgQsN+iZ`(MDPkb^FC}N+I4XVHTOwn~W&j0X(s{!g3B9^rd^KUfgv7D+Oe$ zV>dR*=pz=oI>ZbsH|8gZunrr0QTa10@XJxQy1JOo*-6<%X%8}jsW<*_U8)GLJa^S) zB#QgKwj@_=J%y2bcOtAyL`yxOK+Y^Vs>~0K{GN&rFXf$S=3Tx00Zn_P+6Bta@{cF+ zqNZt^H`+>x5*|!>+xzqMYU^|zQ&5TFOYAt%GmOd$^UipRDdS}*ZNKJFTIjfMtbb&d zxPoNtTIS~#vzo)r6WTfM3KY`&B`??>ssAR=J0qX>;+!LCc3a8)mC)P=>}RY(rm{?% z2Q&`8u2$V_(2Lc$Y9_G7yWH^DMV9-NlgSi2A-;cQaKe}G346{+m_)oHzf622>+u}b zt@gqYc5H}z76~r6n~J;tx#)@6JN}6;(#9-F8Scj%Ft&_3@_x2VoiF^+(Grk$PgdIM z@WPt-Ev-y(6U#Q+6jJo1i|eUQa^1&uD4jmF3drh>ybJJUtf)C(hQ~+1C~Lxh*zdM_ z`>CZ>#%I4Bc`m&7jy=#IDv*Cl)vl|rW`l>fH zl6&47u3nE7wLL9jBEBs32&;13RD~{hazyJ4N&Om=$LFKt*XQ{jdc2-^bE!ZmLMHZH zv{%Vvjpzoo+ecMzy^7mBpl(I<@~mvR;Btde%S^dP0%e24=5Q*Sf${Cz!H__gfwP+% z^v3?GT@*Qc!9uRvvsT}WCVS4`6|Hg(dX${)h)MePOqX6rOMtcX;^F;TL-B@dg_`nN zsf7&e2db66jG$94n-P3gsY@`VVj&HWGUHG4C6M(dVt$>=_C990hk}ifZ{Gzm*+4wy zs_WByA`iyyUKjcj7k6A%frJyMhP`o2rz`R8l^Xfk4{jZ1x}^LX)UW8NW!$fb_Gog) zS#mtruUC6dPBy&0$39j05O*S0;pQ?@Cd@P*Gt-m|&y3`M~kH;z${1&dvqJBdyUa9c;@0|Z&XVc%3`P{Nz`CU?8y49lgm?*E=QxXw!JfyrfB^Xidm%YY?BIY&Vt@dU2H7Th%SZ4Q5Rim zAszGhQJdvnme*I@LlKWzWuQ=rF-_SS+?HSb{N z`OI_N{w0zkYFu5u{0YWQgn3j1?T#9FSFQhN!Pt*@5GjGTHsSwk$&%d(^%_=)*yT;a@r)v zNOR=4Q=rFM$<*8Qs-|JvD`OjnlxjET4(1D*>_V=U*fyyh`$F%W#y>!WLvrBKJmu^!I8CV1mhN-D)&|Zr7Zo0sYcm5<^MGaH$ zr)Tg@qJC=3c~LpPN>a(&8Va492ju#d(d~yA@KT$MGd&5bDx%$L%i8KtZ<4o!y)2jW zWy>(S@!Copy=pbD4zJO4NG-n0*V4--Hfz?R;wzZon2+)ow+66A#uLkT3QH~;iqMYB zsx1a3*^lBHf;%On=yB@T+)VD|Nj)sR{7x>w?PlRJtK(R$K9(dP6JMmxnXo?k9$N$H z5bD00!TRZ3d_S$y3$Z?gRF}!OS(&Pz+_{^5;;Qv+LP>9CC6yIYzL$Ljx>H z<`g$RIK<0r^sxoj>?HlE>t9v_J7)=>1lPaVzHT|OeJG#PHBd#ltJ6KMb~4kH)w1^X zZ0{!au?UzAlsb7a2)t}OE<?R)`PE%Nfc>V=fm)2l6;-C9comg6R}r`+)|NgZS@eTCugqBunl>FH=x zI@?US2uU-=paQ76sYdQSI881s&vZed_&M1-uUV0_Dn2y#FxUHryxPL4{_mNUCzC?= z26C>%^h6QLK0px$$ePlYso$b4qh7FGk$0Mot*a_cD~i2$oh0dVMsIWZ=We;P@;ZJ) zl{=P45}LWc%kotlVt$G*J7=|e*C);IYi9_mZ1!$J0P}|_zh{+jx)h3WtKkLrOMIRh z8lf1~q*_)F^)c0k_2WY}FGz6=<_&WNMc~T03nxy97fkbo6Nl#)*Golk;i)qbF)4dr zKvmaNMy&B}5arO8ojRyq$&$=WW#rFSCR!}C*1CVvI*iOnl47TFt>Qs*8x7QDP3m1* zVS5&+wPGN)S%>QM@Y=4hua#*`#iW|YcETmsXOp7&!|zHS<<^S0U@09n!EjTV@__)B zx80-fyg=2E!OoY&`S4gcE#KGWRg&`y!|y1HKKWAqAaguzUPD(@xX4M&gHKOjl8y0W z)LyfCc+=sgsvENJ9ha6h$&7U5iXS&V7+TFI#gPo$(bu<6*xH~j`gA%%&+R-L&Atju zu@4~+F+LS5cQiRJ19Ct@`dvei=0eCMYVlFQQdjGfy@cX#nG+m`M&5V}CHFiv{%FHe zkGwU9-=K?0q~Q729A`ycm%YeSyD!%?s4n&t*NSprUeP#0zAE>IWTZC%^~| zdr21D$Ucb9P(1C;e|=VFf5h~@;s@k+Bq>fp^Ooe5R~}yEp&vMA>GE~<4yBv+Dbq*UlLQP({)nk}lySYZf~n)el>OM()D8j#ga#~P$jW1s-U<0S zKR@7ifh^(7mefg77O^yZhAPrqG-cEpd5Z-im$LB-2ywAJ z;4=oU{IHKk2H>B#Ab$h%;|CA*i2}8QKL&Q2T?Td;*kxdsfn5f68Q5iDmw{adb{W`Z zV3&bi26h?PWnh?){`{L5fZFpwfalMj1Ly-gE!2Of5A_6pho9FVEdlaVPaw;NJ>iD(Glq*Ip7aX|F%W0i;s^- zu&e*SOn)S15uIaWV|O%6NJ#jp<^QiXDk=&a1in|0Ku;Q|1$lF?+7%L4&|f&)Q7pg-{Q_W}L^ejw;6JPry3{(*muz@Vof@aa?F>*t4HSO0&R z{!X3&JP^PI^Aw=Hf`GGUAaM5$1(?7{;O-X&Tzo?Q60Sa>@O&t6@_dS5SO0&R{uoa$ z9thy$tJ%NX-`%a7T!T?WCcd8y*PY;p_;!T$~}CcKR+3 zHo(=%7Pz7R8m?$N;OcA-T%2~WtN*`De+x?+9tdD<;|S~>(7@5r71%qvz+(qsZ3kiR z_}74+d1C7TtZeKN?CSq7)8Ev>iU$H%SlNKb)^y2>{aiyBNX}H;dj~h=Xu&7?`>9lMCzZAh}78Z6{&aFC(;vm z1OfT43RS4RGz5V91E~K_3)ckx45&Ro@aM7L)ra~6sQ=%h(~bcA_dy6h``7qg_WgMt zs6T-E1E@cM`U9vxfZ8LF{#E*aeX>UyVn_Q!oD_J8^a;wQfcq;L&; zK)WsB-=YKR*#STMSLlT2JMD=;m&4!V_t^Jm9!cTaNI{o1A!vd8HaYACUu^e)Iy+hf zzfAw1@&9LyfBGachV*~N{+~aC_`>f25uE4038-!R-(lxo(5Dc;`b+#S`~GX54#;=+ z6PSyk_N`8PL5l++_+m$a;Fsy&Xo)Wkv7>X_A94SGmHzfH5B+0+`4#H_-@)(k?f=q# zC$~cVd#s`U(B3vD63~f;eKaM4U#9=h_-{vYI+FkYr~wdP*ctod7+D{(Km2ZVIKaB( z9|Z}#-{0eR+lkEA1-b5nIlt8w)>;mGK(7k{81&wc;FszDbN}C&-;oS-LHx8G>32E7 zW5*x5?;QW}ncvm#fOEJ1gXn*|5)!a_7#?*Wb0c#wBIFziEz z;Fsy&Y(*#yu@m<{*Mj7L|M&x34>JC9?5A)1uC^V{-R?;KmuQ2Z*?|LW{}S*%4u5^G zpL6{#`~Eyn7xeR9M@rCaw+}QSwW<>y81SS)@XPf7-^D+od5<&24!WJl|C1B`yx!l@ z|Dgv3Xm|PpjL;tUP!FzW6f&bk9k9mH#oydG$FweExKseEWPB*B#Cn@Onph57< z^l!1=D-8i_4O-BHrUl*3G!WFF&vpNQj(c{-XyE#&emk0^<=g@Qj3+=+XP{KaUtq|QXr*%Pl zI-!k-&$QZ;gRi#xcG`AmA2QyB{y$@D7xZH!=e9V}LePOO7i!S!$%x?B>2I@7y2S=^ z<3hKC9%wt_1N~6{Ug!^q_B;4<|L53mXmQ@`pws05d?y0)qV*4781`TS1MUnkKhl9# zB<`VRy{>Sd^w2X3*va^@lpNLT!+Fv2OG``Kj9l>V>9;xf1H=2K{sgw4m1= zo_A-0$4sCD%?Ph!fIh+u^8_=DDMpC>P;Vr@;B|myO6g zKlp_?3;J@K(|)-B!!@}=TcG_AzfAvDTMB6i$R`8z0eZL&=mXIA`rR1+IEH=z<7emG zj*lRI@q5q%=V^p|v_gHlyx@8;Ogk~#4)y6k_97%FdC-F{PX^HD!2nv^7{FIodeG^~ z0(vm)pxcujbhxm<_h`@$oY|n>Z18$!*r$Uv1MNpby4MuPYI3VqKL@`4;g{A{}sANY+xd~O8B;vn<` zMEgExWPQA#-SY@&^A`ubv8rIM@E%z23IrQt&%wrYA=sFD0l;VsSnKfsiw%#!OtCf? zNtOV8!G}P%4=3n^c6LJhIuKumy7$8P>Vs&9@rUfW?P!?)pv}M2&YyE3_71{*sLKVe z5dsV25r^SK@C)@n0QHCay9Yb;0XE1lwB3~j>d&!r-0P0`!4Dwkem6#7d=9$9ICX{l zw<{ND#~cTp5sCnpr4J@Q+5&)$0b9%O!1hW7*jo4mR>xm}&G96#JsktUco5jeqQP9L z0l;Rcg6=2@&>bKE^<{^{- zpuJe=hd2xuf?uY8t0TQM1k|77hYvuld)$z`$oYp4AbtU31o4kQ`NHq&55fG`>&6B; z+&Dp-_hEobIu8~r?Z9S#Gyroi!S;F?SY0azo8Qa8)=VK-oyY}Cqj6wkG6rnV#RBj> z4(5X}FjbERWAE;OZ>cI^Ecgg~Wd`TuDjxj%w_Sf3-g zw-3fR7Q+TcVSXO<;sArleDHiXM6>5%&>k!a2GTUaT!{tP9)x->z5(0IC18EA49v}y zg1Loqu(ntZ$3FmA41l$XIOqq_U~MuQtWP4okObBzo`KEbD6rk_1-2@T!DyNi=m`@7 z?XWiNfVED~4xo=iyL&yjcH*IbhXaHM7z-WlPodu(atM7L3vKQ9 zhJC0#4r0uQ3k<`#2Rvbo=gkj#0*-_31WoYmjX7AT_XFz#sW9f@9=`y!pDzRJb05Ll z+$Y#Chq=E3Y|od#9FX^i=Fwmi=76oqc(6U02)3uDlS2!5IVUCvC>5IZ&uKzk7U)O`TvK_vg+kTGZ{&J*_k3BdTmA-;~( zHc<0k82>%c7jST{KF@;y7bpsbqvgT33{5am<^q=69V4FL5AlL-J!A^s2r^FTD%8i)JBa4^{DaE5!y zJuvzF0_cx832TF6pws&h%m@5`=*$E267)rgK4|w2cKqSTI2JyO{3Cq)RZOyU{Gt5Kv=epW%VuHQ@QV=fFPi4v_HyXzPF%?>~Y;3@;dl_a65@ z2%pV)V6NkU&vG1~1!~_4>-vG@%P`DM0ciFGVB|T>>F)tpg8DAkfK90L(iGhDp$}{# z+JCQr+CxA1Z|J`<5eZhmMSxAXKWxt>L%)E2FaT@BMh7rmpa%w@oduntVxSRo2x`s? zwf_O6-v{kS&<{C4fSkiY{8OCl+z0ZE^G5cggD@Y#xc7nghwm{UewqHT&Xh)KO&ru? zzz6O%5J>;$@u2U)A7dEUV)*|N48nU2WB9-~I1d))+b$1&&>bWWhSRmc^e20;+7Su1 zVI2=vt6#4psJ9qJG3y&e4#tq;OJ;sb~;z+IIIXTI%er0#?5-{k-8si>VjU<2+6E0ZbEkJ7;6WI9-xN`-qx6xbT} z0UJHGV7b{Cj64+t1Ad2h>X2S9s5`VDi}(X<`yl!eF5vM$!N<>g48Z#i`oLK5It;oo zhryulAutqp6u~dlpA+g2Ydh$BI5;VeG>i zaC_#zssCgo)IV~kK3JQ627T!n_zr8sMWimAg!{mR2h0O@01Q3?-@-(}p#QNQoqJ&} z?)Qbyo-il$`y7V)LqG6=JRoC8`wwt}+ymid2x{JgIl6;EKLM!yaRk3i|4vsfX$Yt} zqO$<>aRHd)5FdcYzHm(th@WG3Y=rhA&*1+<4EVsD2Du!C{0_tCyV2lNU@A)sEH|OS z{8Sp4pM43IW{Y9IhtKq|&fng7wnO#;nC};Aq4rS!xk`v~81rS&2M{3rzpnqbq4rxy zoX@~#fw=^jM`B>?hl1sCU$_rA!@BSZ*#7niY>(UqlaUgTcR{H2VbB9}G1l)W7=Y`= z`X1dG!}=b9`Qa$!2JRF8Lq2|9KLCAe80MTF?_;1BVhHw!g9H)$I{n>wq&wYVJ&WiJ z=fgsuMtmD-5q}@>hxYA^Bm3d8e+2zLM?o*NwFlN&LonA(hD(CQ7gxYmrwdq^jR)Un z(x5Im0DQ}bv5o8n@1P$PL*4(_=VxF}nEnKH{P?5(UxK~?bHU%~kHr2utN{Vk9V|sb ze+q>Ah6{}OM__G0AFK~v1slU!U~@a^o)u_hI3k78&dJJqEcE zgfVjbAK?Vyb^zWJiHSaMA*esp9r8aCB#hvf>EG?iCk+AhMzlt3f*SAmI^yrg{{S5K zLjaHckbUug1}x;QH$V_R-^00Jtuq!P2Ig}$p#Jt?c|Hn0)Biv1oe6xD<+cCm?V|P; zrS`V9RE^r&s#WWbxP(Ok5s+0`1QbzQ_2TvZtCbxBWMN4{638TEC2S%JT6e8hTfKH~ zT?h#znIyBXnM^`L*7N_KcQOzNkO334cJlfB^1k!Vyw7sZdCs$)^PGJ2@kYk}I!50v zt-hV``ek_a2KR-e9JP*B#sJFoo-QK940Y#a+^7A1(fZTg8%o zjg+JB%U04~F-A48<{D^qHFOH0nrlzcMANVs>HeHN(L2C`ELhp!1ii-pviM75fx%y` zlU3^luN8W4MaESojIvR8OOF6ruDK4`x&d0=AePLb;@vq-Sd2GxbE{AJp9i1dos)GO z)@$0`tl{}K)As2Xg0H}T)&z{T9~}?;1^&M$&B52;|4wu~{ML6h4c3ALIcRaA`yC0a zBmEC@HI+Sqd*J&-_&<&-e0M0gSJ=mjui$2>PQOYj(1jJ~cuVqijK|>#u&udP?8vS) z%(EV$iPBc}34Q;X>!mFD2ILL;r`@3FHU5_;Towxs@wqU z{g?;Tu@)5k9sGU^ezI0*-$7fZ5Am(%`-Rfr|0U!7d!^Y0&+W=%pnM1IdJR7_uH(g5 zI8drH82`Z!^J>+D$m(ij7T74eBjq}S|JEL)i>6gce*55kS@Hn*56yd-{}l;W!~*J~ zyhgT_$M8q5cIs$PRXzda- zvx_|)^g*+e^UCF31$z)?Xk$&Gtg5SMn>P64&^2oK)01@3w5sTD%UA;~hb|U)U`xAE z(R=Mb@ygipMCM_d7j;souCp4vS006)SyQ;uUV8#o(oxvg(#8(*e+K+t0sm>|o!h^* zcgBM4j0aj-yKix`ev2+|=iXS`XC80p10Oa39_9(It?2pX$jB4u{0I0<|L&Z~+Riuw z?Y54#-(CAJx&{7U3;(aieh7V)N?F%XZrY{Ftz#{;FxtcoJwlTZUD5fen&(Z7jc$TQ z&_8{kqWAcp^z+zqY$`0u-?h{Q=)G0>ZhWSmsq{ex?~z5?!DofGC7aSP?I!Y~p4r31 zx9b@ESN^IDuz%Oi_`S`O37=`(9uv+hl{y5U11u`6>O;6HgAX}2i9&o@DXGW}|*Z5zt`ubR4@LYKv=69I);b+P-TUu-n z0of+K!}$N=(fO}Bznyk&tlPu?X7=@)k&9-}7S{2fV;%4%`28khe8zvZ%mY{pkQ%_+ zV7rh0z_9;WAWh!cyfYbjVXhw@>mO(Q=i73-Sh8-C3Ur_uK3Fo8C%PsWqJaaxsO;Kk z6Fo&2J83!4_tj}b$Y+Q+N#B(*NYRV@zXAU19P?)S8TgW}^$W>?ojNLPJP*}#5ZBKR zJ94D$$F)w=rTyp7{(qzVXZ%;g`0wESpEV#q<2)bh_Q*hs!TWrJ_sw4Hef`*hDc`aE z5o3>lJpkP+Vt=5mmOW#g69isnoltGSRx@s#%UqK6KIZ=I%>CPS-G}jDX#S^j!0`<|f5Alih|g>gpOQ2Lvp;zS-?$Q|f4 z{+m}1hy_Mn9OyU)`Kb&joZKsn&y>y1_dNm4NA;R}J+hU7%;()9{+Ag4?P2|=hA|%e z*LEw+`;4`E`h8u`(>0+M?`HOec4FuADr>**Fz;s{u;DL&_22!!leRi+!>|u)spGz$ z{-6$EKho}cg?WIkC#+zc_%Lhy)1}SA8jy7YG63IgV?xk%Am;?}Z5hG5G*&9%hZ%iu z%@|4>VNDp=kOjv&t{rsh37Uk{aMCAQ(&>AE%b?K^=rp9)_-|QrQ!F4a@>w@jJir~k zUw@l;fIfGvLmzSN3Ajkpl?k$jip_*fZ5k=Q*QQHL@qF4V<2qzO*X+chVBT+|&9fKK z>Y$$wpzC$5rVG?BR@l}f3p}?0M-R~?MAsqEd5~Dv4HN77VdBmlg4r5t zV;gSoJ^m-(5^G7;er8AquT}1lfpyCJ+l^-`1IjaJ55eF+X{oNMq91S+jh5OsAC&eo z)@uTJA^Tki^L={}bG>J1`#Tr|uoqav7_UCW?{@ln-T!F;hW^(+ptc+SBQH`z9}#$) zcej&v2D)I8G+BNjEw-u1*ChN%#>oM6L0}{NXIyH7zt!63!E*=el#a~XF((hnfL)*U z5KY3gb*&!^|B<`(!{Pr>raa7zHw^1V{@)7!wJ)Zf!*7LgzYgAK4l`gpzm)cUE9u({B#Lv60yY}IWgPbo`~TQ=2hiQL z-;nJG*>9mX>!-#K~+F4FX7jTFD>PR1I@dHzHRzVxWbC&}<< z7d(Fr&^~F`ZFSgvqU)P$K41<1ee?r1VLrz8eAfQo;JuFVB9Z}qj}B<_eJBmSKTCb! zud=`XuX3P4bqe#ynoW#5SX*?g6o39*;?Blb2wK>*Jy2$aK?k1c^PZ%M(pF{5r}`!H zPH256)1Ki>hej%TjsMoP+hRcnukVE4@WeC{gzojT!Udc?1=4nD`dUZRD7O42NX_BTxP0w%0*cfDCjBy{x#TF%ZRIl;hl74$EF!-VMb7zk>>g~=NMV&_*FzU^B&K`mT zx~h(IfSL_=(Z7vls&}_Io}De;KP;8{kJd?Z#kSCzUO@Q@zjZ!egFUeF{s{iJ2X*ZS z9iV;uabWPD-?r-95WDf#{U36V{fMp=Rqm9gznP@&tvIQ9b`E3iF`_hZQby&ucSDF4 zA(;q0>uI`ZdNt@ASJqeqHQD$#=Z;qNBL9cOf9>1gz1A7HvPK(n;Nf23&gL1i;N-q1 zV5go|Xlg~?1GFt+tooH@X0uFHxIsWI*T##1Co# z?Y})3Y5P}OV)YdzEd^^wZEkEvkhMngt9kKPj74W5o0E`x>WywvJ*8JKa%wy?WJ2{9 z?L%qRLo{(AUv3b9=dP@A$lf?)LEAoak=!x8$o~=We+2wh-qYs+^Wn^2CT@okNWx;XSmU)R%lz2JyuF{?-9$E*^CLElKmgC)?d8~o--a$9nIKZ zdEai>{$cYEKiiEy;P`D{^@8e;U`j@et zCrkN{F3hA|aP>h4Z#MMcdH9*q$JGP$P?~Bw+V%n)R0oeY5I`Pk^X^jg8vpGXcg6zh zHV!?E-3!or5Ab&S?4oTtdkQ(El{;RXdE*b0HE;d|vFA>ZYVxjv2G$Le#I^My+5g6T zY57|k`*-vStO2w$Hf&}t*udDo0llE(L-i36{6h@cfVVA-4V(C`5u1=E)$jbqSSO(W z7aKmJe_zK^(gJaAe;B^wV~QSf=Z$8Y89kp*yVtrRiweDW=fD%Lu1?QfJnJ!qf-_+97M+jKdU$40P~5BoA75>Z#Zo z@8Nqsd^dQj{0-IFMOh4fho2jC(0cR~O{hO<`OpK74flvW`yR%Y^Z^^ED0+?m&UIsA z0dmI}JZGW-ANLAF9`rtY0{qwdckt~LT{S-+WpctFOa2t8-28ypUj4NM|B)e0=54Is zy#~MW4`GbhLOX9(UBH;G3186z$VQ8<|8quwcwU#5Qq~mylrEk(7m9Vq!%~$$O>EHF zlYO@Yb0Oc<}@_CBtsd@4*BvU10H0+$(aMEx}KV!+h~8PbEeY%r_k3-kie#Cir(XY=Ga(Q=2+GkphX^S zfU%cfWgzEn<39BHZp^Ii#+(}ZoMJd=kG`TQQn%?|Y1lMFYICNt|8|d5o9-2B(J#dJ zI_vizWl59$MQPG=A%ePY#9Fb7`CMrZ!@TAA6+bMF!kOaA=WMvVX}En)m0Iri7fhGN zf*I17cQ5Hq79Vx@IAwJ_L{H?8`qm-?o?PmcGo3z{zG?GJMKAJyBK$|+EAI>Lh5yswJu<+! zOP?w9-WZ4J{V9gCU<$lb*_;f|u;YS9&AI3T`hj5HbiSJ@j*SnnX7i*RczL6 zZCB+lhkl7MaUHt*K;aB&D!NY^H{K_Kd}IJ!S(`H(J^q+97dQLI1 zwte(RU?Wf+fDF}=PJn*41{v^k4d%_H?cI<5y^rzkj8j&>&UDoFr{fSW<#1CDFXgJ+ ze7~aC`0vWTClTzZ??aE$Z^3Vs=|CYgM!(lX*B~-fTX>(* z=4Jt;k)r_x-X z)i7?-&o$xC`s@@UNL%pJ7M#dIHF~=Y`cyCuumCsos^xP%`pQfBT-pz8eOLlTvnl_5 zj9chC8Sgi2deFG~wZCA#?=Pb7*b1*Vv#vmWbsQ1MorTOiDEspsqHlTdl-4m!&j57Q zF{wYFeUQ8dnENtD>2>~3f&Yws;Bi3pspMo!6j@Z;^ol+8a0}_3LPFp}Z&)_1SOq!6E+p8RzWZI$P0;?SC@-XAA@#mG?*I zKQuelyg!UKA-#0!d2(v=4e_6GSix*$5*a=f{Li8NGe%av*U}#v{MR-gmIu98T@dAe zuj>Tmf1S<)&_6z8AfV%#XC6}YTKo6Ff56;`zEa0S9WoG>2Ys$OHel%5Q`;WC?Ct%? z`YHc|%ynuCXVXVLBtdjw<97JJy%+hP3;&t#8~mqFR2fj-AI5(}22>8B{O?T~gxkNt z|3b#B(7WN82Nk{7{yX@u{0?^Ee@G4tc`(|4R0evF|Do}p&i?}1{^5TueeV9P4=8$# z|MdNb_5X(cH}pMg?Hzi+nD6Txkn%*)do3`OKJFWkQ#`->8 z^9`^6A_sc!Ll$*SD2m>pfw8{EI!3)gYu5i*zt;BOyyKUOUTgpUg8O3u>mjoZ7;7iG zR-$VuteXUMO^tQb5V+URQS>+sl+KDEYf#>TUvT8jBjVrmivumsJ(3Q7+^3iR8n-__ z0GJ!L&oS3;d(0f%@`$;1(=Ux{-PT9V^;;h^*KK*!9Nf(FD0-C!291jzGkZ7vui3Nh zDYJja+>d*S?a^eT6o^tFN`WW^q7;ZyAWDHK1)>y)QXoo!Cy)QXoo!Cy)QXoo!C*dg1tZIkDpf4&Q#kMMo^=U&iWD!|sQThoh*ibSzx%NE(ZdGle|v}uzR z78ZVK>t9e%ke-v1BYAmwlAD{`1^V1%GJR?5pOu-GZpuuP%=K$!{kpYW(+@+oX`N)O zP5#o>KYmqmdeWLyNnD*Gt5P#0F*)rpBqgUy{K}LsZT(lSSeCvzeuX40kCUVoagrFn zAgp=>O^5(+ue6@coHichCEJ z|2^~mqyJm;FV1^&!KL!sg_nt5-&kvm&m4nExdU|I?6x&R4xZ8SGj3P3abk_{|Y0 zS#)ywl~T?!^89$-*TlTyKgF{0yAjtP-p8nnmoEC2QP;hT`^w3d!=N4M6fZjSFzjB` zS9UKv9K}345_=Yg%5X9?FD3ntp+WhwZ%SqSwDf}c>}=%nyOix5DP8(~=)pDa`?8n(6#JHa7h0T++$rr(JWqY63|fA=_`9+% z{`*pv*gs(rHn9{{scivle4&bWyVBblg82Xj0;Qi&)Zwck0&YvX2$Uf6`QkHhP ze6s9^)c1VS?hkFK_tNjlUh-A!Q=CMeTGn#tSiJNcDUa_j6^Z9ZUjK#Pi7A06`~idlsJ)vsdXBe}Pm0Gi5Ta@fY&!_^4PBgRFt1^Tn2Q zA$eaUB`Md5b=PcZF3TbALO@xA@#pVDo8_7o^WBu{qf5XE@LI-E?My-sa*AAsao^Xi0fav^oKEJ@C%wrW#UC# zf6VnqQk6hD-JqJ^S;(&hI+P||CN|=_*Z*<3v^rlVM&k$4=+pSS#AqW zdWl`FF{|rA&AVOd-|pQ&d)O$A?k&=WJRJCRo;bD)74xb~r9A1UQnB(91C=W;?Y107 zIVtnr#LJ{Q8Khnjas5k{T@Y;7*oy*F33zdmQ|`T*_BoCT6Y` ztH^)#<=vJ;%UQkpGTE1OrC3s~5bN5jBCdaF{KYY4@VRo$6=Ei@s^lw-`^wcnC-38< ze9e{c=x4+i7$Hs8WI5p5LJZ^Q;5Biji8b4x86$w&q6z8UK#41gte$?MxAZ@N(X`zm7-mSzZ zCAK-S>cmf+G8^*YSwkC{B~9+Ts5kG~#!5TT`-VSI|Fu`a^Xri3>&2dOy-}t}%As^B zOCCV|c_(c^RwGDhib)A#7RY^gVJy#YE505^66ev>*-oSVmsRId&3-@B3c&4th+ z3pvO_zM+YCC+&L&vE2Vxnw@teD-*$_uJwN(`A^VhO;UkN zN87~n$i=JBCtVKM|67`^O&VY1`>3j z>VkIv3)13UCataq;6HS;h1!52{~wH!s`WQXReCJs90VV=3=x)va#+%aii>w$8G|CO zf7P0SG1k;ucxMQ78!WDMp{s+>-NDIk9hrl~y+h@BwY2+k;4|YpCvD&JEIR)!>dg43 zo|yA>jD>>lK?m&*-yy#BGty$4FYQ$`M66?=#b{~#aI9D}2TQfqd)-hl1bz(ewj5ec zXT~70WZX_%?IGeZ-4=2EEy=gU*rBzX7}&0Lw}~f{G}hlnOziIA;vE}h_3fM{ZPpBF za5AoS?~pd%Z>T46T7$&uB^Gli4l{Jnv0xaCjoOtyw38xPT} z*_qN>xq<%wC45+mLfK{>RH-mH<7S>xK?No=Q45!c_6K0L<0ek3u&hzFicEO4%_Y+`b6 z=pHtH?;?J?hcZ{>O(e|t969h$)%kBxZ^phFw;q1CABF!Ko8D+2)?JV+PMk&eQg_mW zo*wcy;?8rgG2$aBi*Li|Pz?E;F&aNU;`%!>M#Z=?i3d&0-Ed5J4>5_2IM^FH;$a`_ zYQ(}OR{92FrW4ECs=w2ihOhlv8cXt|#s508%@{a<9MIld(1lIVqm|grt&W#u|DRLD zz5N%cO!Cf$?zzO_4#x&J@}wM^$FcJ7^xQ)k9K?3^5l=O+@t%n5?^-`5#!IZ>P~2mU zfjyBpd>WtmP{w4eW#Xo=OJ#M#%>~h zWW@D%WlxUrQVxyxW5j@?yy5G~#C;49Tg^*dY@BUwBesE-txnI8?})V-B<`A?eXeKD z2R9L4gqUo8VmAhJIS*UQukoi&0zvAnXXtA^h@ls8{fUJa<0YP;#ynBlh2!4^3h(3o zLDD3C zT;%o7nG&PC&^RDU_YfU4{?p0T{quS+Nc;5@N2PxA0~$Xo;`$Q{DMsU+hT|aB7HQ0z zf4#UjD*qZorjCB6VO!+ez&5GpyXLjOL~^+1RjG103*h$HDR$e{2^ z{L^zHt^)UigXcsLX)1b71O$oxMJW)aK$HSe3PdSz1O@Qby%JyM$MDyE3}5Dhz<>EM zaODv{|H6HI=9y>as2?&uxx)Qnz&Cf!7p}lZ6bL`zIqENaG-PFEec{WWxo+*8%=M|V zE+YkB*tCN%;S-y_cFh;Q{3{Yx&WTT0Er~0WWku3zu&M*AW$B7lU-*dEsY%+dn z8TiH>1U^$4-Q%;~SCwzxrV1@$U!3fgq$oJ`QWc+pB z85zY%7fwDxU3B?K`AzCqcXGac>XV00V+Zyu_y&G*Arx~x4D<2*i_mYd1V7177o9F2 zE$nORQs5&V;G#9pd*kqv z!$(a0ze*RY|5s$`?!(_}_Y%@d_(8YyFJ5@IDdgMr1H;E_U!3|`MTR|bA-}b~_*U)3 z*TcN!PBG`)C?)DEgpX7SzE|qIrT%Oo|Er{wWu9TmC&u`kU0Js6u^`QCjUx3Z*OVX$|q=7f^8^N~-yW+;uRpNVbqU=k(1plE6@E=m2p$qYW(|5T)O8Vhv zmGLpWa}mBC7k68J>S`Rq-^PbcNF?#sXYLQ|#T z--B<@DttZgsW}!b_})~lx>D-@vQ(P=Z(*;cc55GURbLitT050r?Yo<>cW>}u^KD%& z-e+&e2LnGE{9-Ebr>Vq`J3c6Fu3zDQGF@t4xt%hRKE4!Y z^*vGlmt*8(!S}_y>gVKjb+_d&PrS@z_>`z$NjLr+)%adm(r?B_ZW(fdzo7b)__jm) zH?gJt5MI2Fjqyf&3bLfdwM4`*6B}~;4;*8$I~*((l$rA+wz-} zt~A-yuaC0n)s}ocz%Suwz+b_-ZZPGKr~LWIc_F?a8@MmP9`t$ao$-TnB;Xq{OIkYl z0I22Z`QI2O=CwB<)9Sl`|3qrn^uu}E z@N1}EdyD!HbX$J&ngJ%mKLHzmyWyu0draR12j4rgZj;uI@?*@2&n13_-W|x#J0f*z zkNSJs1%9c>eYQ3&D3BI@I4wsgFE2LF@h+LcSnRqGGc5C17WJirY4ZKYqvd}#bN*jErdut3n0E5N8 z{x-vZz;V2M@GY>dza5+3+q*CS+QBAwX2?EX?c|-@9}DhG?B_R(k>)+A=pTH(@O^5n z!46vO{cA&gz(HGZ{6Sj%f0U;BSESLgTPQ8VYJs`$6WtDb@;tB{WA+c zz-icTV@po@dUYKq9}hO`?yND`evaz4{PuNsn$*78lMT2!j{`@J{%$lj$oP_MoPke8 z5<2u1c!0gVmOb!3eHk{`)JuI+TFuW<*C)kUFrM;ae@@*s552mMlaJcTyR*k*w>rMt z^1CzdGSy&D?Z>X#XV_MEXI~xq-pM=e+(}Zs?P11l=px^5u_u2Yd7~at#=F=s>-)Kk9eC!?Q@g?L)70P8 zDowEW4Q{%>+wyyIrkcWbsWlz8i4ohGVq5Ft9XB?eYK!gPI13xU`y~hr8(i#O{f7N5 z_WIcSMVfBVpyw=Xt!H&xesAtnliJs69lj{GrM1eZ!UwPweX!f|d&1>EXdkThk)JON zxhr@8d*4XtKMnZ_*>{F)J3}_tYIps4LG3kt`Lm^Q+e6EbSnlca5&O(*>bE^M3H!`R zbz2^tgk9$6k4=wG@@{={BKDSNbSZH3L6ibf3jE7bAY@w9r_X1<2F?=EbWZFVP|BUv zr_Yz|8Yw;1PU`|N7#y_I>KC=sf*jGmC(xtz&<5ZHHI@tG1mftNY= zd?aUs>)GcA!8ze0PauiU^wZR}sUv};rllEJo0cwXl2cAu{&9;Ijf{_rlVwX6$+Eab z5*N2vmMmWUneyih->{wUe} zbnFE7pZn_fN4^vOy!+|X`u=0_*?l=%_bW#-fc*P?${C{k);Kr!*n7abKdfiXeqHt) z1;xkNKi)I%G|qMXmV6xdeR_h!|JdK|R~+HqaItYdsh&}KXdgKI?09?2dd6zWk~7%% z`A(PoKbiZ@etLFi8T**JFS(a}%%j5CYUGVLGd)a?1Th1OHXCWH-)K^?{iL%eS_4z_Xy`Fah@6Pn~#!470=4qv#L(Lylei;f7DOU ztE*)Hu5uOoN1R`FIDWgv>v!xe=^0+3^Si?5cwNhxc-Kqa?;j^W_A)$0)crN~0N!R@ zlzjouldS#g8D!(cvz@btjC02hy&FzT_pK`RJg&4Gy5_&~s(#h_-C8|=i+vW(#Oe$^ zC#wDr|J#v&jzM#A?~-QD!)oI!C}R&r^WPB*eK~_l&wM%}4Ncd=xloSuo4e#+xq3i9 zo9?A##2Wh+_H_0DIzgY;bLM6{{MIwrTK%sY`ylPL@36O%P8rxkv){!rUBlRW8^oDI zpws($?=YSZ;cTEGUGlf2#P)M$+{Smq#HDA~90~Rr_J24}n!PV@CeH7meW!g4&IW0% z;hYfHVz$S|8E4cnhxQ_LPgK{&>;II(`3d7$lV=TF&z?CV4PDdsaxP0CcTAW3o$E&T^RgDMYuj$% zxN&EkUw6LGhSh9)pqaI;BRnPul4W8c_zda%iH{ZEApCeE- zrEC7C3H^Mzx+bjWXy_RroTYITc=D%6kmtJQwf}cX>^Cmpi~@Y$_526mJIXuZbo{*Q z<$Q|Z=9yjccW;>7@8Fpch7UDsgRHk62|WkFqkhV|mP{EGx}F=*wNcK6Fg_pUJx!;U zwMX^G=4^v5`FnDv^b4O|&|B-Px^`S!#Cqv74|d7loimmCtM9m$QP)L#eqB-53j-Tx zAK+{QqPiab$NJz`_doOBH?lT(BWr{=vOd@=@NIkQ(!+C(e)>Eq5L!<-%Krl~BAxJV zpFUshA)F?i{&bx}8^ZKL)Z_R5wsh%?b&WGsMf5@XN<<~YuM#NMdGW~xfui)u}fgd>?y`M76n4n+S#f z`FlB|?pekHjMt(-sX+BjV7oqpq7fBV7r*{e^~{rB+RyNmuh`2I@H@Oy_b)msvL zcRBs@xPyD%RVO(z7lJt8$*9^(8a_9oer3xB8QQL$Hb@O}Ep z#87`q-@+Ls-9h`>+M?OFu#{PJ IB+z&MKN#DTOaK4? diff --git a/src/modules/Workspaces/WorkspacesLauncherUI/images/Workspaces.ico b/src/modules/Workspaces/WorkspacesLauncherUI/images/Workspaces.ico deleted file mode 100644 index 14292758fa2a98ddd72db0cfa95c2cfa63997e1c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 467385 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 delta 11 Scmca1dPsD`7Ou&sxcC4cr39z| diff --git a/src/modules/Workspaces/WorkspacesWindowArranger/WorkspacesWindowArrangerResource.base.rc b/src/modules/Workspaces/WorkspacesWindowArranger/WorkspacesWindowArrangerResource.base.rc index acbc7659d5b054fed671261969bad363a28f74d7..a93ab5827ddd43cde754d51a35ac184d51cd0f98 100644 GIT binary patch delta 11 ScmX>kdP8)>7Ou(jxKsci3j~q? delta 11 Scmca1dPsD`7Ou&sxcC4cr39z| From 6d69a79c7579e7876a0b0611feded5b5ec991f1a Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Tue, 1 Oct 2024 18:54:01 +0100 Subject: [PATCH 07/95] 0.85 changelog (#35095) * 0.85 changelog * Mention ZoomIt * Update README.md Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> * Address PR feedback * Update with latest repo changes * Update binary hashes * Fix spellcheck * GPO line in General instead --------- Co-authored-by: Heiko <61519853+htcfreek@users.noreply.github.com> --- README.md | 146 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 82 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index c178872a61..c8d1fd21b2 100644 --- a/README.md +++ b/README.md @@ -15,10 +15,10 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline | [Environment Variables](https://aka.ms/PowerToysOverview_EnvironmentVariables) | [FancyZones](https://aka.ms/PowerToysOverview_FancyZones) | [File Explorer Add-ons](https://aka.ms/PowerToysOverview_FileExplorerAddOns) | | [File Locksmith](https://aka.ms/PowerToysOverview_FileLocksmith) | [Hosts File Editor](https://aka.ms/PowerToysOverview_HostsFileEditor) | [Image Resizer](https://aka.ms/PowerToysOverview_ImageResizer) | | [Keyboard Manager](https://aka.ms/PowerToysOverview_KeyboardManager) | [Mouse utilities](https://aka.ms/PowerToysOverview_MouseUtilities) | [Mouse Without Borders](https://aka.ms/PowerToysOverview_MouseWithoutBorders) | -| [Peek](https://aka.ms/PowerToysOverview_Peek) | [Paste as Plain Text](https://aka.ms/PowerToysOverview_PastePlain) | [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | -| [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) | [Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) | [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | -| [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) | [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | -| [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) | [Workspaces](https://aka.ms/PowerToysOverview_Workspaces) | +| [New+](https://aka.ms/PowerToysOverview_NewPlus) | [Peek](https://aka.ms/PowerToysOverview_Peek) | [Paste as Plain Text](https://aka.ms/PowerToysOverview_PastePlain) | +| [PowerRename](https://aka.ms/PowerToysOverview_PowerRename) | [PowerToys Run](https://aka.ms/PowerToysOverview_PowerToysRun) | [Quick Accent](https://aka.ms/PowerToysOverview_QuickAccent) | +| [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) | +| [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) | [Workspaces](https://aka.ms/PowerToysOverview_Workspaces) | ## Installing and running Microsoft PowerToys @@ -34,19 +34,19 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and click on `Assets` at the bottom to show the files available in the release. Please use the appropriate PowerToys installer that matches your machine's architecture and install scope. For most, it is `x64` and per-user. -[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.85%22 -[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.84%22 -[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.84.1/PowerToysUserSetup-0.84.1-x64.exe -[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.84.1/PowerToysUserSetup-0.84.1-arm64.exe -[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.84.1/PowerToysSetup-0.84.1-x64.exe -[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.84.1/PowerToysSetup-0.84.1-arm64.exe +[github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.86%22 +[github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.85%22 +[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.0/PowerToysUserSetup-0.85.0-x64.exe +[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.0/PowerToysUserSetup-0.85.0-arm64.exe +[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.0/PowerToysSetup-0.85.0-x64.exe +[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.0/PowerToysSetup-0.85.0-arm64.exe | Description | Filename | sha256 hash | |----------------|----------|-------------| -| Per user - x64 | [PowerToysUserSetup-0.84.1-x64.exe][ptUserX64] | 1CDAF3482B031D84DAE15188DE292FB44C5D211698089921040D94B256EBD3CA | -| Per user - ARM64 | [PowerToysUserSetup-0.84.1-arm64.exe][ptUserArm64] | E0207EF5147EE281D4F438E87A30586D8CAA24DE948950FF1B12E05454622CD9 | -| Machine wide - x64 | [PowerToysSetup-0.84.1-x64.exe][ptMachineX64] | 10DF9774DE1857051E135B9790A18A92C5C7F42587C733DEE991186E67231EE0 | -| Machine wide - ARM64 | [PowerToysSetup-0.84.1-arm64.exe][ptMachineArm64] | EB5DDA5EFBA17E813DBF24AFF668DDF5424ED3659234ABBC15441D478D812699 | +| Per user - x64 | [PowerToysUserSetup-0.85.0-x64.exe][ptUserX64] | 28A8BEA61040751287FF47C9BAC627A53A4670CFEA0C17B96EE947219E9A6EA9 | +| Per user - ARM64 | [PowerToysUserSetup-0.85.0-arm64.exe][ptUserArm64] | 2CA077E842B7C53BAFC75A25DBD16C1A4FCE20924C36FDA5AD8CF23CD836B855 | +| Machine wide - x64 | [PowerToysSetup-0.85.0-x64.exe][ptMachineX64] | 4A248AA914EEE339AA99D467FDFBDB1FCD7A49A8564DDBBB811D0EC69CEBAB75 | +| Machine wide - ARM64 | [PowerToysSetup-0.85.0-arm64.exe][ptMachineArm64] | B5FB04EAF44C4203E785411FF55025842B9C39D4970C0C934CB8ADBE79EF31AF | This is our preferred method. @@ -92,99 +92,117 @@ For guidance on developing for PowerToys, please read the [developer docs](/doc/ Our [prioritized roadmap][roadmap] of features and utilities that the core team is focusing on. -### 0.84 - August 2024 Update +### 0.85 - September 2024 Update -In this release, we focused on adding a new utility (PowerToys Workspaces), Advanced paste custom actions feature, stability, and improvements. +In this release, we focused on new features, stability, and improvements. **Highlights** - - New utility: PowerToys Workspaces - this utility can launch a set of applications to a custom layout and configuration on the desktop. App arrangements can be saved as a workspace and then relaunched with one click from the Workspaces Editor or from a desktop shortcut. In the editor, app configuration can be customized using CLI arguments and "launch as admin" modifiers, and app window sizes and positions can be updated as desired. This is our first public version of Workspaces and we are excited for you to try it out for yourself! Make sure to file issues you encounter on our GitHub so the team can continue to improve the utility. - - Known issues - the team is actively working on fixing these: - - Apps that launch as admin are unable to be repositioned to the desired layout. - - Border of "Remove" / "Add Back" app button in editor is not clearly visible on light themes. - - Added Awake --use-parent-pid CLI argument to attach to parent process. Thanks [@dend](https://github.com/dend)! - - Added custom actions - user-specified pre-defined prompts for the AI model. Additionally, actions (both standard and custom) are now searchable from prompt box and Ctrl + number in-app shortcuts are now applicable for first 9 search results. - - Ported all C++/CX code to C++/WinRT as part of a refactor and upgrade series aimed at enabling AOT (Ahead of Time) compilation for enhanced performance and reduced disk footprint. + - New utility: New+ - allows setting a personalized set of templates to quickly create files and folders from a File Explorer context menu. Thanks [@cgaarden](https://github.com/cgaarden)! + - Language selection - it's now possible to select which UI language should be used by PowerToys utilities. + - Lots of quality fixes for Workspaces, improving the number of supported applications. + - Reduced Peek memory usage by fixing image leaks. Thanks [@daverayment](https://github.com/daverayment)! ### General - - Added DSC support for ImageResizer resize sizes property. - + - Added a general setting to select which UI language should be used in PowerToys utilities. + - Fixed internal code of some policies for Group Policy Objects, that were reading registry entries using the wrong internal functions, and structured code better to avoid future mistakes of the same kind. Thanks [@htcfreek](https://github.com/htcfreek)! + ### Advanced Paste - - Added custom actions - user-specified pre-defined prompts for the AI model. Additionally, actions (both standard and custom) are now searchable from prompt box and Ctrl + number in-app shortcuts are now applicable for first 9 search results. + - Fixed some telemetry calls to signal Advanced Paste activation on the cases where a direct shortcut is being used without showing the UI. + - User-defined custom actions can only be used with AI turned on, so custom actions were disabled on Settings when AI is disabled and were hidden from the Advanced Paste UI. ### Awake - - Added --use-parent-pid CLI argument to attach to parent process and fixed issue causing tray icon to disappear. Thanks [@dend](https://github.com/dend)! + - Fixed tray icon behaviors, not appearing and showing incorrect time. Thanks [@dend](https://github.com/dend)! -### Hosts File Editor +### Environment Variables Editor - - Fixed save failure when the hosts file is hidden. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Added the `_NT_SYMBOL_PATH`, `_NT_ALT_SYMBOL_PATH` and `_NT_SYMCACHE_PATH` as variables that are shown as lists. Thanks [@chwarr](https://github.com/chwarr)! -### File Explorer add-ons +### FancyZones - - Fixed multiple preview form positioning issues causing floating, detached windows, CoreWebView2 related exception and process leak. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Allow snapping applications that were launched by Workspaces. -### Keyboard Manager +### File Locksmith - - Convert RemapBufferRow to a struct with descriptive field names. Thanks [@masaru-iritani](https://github.com/masaru-iritani)! - - Fixed issue causing stuck Ctrl key when shortcuts contain AltGr key. + - Fixed an issue causing File Locksmith to be triggered by unrelated verbs in the context menu. + +### Mouse Pointer Crosshairs + + - Allow crosshairs radius to be 0 pixels. Thanks [@octastylos-pseudodipteros](https://github.com/octastylos-pseudodipteros)! + +### New+ + + - New utility - Allows setting a personalized set of templates to quickly create files and folders from a File Explorer context menu. Thanks [@cgaarden](https://github.com/cgaarden)! + - Added missing entry for New+ policy state reporting in the Bug Report tool. Thanks [@htcfreek](https://github.com/htcfreek)! + - Added a policy for enabling/disabling whether filename extensions should be shown. Thanks [@htcfreek](https://github.com/htcfreek)! ### Peek - - Added long paths support. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - -### Quick Accent - - - Moved number superscripts and subscripts from Portuguese to all languages definition. Thanks [@octastylos-pseudodipteros](https://github.com/octastylos-pseudodipteros)! - -### PowerRename - - - Updated the tooltip text of the replace box info button. Thanks [@Agnibaan](https://github.com/Agnibaan)! + - Properly show file's modified date instead of creation date in the file previewer. Thanks [@daverayment](https://github.com/daverayment)! + - Fixed memory leak caused by unmanaged bitmap images not being freed. Thanks [@daverayment](https://github.com/daverayment)! + - Fixed an issue causing Peek to not be displayed the first time when using a preview handler to display files. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Prevent tooltip in file previewer from overlapping with title bar controls. Thanks [@daverayment](https://github.com/daverayment)! + - Fixed memory leaks in thumbnails and refactored image previewer. Thanks [@daverayment](https://github.com/daverayment)! ### PowerToys Run - - Fixed window positioning on start-up introduced in 0.83. - - Improved default web browser detection. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Fixed volume ounces conversion to support both imperial and metric. Thanks [@GhostVaibhav](https://github.com/GhostVaibhav)! - - Fixed thread-safety issue causing results not to be shown on first launch. + - Improved the message boxes to be more specific when PowerToys Run failed to initialize itself or any plugin. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Use capital letters when showing degree results in the Unit Converter plugin. Thanks [@PesBandi](https://github.com/PesBandi)! -### Screen Ruler +### Quick Accent - - Added multiple measurements support for all measuring tools. + - Add the Middle Eastern Romanization character set. Thanks [@PesBandi](https://github.com/PesBandi)! + - Add the degree sign, integral and vertical ellipsis when "All Languages" is selected. Thanks [@rddunphy](https://github.com/rddunphy)! ### Settings - - Improved disabled animations InfoBar in Find My Mouse page. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fixed the link to the Workspaces documentation. (This was a hotfix for 0.84) + - Fixed flyout issues after the Windows App SDK upgrade. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! + - Fixed initialization for the New+ settings page. Thanks [@htcfreek](https://github.com/htcfreek)! + - Fixed enabled state of a control on the New+ settings page if the module is enabled by policy. Thanks [@htcfreek](https://github.com/htcfreek)! + - Fixed a crash when cancelling the template folder selection in the New+ settings page. ### Workspaces - - New utility: PowerToys Workspaces - this utility can launch a set of applications to a custom layout and configuration on the desktop. App arrangements can be saved as a workspace and then relaunched with one click from the Workspaces Editor or from a desktop shortcut. In the editor, app configuration can be customized using CLI arguments and "launch as admin" modifiers, and app window sizes and positions can be updated as desired. This is our first public version of Workspaces and we are excited for you to try it out for yourself! Make sure to file issues you encounter on our GitHub so the team can continue to improve the utility. + - Fixed detecting and snapping applications like Discord. (This was a hotfix for 0.84) + - Fixed detecting and snapping applications like Steam. (This was a hotfix for 0.84) + - Fixed button visibility in the UI. (This was a hotfix for 0.84) + - Fixed an issue launching the wrong project when the editor was closed without saving or cancelling a new project. + - Properly handle repositioning windows running as administrator. + - Properly handle cases where the monitor where a workspace was saved is no longer present. + - Fixed the workspace launcher restarting itself in a loop without success. + - Properly handle standalone applications. + - Fixed issues causing icons to not show. ### Documentation - - Added ChatGPTPowerToys plugin mention to thirdPartyRunPlugins.md. Thanks [@ferraridavide](https://github.com/ferraridavide)! + - Fixed the thirdPartyRunPlugins.md entry for the RDP plugin. Thanks [@YisroelTech](https://github.com/YisroelTech)! ### Development - - Ported all C++/CX code to C++/WinRT. - - Moved Version.props import to Directory.Build.props. - - Extracted self-containment related .csproj properties to src/Common.SelfContained.props. - - Unused and obsolete dependencies cleanup. Thanks [@davidegiacometti](https://github.com/davidegiacometti)! - - Extracted CSWinRT related .csproj properties to src/Common.Dotnet.CsWinRT.props. - - Upgraded Microsoft.Windows.CsWinRT to 2.0.8 and updated verifyDepsJsonLibraryVersions.ps1 to unblock PRs. - - Explicitly Set NuGet Audit Mode to Direct in Directory.Build.props to revert changes made with VS 17.12 update. Thanks [@snickler](https://github.com/snickler)! - - Upgraded UnitsNet to 5.56.0. + - Upgraded Windows App SDK to 1.6. + - Upgraded the Target Platform Version to 10.0.22621.0. + - Added a bot trigger to automatically add a label to Workspaces issues. Thanks [@plante-msft](https://github.com/plante-msft)! + - Fixed a regular expression in the bot triggers for wanting to submit community contributions. Thanks [@PesBandi](https://github.com/PesBandi)! + - Fixed analyzer errors after the Visual Studio 17.12 update. Thanks [@snickler](https://github.com/snickler)! + - Fixed the TSA configuration for release CI builds. + - Refactored automated file component generation during installer builds. + - Rewrote the Azure Devops build system to be more modular and share more definitions between PR CI and Release CI. + - Fixed debugging of the New+ page of the Settings application when a settings file was not present. + - Fixed setting the version of the App Manifest in the File Locksmith and New+ context menu app packages. + - Fixed abstracted UI library nuget package signing on release CI. + - Removed build status from GitHub README. -#### What is being planned for version 0.84 +#### What is being planned for version 0.86 -For [v0.85][github-next-release-work], we'll work on the items below: +For [v0.86][github-next-release-work], we'll work on the items below: - Stability / bug fixes - - Language selection - New module: File Actions Menu - - New module: New+ + - Integrate Sysinternals ZoomIt ## PowerToys Community From bcb5ce895ca4712a5f9f20dc00df3a3bacc7601a Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Thu, 3 Oct 2024 15:08:28 +0100 Subject: [PATCH 08/95] [New+]Fix call enabled telemetry event (#35236) --- .../powertoys_module.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/powertoys_module.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu/powertoys_module.cpp index 2e6dccfdee..303f072e3b 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/powertoys_module.cpp +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/powertoys_module.cpp @@ -91,6 +91,9 @@ public: { Logger::info("New+ enabled via Settings UI"); + // Log telemetry + Trace::EventToggleOnOff(true); + newplus::utilities::register_msix_package(); powertoy_new_enabled = true; @@ -99,8 +102,7 @@ public: virtual void disable() override { Logger::info("New+ disabled via Settings UI"); - - powertoy_new_enabled = false; + Disable(true); } virtual bool is_enabled() override @@ -125,12 +127,23 @@ public: virtual void destroy() override { + Disable(false); delete this; } private: bool powertoy_new_enabled = false; + void Disable(bool const traceEvent) + { + // Log telemetry + if (traceEvent) + { + Trace::EventToggleOnOff(false); + } + powertoy_new_enabled = false; + } + void init_settings() { powertoy_new_enabled = NewSettingsInstance().GetEnabled(); From 577044163e53448f2bee154fd72d6e6e1e296cd0 Mon Sep 17 00:00:00 2001 From: Seraphima Zykova Date: Thu, 3 Oct 2024 17:09:13 +0300 Subject: [PATCH 09/95] [Workspaces]Fix launching incorrect workspace via shortcut (#35233) ensure one launcher instance is running --- src/modules/Workspaces/WorkspacesLauncher/main.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/modules/Workspaces/WorkspacesLauncher/main.cpp b/src/modules/Workspaces/WorkspacesLauncher/main.cpp index 38e88acd48..9f3d151097 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/main.cpp +++ b/src/modules/Workspaces/WorkspacesLauncher/main.cpp @@ -16,6 +16,7 @@ const std::wstring moduleName = L"Workspaces\\WorkspacesLauncher"; const std::wstring internalPath = L""; +const std::wstring instanceMutexName = L"Local\\PowerToys_WorkspacesLauncher_InstanceMutex"; int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdline, int cmdShow) { @@ -28,6 +29,18 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdline, int cm return 0; } + auto mutex = CreateMutex(nullptr, true, instanceMutexName.c_str()); + if (mutex == nullptr) + { + Logger::error(L"Failed to create mutex. {}", get_last_error_or_default(GetLastError())); + } + + if (GetLastError() == ERROR_ALREADY_EXISTS) + { + Logger::warn(L"WorkspacesLauncher instance is already running"); + return 0; + } + std::wstring cmdLineStr{ GetCommandLineW() }; auto cmdArgs = split(cmdLineStr, L" "); if (cmdArgs.workspaceId.empty()) From e7175302c9226d7fab8ef3d9271abd188758ffbb Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Thu, 3 Oct 2024 17:24:23 +0100 Subject: [PATCH 10/95] [Settings]Fix crash showing non-existent New+ template folder (#35237) [Settings]Fix crash showing non-existent template folder --- .../ViewModels/NewPlusViewModel.cs | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/settings-ui/Settings.UI/ViewModels/NewPlusViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/NewPlusViewModel.cs index cab1a9df77..1a92af09da 100644 --- a/src/settings-ui/Settings.UI/ViewModels/NewPlusViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/NewPlusViewModel.cs @@ -250,12 +250,21 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private void OpenNewTemplateFolder() { - var process = new ProcessStartInfo() + try { - FileName = _templateLocation, - UseShellExecute = true, - }; - Process.Start(process); + CopyTemplateExamples(_templateLocation); + + var process = new ProcessStartInfo() + { + FileName = _templateLocation, + UseShellExecute = true, + }; + Process.Start(process); + } + catch (Exception ex) + { + Logger.LogError("Failed to show NewPlus template folder.", ex); + } } private async void PickNewTemplateFolder() From 300d44f17bb1a631e3538d54369fc2cbf0a6e1e1 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Mon, 7 Oct 2024 09:44:25 -0700 Subject: [PATCH 11/95] Adjusting 8LWXpg user name in so it doesn't throw a spell check (#35221) * Update names.txt * Update expect.txt * Update expect.txt --- .github/actions/spell-check/allow/names.txt | 1 - .github/actions/spell-check/expect.txt | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/actions/spell-check/allow/names.txt b/.github/actions/spell-check/allow/names.txt index 95c1427b9d..97638a1684 100644 --- a/.github/actions/spell-check/allow/names.txt +++ b/.github/actions/spell-check/allow/names.txt @@ -28,7 +28,6 @@ videoconference # USERS -LWXpg # (number eight)LWXpg is actual user name but spell checker throws error with a numeric leading value ... which is kinda odd Adoumie Advaith alekhyareddy diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 47662c3766..8ff275bc95 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1,6 +1,9 @@ # FALSE POSITIVES ## "PackagemanagerWrapper.cs" should be "PackageManagerWrapper.cs" ## NOTICE.MD > MOZILLA PUBLIC LICENSE v1.1 + +# user name but user folder causes a flag +8LWXpg aaaa abcdefghjkmnpqrstuvxyz abgr From 10b868717411f074c6cc1509c3b8182a05981a19 Mon Sep 17 00:00:00 2001 From: Ethan Fang <117125208+ethanfangg@users.noreply.github.com> Date: Mon, 7 Oct 2024 14:42:14 -0700 Subject: [PATCH 12/95] Updated README.md for 0.85.1 release (#35296) --- README.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c8d1fd21b2..aa0648fb40 100644 --- a/README.md +++ b/README.md @@ -36,17 +36,17 @@ Go to the [Microsoft PowerToys GitHub releases page][github-release-link] and cl [github-next-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.86%22 [github-current-release-work]: https://github.com/microsoft/PowerToys/issues?q=is%3Aissue+milestone%3A%22PowerToys+0.85%22 -[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.0/PowerToysUserSetup-0.85.0-x64.exe -[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.0/PowerToysUserSetup-0.85.0-arm64.exe -[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.0/PowerToysSetup-0.85.0-x64.exe -[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.0/PowerToysSetup-0.85.0-arm64.exe - +[ptUserX64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.1/PowerToysUserSetup-0.85.1-x64.exe +[ptUserArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.1/PowerToysUserSetup-0.85.1-arm64.exe +[ptMachineX64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.1/PowerToysSetup-0.85.1-x64.exe +[ptMachineArm64]: https://github.com/microsoft/PowerToys/releases/download/v0.85.1/PowerToysSetup-0.85.1-arm64.exe + | Description | Filename | sha256 hash | |----------------|----------|-------------| -| Per user - x64 | [PowerToysUserSetup-0.85.0-x64.exe][ptUserX64] | 28A8BEA61040751287FF47C9BAC627A53A4670CFEA0C17B96EE947219E9A6EA9 | -| Per user - ARM64 | [PowerToysUserSetup-0.85.0-arm64.exe][ptUserArm64] | 2CA077E842B7C53BAFC75A25DBD16C1A4FCE20924C36FDA5AD8CF23CD836B855 | -| Machine wide - x64 | [PowerToysSetup-0.85.0-x64.exe][ptMachineX64] | 4A248AA914EEE339AA99D467FDFBDB1FCD7A49A8564DDBBB811D0EC69CEBAB75 | -| Machine wide - ARM64 | [PowerToysSetup-0.85.0-arm64.exe][ptMachineArm64] | B5FB04EAF44C4203E785411FF55025842B9C39D4970C0C934CB8ADBE79EF31AF | +| Per user - x64 | [PowerToysUserSetup-0.85.1-x64.exe][ptUserX64] | 5F287C34BF68972C55D7C26585EA5C449B0DBA7D458BF7039CFF448E1D7B732B | +| Per user - ARM64 | [PowerToysUserSetup-0.85.1-arm64.exe][ptUserArm64] | 6D5C3B24156E6E66FD38AD15076B8442F0A1C5CFCBBDC33AD478FB27E5E086AE | +| Machine wide - x64 | [PowerToysSetup-0.85.1-x64.exe][ptMachineX64] | 1CDD3C9602F6E5DDC19C66A4FDFE4231389C08E6A037DD22C0A6471F10C7BE02 | +| Machine wide - ARM64 | [PowerToysSetup-0.85.1-arm64.exe][ptMachineArm64] | 6F4DC0217495973B974B7AC1099FD01A2A0FCEE96E8719074EC97FBBC0ECAC4A | This is our preferred method. From 07ec7980d16ebe6376fe52c66c8115f445a1a891 Mon Sep 17 00:00:00 2001 From: "Dustin L. Howett" Date: Tue, 8 Oct 2024 14:12:54 -0500 Subject: [PATCH 13/95] ci: move the precheck dependency+condition up to the stage (#35321) This should prevent the tests from running (and failing) when PreCheck skips the build. --- .pipelines/v2/templates/pipeline-ci-build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pipelines/v2/templates/pipeline-ci-build.yml b/.pipelines/v2/templates/pipeline-ci-build.yml index 17f4654ccd..5410cd030b 100644 --- a/.pipelines/v2/templates/pipeline-ci-build.yml +++ b/.pipelines/v2/templates/pipeline-ci-build.yml @@ -32,12 +32,12 @@ stages: displayName: Build ${{ platform }} ${{ if ne(variables['Build.Reason'], 'Manual') }}: dependsOn: [Precheck] + condition: and(succeeded(), ne(dependencies.Precheck.outputs['Precheck.verifyBuildRequest.skipBuild'], 'Yes')) ${{ else }}: dependsOn: [] jobs: - template: job-build-project.yml parameters: - condition: and(succeeded(), or(eq(variables['Build.Reason'], 'Manual'), ne(stageDependencies.Precheck.Precheck.outputs['verifyBuildRequest.skipBuild'], 'Yes'))) pool: ${{ if eq(variables['System.CollectionId'], 'cb55739e-4afe-46a3-970f-1b49d8ee7564') }}: name: SHINE-INT-L From 2146dff3bfc374ceec466e4c6f091d47f08be130 Mon Sep 17 00:00:00 2001 From: Aaron Junker-Wildi Date: Thu, 10 Oct 2024 01:00:51 +0200 Subject: [PATCH 14/95] Add the new issue types to the issue templates (#35352) We added issue types to our GitHub org. This PR adds those to the corresponding issue templates. --- .github/ISSUE_TEMPLATE/bug_report.yml | 1 + .github/ISSUE_TEMPLATE/feature_request.yml | 1 + .github/ISSUE_TEMPLATE/translation_issue.yml | 1 + 3 files changed, 3 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 56800fb1d6..f2eb897a4d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -1,5 +1,6 @@ name: "🕷️ Bug report" description: Report errors or unexpected behavior +type: Bug labels: - Issue-Bug - Needs-Triage diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml index ade50bf5f5..d7d092dbca 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -1,5 +1,6 @@ name: "⭐ Feature or enhancement request" description: Propose something new. +type: Feature labels: - Needs-Triage body: diff --git a/.github/ISSUE_TEMPLATE/translation_issue.yml b/.github/ISSUE_TEMPLATE/translation_issue.yml index 7cb712df30..69787a3ed6 100644 --- a/.github/ISSUE_TEMPLATE/translation_issue.yml +++ b/.github/ISSUE_TEMPLATE/translation_issue.yml @@ -1,5 +1,6 @@ name: "🌐 Localization/Translation issue" description: Report incorrect translations. +type: Bug labels: - Issue-Bug - Area-Localization From e1231235fe98eca4573f9ba40a71d4921bf5cf89 Mon Sep 17 00:00:00 2001 From: Jeremy Sinclair <4016293+snickler@users.noreply.github.com> Date: Thu, 10 Oct 2024 16:02:40 -0400 Subject: [PATCH 15/95] [Security][Deps]Update System.Text.Json, System.Runtime.Caching and their dependencies to latest versions (#35353) * [Deps] Update System.Text.Json to 8.0.5 * [Deps] Update System.Runtime.Caching to 8.0.1 * Update NOTICE.md * [Deps] Update Microsoft.Windows.Compatibility to 8.0.10 * Update NOTICE.md * Hack cleanup --------- Co-authored-by: Jaime Bernardo --- Directory.Packages.props | 20 +++++++--------- NOTICE.md | 16 ++++++------- .../Hosts/Hosts.Tests/Hosts.Tests.csproj | 4 ---- ....PowerToys.Run.Plugin.UnitConverter.csproj | 8 ------- ...werToys.Run.Plugin.VSCodeWorkspaces.csproj | 8 ------- ...PowerToys.Run.Plugin.ValueGenerator.csproj | 11 --------- ...nity.PowerToys.Run.Plugin.WebSearch.csproj | 11 --------- .../Microsoft.Plugin.Folder.csproj | 8 ------- .../Microsoft.Plugin.Indexer.csproj | 20 ---------------- .../Microsoft.Plugin.Program.csproj | 11 --------- .../Microsoft.Plugin.Shell.csproj | 11 --------- .../Microsoft.Plugin.Uri.csproj | 11 --------- .../Microsoft.Plugin.WindowWalker.csproj | 11 --------- ...oft.PowerToys.Run.Plugin.Calculator.csproj | 8 ------- ...rosoft.PowerToys.Run.Plugin.History.csproj | 24 ------------------- ...rosoft.PowerToys.Run.Plugin.OneNote.csproj | 8 ------- ...soft.PowerToys.Run.Plugin.PowerToys.csproj | 11 --------- ...osoft.PowerToys.Run.Plugin.Registry.csproj | 11 --------- ...rosoft.PowerToys.Run.Plugin.Service.csproj | 8 ------- ...crosoft.PowerToys.Run.Plugin.System.csproj | 11 --------- ...osoft.PowerToys.Run.Plugin.TimeDate.csproj | 11 --------- ...owerToys.Run.Plugin.WindowsSettings.csproj | 11 --------- ...owerToys.Run.Plugin.WindowsTerminal.csproj | 11 --------- .../PowerLauncher/PowerLauncher.csproj | 6 ----- .../Wox.Infrastructure.csproj | 8 ------- .../launcher/Wox.Plugin/Wox.Plugin.csproj | 8 ------- src/modules/launcher/Wox.Test/Wox.Test.csproj | 24 ------------------- .../Settings.UI.UnitTests.csproj | 8 ------- 28 files changed, 15 insertions(+), 303 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 0967532dc3..767ffa86ee 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -35,7 +35,7 @@ - + - - - + - - - - + + - + @@ -93,4 +89,4 @@ - \ No newline at end of file + diff --git a/NOTICE.md b/NOTICE.md index d4328bbfc4..bdd5b0cbfa 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1327,7 +1327,7 @@ EXHIBIT A -Mozilla Public License. - Microsoft.Toolkit.Uwp.Notifications 7.1.2 - Microsoft.Web.WebView2 1.0.2739.15 - Microsoft.Win32.SystemEvents 8.0.0 -- Microsoft.Windows.Compatibility 8.0.7 +- Microsoft.Windows.Compatibility 8.0.10 - Microsoft.Windows.CsWin32 0.2.46-beta - Microsoft.Windows.CsWinRT 2.1.1 - Microsoft.Windows.SDK.BuildTools 10.0.22621.2428 @@ -1347,21 +1347,19 @@ EXHIBIT A -Mozilla Public License. - System.CodeDom 8.0.0 - System.CommandLine 2.0.0-beta4.22272.1 - System.ComponentModel.Composition 8.0.0 -- System.Configuration.ConfigurationManager 8.0.0 -- System.Data.OleDb 8.0.0 +- System.Configuration.ConfigurationManager 8.0.1 +- System.Data.OleDb 8.0.1 - System.Data.SqlClient 4.8.6 -- System.Diagnostics.EventLog 8.0.0 -- System.Diagnostics.PerformanceCounter 8.0.0 +- System.Diagnostics.EventLog 8.0.1 - System.Drawing.Common 8.0.6 - System.IO.Abstractions 17.2.3 - System.IO.Abstractions.TestingHelpers 17.2.3 - System.Management 8.0.0 - System.Reactive 6.0.1 -- System.Runtime.Caching 8.0.0 -- System.Security.Cryptography.ProtectedData 8.0.0 -- System.ServiceProcess.ServiceController 8.0.0 +- System.Runtime.Caching 8.0.1 +- System.ServiceProcess.ServiceController 8.0.1 - System.Text.Encoding.CodePages 8.0.0 -- System.Text.Json 8.0.4 +- System.Text.Json 8.0.5 - UnicodeInformation 2.6.0 - UnitsNet 5.56.0 - UTF.Unknown 2.5.1 diff --git a/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj b/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj index 9032f1ffc0..8d332f9ea5 100644 --- a/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj +++ b/src/modules/Hosts/Hosts.Tests/Hosts.Tests.csproj @@ -20,10 +20,6 @@ runtime - - - runtime - diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj index 9e2f9e1c36..8bfd37e7a8 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Community.PowerToys.Run.Plugin.UnitConverter.csproj @@ -32,14 +32,6 @@ - - - runtime - - - - runtime - diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj index c61a0554ca..f482308c42 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.VSCodeWorkspaces/Community.PowerToys.Run.Plugin.VSCodeWorkspaces.csproj @@ -28,14 +28,6 @@ - - - runtime - - - - runtime - diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Community.PowerToys.Run.Plugin.ValueGenerator.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Community.PowerToys.Run.Plugin.ValueGenerator.csproj index 9db52b8c9f..31ba1ce1e8 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Community.PowerToys.Run.Plugin.ValueGenerator.csproj +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Community.PowerToys.Run.Plugin.ValueGenerator.csproj @@ -23,17 +23,6 @@ - - - - runtime - - - - runtime - - - PreserveNewest diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Community.PowerToys.Run.Plugin.WebSearch.csproj b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Community.PowerToys.Run.Plugin.WebSearch.csproj index dc62d6f283..623ea68075 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Community.PowerToys.Run.Plugin.WebSearch.csproj +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Community.PowerToys.Run.Plugin.WebSearch.csproj @@ -24,17 +24,6 @@ - - - - runtime - - - - runtime - - - PreserveNewest diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj index 0461e44c9f..9207e9dd56 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Folder/Microsoft.Plugin.Folder.csproj @@ -41,14 +41,6 @@ - - - runtime - - - - runtime - diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj index af67ad5ef7..da46792746 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Indexer/Microsoft.Plugin.Indexer.csproj @@ -17,26 +17,6 @@ - - - runtime - - - - runtime - - - - runtime - - - - runtime - - - - runtime - diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Microsoft.Plugin.Program.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Microsoft.Plugin.Program.csproj index 17e802bd5a..bf0034286f 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Microsoft.Plugin.Program.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Program/Microsoft.Plugin.Program.csproj @@ -31,17 +31,6 @@ - - - - runtime - - - - runtime - - - PreserveNewest diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Microsoft.Plugin.Shell.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Microsoft.Plugin.Shell.csproj index 5e951fc922..8ac68f7c14 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Microsoft.Plugin.Shell.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Shell/Microsoft.Plugin.Shell.csproj @@ -27,17 +27,6 @@ - - - - runtime - - - - runtime - - - PreserveNewest diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Microsoft.Plugin.Uri.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Microsoft.Plugin.Uri.csproj index 4dc88cfea4..876096794e 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Microsoft.Plugin.Uri.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.Uri/Microsoft.Plugin.Uri.csproj @@ -24,17 +24,6 @@ - - - - runtime - - - - runtime - - - PreserveNewest diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Microsoft.Plugin.WindowWalker.csproj b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Microsoft.Plugin.WindowWalker.csproj index c8c249b57e..c814348273 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Microsoft.Plugin.WindowWalker.csproj +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Microsoft.Plugin.WindowWalker.csproj @@ -30,17 +30,6 @@ - - - - runtime - - - - runtime - - - True diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj index 2545b7c3bc..9f4123d32a 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/Microsoft.PowerToys.Run.Plugin.Calculator.csproj @@ -32,14 +32,6 @@ - - - runtime - - - - runtime - diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj index 7571643e15..ae1fac1797 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.History/Microsoft.PowerToys.Run.Plugin.History.csproj @@ -39,30 +39,6 @@ runtime - - - runtime - - - - runtime - - - - runtime - - - - runtime - - - - runtime - - - - runtime - diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj index 8cf3825c6a..4cc88d06e6 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.OneNote/Microsoft.PowerToys.Run.Plugin.OneNote.csproj @@ -41,14 +41,6 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - - - runtime - - - - runtime - diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.PowerToys/Microsoft.PowerToys.Run.Plugin.PowerToys.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.PowerToys/Microsoft.PowerToys.Run.Plugin.PowerToys.csproj index 6ef6bb4899..d31dab3b80 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.PowerToys/Microsoft.PowerToys.Run.Plugin.PowerToys.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.PowerToys/Microsoft.PowerToys.Run.Plugin.PowerToys.csproj @@ -31,17 +31,6 @@ - - - - runtime - - - - runtime - - - True diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Microsoft.PowerToys.Run.Plugin.Registry.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Microsoft.PowerToys.Run.Plugin.Registry.csproj index 08bc952a0c..fba2adb1c8 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Microsoft.PowerToys.Run.Plugin.Registry.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Registry/Microsoft.PowerToys.Run.Plugin.Registry.csproj @@ -22,17 +22,6 @@ - - - - runtime - - - - runtime - - - PreserveNewest diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj index b3570eb53f..15a60dca21 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Service/Microsoft.PowerToys.Run.Plugin.Service.csproj @@ -20,14 +20,6 @@ - - - runtime - - - - runtime - diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Microsoft.PowerToys.Run.Plugin.System.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Microsoft.PowerToys.Run.Plugin.System.csproj index 186dd449f8..e868337841 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Microsoft.PowerToys.Run.Plugin.System.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.System/Microsoft.PowerToys.Run.Plugin.System.csproj @@ -20,17 +20,6 @@ - - - - runtime - - - - runtime - - - PreserveNewest diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Microsoft.PowerToys.Run.Plugin.TimeDate.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Microsoft.PowerToys.Run.Plugin.TimeDate.csproj index 886b0f7cce..4236156764 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Microsoft.PowerToys.Run.Plugin.TimeDate.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Microsoft.PowerToys.Run.Plugin.TimeDate.csproj @@ -20,17 +20,6 @@ - - - - runtime - - - - runtime - - - PreserveNewest diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Microsoft.PowerToys.Run.Plugin.WindowsSettings.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Microsoft.PowerToys.Run.Plugin.WindowsSettings.csproj index ba25563be6..9e7d5d6d2a 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Microsoft.PowerToys.Run.Plugin.WindowsSettings.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsSettings/Microsoft.PowerToys.Run.Plugin.WindowsSettings.csproj @@ -29,17 +29,6 @@ - - - - runtime - - - - runtime - - - PreserveNewest diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.csproj b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.csproj index af3fed4e68..25c35fceaa 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.csproj +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.WindowsTerminal/Microsoft.PowerToys.Run.Plugin.WindowsTerminal.csproj @@ -17,17 +17,6 @@ - - - - runtime - - - - runtime - - - PreserveNewest diff --git a/src/modules/launcher/PowerLauncher/PowerLauncher.csproj b/src/modules/launcher/PowerLauncher/PowerLauncher.csproj index 3f18eafe12..b093bbc575 100644 --- a/src/modules/launcher/PowerLauncher/PowerLauncher.csproj +++ b/src/modules/launcher/PowerLauncher/PowerLauncher.csproj @@ -59,14 +59,8 @@ - - - - - runtime - diff --git a/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj b/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj index e4496cd3a6..6490b7cee5 100644 --- a/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj +++ b/src/modules/launcher/Wox.Infrastructure/Wox.Infrastructure.csproj @@ -33,14 +33,6 @@ - - - runtime - - - - runtime - runtime diff --git a/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj b/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj index 16b906f634..e9179e6ddf 100644 --- a/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj +++ b/src/modules/launcher/Wox.Plugin/Wox.Plugin.csproj @@ -33,14 +33,6 @@ - - - runtime - - - - runtime - diff --git a/src/modules/launcher/Wox.Test/Wox.Test.csproj b/src/modules/launcher/Wox.Test/Wox.Test.csproj index c50c1b2ee4..788ade0136 100644 --- a/src/modules/launcher/Wox.Test/Wox.Test.csproj +++ b/src/modules/launcher/Wox.Test/Wox.Test.csproj @@ -31,33 +31,9 @@ - - - runtime - - - - runtime - - - - runtime - - - - runtime - - - - runtime - runtime - - - runtime - diff --git a/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj b/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj index 950cced18b..d3709d8fca 100644 --- a/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj +++ b/src/settings-ui/Settings.UI.UnitTests/Settings.UI.UnitTests.csproj @@ -16,14 +16,6 @@ - - - runtime - - - - runtime - From d3869942c38ac80acc6069877f03668f28f0eb1d Mon Sep 17 00:00:00 2001 From: 8LWXpg <105704427+8LWXpg@users.noreply.github.com> Date: Fri, 11 Oct 2024 06:18:49 +0800 Subject: [PATCH 16/95] [PTRun][Docs] Add HackMD to Third-Party plugins (#35275) Update thirdPartyRunPlugins.md --- doc/thirdPartyRunPlugins.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/thirdPartyRunPlugins.md b/doc/thirdPartyRunPlugins.md index 4db22a1c7e..4f58b1bea5 100644 --- a/doc/thirdPartyRunPlugins.md +++ b/doc/thirdPartyRunPlugins.md @@ -56,3 +56,4 @@ Below are community created plugins that target a website or software. They are | [Scoop](https://github.com/Quriz/PowerToysRunScoop) | [Quriz](https://github.com/Quriz) | Search and install packages from Scoop | | [Spotify](https://github.com/waaverecords/PowerToys-Run-Spotify) | [waaverecords](https://github.com/waaverecords) | Search Spotify and control its player | | [PowerSearch for 1Password](https://github.com/KairuDeibisu/PowerToysRunPlugin1Password) | [KairuDeibisu](https://github.com/KairuDeibisu) | An unofficial plugin for searching 1Password for usernames and passwords | +| [HackMD](https://github.com/8LWXpg/PowerToysRun-HackMD) | [8LWXpg](https://github.com/8LWXpg) | Open HackMD notes | From ca1b0acbc98bdbe3b2756c332e00a94d5245b024 Mon Sep 17 00:00:00 2001 From: gokcekantarci <115616017+gokcekantarci@users.noreply.github.com> Date: Fri, 11 Oct 2024 13:51:02 +0300 Subject: [PATCH 17/95] [Deps]Upgrade WpfUI to 3.0.5 --- Directory.Packages.props | 2 +- NOTICE.md | 2 +- .../colorPicker/ColorPickerUI/Views/ColorEditorView.xaml | 2 ++ src/modules/launcher/PowerLauncher/MainWindow.xaml | 3 ++- src/modules/launcher/PowerLauncher/ResultList.xaml | 2 ++ 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 767ffa86ee..d4b27080d5 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -82,7 +82,7 @@ - + diff --git a/NOTICE.md b/NOTICE.md index bdd5b0cbfa..ddc6de142e 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1364,4 +1364,4 @@ EXHIBIT A -Mozilla Public License. - UnitsNet 5.56.0 - UTF.Unknown 2.5.1 - WinUIEx 2.2.0 -- WPF-UI 3.0.0 +- WPF-UI 3.0.5 diff --git a/src/modules/colorPicker/ColorPickerUI/Views/ColorEditorView.xaml b/src/modules/colorPicker/ColorPickerUI/Views/ColorEditorView.xaml index 269805aec9..d63f83a40b 100644 --- a/src/modules/colorPicker/ColorPickerUI/Views/ColorEditorView.xaml +++ b/src/modules/colorPicker/ColorPickerUI/Views/ColorEditorView.xaml @@ -46,6 +46,8 @@ VerticalAlignment="Top" VerticalContentAlignment="Top" AutomationProperties.Name="{x:Static p:Resources.Color_History}" + Background="Transparent" + BorderBrush="Transparent" ItemsSource="{Binding ColorsHistory}" KeyboardNavigation.DirectionalNavigation="Contained" MouseWheel="HistoryColors_OnMouseWheelScroll" diff --git a/src/modules/launcher/PowerLauncher/MainWindow.xaml b/src/modules/launcher/PowerLauncher/MainWindow.xaml index 764dee2bba..30acc5c3c0 100644 --- a/src/modules/launcher/PowerLauncher/MainWindow.xaml +++ b/src/modules/launcher/PowerLauncher/MainWindow.xaml @@ -72,9 +72,10 @@ x:Name="pluginsHintsList" Grid.Row="1" Margin="16,0,0,0" - Padding="0,0,32,0" ItemContainerStyle="{StaticResource PluginsListViewItemStyle}" ItemsSource="{Binding Plugins}" + Background="Transparent" + BorderBrush="Transparent" PreviewMouseLeftButtonUp="PluginsHintsList_PreviewMouseLeftButtonUp" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" diff --git a/src/modules/launcher/PowerLauncher/ResultList.xaml b/src/modules/launcher/PowerLauncher/ResultList.xaml index 7eda85a709..7e387e9807 100644 --- a/src/modules/launcher/PowerLauncher/ResultList.xaml +++ b/src/modules/launcher/PowerLauncher/ResultList.xaml @@ -32,6 +32,8 @@ Date: Fri, 11 Oct 2024 09:35:13 -0400 Subject: [PATCH 18/95] [Deps] WinAppSDK 1.6.1 and CsWinRT 2.1.5 update (#35381) * [Deps] Update CsWinRT to 2.1.5 * [Deps] Update WinAppSDK to 1.6.1 * [Props] Updated WindowsSdkPackageVersion to 22621.48 in Common.Dotnet.CsWinRT.props * Updated NOTICE.md * [Peek] Resolve ambiguity with Color struct --- Directory.Packages.props | 4 ++-- NOTICE.md | 4 ++-- src/Common.Dotnet.CsWinRT.props | 2 +- .../MeasureToolCore/PowerToys.MeasureToolCore.vcxproj | 8 ++++---- src/modules/MeasureTool/MeasureToolCore/packages.config | 2 +- .../Peek.FilePreviewer/Controls/BrowserControl.xaml.cs | 2 +- .../powerrename/PowerRenameUILib/PowerRenameUI.vcxproj | 8 ++++---- src/modules/powerrename/PowerRenameUILib/packages.config | 2 +- 8 files changed, 16 insertions(+), 16 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index d4b27080d5..e0797a6270 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -42,9 +42,9 @@ TODO: in Common.Dotnet.CsWinRT.props, on upgrade, verify RemoveCsWinRTPackageAnalyzer is no longer needed. This is present due to a bug in CsWinRT where WPF projects cause the analyzer to fail. --> - + - + diff --git a/NOTICE.md b/NOTICE.md index ddc6de142e..a8059397e5 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1329,9 +1329,9 @@ EXHIBIT A -Mozilla Public License. - Microsoft.Win32.SystemEvents 8.0.0 - Microsoft.Windows.Compatibility 8.0.10 - Microsoft.Windows.CsWin32 0.2.46-beta -- Microsoft.Windows.CsWinRT 2.1.1 +- Microsoft.Windows.CsWinRT 2.1.5 - Microsoft.Windows.SDK.BuildTools 10.0.22621.2428 -- Microsoft.WindowsAppSDK 1.6.240829007 +- Microsoft.WindowsAppSDK 1.6.240923002 - Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9 - Microsoft.Xaml.Behaviors.Wpf 1.1.39 - ModernWpfUI 0.9.4 diff --git a/src/Common.Dotnet.CsWinRT.props b/src/Common.Dotnet.CsWinRT.props index 514997e71b..1669cba196 100644 --- a/src/Common.Dotnet.CsWinRT.props +++ b/src/Common.Dotnet.CsWinRT.props @@ -2,7 +2,7 @@ - 10.0.22621.38 + 10.0.22621.48 net8.0-windows10.0.22621.0 10.0.19041.0 10.0.19041.0 diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj index 444657646e..b05546d9aa 100644 --- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj +++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj @@ -1,6 +1,6 @@  - + @@ -137,8 +137,8 @@ - + @@ -149,8 +149,8 @@ - - + + \ No newline at end of file diff --git a/src/modules/MeasureTool/MeasureToolCore/packages.config b/src/modules/MeasureTool/MeasureToolCore/packages.config index 7043c92ee0..ed165ac47c 100644 --- a/src/modules/MeasureTool/MeasureToolCore/packages.config +++ b/src/modules/MeasureTool/MeasureToolCore/packages.config @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs b/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs index 1f1f6f9fbe..eda90df8f9 100644 --- a/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs +++ b/src/modules/peek/Peek.FilePreviewer/Controls/BrowserControl.xaml.cs @@ -164,7 +164,7 @@ namespace Peek.FilePreviewer.Controls if (!_originalBackgroundColor.HasValue) { // HACK: We used to store PreviewBrowser.DefaultBackgroundColor here, but WebView started returning transparent when running without a debugger attached. We want html files to be seen as in the browser, which has white as a default background color. - _originalBackgroundColor = Colors.White; + _originalBackgroundColor = Microsoft.UI.Colors.White; } // Setting the background color to transparent when initially loading the WebView2 component. diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj index fc9bd9b1f6..192a1528bd 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj @@ -1,6 +1,6 @@  - + @@ -205,8 +205,8 @@ - + @@ -219,9 +219,9 @@ - - + + diff --git a/src/modules/powerrename/PowerRenameUILib/packages.config b/src/modules/powerrename/PowerRenameUILib/packages.config index 360805a64d..1ff9b46877 100644 --- a/src/modules/powerrename/PowerRenameUILib/packages.config +++ b/src/modules/powerrename/PowerRenameUILib/packages.config @@ -6,5 +6,5 @@ - + \ No newline at end of file From 3a228cc46e0d6ee0001351876e2622133b86d260 Mon Sep 17 00:00:00 2001 From: gokcekantarci <115616017+gokcekantarci@users.noreply.github.com> Date: Fri, 11 Oct 2024 17:51:22 +0300 Subject: [PATCH 19/95] [PTRun]Fix CultureNotFoundException Handling (#35256) --- .../PowerLauncher/ViewModel/MainViewModel.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs b/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs index 809c660cd1..5d6c55b46d 100644 --- a/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs +++ b/src/modules/launcher/PowerLauncher/ViewModel/MainViewModel.cs @@ -1197,9 +1197,17 @@ namespace PowerLauncher.ViewModel public static FlowDirection GetLanguageFlowDirection() { - bool isCurrentLanguageRightToLeft = System.Windows.Input.InputLanguageManager.Current.CurrentInputLanguage.TextInfo.IsRightToLeft; + try + { + bool isCurrentLanguageRightToLeft = System.Windows.Input.InputLanguageManager.Current.CurrentInputLanguage.TextInfo.IsRightToLeft; - return isCurrentLanguageRightToLeft ? FlowDirection.RightToLeft : FlowDirection.LeftToRight; + return isCurrentLanguageRightToLeft ? FlowDirection.RightToLeft : FlowDirection.LeftToRight; + } + catch (CultureNotFoundException ex) + { + Log.Exception($"CultureNotFoundException: {ex.Message}", ex, MethodBase.GetCurrentMethod().DeclaringType); + return FlowDirection.LeftToRight; // default FlowDirection.LeftToRight + } } protected virtual void Dispose(bool disposing) From 1d352c686aa9bbbfa6fdfc8b8f322d6a4613a9a2 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Fri, 11 Oct 2024 11:40:36 -0700 Subject: [PATCH 20/95] removing dup entry in spelling (#35379) Update expect.txt --- .github/actions/spell-check/expect.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 8ff275bc95..83689c8071 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -32,7 +32,6 @@ AFFINETRANSFORM AFX AGGREGATABLE AHybrid -AKV akv ALarger ALLAPPS From 89ec5be5ba717e44981d2f788b70b0ff37974388 Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Fri, 11 Oct 2024 11:56:55 -0700 Subject: [PATCH 21/95] Removing Markdown exclude (#35380) Update ci.yml --- .pipelines/v2/ci.yml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.pipelines/v2/ci.yml b/.pipelines/v2/ci.yml index 5e7bf26676..a68de324ee 100644 --- a/.pipelines/v2/ci.yml +++ b/.pipelines/v2/ci.yml @@ -4,22 +4,22 @@ trigger: include: - main - stable - paths: - exclude: - - doc/* - - temp/* - - tools/* - - '**.md' +# paths: +# exclude: +# - doc/* +# - temp/* +# - tools/* +# - '**.md' pr: branches: include: - main - stable - paths: - exclude: - - '**.md' - - doc +# paths: +# exclude: +# - '**.md' +# - doc name: $(BuildDefinitionName)_$(date:yyMM).$(date:dd)$(rev:rrr) From 9994fd771515e6e5ff1ce22299c8e816cd9df27c Mon Sep 17 00:00:00 2001 From: Seraphima Zykova Date: Mon, 14 Oct 2024 16:51:02 +0300 Subject: [PATCH 22/95] [Workspaces] Sequential launch (#35297) --- .../WorkspacesEditor/Data/ProjectData.cs | 2 + .../WorkspacesEditor/Models/Application.cs | 3 + .../WorkspacesEditor/Models/Project.cs | 1 + .../Utils/WorkspacesEditorIO.cs | 1 + .../WorkspacesLauncher/AppLauncher.cpp | 47 ------- .../WorkspacesLauncher/AppLauncher.h | 4 +- .../WorkspacesLauncher/Launcher.cpp | 93 +++++++++++- .../Workspaces/WorkspacesLauncher/Launcher.h | 18 ++- .../WorkspacesLauncher/LauncherUIHelper.cpp | 4 +- .../WorkspacesLauncher/LauncherUIHelper.h | 2 +- .../WindowArrangerHelper.cpp | 5 + .../WorkspacesLauncher/WindowArrangerHelper.h | 1 + .../Workspaces/WorkspacesLauncher/main.cpp | 32 +++++ .../WorkspacesLauncherUI/App.xaml.cs | 8 ++ .../Data/LaunchingState.cs | 1 + .../ViewModels/MainViewModel.cs | 12 +- .../Workspaces/WorkspacesLib/AppUtils.cpp | 37 +++++ .../Workspaces/WorkspacesLib/AppUtils.h | 5 + .../WorkspacesLib/LaunchingStateEnum.h | 3 +- .../WorkspacesLib/LaunchingStatus.cpp | 133 ++++++++++++++---- .../WorkspacesLib/LaunchingStatus.h | 14 +- .../WorkspacesLib/WorkspacesData.cpp | 9 ++ .../Workspaces/WorkspacesLib/WorkspacesData.h | 1 + .../WindowArranger.cpp | 126 +++++++++++------ .../WorkspacesWindowArranger/WindowArranger.h | 10 +- .../WorkspacesWindowArranger/main.cpp | 5 +- 26 files changed, 419 insertions(+), 158 deletions(-) diff --git a/src/modules/Workspaces/WorkspacesEditor/Data/ProjectData.cs b/src/modules/Workspaces/WorkspacesEditor/Data/ProjectData.cs index 281ac67abc..7eb0a63831 100644 --- a/src/modules/Workspaces/WorkspacesEditor/Data/ProjectData.cs +++ b/src/modules/Workspaces/WorkspacesEditor/Data/ProjectData.cs @@ -25,6 +25,8 @@ namespace WorkspacesEditor.Data public int Height { get; set; } } + public string Id { get; set; } + public string Application { get; set; } public string ApplicationPath { get; set; } diff --git a/src/modules/Workspaces/WorkspacesEditor/Models/Application.cs b/src/modules/Workspaces/WorkspacesEditor/Models/Application.cs index c31087c20c..294d59854d 100644 --- a/src/modules/Workspaces/WorkspacesEditor/Models/Application.cs +++ b/src/modules/Workspaces/WorkspacesEditor/Models/Application.cs @@ -31,6 +31,7 @@ namespace WorkspacesEditor.Models public Application(Application other) { + Id = other.Id; AppName = other.AppName; AppPath = other.AppPath; AppTitle = other.AppTitle; @@ -95,6 +96,8 @@ namespace WorkspacesEditor.Models } } + public string Id { get; set; } + public string AppName { get; set; } public string AppPath { get; set; } diff --git a/src/modules/Workspaces/WorkspacesEditor/Models/Project.cs b/src/modules/Workspaces/WorkspacesEditor/Models/Project.cs index a9a6f407ba..07d7ad40a1 100644 --- a/src/modules/Workspaces/WorkspacesEditor/Models/Project.cs +++ b/src/modules/Workspaces/WorkspacesEditor/Models/Project.cs @@ -251,6 +251,7 @@ namespace WorkspacesEditor.Models { Models.Application newApp = new Models.Application() { + Id = app.Id != null ? app.Id : $"{{{Guid.NewGuid().ToString()}}}", AppName = app.Application, AppPath = app.ApplicationPath, AppTitle = app.Title, diff --git a/src/modules/Workspaces/WorkspacesEditor/Utils/WorkspacesEditorIO.cs b/src/modules/Workspaces/WorkspacesEditor/Utils/WorkspacesEditorIO.cs index da128f2ee4..a3a4bc418d 100644 --- a/src/modules/Workspaces/WorkspacesEditor/Utils/WorkspacesEditorIO.cs +++ b/src/modules/Workspaces/WorkspacesEditor/Utils/WorkspacesEditorIO.cs @@ -97,6 +97,7 @@ namespace WorkspacesEditor.Utils { wrapper.Applications.Add(new ProjectData.ApplicationWrapper { + Id = app.Id, Application = app.AppName, ApplicationPath = app.AppPath, Title = app.AppTitle, diff --git a/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp b/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp index f9aed31fe6..3c58c267f9 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp +++ b/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.cpp @@ -10,8 +10,6 @@ #include -#include - #include using namespace winrt; @@ -20,26 +18,6 @@ using namespace Windows::Management::Deployment; namespace AppLauncher { - void UpdatePackagedApps(std::vector& apps, const Utils::Apps::AppList& installedApps) - { - for (auto& app : apps) - { - // Packaged apps have version in the path, it will be outdated after update. - // We need make sure the current package is up to date. - if (!app.packageFullName.empty()) - { - auto installedApp = std::find_if(installedApps.begin(), installedApps.end(), [&](const Utils::Apps::AppData& val) { return val.name == app.name; }); - if (installedApp != installedApps.end() && app.packageFullName != installedApp->packageFullName) - { - std::wstring exeFileName = app.path.substr(app.path.find_last_of(L"\\") + 1); - app.packageFullName = installedApp->packageFullName; - app.path = installedApp->installPath + L"\\" + exeFileName; - Logger::trace(L"Updated package full name for {}: {}", app.name, app.packageFullName); - } - } - } - } - Result LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs, bool elevated) { std::wstring dir = std::filesystem::path(appPath).parent_path(); @@ -181,29 +159,4 @@ namespace AppLauncher Logger::trace(L"{} {} at {}", app.name, (launched ? L"launched" : L"not launched"), app.path); return launched; } - - bool Launch(WorkspacesData::WorkspacesProject& project, LaunchingStatus& launchingStatus, ErrorList& launchErrors) - { - bool launchedSuccessfully{ true }; - - auto installedApps = Utils::Apps::GetAppsList(); - UpdatePackagedApps(project.apps, installedApps); - - // Launch apps - for (auto& app : project.apps) - { - if (!Launch(app, launchErrors)) - { - Logger::error(L"Failed to launch {}", app.name); - launchingStatus.Update(app, LaunchingState::Failed); - launchedSuccessfully = false; - } - else - { - launchingStatus.Update(app, LaunchingState::Launched); - } - } - - return launchedSuccessfully; - } } \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.h b/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.h index 0827afc4c9..99ddeab6c8 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.h +++ b/src/modules/Workspaces/WorkspacesLauncher/AppLauncher.h @@ -2,6 +2,7 @@ #include +#include #include #include #include @@ -10,7 +11,6 @@ namespace AppLauncher { using ErrorList = std::vector>; + bool Launch(const WorkspacesData::WorkspacesProject::Application& app, ErrorList& launchErrors); Result LaunchApp(const std::wstring& appPath, const std::wstring& commandLineArgs, bool elevated); - - bool Launch(WorkspacesData::WorkspacesProject& project, LaunchingStatus& launchingStatus, ErrorList& launchErrors); } diff --git a/src/modules/Workspaces/WorkspacesLauncher/Launcher.cpp b/src/modules/Workspaces/WorkspacesLauncher/Launcher.cpp index b660d0d939..6483c8f477 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/Launcher.cpp +++ b/src/modules/Workspaces/WorkspacesLauncher/Launcher.cpp @@ -8,6 +8,7 @@ #include #include +#include Launcher::Launcher(const WorkspacesData::WorkspacesProject& project, std::vector& workspaces, @@ -16,10 +17,13 @@ Launcher::Launcher(const WorkspacesData::WorkspacesProject& project, m_workspaces(workspaces), m_invokePoint(invokePoint), m_start(std::chrono::high_resolution_clock::now()), - m_uiHelper(std::make_unique()), + m_uiHelper(std::make_unique(std::bind(&Launcher::handleUIMessage, this, std::placeholders::_1))), m_windowArrangerHelper(std::make_unique(std::bind(&Launcher::handleWindowArrangerMessage, this, std::placeholders::_1))), - m_launchingStatus(m_project, std::bind(&LauncherUIHelper::UpdateLaunchStatus, m_uiHelper.get(), std::placeholders::_1)) + m_launchingStatus(m_project) { + // main thread + Logger::info(L"Launch Workspace {} : {}", m_project.name, m_project.id); + m_uiHelper->LaunchUI(); m_uiHelper->UpdateLaunchStatus(m_launchingStatus.Get()); @@ -48,6 +52,7 @@ Launcher::Launcher(const WorkspacesData::WorkspacesProject& project, Launcher::~Launcher() { + // main thread, will wait until arranger is finished Logger::trace(L"Finalizing launch"); // update last-launched time @@ -86,20 +91,81 @@ Launcher::~Launcher() } } + std::lock_guard lock(m_launchErrorsMutex); Trace::Workspaces::Launch(m_launchedSuccessfully, m_project, m_invokePoint, duration.count(), differentSetup, m_launchErrors); } -void Launcher::Launch() +void Launcher::Launch() // Launching thread { - Logger::info(L"Launch Workspace {} : {}", m_project.name, m_project.id); - m_launchedSuccessfully = AppLauncher::Launch(m_project, m_launchingStatus, m_launchErrors); + const long maxWaitTimeMs = 3000; + const long ms = 100; + + // Launch apps + for (auto appState = m_launchingStatus.GetNext(LaunchingState::Waiting); appState.has_value(); appState = m_launchingStatus.GetNext(LaunchingState::Waiting)) + { + auto app = appState.value().application; + + long waitingTime = 0; + bool additionalWait = false; + while (!m_launchingStatus.AllInstancesOfTheAppLaunchedAndMoved(app) && waitingTime < maxWaitTimeMs) + { + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + waitingTime += ms; + additionalWait = true; + } + + if (additionalWait) + { + // Resolves an issue when Outlook does not launch when launching one after another. + // Launching Outlook instances right one after another causes error message. + // Launching Outlook instances with less than 1-second delay causes the second window not to appear + // even though there wasn't a launch error. + std::this_thread::sleep_for(std::chrono::milliseconds(1000)); + } + + if (waitingTime >= maxWaitTimeMs) + { + Logger::info(L"Waiting time for launching next {} instance expired", app.name); + } + + bool launched{ false }; + { + std::lock_guard lock(m_launchErrorsMutex); + launched = AppLauncher::Launch(app, m_launchErrors); + } + + if (launched) + { + m_launchingStatus.Update(app, LaunchingState::Launched); + } + else + { + Logger::error(L"Failed to launch {}", app.name); + m_launchingStatus.Update(app, LaunchingState::Failed); + m_launchedSuccessfully = false; + } + + auto status = m_launchingStatus.Get(app); // updated after launch status + if (status.has_value()) + { + { + std::lock_guard lock(m_windowArrangerHelperMutex); + m_windowArrangerHelper->UpdateLaunchStatus(status.value()); + } + } + + { + std::lock_guard lock(m_uiHelperMutex); + m_uiHelper->UpdateLaunchStatus(m_launchingStatus.Get()); + } + } } -void Launcher::handleWindowArrangerMessage(const std::wstring& msg) +void Launcher::handleWindowArrangerMessage(const std::wstring& msg) // WorkspacesArranger IPC thread { if (msg == L"ready") { - Launch(); + std::thread([&]() { Launch(); }).detach(); } else { @@ -109,6 +175,11 @@ void Launcher::handleWindowArrangerMessage(const std::wstring& msg) if (data.has_value()) { m_launchingStatus.Update(data.value().application, data.value().state); + + { + std::lock_guard lock(m_uiHelperMutex); + m_uiHelper->UpdateLaunchStatus(m_launchingStatus.Get()); + } } else { @@ -121,3 +192,11 @@ void Launcher::handleWindowArrangerMessage(const std::wstring& msg) } } } + +void Launcher::handleUIMessage(const std::wstring& msg) // UI IPC thread +{ + if (msg == L"cancel") + { + m_launchingStatus.Cancel(); + } +} diff --git a/src/modules/Workspaces/WorkspacesLauncher/Launcher.h b/src/modules/Workspaces/WorkspacesLauncher/Launcher.h index 36f17329d2..9811be25c5 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/Launcher.h +++ b/src/modules/Workspaces/WorkspacesLauncher/Launcher.h @@ -14,18 +14,24 @@ public: Launcher(const WorkspacesData::WorkspacesProject& project, std::vector& workspaces, InvokePoint invokePoint); ~Launcher(); - void Launch(); - private: WorkspacesData::WorkspacesProject m_project; std::vector& m_workspaces; const InvokePoint m_invokePoint; const std::chrono::steady_clock::time_point m_start; - std::unique_ptr m_uiHelper; - std::unique_ptr m_windowArrangerHelper; + std::atomic m_launchedSuccessfully{}; LaunchingStatus m_launchingStatus; - bool m_launchedSuccessfully{}; - std::vector> m_launchErrors{}; + std::unique_ptr m_uiHelper; + std::mutex m_uiHelperMutex; + + std::unique_ptr m_windowArrangerHelper; + std::mutex m_windowArrangerHelperMutex; + + std::vector> m_launchErrors{}; + std::mutex m_launchErrorsMutex; + + void Launch(); void handleWindowArrangerMessage(const std::wstring& msg); + void handleUIMessage(const std::wstring& msg); }; diff --git a/src/modules/Workspaces/WorkspacesLauncher/LauncherUIHelper.cpp b/src/modules/Workspaces/WorkspacesLauncher/LauncherUIHelper.cpp index b35c6d3657..8bcccaec91 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/LauncherUIHelper.cpp +++ b/src/modules/Workspaces/WorkspacesLauncher/LauncherUIHelper.cpp @@ -9,9 +9,9 @@ #include -LauncherUIHelper::LauncherUIHelper() : +LauncherUIHelper::LauncherUIHelper(std::function ipcCallback) : m_processId{}, - m_ipcHelper(IPCHelperStrings::LauncherUIPipeName, IPCHelperStrings::UIPipeName, nullptr) + m_ipcHelper(IPCHelperStrings::LauncherUIPipeName, IPCHelperStrings::UIPipeName, ipcCallback) { } diff --git a/src/modules/Workspaces/WorkspacesLauncher/LauncherUIHelper.h b/src/modules/Workspaces/WorkspacesLauncher/LauncherUIHelper.h index 20704f13a2..bb43543e3f 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/LauncherUIHelper.h +++ b/src/modules/Workspaces/WorkspacesLauncher/LauncherUIHelper.h @@ -6,7 +6,7 @@ class LauncherUIHelper { public: - LauncherUIHelper(); + LauncherUIHelper(std::function ipcCallback); ~LauncherUIHelper(); void LaunchUI(); diff --git a/src/modules/Workspaces/WorkspacesLauncher/WindowArrangerHelper.cpp b/src/modules/Workspaces/WorkspacesLauncher/WindowArrangerHelper.cpp index 600038ea61..cb2075aba9 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/WindowArrangerHelper.cpp +++ b/src/modules/Workspaces/WorkspacesLauncher/WindowArrangerHelper.cpp @@ -69,3 +69,8 @@ void WindowArrangerHelper::Launch(const std::wstring& projectId, bool elevated, Logger::error(L"Failed to launch PowerToys.WorkspacesWindowArranger: {}", res.error()); } } + +void WindowArrangerHelper::UpdateLaunchStatus(const WorkspacesData::LaunchingAppState& appState) const +{ + m_ipcHelper.send(WorkspacesData::AppLaunchInfoJSON::ToJson({ appState.application, nullptr, appState.state }).ToString().c_str()); +} diff --git a/src/modules/Workspaces/WorkspacesLauncher/WindowArrangerHelper.h b/src/modules/Workspaces/WorkspacesLauncher/WindowArrangerHelper.h index 22d7e3ea1c..8e0ee5d473 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/WindowArrangerHelper.h +++ b/src/modules/Workspaces/WorkspacesLauncher/WindowArrangerHelper.h @@ -12,6 +12,7 @@ public: ~WindowArrangerHelper(); void Launch(const std::wstring& projectId, bool elevated, std::function keepWaitingCallback); + void UpdateLaunchStatus(const WorkspacesData::LaunchingAppState& appState) const; private: DWORD m_processId; diff --git a/src/modules/Workspaces/WorkspacesLauncher/main.cpp b/src/modules/Workspaces/WorkspacesLauncher/main.cpp index 9f3d151097..3e7c8c7db1 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/main.cpp +++ b/src/modules/Workspaces/WorkspacesLauncher/main.cpp @@ -13,6 +13,7 @@ #include #include +#include const std::wstring moduleName = L"Workspaces\\WorkspacesLauncher"; const std::wstring internalPath = L""; @@ -161,6 +162,37 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdline, int cm return 1; } + // prepare project in advance + auto installedApps = Utils::Apps::GetAppsList(); + bool updatedApps = Utils::Apps::UpdateWorkspacesApps(projectToLaunch, installedApps); + bool updatedIds = false; + + // verify apps have ids + for (auto& app : projectToLaunch.apps) + { + if (app.id.empty()) + { + app.id = CreateGuidString(); + updatedIds = true; + } + } + + // update the file before launching, so WorkspacesWindowArranger and WorkspacesLauncherUI could get updated app paths + if (updatedApps || updatedIds) + { + for (int i = 0; i < workspaces.size(); i++) + { + if (workspaces[i].id == projectToLaunch.id) + { + workspaces[i] = projectToLaunch; + break; + } + } + + json::to_file(WorkspacesData::WorkspacesFile(), WorkspacesData::WorkspacesListJSON::ToJson(workspaces)); + } + + // launch Launcher launcher(projectToLaunch, workspaces, cmdArgs.invokePoint); Logger::trace("Finished"); diff --git a/src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs b/src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs index a065918523..64686fc139 100644 --- a/src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs +++ b/src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs @@ -37,6 +37,14 @@ namespace WorkspacesLauncherUI { } + public static void SendIPCMessage(string message) + { + if (ipcmanager != null) + { + ipcmanager.Send(message); + } + } + private void OnStartup(object sender, StartupEventArgs e) { Logger.InitializeLogger("\\Workspaces\\WorkspacesLauncherUI"); diff --git a/src/modules/Workspaces/WorkspacesLauncherUI/Data/LaunchingState.cs b/src/modules/Workspaces/WorkspacesLauncherUI/Data/LaunchingState.cs index 9ad8c958d2..67cd951b8b 100644 --- a/src/modules/Workspaces/WorkspacesLauncherUI/Data/LaunchingState.cs +++ b/src/modules/Workspaces/WorkspacesLauncherUI/Data/LaunchingState.cs @@ -11,5 +11,6 @@ namespace WorkspacesLauncherUI.Data Launched, LaunchedAndMoved, Failed, + Canceled, } } diff --git a/src/modules/Workspaces/WorkspacesLauncherUI/ViewModels/MainViewModel.cs b/src/modules/Workspaces/WorkspacesLauncherUI/ViewModels/MainViewModel.cs index ed6cdd8f31..7002c9ef05 100644 --- a/src/modules/Workspaces/WorkspacesLauncherUI/ViewModels/MainViewModel.cs +++ b/src/modules/Workspaces/WorkspacesLauncherUI/ViewModels/MainViewModel.cs @@ -19,7 +19,6 @@ namespace WorkspacesLauncherUI.ViewModels private StatusWindow _snapshotWindow; private int launcherProcessID; - private bool _exiting; public event PropertyChangedEventHandler PropertyChanged; @@ -30,8 +29,6 @@ namespace WorkspacesLauncherUI.ViewModels public MainViewModel() { - _exiting = false; - // receive IPC Message App.IPCMessageReceivedCallback = (string msg) => { @@ -50,11 +47,6 @@ namespace WorkspacesLauncherUI.ViewModels private void HandleAppLaunchingState(AppLaunchData.AppLaunchDataWrapper appLaunchData) { - if (_exiting) - { - return; - } - launcherProcessID = appLaunchData.LauncherProcessID; List appLaunchingList = new List(); foreach (var app in appLaunchData.AppLaunchInfos.AppLaunchInfoList) @@ -90,9 +82,7 @@ namespace WorkspacesLauncherUI.ViewModels internal void CancelLaunch() { - _exiting = true; - Process proc = Process.GetProcessById(launcherProcessID); - proc.Kill(); + App.SendIPCMessage("cancel"); } } } diff --git a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp index 2d69d6f86f..9b65b64b29 100644 --- a/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp +++ b/src/modules/Workspaces/WorkspacesLib/AppUtils.cpp @@ -341,5 +341,42 @@ namespace Utils return Utils::Apps::GetApp(processPath, pid, apps); } + + bool UpdateAppVersion(WorkspacesData::WorkspacesProject::Application& app, const AppList& installedApps) + { + auto installedApp = std::find_if(installedApps.begin(), installedApps.end(), [&](const AppData& val) { return val.name == app.name; }); + if (installedApp == installedApps.end()) + { + return false; + } + + // Packaged apps have version in the path, it will be outdated after update. + // We need make sure the current package is up to date. + if (!app.packageFullName.empty()) + { + if (app.packageFullName != installedApp->packageFullName) + { + std::wstring exeFileName = app.path.substr(app.path.find_last_of(L"\\") + 1); + app.packageFullName = installedApp->packageFullName; + app.path = installedApp->installPath + L"\\" + exeFileName; + Logger::trace(L"Updated package full name for {}: {}", app.name, app.packageFullName); + return true; + } + } + + return false; + } + + bool UpdateWorkspacesApps(WorkspacesData::WorkspacesProject& workspace, const AppList& installedApps) + { + bool updated = false; + for (auto& app : workspace.apps) + { + updated |= UpdateAppVersion(app, installedApps); + } + + return updated; + } + } } \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib/AppUtils.h b/src/modules/Workspaces/WorkspacesLib/AppUtils.h index 596d7d0a85..80c88f2edd 100644 --- a/src/modules/Workspaces/WorkspacesLib/AppUtils.h +++ b/src/modules/Workspaces/WorkspacesLib/AppUtils.h @@ -1,5 +1,7 @@ #pragma once +#include + namespace Utils { namespace Apps @@ -21,5 +23,8 @@ namespace Utils AppList GetAppsList(); std::optional GetApp(const std::wstring& appPath, DWORD pid, const AppList& apps); std::optional GetApp(HWND window, const AppList& apps); + + bool UpdateAppVersion(WorkspacesData::WorkspacesProject::Application& app, const AppList& installedApps); + bool UpdateWorkspacesApps(WorkspacesData::WorkspacesProject& workspace, const AppList& installedApps); } } \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib/LaunchingStateEnum.h b/src/modules/Workspaces/WorkspacesLib/LaunchingStateEnum.h index 2fbaf2fe97..ab229a08f2 100644 --- a/src/modules/Workspaces/WorkspacesLib/LaunchingStateEnum.h +++ b/src/modules/Workspaces/WorkspacesLib/LaunchingStateEnum.h @@ -6,5 +6,6 @@ enum class LaunchingState Waiting = 0, Launched, LaunchedAndMoved, - Failed + Failed, + Canceled, }; \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib/LaunchingStatus.cpp b/src/modules/Workspaces/WorkspacesLib/LaunchingStatus.cpp index 46c4bb6e3f..f41a872313 100644 --- a/src/modules/Workspaces/WorkspacesLib/LaunchingStatus.cpp +++ b/src/modules/Workspaces/WorkspacesLib/LaunchingStatus.cpp @@ -3,8 +3,7 @@ #include -LaunchingStatus::LaunchingStatus(const WorkspacesData::WorkspacesProject& project, std::function updateCallback) : - m_updateCallback(updateCallback) +LaunchingStatus::LaunchingStatus(const WorkspacesData::WorkspacesProject& project) { std::unique_lock lock(m_mutex); for (const auto& app : project.apps) @@ -13,26 +12,6 @@ LaunchingStatus::LaunchingStatus(const WorkspacesData::WorkspacesProject& projec } } -const WorkspacesData::LaunchingAppStateMap& LaunchingStatus::Get() noexcept -{ - std::shared_lock lock(m_mutex); - return m_appsState; -} - -bool LaunchingStatus::AllLaunchedAndMoved() noexcept -{ - std::shared_lock lock(m_mutex); - for (const auto& [app, data] : m_appsState) - { - if (data.state != LaunchingState::Failed && data.state != LaunchingState::LaunchedAndMoved) - { - return false; - } - } - - return true; -} - bool LaunchingStatus::AllLaunched() noexcept { std::shared_lock lock(m_mutex); @@ -47,6 +26,86 @@ bool LaunchingStatus::AllLaunched() noexcept return true; } +bool LaunchingStatus::AllLaunchedAndMoved() noexcept +{ + std::shared_lock lock(m_mutex); + for (const auto& [app, data] : m_appsState) + { + if (data.state != LaunchingState::Failed && + data.state != LaunchingState::Canceled && + data.state != LaunchingState::LaunchedAndMoved) + { + return false; + } + } + + return true; +} + +bool LaunchingStatus::AllInstancesOfTheAppLaunchedAndMoved(const WorkspacesData::WorkspacesProject::Application& application) noexcept +{ + std::shared_lock lock(m_mutex); + + for (const auto& [app, state] : m_appsState) + { + if (app.name == application.name || app.path == application.path) + { + if (state.state == LaunchingState::Launched) + { + return false; + } + } + } + + return true; +} + +const WorkspacesData::LaunchingAppStateMap& LaunchingStatus::Get() noexcept +{ + std::shared_lock lock(m_mutex); + return m_appsState; +} + +std::optional LaunchingStatus::Get(const WorkspacesData::WorkspacesProject::Application& app) noexcept +{ + std::shared_lock lock(m_mutex); + if (m_appsState.contains(app)) + { + return m_appsState.at(app); + } + + return std::nullopt; +} + +std::optional LaunchingStatus::GetNext(LaunchingState state) noexcept +{ + std::shared_lock lock(m_mutex); + for (const auto& [app, appState] : m_appsState) + { + if (appState.state == state) + { + return appState; + } + } + + return std::nullopt; +} + +bool LaunchingStatus::IsWindowProcessed(HWND window) noexcept +{ + std::shared_lock lock(m_mutex); + + for (const auto& [app, state] : m_appsState) + { + if (state.window == window) + { + return true; + } + } + + return false; +} + void LaunchingStatus::Update(const WorkspacesData::WorkspacesProject::Application& app, LaunchingState state) { std::unique_lock lock(m_mutex); @@ -57,9 +116,29 @@ void LaunchingStatus::Update(const WorkspacesData::WorkspacesProject::Applicatio } m_appsState[app].state = state; - - if (m_updateCallback) - { - m_updateCallback(m_appsState); - } } + +void LaunchingStatus::Update(const WorkspacesData::WorkspacesProject::Application& app, HWND window, LaunchingState state) +{ + std::unique_lock lock(m_mutex); + if (!m_appsState.contains(app)) + { + Logger::error(L"Error updating state: app {} is not tracked in the project", app.name); + return; + } + + m_appsState[app].state = state; + m_appsState[app].window = window; +} + +void LaunchingStatus::Cancel() +{ + std::unique_lock lock(m_mutex); + for (auto& [app, state] : m_appsState) + { + if (state.state == LaunchingState::Waiting) + { + state.state = LaunchingState::Canceled; + } + } +} \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesLib/LaunchingStatus.h b/src/modules/Workspaces/WorkspacesLib/LaunchingStatus.h index eec0b1b0f6..2a0b679f08 100644 --- a/src/modules/Workspaces/WorkspacesLib/LaunchingStatus.h +++ b/src/modules/Workspaces/WorkspacesLib/LaunchingStatus.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include @@ -8,17 +7,24 @@ class LaunchingStatus { public: - LaunchingStatus(const WorkspacesData::WorkspacesProject& project, std::function updateCallback); + LaunchingStatus(const WorkspacesData::WorkspacesProject& project); ~LaunchingStatus() = default; - bool AllLaunchedAndMoved() noexcept; bool AllLaunched() noexcept; + bool AllLaunchedAndMoved() noexcept; + bool AllInstancesOfTheAppLaunchedAndMoved(const WorkspacesData::WorkspacesProject::Application& app) noexcept; + const WorkspacesData::LaunchingAppStateMap& Get() noexcept; + std::optional Get(const WorkspacesData::WorkspacesProject::Application& app) noexcept; + std::optional GetNext(LaunchingState state) noexcept; + + bool IsWindowProcessed(HWND window) noexcept; void Update(const WorkspacesData::WorkspacesProject::Application& app, LaunchingState state); + void Update(const WorkspacesData::WorkspacesProject::Application& app, HWND window, LaunchingState state); + void Cancel(); private: WorkspacesData::LaunchingAppStateMap m_appsState; - std::function m_updateCallback; std::shared_mutex m_mutex; }; diff --git a/src/modules/Workspaces/WorkspacesLib/WorkspacesData.cpp b/src/modules/Workspaces/WorkspacesLib/WorkspacesData.cpp index d71619f90b..8a15001eac 100644 --- a/src/modules/Workspaces/WorkspacesLib/WorkspacesData.cpp +++ b/src/modules/Workspaces/WorkspacesLib/WorkspacesData.cpp @@ -3,6 +3,8 @@ #include +#include + namespace NonLocalizable { const inline wchar_t ModuleKey[] = L"Workspaces"; @@ -72,6 +74,7 @@ namespace WorkspacesData namespace NonLocalizable { + const static wchar_t* AppIdID = L"id"; const static wchar_t* AppNameID = L"application"; const static wchar_t* AppPathID = L"application-path"; const static wchar_t* AppPackageFullNameID = L"package-full-name"; @@ -89,6 +92,7 @@ namespace WorkspacesData json::JsonObject ToJson(const WorkspacesProject::Application& data) { json::JsonObject json{}; + json.SetNamedValue(NonLocalizable::AppIdID, json::value(data.id)); json.SetNamedValue(NonLocalizable::AppNameID, json::value(data.name)); json.SetNamedValue(NonLocalizable::AppPathID, json::value(data.path)); json.SetNamedValue(NonLocalizable::AppTitleID, json::value(data.title)); @@ -110,6 +114,11 @@ namespace WorkspacesData WorkspacesProject::Application result; try { + if (json.HasKey(NonLocalizable::AppIdID)) + { + result.id = json.GetNamedString(NonLocalizable::AppIdID); + } + if (json.HasKey(NonLocalizable::AppNameID)) { result.name = json.GetNamedString(NonLocalizable::AppNameID); diff --git a/src/modules/Workspaces/WorkspacesLib/WorkspacesData.h b/src/modules/Workspaces/WorkspacesLib/WorkspacesData.h index 40252850a3..d728337996 100644 --- a/src/modules/Workspaces/WorkspacesLib/WorkspacesData.h +++ b/src/modules/Workspaces/WorkspacesLib/WorkspacesData.h @@ -25,6 +25,7 @@ namespace WorkspacesData auto operator<=>(const Position&) const = default; }; + std::wstring id; std::wstring name; std::wstring title; std::wstring path; diff --git a/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp b/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp index 18bd2a049f..5c176c173f 100644 --- a/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp +++ b/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.cpp @@ -94,45 +94,47 @@ namespace FancyZones } -WindowArranger::WindowArranger(WorkspacesData::WorkspacesProject project, const IPCHelper& ipcHelper) : +WindowArranger::WindowArranger(WorkspacesData::WorkspacesProject project) : m_project(project), m_windowsBefore(WindowEnumerator::Enumerate(WindowFilter::Filter)), m_monitors(MonitorUtils::IdentifyMonitors()), m_installedApps(Utils::Apps::GetAppsList()), //m_windowCreationHandler(std::bind(&WindowArranger::onWindowCreated, this, std::placeholders::_1)), - m_ipcHelper(ipcHelper) + m_ipcHelper(IPCHelperStrings::WindowArrangerPipeName, IPCHelperStrings::LauncherArrangerPipeName, std::bind(&WindowArranger::receiveIpcMessage, this, std::placeholders::_1)), + m_launchingStatus(m_project) { - for (auto& app : project.apps) - { - m_launchingApps.insert({ app, { app, nullptr } }); - } - m_ipcHelper.send(L"ready"); - for (int attempt = 0; attempt < 50 && !allWindowsFound(); attempt++) - { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); + const long maxLaunchingWaitingTime = 10000, maxRepositionWaitingTime = 3000, ms = 300; + long waitingTime{ 0 }; - std::vector windowsAfter = WindowEnumerator::Enumerate(WindowFilter::Filter); - std::vector windowsDiff{}; - std::copy_if(windowsAfter.begin(), windowsAfter.end(), std::back_inserter(windowsDiff), [&](HWND window) { return std::find(m_windowsBefore.begin(), m_windowsBefore.end(), window) == m_windowsBefore.end(); }); - - for (HWND window : windowsDiff) - { - processWindow(window); - } + // process launching windows + while (!m_launchingStatus.AllLaunched() && waitingTime < maxLaunchingWaitingTime) + { + processWindows(false); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + waitingTime += ms; } - bool allFound = allWindowsFound(); - Logger::info(L"Finished moving new windows, all windows found: {}", allFound); - - if (!allFound) + if (waitingTime >= maxLaunchingWaitingTime) { - std::vector allWindows = WindowEnumerator::Enumerate(WindowFilter::Filter); - for (HWND window : allWindows) - { - processWindow(window); - } + Logger::info(L"Launching timeout expired"); + } + + Logger::info(L"Finished moving new windows"); + + // wait for 3 seconds after all apps launched + waitingTime = 0; + while (!m_launchingStatus.AllLaunchedAndMoved() && waitingTime < maxRepositionWaitingTime) + { + processWindows(true); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + waitingTime += ms; + } + + if (waitingTime >= maxRepositionWaitingTime) + { + Logger::info(L"Repositioning timeout expired"); } } @@ -146,11 +148,26 @@ WindowArranger::WindowArranger(WorkspacesData::WorkspacesProject project, const // processWindow(window); //} +void WindowArranger::processWindows(bool processAll) +{ + std::vector windows = WindowEnumerator::Enumerate(WindowFilter::Filter); + + if (!processAll) + { + std::vector windowsDiff{}; + std::copy_if(windows.begin(), windows.end(), std::back_inserter(windowsDiff), [&](HWND window) { return std::find(m_windowsBefore.begin(), m_windowsBefore.end(), window) == m_windowsBefore.end(); }); + windows = windowsDiff; + } + + for (HWND window : windows) + { + processWindow(window); + } +} + void WindowArranger::processWindow(HWND window) { - // check if this window is already handled - auto windowIter = std::find_if(m_launchingApps.begin(), m_launchingApps.end(), [&](const auto& val) { return val.second.window == window; }); - if (windowIter != m_launchingApps.end()) + if (m_launchingStatus.IsWindowProcessed(window)) { return; } @@ -176,27 +193,34 @@ void WindowArranger::processWindow(HWND window) return; } - auto iter = std::find_if(m_launchingApps.begin(), m_launchingApps.end(), [&](const auto& val) + const auto& apps = m_launchingStatus.Get(); + auto iter = std::find_if(apps.begin(), apps.end(), [&](const auto& val) { - return val.second.state == LaunchingState::Waiting && !val.second.window && (val.first.name == data.value().name || val.first.path == data.value().installPath); + return val.second.state == LaunchingState::Launched && + !val.second.window && + (val.first.name == data.value().name || val.first.path == data.value().installPath); }); - if (iter == m_launchingApps.end()) + + if (iter == apps.end()) { - Logger::info(L"A window of {} is not in the project", processPath); + Logger::info(L"Skip {}", processPath); return; } - iter->second.window = window; if (moveWindow(window, iter->first)) { - iter->second.state = LaunchingState::LaunchedAndMoved; + m_launchingStatus.Update(iter->first, window, LaunchingState::LaunchedAndMoved); } else { - iter->second.state = LaunchingState::Failed; + m_launchingStatus.Update(iter->first, window, LaunchingState::Failed); } - m_ipcHelper.send(WorkspacesData::AppLaunchInfoJSON::ToJson({iter->first, nullptr, iter->second.state}).ToString().c_str()); + auto state = m_launchingStatus.Get(iter->first); + if (state.has_value()) + { + sendUpdatedState(state.value()); + } } bool WindowArranger::moveWindow(HWND window, const WorkspacesData::WorkspacesProject::Application& app) @@ -247,9 +271,27 @@ bool WindowArranger::moveWindow(HWND window, const WorkspacesData::WorkspacesPro } } -bool WindowArranger::allWindowsFound() const +void WindowArranger::receiveIpcMessage(const std::wstring& message) { - return std::find_if(m_launchingApps.begin(), m_launchingApps.end(), [&](const std::pair& val) { - return val.second.window == nullptr; - }) == m_launchingApps.end(); + try + { + auto data = WorkspacesData::AppLaunchInfoJSON::FromJson(json::JsonValue::Parse(message).GetObjectW()); + if (data.has_value()) + { + m_launchingStatus.Update(data.value().application, data.value().state); + } + else + { + Logger::error(L"Failed to parse message from WorkspacesLauncher"); + } + } + catch (const winrt::hresult_error&) + { + Logger::error(L"Failed to parse message from WorkspacesLauncher"); + } +} + +void WindowArranger::sendUpdatedState(const WorkspacesData::LaunchingAppState& data) const +{ + m_ipcHelper.send(WorkspacesData::AppLaunchInfoJSON::ToJson({ data.application, nullptr, data.state }).ToString().c_str()); } diff --git a/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.h b/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.h index e18b52829b..da708e550f 100644 --- a/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.h +++ b/src/modules/Workspaces/WorkspacesWindowArranger/WindowArranger.h @@ -10,7 +10,7 @@ class WindowArranger { public: - WindowArranger(WorkspacesData::WorkspacesProject project, const IPCHelper& ipcHelper); + WindowArranger(WorkspacesData::WorkspacesProject project); ~WindowArranger() = default; private: @@ -19,12 +19,14 @@ private: const std::vector m_monitors; const Utils::Apps::AppList m_installedApps; //const WindowCreationHandler m_windowCreationHandler; - const IPCHelper& m_ipcHelper; - WorkspacesData::LaunchingAppStateMap m_launchingApps{}; + IPCHelper m_ipcHelper; + LaunchingStatus m_launchingStatus; //void onWindowCreated(HWND window); + void processWindows(bool processAll); void processWindow(HWND window); bool moveWindow(HWND window, const WorkspacesData::WorkspacesProject::Application& app); - bool allWindowsFound() const; + void receiveIpcMessage(const std::wstring& message); + void sendUpdatedState(const WorkspacesData::LaunchingAppState& data) const; }; diff --git a/src/modules/Workspaces/WorkspacesWindowArranger/main.cpp b/src/modules/Workspaces/WorkspacesWindowArranger/main.cpp index 1c6a72dae5..0d95243694 100644 --- a/src/modules/Workspaces/WorkspacesWindowArranger/main.cpp +++ b/src/modules/Workspaces/WorkspacesWindowArranger/main.cpp @@ -90,12 +90,9 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdline, int cm return 1; } - // IPC - IPCHelper ipc(IPCHelperStrings::WindowArrangerPipeName, IPCHelperStrings::LauncherArrangerPipeName, nullptr); - // arrange windows Logger::info(L"Arrange windows from Workspace {} : {}", projectToLaunch.name, projectToLaunch.id); - WindowArranger windowArranger(projectToLaunch, ipc); + WindowArranger windowArranger(projectToLaunch); //run_message_loop(); Logger::debug(L"Arranger finished"); From d51ca9f8d4ebf15dee7060315e75aca3ccf941f1 Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Mon, 14 Oct 2024 14:59:10 +0100 Subject: [PATCH 23/95] [ci]Fix running xaml styles in CI (#35426) XAML style checkers aren't running right now in PR CI. This allowed some XAML style errors to cause build errors in release CI. This PR contains the following fixes: - Fix XAML style of files that have slipped. - Add errors to the scripts that depend on dotnet commands if it fails. - Add .NET 6 on CI so that applyXamlStyling.ps1 and verifyNugetPackages.ps1 run correctly again. --- .pipelines/applyXamlStyling.ps1 | 5 ++++- .pipelines/v2/templates/job-build-project.yml | 10 ++++------ .pipelines/verifyNugetPackages.ps1 | 6 ++++++ src/modules/launcher/PowerLauncher/MainWindow.xaml | 4 ++-- src/modules/launcher/PowerLauncher/ResultList.xaml | 4 ++-- 5 files changed, 18 insertions(+), 11 deletions(-) diff --git a/.pipelines/applyXamlStyling.ps1 b/.pipelines/applyXamlStyling.ps1 index 8a9dd1b1df..e47edd0c0f 100644 --- a/.pipelines/applyXamlStyling.ps1 +++ b/.pipelines/applyXamlStyling.ps1 @@ -117,7 +117,10 @@ else { Write-Error 'XAML Styling is incorrect, please run `.\.pipelines\applyXamlStyling.ps1 -Main` locally.' } - + if ($lastExitCode -lt 0) + { + Write-Error "Error running dotnet tool run, with the exit code $lastExitCode. Please verify logs and running environment." + } # Return XAML Styler Status exit $lastExitCode } diff --git a/.pipelines/v2/templates/job-build-project.yml b/.pipelines/v2/templates/job-build-project.yml index d24158658b..565ba3cab7 100644 --- a/.pipelines/v2/templates/job-build-project.yml +++ b/.pipelines/v2/templates/job-build-project.yml @@ -127,12 +127,10 @@ jobs: Write-Host "##vso[task.setvariable variable=MSBuildCacheParameters]$MSBuildCacheParameters" displayName: Prepare MSBuildCache variables - - ${{ if eq(parameters.codeSign, true) }}: - # Only required if we're using ESRP - - template: steps-ensure-dotnet-version.yml - parameters: - sdk: true - version: '6.0' + - template: steps-ensure-dotnet-version.yml + parameters: + sdk: true + version: '6.0' - template: steps-ensure-dotnet-version.yml parameters: diff --git a/.pipelines/verifyNugetPackages.ps1 b/.pipelines/verifyNugetPackages.ps1 index 1fcc5237f0..54d0137121 100644 --- a/.pipelines/verifyNugetPackages.ps1 +++ b/.pipelines/verifyNugetPackages.ps1 @@ -8,6 +8,12 @@ Write-Host "Verifying Nuget packages for $solution" dotnet tool restore dotnet consolidate -s $solution +if ($lastExitCode -ne 0) +{ + $result = $lastExitCode + Write-Error "Error running dotnet consolidate, with the exit code $lastExitCode. Please verify logs and running environment." + exit $result +} if (-not $?) { diff --git a/src/modules/launcher/PowerLauncher/MainWindow.xaml b/src/modules/launcher/PowerLauncher/MainWindow.xaml index 30acc5c3c0..c8f1111e06 100644 --- a/src/modules/launcher/PowerLauncher/MainWindow.xaml +++ b/src/modules/launcher/PowerLauncher/MainWindow.xaml @@ -72,10 +72,10 @@ x:Name="pluginsHintsList" Grid.Row="1" Margin="16,0,0,0" + Background="Transparent" + BorderBrush="Transparent" ItemContainerStyle="{StaticResource PluginsListViewItemStyle}" ItemsSource="{Binding Plugins}" - Background="Transparent" - BorderBrush="Transparent" PreviewMouseLeftButtonUp="PluginsHintsList_PreviewMouseLeftButtonUp" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto" diff --git a/src/modules/launcher/PowerLauncher/ResultList.xaml b/src/modules/launcher/PowerLauncher/ResultList.xaml index 7e387e9807..b829d5218f 100644 --- a/src/modules/launcher/PowerLauncher/ResultList.xaml +++ b/src/modules/launcher/PowerLauncher/ResultList.xaml @@ -32,9 +32,9 @@ Date: Thu, 17 Oct 2024 05:14:57 -0400 Subject: [PATCH 24/95] [Analyzers] Resolve Stylecop SA1516 violations and others to enable fully building on VS 17.12 (#35248) * [Analyzers][Settings] Fix SA1516 * [Analyzers][Workspaces] Fix SA1516 * [Analyzers][Awake] Fix SA1516 * [Analyzers][Wox] Fix SA1516 * [MWB] Disable CA1716 warning on class name * [Wox] Update ExecuteFilePath property visibility for Json Source Generator * [Analyzers][MWB] Fix CA1716 on NativeMethods. --------- Co-authored-by: Jaime Bernardo --- src/modules/MouseWithoutBorders/App/Class/Extensions.cs | 3 +++ src/modules/MouseWithoutBorders/App/Class/NativeMethods.cs | 3 +++ src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs | 1 + .../Workspaces/WorkspacesLauncherUI/Data/AppLaunchData.cs | 2 ++ .../Workspaces/WorkspacesLauncherUI/Data/AppLaunchInfoData.cs | 1 + .../Workspaces/WorkspacesLauncherUI/Models/AppLaunching.cs | 1 + .../WorkspacesLauncherUI/ViewModels/MainViewModel.cs | 1 + src/modules/awake/Awake/Core/Manager.cs | 1 + .../Awake/Core/Threading/SingleThreadSynchronizationContext.cs | 1 + src/modules/awake/Awake/Core/TrayHelper.cs | 1 + src/modules/awake/Awake/Program.cs | 1 + src/modules/launcher/Wox.Plugin/PluginLoadContext.cs | 1 + src/modules/launcher/Wox.Plugin/PluginMetadata.cs | 3 ++- src/settings-ui/Settings.UI.Library/EnabledModules.cs | 1 + .../Settings.UI.Library/EnvironmentVariablesProperties.cs | 1 + .../Settings.UI.Library/EnvironmentVariablesSettings.cs | 1 + src/settings-ui/Settings.UI.Library/FZConfigProperties.cs | 1 + src/settings-ui/Settings.UI.Library/FancyZonesSettings.cs | 1 + .../Settings.UI.Library/FileLocksmithLocalProperties.cs | 1 + src/settings-ui/Settings.UI.Library/FileLocksmithSettings.cs | 1 + src/settings-ui/Settings.UI.Library/FindMyMouseProperties.cs | 1 + src/settings-ui/Settings.UI.Library/FindMyMouseSettings.cs | 1 + src/settings-ui/Settings.UI.Library/GeneralSettings.cs | 1 + src/settings-ui/Settings.UI.Library/Helpers/ColorNameHelper.cs | 1 + src/settings-ui/Settings.UI.Library/HostsProperties.cs | 1 + src/settings-ui/Settings.UI.Library/HostsSettings.cs | 1 + src/settings-ui/Settings.UI.Library/HotkeySettings.cs | 1 + .../Settings.UI.Library/HotkeySettingsControlHook.cs | 1 + src/settings-ui/Settings.UI.Library/ISettingsUtils.cs | 1 + src/settings-ui/Settings.UI.Library/ImageResizerProperties.cs | 1 + src/settings-ui/Settings.UI.Library/ImageResizerSettings.cs | 1 + src/settings-ui/Settings.UI.Library/KeyboardManagerProfile.cs | 1 + .../Settings.UI.Library/KeyboardManagerProperties.cs | 1 + src/settings-ui/Settings.UI.Library/KeyboardManagerSettings.cs | 1 + src/settings-ui/Settings.UI.Library/KeysDataModel.cs | 1 + src/settings-ui/Settings.UI.Library/MeasureToolProperties.cs | 1 + src/settings-ui/Settings.UI.Library/MeasureToolSettings.cs | 1 + .../Settings.UI.Library/MouseHighlighterProperties.cs | 1 + .../Settings.UI.Library/MouseHighlighterSettings.cs | 1 + src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs | 1 + src/settings-ui/Settings.UI.Library/MouseJumpSettings.cs | 1 + .../Settings.UI.Library/MousePointerCrosshairsProperties.cs | 1 + .../Settings.UI.Library/MousePointerCrosshairsSettings.cs | 1 + .../Settings.UI.Library/MouseWithoutBordersProperties.cs | 1 + .../Settings.UI.Library/MouseWithoutBordersSettings.cs | 1 + src/settings-ui/Settings.UI.Library/NewPlusSettings.cs | 1 + src/settings-ui/Settings.UI.Library/PeekPreviewSettings.cs | 1 + src/settings-ui/Settings.UI.Library/PeekProperties.cs | 1 + src/settings-ui/Settings.UI.Library/PeekSettings.cs | 1 + src/settings-ui/Settings.UI.Library/PowerAccentProperties.cs | 1 + src/settings-ui/Settings.UI.Library/PowerAccentSettings.cs | 1 + src/settings-ui/Settings.UI.Library/PowerLauncherProperties.cs | 1 + src/settings-ui/Settings.UI.Library/PowerLauncherSettings.cs | 1 + src/settings-ui/Settings.UI.Library/PowerOcrProperties.cs | 1 + src/settings-ui/Settings.UI.Library/PowerOcrSettings.cs | 1 + src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs | 1 + src/settings-ui/Settings.UI.Library/PowerPreviewSettings.cs | 1 + .../Settings.UI.Library/PowerRenameLocalProperties.cs | 1 + src/settings-ui/Settings.UI.Library/PowerRenameProperties.cs | 1 + src/settings-ui/Settings.UI.Library/PowerRenameSettings.cs | 1 + src/settings-ui/Settings.UI.Library/RegistryPreviewSettings.cs | 1 + src/settings-ui/Settings.UI.Library/SettingPath.cs | 1 + .../Settings.UI.Library/SettingsBackupAndRestoreUtils.cs | 1 + src/settings-ui/Settings.UI.Library/SettingsRepository`1.cs | 1 + src/settings-ui/Settings.UI.Library/SettingsUtils.cs | 1 + src/settings-ui/Settings.UI.Library/ShortcutGuideProperties.cs | 1 + src/settings-ui/Settings.UI.Library/ShortcutGuideSettings.cs | 1 + .../Telemetry/Events/CmdNotFoundInstallEvent.cs | 1 + .../Telemetry/Events/CmdNotFoundUninstallEvent.cs | 1 + .../Settings.UI.Library/Telemetry/Events/OobeModuleRunEvent.cs | 1 + .../Settings.UI.Library/Telemetry/Events/OobeSectionEvent.cs | 1 + .../Settings.UI.Library/Telemetry/Events/OobeSettingsEvent.cs | 1 + .../Settings.UI.Library/Telemetry/Events/OobeStartedEvent.cs | 1 + .../Telemetry/Events/OobeVariantAssignmentEvent.cs | 1 + .../Settings.UI.Library/Telemetry/Events/ScoobeStartedEvent.cs | 1 + .../Settings.UI.Library/Telemetry/Events/SettingsBootEvent.cs | 1 + .../Telemetry/Events/SettingsEnabledEvent.cs | 1 + .../Telemetry/Events/TrayFlyoutActivatedEvent.cs | 1 + .../Telemetry/Events/TrayFlyoutModuleRunEvent.cs | 1 + .../Settings.UI.Library/Utilities/CommandLineUtils.cs | 1 + .../Utilities/GetSettingCommandLineCommand.cs | 1 + src/settings-ui/Settings.UI.Library/Utilities/Helper.cs | 1 + .../Utilities/SetAdditionalSettingsCommandLineCommand.cs | 1 + .../Utilities/SetSettingCommandLineCommand.cs | 1 + .../Settings.UI.Library/VideoConferenceConfigProperties.cs | 1 + src/settings-ui/Settings.UI.Library/VideoConferenceSettings.cs | 1 + src/settings-ui/Settings.UI.Library/WorkspacesSettings.cs | 1 + .../BackwardsCompatibility/BackCompatTestProperties.cs | 1 + .../Settings.UI.UnitTests/Cmd/SetSettingCommandTests.cs | 1 + .../Settings.UI.UnitTests/Mocks/IIOProviderMocks.cs | 1 + .../Settings.UI.UnitTests/ModelsTests/HelperTest.cs | 1 + .../Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs | 1 + .../Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs | 1 + .../Settings.UI.UnitTests/ViewModelTests/FancyZones.cs | 1 + .../Settings.UI.UnitTests/ViewModelTests/General.cs | 2 ++ .../Settings.UI.UnitTests/ViewModelTests/ImageResizer.cs | 1 + .../Settings.UI.UnitTests/ViewModelTests/KeyboardManager.cs | 1 + .../ViewModelTests/PowerLauncherViewModelTest.cs | 1 + .../Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs | 1 + .../Settings.UI.UnitTests/ViewModelTests/PowerRename.cs | 1 + .../Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs | 1 + .../Settings.UI/Activation/DefaultActivationHandler.cs | 1 + .../Settings.UI/Converters/AwakeModeToIntConverter.cs | 1 + src/settings-ui/Settings.UI/Converters/ColorFormatConverter.cs | 1 + .../Settings.UI/Converters/ImageResizerFitToIntConverter.cs | 1 + .../Settings.UI/Converters/ImageResizerFitToStringConverter.cs | 1 + .../Settings.UI/Converters/ImageResizerUnitToIntConverter.cs | 1 + .../Converters/ImageResizerUnitToStringConverter.cs | 1 + .../Converters/IndexBitFieldToVisibilityConverter.cs | 1 + .../Settings.UI/Converters/RunOptionTemplateSelector.cs | 1 + .../Settings.UI/Converters/StringToInfoBarSeverityConverter.cs | 1 + .../Settings.UI/Converters/UpdateStateToBoolConverter.cs | 1 + src/settings-ui/Settings.UI/Helpers/NavHelper.cs | 1 + src/settings-ui/Settings.UI/Helpers/StartProcessHelper.cs | 1 + src/settings-ui/Settings.UI/Helpers/WindowHelper.cs | 1 + src/settings-ui/Settings.UI/Services/ActivationService.cs | 1 + src/settings-ui/Settings.UI/Services/NavigationService.cs | 1 + src/settings-ui/Settings.UI/Services/ThemeService.cs | 1 + src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs | 1 + .../SettingsXAML/Controls/CheckBoxWithDescriptionControl.cs | 1 + .../SettingsXAML/Controls/ColorFormatEditor.xaml.cs | 1 + .../SettingsXAML/Controls/FancyZonesPreviewControl.xaml.cs | 1 + .../Controls/IsEnabledTextBlock/IsEnabledTextBlock.cs | 1 + .../SettingsXAML/Controls/PowerAccentShortcutControl.xaml.cs | 1 + .../SettingsXAML/Controls/SettingsGroup/SettingsGroup.cs | 1 + .../Controls/SettingsPageControl/SettingsPageControl.xaml.cs | 1 + .../Controls/ShortcutControl/ShortcutControl.xaml.cs | 1 + .../ShortcutControl/ShortcutDialogContentControl.xaml.cs | 1 + .../ShortcutControl/ShortcutWithTextLabelControl.xaml.cs | 1 + .../Settings.UI/SettingsXAML/Flyout/AppsListPage.xaml.cs | 1 + .../Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs | 1 + src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs | 1 + .../SettingsXAML/OOBE/Views/OobeColorPicker.xaml.cs | 1 + .../SettingsXAML/OOBE/Views/OobeEnvironmentVariables.xaml.cs | 1 + .../Settings.UI/SettingsXAML/OOBE/Views/OobeHosts.xaml.cs | 1 + .../SettingsXAML/OOBE/Views/OobeOverviewPlaceholder.xaml.cs | 1 + .../Settings.UI/SettingsXAML/OOBE/Views/OobeRun.xaml.cs | 1 + .../Settings.UI/SettingsXAML/OOBE/Views/OobeShellPage.xaml.cs | 1 + .../SettingsXAML/OOBE/Views/OobeShortcutGuide.xaml.cs | 1 + .../Settings.UI/SettingsXAML/OOBE/Views/OobeWhatsNew.xaml.cs | 1 + src/settings-ui/Settings.UI/SettingsXAML/OobeWindow.xaml.cs | 1 + .../Settings.UI/SettingsXAML/Views/AdvancedPaste.xaml.cs | 1 + .../Settings.UI/SettingsXAML/Views/AwakePage.xaml.cs | 1 + .../Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml.cs | 1 + .../Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs | 1 + .../Settings.UI/SettingsXAML/Views/GeneralPage.xaml.cs | 1 + .../Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml.cs | 1 + .../Settings.UI/SettingsXAML/Views/KeyboardManagerPage.xaml.cs | 1 + .../Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml.cs | 1 + .../SettingsXAML/Views/MouseWithoutBordersPage.xaml.cs | 2 ++ .../Settings.UI/SettingsXAML/Views/NewPlusPage.xaml.cs | 1 + .../Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml.cs | 1 + .../Settings.UI/SettingsXAML/Views/ShellPage.xaml.cs | 1 + .../Settings.UI/SettingsXAML/Views/VideoConference.xaml.cs | 1 + .../Settings.UI/ViewModels/AdvancedPasteViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/AlwaysOnTopViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/AwakeViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/CmdNotFoundViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/CropAndLockViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/DashboardListItem.cs | 1 + src/settings-ui/Settings.UI/ViewModels/DashboardModuleItem.cs | 1 + src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs | 1 + .../Settings.UI/ViewModels/EnvironmentVariablesViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/FancyZonesViewModel.cs | 1 + .../Settings.UI/ViewModels/FileLocksmithViewModel.cs | 1 + .../Settings.UI/ViewModels/Flyout/AllAppsViewModel.cs | 1 + .../Settings.UI/ViewModels/Flyout/FlyoutMenuItem.cs | 1 + .../Settings.UI/ViewModels/Flyout/LauncherViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/HostsViewModel.cs | 1 + .../Settings.UI/ViewModels/ImageResizerViewModel.cs | 1 + .../Settings.UI/ViewModels/KeyboardManagerViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/MeasureToolViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs | 1 + .../Settings.UI/ViewModels/MouseWithoutBordersViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/NewPlusViewModel.cs | 2 ++ src/settings-ui/Settings.UI/ViewModels/PeekViewModel.cs | 1 + .../Settings.UI/ViewModels/PluginAdditionalOptionViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs | 1 + .../Settings.UI/ViewModels/PowerLauncherPluginViewModel.cs | 1 + .../Settings.UI/ViewModels/PowerLauncherViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/PowerOcrViewModel.cs | 1 + .../Settings.UI/ViewModels/PowerPreviewViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/PowerRenameViewModel.cs | 1 + .../Settings.UI/ViewModels/RegistryPreviewViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/ShellViewModel.cs | 1 + .../Settings.UI/ViewModels/ShortcutGuideViewModel.cs | 1 + .../Settings.UI/ViewModels/VideoConferenceViewModel.cs | 1 + src/settings-ui/Settings.UI/ViewModels/WorkspacesViewModel.cs | 1 + 190 files changed, 199 insertions(+), 1 deletion(-) diff --git a/src/modules/MouseWithoutBorders/App/Class/Extensions.cs b/src/modules/MouseWithoutBorders/App/Class/Extensions.cs index e199a5797c..904a14a3a5 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Extensions.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Extensions.cs @@ -6,7 +6,10 @@ using System.ComponentModel; using System.Diagnostics; using System.IO; +// Disable the warning to preserve original code +#pragma warning disable CA1716 namespace MouseWithoutBorders.Class +#pragma warning restore CA1716 { internal static class Extensions { diff --git a/src/modules/MouseWithoutBorders/App/Class/NativeMethods.cs b/src/modules/MouseWithoutBorders/App/Class/NativeMethods.cs index 758b41d331..01371bb2ac 100644 --- a/src/modules/MouseWithoutBorders/App/Class/NativeMethods.cs +++ b/src/modules/MouseWithoutBorders/App/Class/NativeMethods.cs @@ -36,7 +36,10 @@ using System.Text; [module: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope = "member", Target = "MouseWithoutBorders.NativeMethods.#GetAsyncKeyState(System.IntPtr)", MessageId = "0", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", Scope = "member", Target = "MouseWithoutBorders.NativeMethods.#GetAsyncKeyState(System.IntPtr)", MessageId = "return", Justification = "Dotnet port with style preservation")] +// Disable the warning to preserve original code +#pragma warning disable CA1716 namespace MouseWithoutBorders.Class +#pragma warning restore CA1716 { internal partial class NativeMethods { diff --git a/src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs b/src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs index 64686fc139..3359c001d5 100644 --- a/src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs +++ b/src/modules/Workspaces/WorkspacesLauncherUI/App.xaml.cs @@ -6,6 +6,7 @@ using System; using System.Globalization; using System.Threading; using System.Windows; + using Common.UI; using ManagedCommon; using PowerToys.Interop; diff --git a/src/modules/Workspaces/WorkspacesLauncherUI/Data/AppLaunchData.cs b/src/modules/Workspaces/WorkspacesLauncherUI/Data/AppLaunchData.cs index dc19b86647..6e9ad24379 100644 --- a/src/modules/Workspaces/WorkspacesLauncherUI/Data/AppLaunchData.cs +++ b/src/modules/Workspaces/WorkspacesLauncherUI/Data/AppLaunchData.cs @@ -3,7 +3,9 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Workspaces.Data; + using static WorkspacesLauncherUI.Data.AppLaunchData; using static WorkspacesLauncherUI.Data.AppLaunchInfosData; diff --git a/src/modules/Workspaces/WorkspacesLauncherUI/Data/AppLaunchInfoData.cs b/src/modules/Workspaces/WorkspacesLauncherUI/Data/AppLaunchInfoData.cs index aa64510ba9..c01ffaba8c 100644 --- a/src/modules/Workspaces/WorkspacesLauncherUI/Data/AppLaunchInfoData.cs +++ b/src/modules/Workspaces/WorkspacesLauncherUI/Data/AppLaunchInfoData.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Workspaces.Data; using static WorkspacesLauncherUI.Data.AppLaunchInfoData; diff --git a/src/modules/Workspaces/WorkspacesLauncherUI/Models/AppLaunching.cs b/src/modules/Workspaces/WorkspacesLauncherUI/Models/AppLaunching.cs index f3878a3847..073ee9c864 100644 --- a/src/modules/Workspaces/WorkspacesLauncherUI/Models/AppLaunching.cs +++ b/src/modules/Workspaces/WorkspacesLauncherUI/Models/AppLaunching.cs @@ -13,6 +13,7 @@ using System.Text.Json.Serialization; using System.Text.RegularExpressions; using System.Windows.Media; using System.Windows.Media.Imaging; + using ManagedCommon; using Windows.Management.Deployment; using WorkspacesLauncherUI.Data; diff --git a/src/modules/Workspaces/WorkspacesLauncherUI/ViewModels/MainViewModel.cs b/src/modules/Workspaces/WorkspacesLauncherUI/ViewModels/MainViewModel.cs index 7002c9ef05..103b08d6f7 100644 --- a/src/modules/Workspaces/WorkspacesLauncherUI/ViewModels/MainViewModel.cs +++ b/src/modules/Workspaces/WorkspacesLauncherUI/ViewModels/MainViewModel.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; + using ManagedCommon; using WorkspacesLauncherUI.Data; using WorkspacesLauncherUI.Models; diff --git a/src/modules/awake/Awake/Core/Manager.cs b/src/modules/awake/Awake/Core/Manager.cs index f237250635..ba69f9c8df 100644 --- a/src/modules/awake/Awake/Core/Manager.cs +++ b/src/modules/awake/Awake/Core/Manager.cs @@ -14,6 +14,7 @@ using System.Runtime.InteropServices; using System.Text; using System.Text.Json; using System.Threading; + using Awake.Core.Models; using Awake.Core.Native; using Awake.Properties; diff --git a/src/modules/awake/Awake/Core/Threading/SingleThreadSynchronizationContext.cs b/src/modules/awake/Awake/Core/Threading/SingleThreadSynchronizationContext.cs index 04c28dfd34..e45c13bad0 100644 --- a/src/modules/awake/Awake/Core/Threading/SingleThreadSynchronizationContext.cs +++ b/src/modules/awake/Awake/Core/Threading/SingleThreadSynchronizationContext.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Threading; + using ManagedCommon; namespace Awake.Core.Threading diff --git a/src/modules/awake/Awake/Core/TrayHelper.cs b/src/modules/awake/Awake/Core/TrayHelper.cs index 0545f97a8f..8568f80cfa 100644 --- a/src/modules/awake/Awake/Core/TrayHelper.cs +++ b/src/modules/awake/Awake/Core/TrayHelper.cs @@ -9,6 +9,7 @@ using System.Drawing; using System.Linq; using System.Runtime.InteropServices; using System.Threading; + using Awake.Core.Models; using Awake.Core.Native; using Awake.Core.Threading; diff --git a/src/modules/awake/Awake/Program.cs b/src/modules/awake/Awake/Program.cs index d1311ef51f..2dd22db874 100644 --- a/src/modules/awake/Awake/Program.cs +++ b/src/modules/awake/Awake/Program.cs @@ -15,6 +15,7 @@ using System.Reflection; using System.Text.Json; using System.Threading; using System.Threading.Tasks; + using Awake.Core; using Awake.Core.Models; using Awake.Core.Native; diff --git a/src/modules/launcher/Wox.Plugin/PluginLoadContext.cs b/src/modules/launcher/Wox.Plugin/PluginLoadContext.cs index 9f4bd29632..b0c618b5cf 100644 --- a/src/modules/launcher/Wox.Plugin/PluginLoadContext.cs +++ b/src/modules/launcher/Wox.Plugin/PluginLoadContext.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.Loader; + using ManagedCommon; namespace Wox.Plugin diff --git a/src/modules/launcher/Wox.Plugin/PluginMetadata.cs b/src/modules/launcher/Wox.Plugin/PluginMetadata.cs index 6ee9703bd0..9a22fa485a 100644 --- a/src/modules/launcher/Wox.Plugin/PluginMetadata.cs +++ b/src/modules/launcher/Wox.Plugin/PluginMetadata.cs @@ -36,8 +36,9 @@ namespace Wox.Plugin [JsonIgnore] public bool IsEnabledPolicyConfigured { get; set; } + // Needs to be other than private set in order to be visible to the Json Source Generator [JsonInclude] - public string ExecuteFilePath { get; private set; } + public string ExecuteFilePath { get; internal set; } public string ExecuteFileName { get; set; } diff --git a/src/settings-ui/Settings.UI.Library/EnabledModules.cs b/src/settings-ui/Settings.UI.Library/EnabledModules.cs index ebf70464d5..604b4c46ab 100644 --- a/src/settings-ui/Settings.UI.Library/EnabledModules.cs +++ b/src/settings-ui/Settings.UI.Library/EnabledModules.cs @@ -6,6 +6,7 @@ using System; using System.Runtime.CompilerServices; using System.Text.Json; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.Telemetry; using Microsoft.PowerToys.Telemetry; diff --git a/src/settings-ui/Settings.UI.Library/EnvironmentVariablesProperties.cs b/src/settings-ui/Settings.UI.Library/EnvironmentVariablesProperties.cs index 2d2b93d95f..9319741de0 100644 --- a/src/settings-ui/Settings.UI.Library/EnvironmentVariablesProperties.cs +++ b/src/settings-ui/Settings.UI.Library/EnvironmentVariablesProperties.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Settings.UI.Library.Enumerations; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/EnvironmentVariablesSettings.cs b/src/settings-ui/Settings.UI.Library/EnvironmentVariablesSettings.cs index dfa79a537b..d54641e977 100644 --- a/src/settings-ui/Settings.UI.Library/EnvironmentVariablesSettings.cs +++ b/src/settings-ui/Settings.UI.Library/EnvironmentVariablesSettings.cs @@ -5,6 +5,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/FZConfigProperties.cs b/src/settings-ui/Settings.UI.Library/FZConfigProperties.cs index 51a0269523..9150cdbe90 100644 --- a/src/settings-ui/Settings.UI.Library/FZConfigProperties.cs +++ b/src/settings-ui/Settings.UI.Library/FZConfigProperties.cs @@ -4,6 +4,7 @@ using System.Text.Json; using System.Text.Json.Serialization; + using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/FancyZonesSettings.cs b/src/settings-ui/Settings.UI.Library/FancyZonesSettings.cs index bff72a5984..817b128aa4 100644 --- a/src/settings-ui/Settings.UI.Library/FancyZonesSettings.cs +++ b/src/settings-ui/Settings.UI.Library/FancyZonesSettings.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/FileLocksmithLocalProperties.cs b/src/settings-ui/Settings.UI.Library/FileLocksmithLocalProperties.cs index f998e5b4dd..747baf6dff 100644 --- a/src/settings-ui/Settings.UI.Library/FileLocksmithLocalProperties.cs +++ b/src/settings-ui/Settings.UI.Library/FileLocksmithLocalProperties.cs @@ -4,6 +4,7 @@ using System.Text.Json; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/FileLocksmithSettings.cs b/src/settings-ui/Settings.UI.Library/FileLocksmithSettings.cs index 683746392f..30ee899173 100644 --- a/src/settings-ui/Settings.UI.Library/FileLocksmithSettings.cs +++ b/src/settings-ui/Settings.UI.Library/FileLocksmithSettings.cs @@ -4,6 +4,7 @@ using System; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/FindMyMouseProperties.cs b/src/settings-ui/Settings.UI.Library/FindMyMouseProperties.cs index 64ca0457aa..a028eb9e43 100644 --- a/src/settings-ui/Settings.UI.Library/FindMyMouseProperties.cs +++ b/src/settings-ui/Settings.UI.Library/FindMyMouseProperties.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/FindMyMouseSettings.cs b/src/settings-ui/Settings.UI.Library/FindMyMouseSettings.cs index 921bd62992..aca45d0b01 100644 --- a/src/settings-ui/Settings.UI.Library/FindMyMouseSettings.cs +++ b/src/settings-ui/Settings.UI.Library/FindMyMouseSettings.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/GeneralSettings.cs b/src/settings-ui/Settings.UI.Library/GeneralSettings.cs index 6873b1962a..ed7b503150 100644 --- a/src/settings-ui/Settings.UI.Library/GeneralSettings.cs +++ b/src/settings-ui/Settings.UI.Library/GeneralSettings.cs @@ -5,6 +5,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; + using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; using Microsoft.PowerToys.Settings.UI.Library.Utilities; diff --git a/src/settings-ui/Settings.UI.Library/Helpers/ColorNameHelper.cs b/src/settings-ui/Settings.UI.Library/Helpers/ColorNameHelper.cs index 02c944385f..6d00db0b28 100644 --- a/src/settings-ui/Settings.UI.Library/Helpers/ColorNameHelper.cs +++ b/src/settings-ui/Settings.UI.Library/Helpers/ColorNameHelper.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Drawing; + using global::Settings.UI.Library.Resources; using ManagedCommon; diff --git a/src/settings-ui/Settings.UI.Library/HostsProperties.cs b/src/settings-ui/Settings.UI.Library/HostsProperties.cs index ea05a34400..90a576601d 100644 --- a/src/settings-ui/Settings.UI.Library/HostsProperties.cs +++ b/src/settings-ui/Settings.UI.Library/HostsProperties.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Settings.UI.Library.Attributes; using Settings.UI.Library.Enumerations; diff --git a/src/settings-ui/Settings.UI.Library/HostsSettings.cs b/src/settings-ui/Settings.UI.Library/HostsSettings.cs index 5e15a7e664..bb339f178c 100644 --- a/src/settings-ui/Settings.UI.Library/HostsSettings.cs +++ b/src/settings-ui/Settings.UI.Library/HostsSettings.cs @@ -5,6 +5,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/HotkeySettings.cs b/src/settings-ui/Settings.UI.Library/HotkeySettings.cs index 38018f59d1..ff588eafbd 100644 --- a/src/settings-ui/Settings.UI.Library/HotkeySettings.cs +++ b/src/settings-ui/Settings.UI.Library/HotkeySettings.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Globalization; using System.Text; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Utilities; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/HotkeySettingsControlHook.cs b/src/settings-ui/Settings.UI.Library/HotkeySettingsControlHook.cs index cc932b3584..0ff67df638 100644 --- a/src/settings-ui/Settings.UI.Library/HotkeySettingsControlHook.cs +++ b/src/settings-ui/Settings.UI.Library/HotkeySettingsControlHook.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using PowerToys.Interop; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/ISettingsUtils.cs b/src/settings-ui/Settings.UI.Library/ISettingsUtils.cs index a6f340e83c..10e8bf26ac 100644 --- a/src/settings-ui/Settings.UI.Library/ISettingsUtils.cs +++ b/src/settings-ui/Settings.UI.Library/ISettingsUtils.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/ImageResizerProperties.cs b/src/settings-ui/Settings.UI.Library/ImageResizerProperties.cs index 8d57055881..acef5a2f4d 100644 --- a/src/settings-ui/Settings.UI.Library/ImageResizerProperties.cs +++ b/src/settings-ui/Settings.UI.Library/ImageResizerProperties.cs @@ -6,6 +6,7 @@ using System; using System.Collections.ObjectModel; using System.Text.Json; using System.Text.Json.Serialization; + using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/ImageResizerSettings.cs b/src/settings-ui/Settings.UI.Library/ImageResizerSettings.cs index f43c76ba70..97f785d6c2 100644 --- a/src/settings-ui/Settings.UI.Library/ImageResizerSettings.cs +++ b/src/settings-ui/Settings.UI.Library/ImageResizerSettings.cs @@ -5,6 +5,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/KeyboardManagerProfile.cs b/src/settings-ui/Settings.UI.Library/KeyboardManagerProfile.cs index f47b1f0ded..983f9a1f6a 100644 --- a/src/settings-ui/Settings.UI.Library/KeyboardManagerProfile.cs +++ b/src/settings-ui/Settings.UI.Library/KeyboardManagerProfile.cs @@ -4,6 +4,7 @@ using System.Text.Json; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/KeyboardManagerProperties.cs b/src/settings-ui/Settings.UI.Library/KeyboardManagerProperties.cs index 86ab049f7c..25a84dbb2c 100644 --- a/src/settings-ui/Settings.UI.Library/KeyboardManagerProperties.cs +++ b/src/settings-ui/Settings.UI.Library/KeyboardManagerProperties.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; + using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/KeyboardManagerSettings.cs b/src/settings-ui/Settings.UI.Library/KeyboardManagerSettings.cs index a52fdceea2..5682d6d865 100644 --- a/src/settings-ui/Settings.UI.Library/KeyboardManagerSettings.cs +++ b/src/settings-ui/Settings.UI.Library/KeyboardManagerSettings.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/KeysDataModel.cs b/src/settings-ui/Settings.UI.Library/KeysDataModel.cs index 5788aad96b..1232d384b8 100644 --- a/src/settings-ui/Settings.UI.Library/KeysDataModel.cs +++ b/src/settings-ui/Settings.UI.Library/KeysDataModel.cs @@ -14,6 +14,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using System.Threading; using System.Windows.Input; + using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands; diff --git a/src/settings-ui/Settings.UI.Library/MeasureToolProperties.cs b/src/settings-ui/Settings.UI.Library/MeasureToolProperties.cs index 97a0c3219a..ebda5953d0 100644 --- a/src/settings-ui/Settings.UI.Library/MeasureToolProperties.cs +++ b/src/settings-ui/Settings.UI.Library/MeasureToolProperties.cs @@ -4,6 +4,7 @@ using System.Text.Json; using System.Text.Json.Serialization; + using Settings.UI.Library.Attributes; using Settings.UI.Library.Enumerations; diff --git a/src/settings-ui/Settings.UI.Library/MeasureToolSettings.cs b/src/settings-ui/Settings.UI.Library/MeasureToolSettings.cs index 65275ba6ce..5720c70ca5 100644 --- a/src/settings-ui/Settings.UI.Library/MeasureToolSettings.cs +++ b/src/settings-ui/Settings.UI.Library/MeasureToolSettings.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/MouseHighlighterProperties.cs b/src/settings-ui/Settings.UI.Library/MouseHighlighterProperties.cs index bb847c2e30..298b0d9230 100644 --- a/src/settings-ui/Settings.UI.Library/MouseHighlighterProperties.cs +++ b/src/settings-ui/Settings.UI.Library/MouseHighlighterProperties.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/MouseHighlighterSettings.cs b/src/settings-ui/Settings.UI.Library/MouseHighlighterSettings.cs index b677ee2175..e23a7fe288 100644 --- a/src/settings-ui/Settings.UI.Library/MouseHighlighterSettings.cs +++ b/src/settings-ui/Settings.UI.Library/MouseHighlighterSettings.cs @@ -5,6 +5,7 @@ using System.Globalization; using System.Runtime.InteropServices; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs b/src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs index c24d949dc1..0c26e86124 100644 --- a/src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs +++ b/src/settings-ui/Settings.UI.Library/MouseJumpProperties.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/MouseJumpSettings.cs b/src/settings-ui/Settings.UI.Library/MouseJumpSettings.cs index 596e6cd91b..fbeb1eaf68 100644 --- a/src/settings-ui/Settings.UI.Library/MouseJumpSettings.cs +++ b/src/settings-ui/Settings.UI.Library/MouseJumpSettings.cs @@ -5,6 +5,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/MousePointerCrosshairsProperties.cs b/src/settings-ui/Settings.UI.Library/MousePointerCrosshairsProperties.cs index 46c9e4fe03..9b0e530a2a 100644 --- a/src/settings-ui/Settings.UI.Library/MousePointerCrosshairsProperties.cs +++ b/src/settings-ui/Settings.UI.Library/MousePointerCrosshairsProperties.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/MousePointerCrosshairsSettings.cs b/src/settings-ui/Settings.UI.Library/MousePointerCrosshairsSettings.cs index 7c1f253271..2658a2adec 100644 --- a/src/settings-ui/Settings.UI.Library/MousePointerCrosshairsSettings.cs +++ b/src/settings-ui/Settings.UI.Library/MousePointerCrosshairsSettings.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/MouseWithoutBordersProperties.cs b/src/settings-ui/Settings.UI.Library/MouseWithoutBordersProperties.cs index 5bf4ae53de..265b8a1e2d 100644 --- a/src/settings-ui/Settings.UI.Library/MouseWithoutBordersProperties.cs +++ b/src/settings-ui/Settings.UI.Library/MouseWithoutBordersProperties.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text.Json.Serialization; + using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/MouseWithoutBordersSettings.cs b/src/settings-ui/Settings.UI.Library/MouseWithoutBordersSettings.cs index f030d98bf1..6a51a150e5 100644 --- a/src/settings-ui/Settings.UI.Library/MouseWithoutBordersSettings.cs +++ b/src/settings-ui/Settings.UI.Library/MouseWithoutBordersSettings.cs @@ -6,6 +6,7 @@ using System; using System.Diagnostics; using System.Text.Json; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/NewPlusSettings.cs b/src/settings-ui/Settings.UI.Library/NewPlusSettings.cs index 2450898684..7a37c72a84 100644 --- a/src/settings-ui/Settings.UI.Library/NewPlusSettings.cs +++ b/src/settings-ui/Settings.UI.Library/NewPlusSettings.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.RegularExpressions; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; using Settings.UI.Library.Resources; diff --git a/src/settings-ui/Settings.UI.Library/PeekPreviewSettings.cs b/src/settings-ui/Settings.UI.Library/PeekPreviewSettings.cs index 75562ca9a2..f098b18052 100644 --- a/src/settings-ui/Settings.UI.Library/PeekPreviewSettings.cs +++ b/src/settings-ui/Settings.UI.Library/PeekPreviewSettings.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; diff --git a/src/settings-ui/Settings.UI.Library/PeekProperties.cs b/src/settings-ui/Settings.UI.Library/PeekProperties.cs index 6932177df1..1e4514d866 100644 --- a/src/settings-ui/Settings.UI.Library/PeekProperties.cs +++ b/src/settings-ui/Settings.UI.Library/PeekProperties.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json; + using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/PeekSettings.cs b/src/settings-ui/Settings.UI.Library/PeekSettings.cs index 3302f15c64..f5ad2a0e26 100644 --- a/src/settings-ui/Settings.UI.Library/PeekSettings.cs +++ b/src/settings-ui/Settings.UI.Library/PeekSettings.cs @@ -5,6 +5,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/PowerAccentProperties.cs b/src/settings-ui/Settings.UI.Library/PowerAccentProperties.cs index f471f51c13..4ab0b955c7 100644 --- a/src/settings-ui/Settings.UI.Library/PowerAccentProperties.cs +++ b/src/settings-ui/Settings.UI.Library/PowerAccentProperties.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Enumerations; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/PowerAccentSettings.cs b/src/settings-ui/Settings.UI.Library/PowerAccentSettings.cs index 6302fd5058..f080dcb093 100644 --- a/src/settings-ui/Settings.UI.Library/PowerAccentSettings.cs +++ b/src/settings-ui/Settings.UI.Library/PowerAccentSettings.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/PowerLauncherProperties.cs b/src/settings-ui/Settings.UI.Library/PowerLauncherProperties.cs index efe7e1e446..590fb2e290 100644 --- a/src/settings-ui/Settings.UI.Library/PowerLauncherProperties.cs +++ b/src/settings-ui/Settings.UI.Library/PowerLauncherProperties.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using ManagedCommon; using Settings.UI.Library.Attributes; diff --git a/src/settings-ui/Settings.UI.Library/PowerLauncherSettings.cs b/src/settings-ui/Settings.UI.Library/PowerLauncherSettings.cs index 4c7ad70e0c..c21ce67df5 100644 --- a/src/settings-ui/Settings.UI.Library/PowerLauncherSettings.cs +++ b/src/settings-ui/Settings.UI.Library/PowerLauncherSettings.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/PowerOcrProperties.cs b/src/settings-ui/Settings.UI.Library/PowerOcrProperties.cs index 0ea4fcca54..5cba3cfb3e 100644 --- a/src/settings-ui/Settings.UI.Library/PowerOcrProperties.cs +++ b/src/settings-ui/Settings.UI.Library/PowerOcrProperties.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json; + using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/PowerOcrSettings.cs b/src/settings-ui/Settings.UI.Library/PowerOcrSettings.cs index 9bf9db87d6..46d176d2b0 100644 --- a/src/settings-ui/Settings.UI.Library/PowerOcrSettings.cs +++ b/src/settings-ui/Settings.UI.Library/PowerOcrSettings.cs @@ -5,6 +5,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs b/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs index 1c09438b61..28d76dd067 100644 --- a/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs +++ b/src/settings-ui/Settings.UI.Library/PowerPreviewProperties.cs @@ -5,6 +5,7 @@ using System.Runtime.CompilerServices; using System.Text.Json; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.Telemetry; using Microsoft.PowerToys.Telemetry; using Settings.UI.Library.Enumerations; diff --git a/src/settings-ui/Settings.UI.Library/PowerPreviewSettings.cs b/src/settings-ui/Settings.UI.Library/PowerPreviewSettings.cs index 5cdb94b923..ba0ac30bb3 100644 --- a/src/settings-ui/Settings.UI.Library/PowerPreviewSettings.cs +++ b/src/settings-ui/Settings.UI.Library/PowerPreviewSettings.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/PowerRenameLocalProperties.cs b/src/settings-ui/Settings.UI.Library/PowerRenameLocalProperties.cs index 5812b2a082..726faf2bc0 100644 --- a/src/settings-ui/Settings.UI.Library/PowerRenameLocalProperties.cs +++ b/src/settings-ui/Settings.UI.Library/PowerRenameLocalProperties.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/PowerRenameProperties.cs b/src/settings-ui/Settings.UI.Library/PowerRenameProperties.cs index dfe27855ec..dcc10a7bdc 100644 --- a/src/settings-ui/Settings.UI.Library/PowerRenameProperties.cs +++ b/src/settings-ui/Settings.UI.Library/PowerRenameProperties.cs @@ -4,6 +4,7 @@ using System; using System.Text.Json.Serialization; + using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/PowerRenameSettings.cs b/src/settings-ui/Settings.UI.Library/PowerRenameSettings.cs index 39c20a3af1..50bc8cdfe3 100644 --- a/src/settings-ui/Settings.UI.Library/PowerRenameSettings.cs +++ b/src/settings-ui/Settings.UI.Library/PowerRenameSettings.cs @@ -4,6 +4,7 @@ using System; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/RegistryPreviewSettings.cs b/src/settings-ui/Settings.UI.Library/RegistryPreviewSettings.cs index d7dab39ef5..6d9179aba7 100644 --- a/src/settings-ui/Settings.UI.Library/RegistryPreviewSettings.cs +++ b/src/settings-ui/Settings.UI.Library/RegistryPreviewSettings.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/SettingPath.cs b/src/settings-ui/Settings.UI.Library/SettingPath.cs index 3768db9976..94c5d83ca1 100644 --- a/src/settings-ui/Settings.UI.Library/SettingPath.cs +++ b/src/settings-ui/Settings.UI.Library/SettingPath.cs @@ -4,6 +4,7 @@ using System; using System.IO.Abstractions; + using Microsoft.PowerToys.Settings.UI.Library.Utilities; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs b/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs index 98be5a6288..a31878f240 100644 --- a/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs +++ b/src/settings-ui/Settings.UI.Library/SettingsBackupAndRestoreUtils.cs @@ -15,6 +15,7 @@ using System.Text.Json; using System.Text.Json.Nodes; using System.Text.RegularExpressions; using System.Threading; + using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Utilities; diff --git a/src/settings-ui/Settings.UI.Library/SettingsRepository`1.cs b/src/settings-ui/Settings.UI.Library/SettingsRepository`1.cs index b9d27dedb9..7f634a2012 100644 --- a/src/settings-ui/Settings.UI.Library/SettingsRepository`1.cs +++ b/src/settings-ui/Settings.UI.Library/SettingsRepository`1.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/SettingsUtils.cs b/src/settings-ui/Settings.UI.Library/SettingsUtils.cs index 30002d956e..13065b1a71 100644 --- a/src/settings-ui/Settings.UI.Library/SettingsUtils.cs +++ b/src/settings-ui/Settings.UI.Library/SettingsUtils.cs @@ -6,6 +6,7 @@ using System; using System.IO; using System.IO.Abstractions; using System.Text.Json; + using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; diff --git a/src/settings-ui/Settings.UI.Library/ShortcutGuideProperties.cs b/src/settings-ui/Settings.UI.Library/ShortcutGuideProperties.cs index b84fe8b0ae..d34a2f748a 100644 --- a/src/settings-ui/Settings.UI.Library/ShortcutGuideProperties.cs +++ b/src/settings-ui/Settings.UI.Library/ShortcutGuideProperties.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/ShortcutGuideSettings.cs b/src/settings-ui/Settings.UI.Library/ShortcutGuideSettings.cs index 174aa4e217..c39e757fe3 100644 --- a/src/settings-ui/Settings.UI.Library/ShortcutGuideSettings.cs +++ b/src/settings-ui/Settings.UI.Library/ShortcutGuideSettings.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/CmdNotFoundInstallEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/CmdNotFoundInstallEvent.cs index 97d700a8e5..6af5444174 100644 --- a/src/settings-ui/Settings.UI.Library/Telemetry/Events/CmdNotFoundInstallEvent.cs +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/CmdNotFoundInstallEvent.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Tracing; + using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/CmdNotFoundUninstallEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/CmdNotFoundUninstallEvent.cs index ae469903ee..36f90bcfd7 100644 --- a/src/settings-ui/Settings.UI.Library/Telemetry/Events/CmdNotFoundUninstallEvent.cs +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/CmdNotFoundUninstallEvent.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Tracing; + using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeModuleRunEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeModuleRunEvent.cs index 4b0721d410..b6316cd2db 100644 --- a/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeModuleRunEvent.cs +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeModuleRunEvent.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Tracing; + using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeSectionEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeSectionEvent.cs index bb3265773d..4d762220c3 100644 --- a/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeSectionEvent.cs +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeSectionEvent.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Tracing; + using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeSettingsEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeSettingsEvent.cs index f0e7293ed3..499a1b98b3 100644 --- a/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeSettingsEvent.cs +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeSettingsEvent.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Tracing; + using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeStartedEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeStartedEvent.cs index 771cdfc402..8912ea3156 100644 --- a/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeStartedEvent.cs +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeStartedEvent.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Tracing; + using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeVariantAssignmentEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeVariantAssignmentEvent.cs index 8f17b58dc3..d4a30550eb 100644 --- a/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeVariantAssignmentEvent.cs +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/OobeVariantAssignmentEvent.cs @@ -4,6 +4,7 @@ using System; using System.Diagnostics.Tracing; + using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/ScoobeStartedEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/ScoobeStartedEvent.cs index 63b1bf48e2..f8f6c10c41 100644 --- a/src/settings-ui/Settings.UI.Library/Telemetry/Events/ScoobeStartedEvent.cs +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/ScoobeStartedEvent.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Tracing; + using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/SettingsBootEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/SettingsBootEvent.cs index 376ae6f16a..edb989199f 100644 --- a/src/settings-ui/Settings.UI.Library/Telemetry/Events/SettingsBootEvent.cs +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/SettingsBootEvent.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Tracing; + using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/SettingsEnabledEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/SettingsEnabledEvent.cs index 5cf0ea697a..7abfc8e2c2 100644 --- a/src/settings-ui/Settings.UI.Library/Telemetry/Events/SettingsEnabledEvent.cs +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/SettingsEnabledEvent.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Tracing; + using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutActivatedEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutActivatedEvent.cs index 8e075aea0c..91600660e4 100644 --- a/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutActivatedEvent.cs +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutActivatedEvent.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Tracing; + using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; diff --git a/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutModuleRunEvent.cs b/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutModuleRunEvent.cs index c1813e9adb..167cf48dfb 100644 --- a/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutModuleRunEvent.cs +++ b/src/settings-ui/Settings.UI.Library/Telemetry/Events/TrayFlyoutModuleRunEvent.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics.Tracing; + using Microsoft.PowerToys.Telemetry; using Microsoft.PowerToys.Telemetry.Events; diff --git a/src/settings-ui/Settings.UI.Library/Utilities/CommandLineUtils.cs b/src/settings-ui/Settings.UI.Library/Utilities/CommandLineUtils.cs index 91703a69c6..a7702e17a2 100644 --- a/src/settings-ui/Settings.UI.Library/Utilities/CommandLineUtils.cs +++ b/src/settings-ui/Settings.UI.Library/Utilities/CommandLineUtils.cs @@ -5,6 +5,7 @@ using System; using System.Linq; using System.Reflection; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI.Library/Utilities/GetSettingCommandLineCommand.cs b/src/settings-ui/Settings.UI.Library/Utilities/GetSettingCommandLineCommand.cs index e74469795e..a4ad1d1862 100644 --- a/src/settings-ui/Settings.UI.Library/Utilities/GetSettingCommandLineCommand.cs +++ b/src/settings-ui/Settings.UI.Library/Utilities/GetSettingCommandLineCommand.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.Globalization; using System.Text.Json; using System.Xml; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; using Settings.UI.Library.Attributes; diff --git a/src/settings-ui/Settings.UI.Library/Utilities/Helper.cs b/src/settings-ui/Settings.UI.Library/Utilities/Helper.cs index 3b0f377810..fc1f8d6f7c 100644 --- a/src/settings-ui/Settings.UI.Library/Utilities/Helper.cs +++ b/src/settings-ui/Settings.UI.Library/Utilities/Helper.cs @@ -9,6 +9,7 @@ using System.IO.Abstractions; using System.Linq; using System.Net.NetworkInformation; using System.Security.Principal; + using Microsoft.PowerToys.Settings.UI.Library.CustomAction; namespace Microsoft.PowerToys.Settings.UI.Library.Utilities diff --git a/src/settings-ui/Settings.UI.Library/Utilities/SetAdditionalSettingsCommandLineCommand.cs b/src/settings-ui/Settings.UI.Library/Utilities/SetAdditionalSettingsCommandLineCommand.cs index 122dccfd4b..29f47a4347 100644 --- a/src/settings-ui/Settings.UI.Library/Utilities/SetAdditionalSettingsCommandLineCommand.cs +++ b/src/settings-ui/Settings.UI.Library/Utilities/SetAdditionalSettingsCommandLineCommand.cs @@ -12,6 +12,7 @@ using System.Linq; using System.Reflection; using System.Text.Json; using System.Text.Json.Nodes; + using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI.Library/Utilities/SetSettingCommandLineCommand.cs b/src/settings-ui/Settings.UI.Library/Utilities/SetSettingCommandLineCommand.cs index 1e839a17c8..ab5a88c5d8 100644 --- a/src/settings-ui/Settings.UI.Library/Utilities/SetSettingCommandLineCommand.cs +++ b/src/settings-ui/Settings.UI.Library/Utilities/SetSettingCommandLineCommand.cs @@ -4,6 +4,7 @@ using System; using System.Reflection; + using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI.Library/VideoConferenceConfigProperties.cs b/src/settings-ui/Settings.UI.Library/VideoConferenceConfigProperties.cs index c1df38908e..9c7b33f804 100644 --- a/src/settings-ui/Settings.UI.Library/VideoConferenceConfigProperties.cs +++ b/src/settings-ui/Settings.UI.Library/VideoConferenceConfigProperties.cs @@ -4,6 +4,7 @@ using System.Text.Json; using System.Text.Json.Serialization; + using Settings.UI.Library.Attributes; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/VideoConferenceSettings.cs b/src/settings-ui/Settings.UI.Library/VideoConferenceSettings.cs index 80ead8732e..c33bcdaaa6 100644 --- a/src/settings-ui/Settings.UI.Library/VideoConferenceSettings.cs +++ b/src/settings-ui/Settings.UI.Library/VideoConferenceSettings.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.Library/WorkspacesSettings.cs b/src/settings-ui/Settings.UI.Library/WorkspacesSettings.cs index 00709e418c..1e3ce2261e 100644 --- a/src/settings-ui/Settings.UI.Library/WorkspacesSettings.cs +++ b/src/settings-ui/Settings.UI.Library/WorkspacesSettings.cs @@ -5,6 +5,7 @@ using System; using System.Text.Json; using System.Text.Json.Serialization; + using Microsoft.PowerToys.Settings.UI.Library.Interfaces; namespace Microsoft.PowerToys.Settings.UI.Library diff --git a/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs b/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs index b1c03a5d89..b2048fa573 100644 --- a/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs +++ b/src/settings-ui/Settings.UI.UnitTests/BackwardsCompatibility/BackCompatTestProperties.cs @@ -7,6 +7,7 @@ using System.Globalization; using System.IO.Abstractions; using System.Linq.Expressions; using System.Text; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; diff --git a/src/settings-ui/Settings.UI.UnitTests/Cmd/SetSettingCommandTests.cs b/src/settings-ui/Settings.UI.UnitTests/Cmd/SetSettingCommandTests.cs index 572682854e..4c6d1babfd 100644 --- a/src/settings-ui/Settings.UI.UnitTests/Cmd/SetSettingCommandTests.cs +++ b/src/settings-ui/Settings.UI.UnitTests/Cmd/SetSettingCommandTests.cs @@ -4,6 +4,7 @@ using System; using System.IO.Abstractions.TestingHelpers; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/src/settings-ui/Settings.UI.UnitTests/Mocks/IIOProviderMocks.cs b/src/settings-ui/Settings.UI.UnitTests/Mocks/IIOProviderMocks.cs index 647e3670f3..b5e531d742 100644 --- a/src/settings-ui/Settings.UI.UnitTests/Mocks/IIOProviderMocks.cs +++ b/src/settings-ui/Settings.UI.UnitTests/Mocks/IIOProviderMocks.cs @@ -5,6 +5,7 @@ using System; using System.IO.Abstractions; using System.Linq.Expressions; + using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Moq; diff --git a/src/settings-ui/Settings.UI.UnitTests/ModelsTests/HelperTest.cs b/src/settings-ui/Settings.UI.UnitTests/ModelsTests/HelperTest.cs index dd362e38da..aaa6e87d18 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ModelsTests/HelperTest.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ModelsTests/HelperTest.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/src/settings-ui/Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs b/src/settings-ui/Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs index 62e7f7b9af..9b75925cb8 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ModelsTests/SettingsUtilsTests.cs @@ -6,6 +6,7 @@ using System; using System.IO.Abstractions.TestingHelpers; using System.Linq; using System.Text.Json; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; using Microsoft.PowerToys.Settings.UnitTest; diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs index 5fe20f3cda..baf46827c4 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ColorPicker.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Text.Json; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/FancyZones.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/FancyZones.cs index 0239fbfd71..ef230bde0a 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/FancyZones.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/FancyZones.cs @@ -4,6 +4,7 @@ using System; using System.Text.Json; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/General.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/General.cs index 56da0a21bb..70bfbe4fba 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/General.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/General.cs @@ -3,12 +3,14 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; + using JsonSerializer = System.Text.Json.JsonSerializer; namespace ViewModelTests diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ImageResizer.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ImageResizer.cs index 4bd392008c..1d2c62daec 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ImageResizer.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ImageResizer.cs @@ -6,6 +6,7 @@ using System; using System.IO.Abstractions.TestingHelpers; using System.Linq; using System.Text.Json; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/KeyboardManager.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/KeyboardManager.cs index 2234540f4f..89491b2b99 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/KeyboardManager.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/KeyboardManager.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.VisualStudio.TestTools.UnitTesting; diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs index a5d09f61b2..f1084d498a 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerLauncherViewModelTest.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility; using Microsoft.PowerToys.Settings.UI.ViewModels; diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs index f70357293e..373b9a3580 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerPreview.cs @@ -4,6 +4,7 @@ using System; using System.Text.Json; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerRename.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerRename.cs index 441fd906e7..278975183b 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerRename.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/PowerRename.cs @@ -4,6 +4,7 @@ using System; using System.Text.Json; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; diff --git a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs index b162ecc713..3613d0cfa3 100644 --- a/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs +++ b/src/settings-ui/Settings.UI.UnitTests/ViewModelTests/ShortcutGuide.cs @@ -4,6 +4,7 @@ using System; using System.Text.Json; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.UnitTests.BackwardsCompatibility; using Microsoft.PowerToys.Settings.UI.UnitTests.Mocks; diff --git a/src/settings-ui/Settings.UI/Activation/DefaultActivationHandler.cs b/src/settings-ui/Settings.UI/Activation/DefaultActivationHandler.cs index c501a23e6b..946fab205c 100644 --- a/src/settings-ui/Settings.UI/Activation/DefaultActivationHandler.cs +++ b/src/settings-ui/Settings.UI/Activation/DefaultActivationHandler.cs @@ -4,6 +4,7 @@ using System; using System.Threading.Tasks; + using Microsoft.PowerToys.Settings.UI.Services; using Windows.ApplicationModel.Activation; diff --git a/src/settings-ui/Settings.UI/Converters/AwakeModeToIntConverter.cs b/src/settings-ui/Settings.UI/Converters/AwakeModeToIntConverter.cs index 1bbae1f8c2..1119b91154 100644 --- a/src/settings-ui/Settings.UI/Converters/AwakeModeToIntConverter.cs +++ b/src/settings-ui/Settings.UI/Converters/AwakeModeToIntConverter.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.UI.Xaml.Data; diff --git a/src/settings-ui/Settings.UI/Converters/ColorFormatConverter.cs b/src/settings-ui/Settings.UI/Converters/ColorFormatConverter.cs index 8b81200d64..428b5d60a5 100644 --- a/src/settings-ui/Settings.UI/Converters/ColorFormatConverter.cs +++ b/src/settings-ui/Settings.UI/Converters/ColorFormatConverter.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using ManagedCommon; using Microsoft.UI.Xaml.Data; diff --git a/src/settings-ui/Settings.UI/Converters/ImageResizerFitToIntConverter.cs b/src/settings-ui/Settings.UI/Converters/ImageResizerFitToIntConverter.cs index 8a68025197..1439e97e7f 100644 --- a/src/settings-ui/Settings.UI/Converters/ImageResizerFitToIntConverter.cs +++ b/src/settings-ui/Settings.UI/Converters/ImageResizerFitToIntConverter.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.UI.Xaml.Data; diff --git a/src/settings-ui/Settings.UI/Converters/ImageResizerFitToStringConverter.cs b/src/settings-ui/Settings.UI/Converters/ImageResizerFitToStringConverter.cs index d9a740db95..bfc991c070 100644 --- a/src/settings-ui/Settings.UI/Converters/ImageResizerFitToStringConverter.cs +++ b/src/settings-ui/Settings.UI/Converters/ImageResizerFitToStringConverter.cs @@ -4,6 +4,7 @@ using System; using System.Globalization; + using Microsoft.UI.Xaml.Data; namespace Microsoft.PowerToys.Settings.UI.Converters diff --git a/src/settings-ui/Settings.UI/Converters/ImageResizerUnitToIntConverter.cs b/src/settings-ui/Settings.UI/Converters/ImageResizerUnitToIntConverter.cs index 0132b86241..0a5087778e 100644 --- a/src/settings-ui/Settings.UI/Converters/ImageResizerUnitToIntConverter.cs +++ b/src/settings-ui/Settings.UI/Converters/ImageResizerUnitToIntConverter.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.UI.Xaml.Data; diff --git a/src/settings-ui/Settings.UI/Converters/ImageResizerUnitToStringConverter.cs b/src/settings-ui/Settings.UI/Converters/ImageResizerUnitToStringConverter.cs index 6dc89e65fb..ab4aec8195 100644 --- a/src/settings-ui/Settings.UI/Converters/ImageResizerUnitToStringConverter.cs +++ b/src/settings-ui/Settings.UI/Converters/ImageResizerUnitToStringConverter.cs @@ -4,6 +4,7 @@ using System; using System.Globalization; + using Microsoft.UI.Xaml.Data; namespace Microsoft.PowerToys.Settings.UI.Converters diff --git a/src/settings-ui/Settings.UI/Converters/IndexBitFieldToVisibilityConverter.cs b/src/settings-ui/Settings.UI/Converters/IndexBitFieldToVisibilityConverter.cs index 3295294550..fadefcbfaa 100644 --- a/src/settings-ui/Settings.UI/Converters/IndexBitFieldToVisibilityConverter.cs +++ b/src/settings-ui/Settings.UI/Converters/IndexBitFieldToVisibilityConverter.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Data; diff --git a/src/settings-ui/Settings.UI/Converters/RunOptionTemplateSelector.cs b/src/settings-ui/Settings.UI/Converters/RunOptionTemplateSelector.cs index 0e742e855a..0093f5f28f 100644 --- a/src/settings-ui/Settings.UI/Converters/RunOptionTemplateSelector.cs +++ b/src/settings-ui/Settings.UI/Converters/RunOptionTemplateSelector.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; using Microsoft.UI.Xaml; diff --git a/src/settings-ui/Settings.UI/Converters/StringToInfoBarSeverityConverter.cs b/src/settings-ui/Settings.UI/Converters/StringToInfoBarSeverityConverter.cs index 548b68889b..af64f9e42f 100644 --- a/src/settings-ui/Settings.UI/Converters/StringToInfoBarSeverityConverter.cs +++ b/src/settings-ui/Settings.UI/Converters/StringToInfoBarSeverityConverter.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.UI.Xaml.Data; namespace Microsoft.PowerToys.Settings.UI.Converters diff --git a/src/settings-ui/Settings.UI/Converters/UpdateStateToBoolConverter.cs b/src/settings-ui/Settings.UI/Converters/UpdateStateToBoolConverter.cs index ecc19dbd7c..15d874a430 100644 --- a/src/settings-ui/Settings.UI/Converters/UpdateStateToBoolConverter.cs +++ b/src/settings-ui/Settings.UI/Converters/UpdateStateToBoolConverter.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.UI.Xaml.Data; namespace Microsoft.PowerToys.Settings.UI.Converters diff --git a/src/settings-ui/Settings.UI/Helpers/NavHelper.cs b/src/settings-ui/Settings.UI/Helpers/NavHelper.cs index 12375832da..52fd7a753f 100644 --- a/src/settings-ui/Settings.UI/Helpers/NavHelper.cs +++ b/src/settings-ui/Settings.UI/Helpers/NavHelper.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/settings-ui/Settings.UI/Helpers/StartProcessHelper.cs b/src/settings-ui/Settings.UI/Helpers/StartProcessHelper.cs index b4a2e6b6d5..816b6bcc63 100644 --- a/src/settings-ui/Settings.UI/Helpers/StartProcessHelper.cs +++ b/src/settings-ui/Settings.UI/Helpers/StartProcessHelper.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Diagnostics; + using Common.UI; namespace Microsoft.PowerToys.Settings.UI.Helpers diff --git a/src/settings-ui/Settings.UI/Helpers/WindowHelper.cs b/src/settings-ui/Settings.UI/Helpers/WindowHelper.cs index 440d6cf902..489e2b9a4a 100644 --- a/src/settings-ui/Settings.UI/Helpers/WindowHelper.cs +++ b/src/settings-ui/Settings.UI/Helpers/WindowHelper.cs @@ -6,6 +6,7 @@ using System; using System.IO; using System.Runtime.InteropServices; using System.Text.Json; + using Microsoft.UI.Xaml; namespace Microsoft.PowerToys.Settings.UI.Helpers diff --git a/src/settings-ui/Settings.UI/Services/ActivationService.cs b/src/settings-ui/Settings.UI/Services/ActivationService.cs index 0d6d241631..86ad2e4d7c 100644 --- a/src/settings-ui/Settings.UI/Services/ActivationService.cs +++ b/src/settings-ui/Settings.UI/Services/ActivationService.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; + using Microsoft.PowerToys.Settings.UI.Activation; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/settings-ui/Settings.UI/Services/NavigationService.cs b/src/settings-ui/Settings.UI/Services/NavigationService.cs index be720e2ec6..b70976bd01 100644 --- a/src/settings-ui/Settings.UI/Services/NavigationService.cs +++ b/src/settings-ui/Settings.UI/Services/NavigationService.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media.Animation; diff --git a/src/settings-ui/Settings.UI/Services/ThemeService.cs b/src/settings-ui/Settings.UI/Services/ThemeService.cs index e817c275e4..8df79eca8f 100644 --- a/src/settings-ui/Settings.UI/Services/ThemeService.cs +++ b/src/settings-ui/Settings.UI/Services/ThemeService.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Interfaces; using Microsoft.UI.Xaml; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs index a820502dcd..4c44dd250b 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs @@ -9,6 +9,7 @@ using System.IO; using System.Linq; using System.Text.Json; using System.Threading.Tasks; + using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/CheckBoxWithDescriptionControl.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/CheckBoxWithDescriptionControl.cs index 9345f442f2..d8b5b6e31a 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/CheckBoxWithDescriptionControl.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/CheckBoxWithDescriptionControl.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.ComponentModel; + using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Automation; using Microsoft.UI.Xaml.Controls; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/ColorFormatEditor.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ColorFormatEditor.xaml.cs index 9ed817d697..76681d8b46 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/ColorFormatEditor.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ColorFormatEditor.xaml.cs @@ -7,6 +7,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Security.Cryptography; using System.Windows.Input; + using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/FancyZonesPreviewControl.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/FancyZonesPreviewControl.xaml.cs index 405d5d6e7d..de05fe790d 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/FancyZonesPreviewControl.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/FancyZonesPreviewControl.xaml.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.IO; using System.Text; + using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/IsEnabledTextBlock/IsEnabledTextBlock.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/IsEnabledTextBlock/IsEnabledTextBlock.cs index c013a87f40..82c6b3a986 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/IsEnabledTextBlock/IsEnabledTextBlock.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/IsEnabledTextBlock/IsEnabledTextBlock.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.ComponentModel; + using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/PowerAccentShortcutControl.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/PowerAccentShortcutControl.xaml.cs index d746f738ce..24fa76619b 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/PowerAccentShortcutControl.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/PowerAccentShortcutControl.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; + using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/SettingsGroup/SettingsGroup.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/SettingsGroup/SettingsGroup.cs index 8b19fbf3ba..b863900fe3 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/SettingsGroup/SettingsGroup.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/SettingsGroup/SettingsGroup.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.ComponentModel; + using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Automation.Peers; using Microsoft.UI.Xaml.Controls; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/SettingsPageControl/SettingsPageControl.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/SettingsPageControl/SettingsPageControl.xaml.cs index 6124356fc7..dca1fbae8e 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/SettingsPageControl/SettingsPageControl.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/SettingsPageControl/SettingsPageControl.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.ObjectModel; + using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutControl.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutControl.xaml.cs index 3fbcdeef21..e33127572d 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutControl.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutControl.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using CommunityToolkit.WinUI; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutDialogContentControl.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutDialogContentControl.xaml.cs index 79059e8aa8..5d44f7c451 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutDialogContentControl.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutDialogContentControl.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; + using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutWithTextLabelControl.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutWithTextLabelControl.xaml.cs index 66e052da37..ed18669eba 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutWithTextLabelControl.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Controls/ShortcutControl/ShortcutWithTextLabelControl.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; + using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Flyout/AppsListPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Flyout/AppsListPage.xaml.cs index cc952772a9..b58636f41b 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Flyout/AppsListPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Flyout/AppsListPage.xaml.cs @@ -4,6 +4,7 @@ using System; using System.Collections.ObjectModel; using System.Threading; + using global::Windows.System; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs index 5c778fbbdd..a2a8b313ac 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Flyout/LaunchPage.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; using System.Threading; + using global::Windows.System; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Controls; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs index c665e3a21a..e85d377da2 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/MainWindow.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using ManagedCommon; using Microsoft.PowerLauncher.Telemetry; using Microsoft.PowerToys.Settings.UI.Helpers; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeColorPicker.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeColorPicker.xaml.cs index 3ed13e6e84..444b2d7295 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeColorPicker.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeColorPicker.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Threading; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.OOBE.Enums; using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeEnvironmentVariables.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeEnvironmentVariables.xaml.cs index d02a2f8e1b..af20978b42 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeEnvironmentVariables.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeEnvironmentVariables.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Threading; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.OOBE.Enums; using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeHosts.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeHosts.xaml.cs index 25dc699889..d47af96c7f 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeHosts.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeHosts.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Threading; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.OOBE.Enums; using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverviewPlaceholder.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverviewPlaceholder.xaml.cs index 18a18189ab..c7767e2424 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverviewPlaceholder.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverviewPlaceholder.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Threading.Tasks; + using AllExperiments; using Microsoft.PowerToys.Settings.UI.OOBE.Enums; using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeRun.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeRun.xaml.cs index 29ed8b72d4..3cb8593922 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeRun.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeRun.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System.Threading; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.OOBE.Enums; using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShellPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShellPage.xaml.cs index 09186b4634..02df4de735 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShellPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShellPage.xaml.cs @@ -5,6 +5,7 @@ using System; using System.Collections.ObjectModel; using System.Globalization; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.OOBE.Enums; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShortcutGuide.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShortcutGuide.xaml.cs index 11b8b678f9..5702ddcb9f 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShortcutGuide.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeShortcutGuide.xaml.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.OOBE.Enums; using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeWhatsNew.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeWhatsNew.xaml.cs index d5cece086b..760b4acaa1 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeWhatsNew.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeWhatsNew.xaml.cs @@ -13,6 +13,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using System.Text.RegularExpressions; using System.Threading.Tasks; + using CommunityToolkit.WinUI.UI.Controls; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.OOBE.Enums; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OobeWindow.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OobeWindow.xaml.cs index 7fb66e711e..ac8c3168b7 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OobeWindow.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OobeWindow.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.OOBE.Enums; using Microsoft.PowerToys.Settings.UI.OOBE.Views; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPaste.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPaste.xaml.cs index d24d13dabc..a395ac767b 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPaste.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPaste.xaml.cs @@ -6,6 +6,7 @@ using System; using System.Linq; using System.Threading.Tasks; using System.Windows.Input; + using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/AwakePage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/AwakePage.xaml.cs index 25822b303e..b30ff66844 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/AwakePage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/AwakePage.xaml.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.IO.Abstractions; + using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml.cs index d3c8cc0759..abb53f2b8f 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/ColorPickerPage.xaml.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Windows.Input; + using CommunityToolkit.WinUI.Controls; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs index 4130f94f8a..394b1d6de6 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/DashboardPage.xaml.cs @@ -5,6 +5,7 @@ using System; using System.Threading; using System.Threading.Tasks; + using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml.cs index 139ecef57c..bdcce77c5a 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/GeneralPage.xaml.cs @@ -5,6 +5,7 @@ using System; using System.Threading; using System.Threading.Tasks; + using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml.cs index cd5aedb792..e2dd14f2ad 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/ImageResizerPage.xaml.cs @@ -4,6 +4,7 @@ using System; using System.Globalization; + using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/KeyboardManagerPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/KeyboardManagerPage.xaml.cs index 9d84968206..4aa0e9a31b 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/KeyboardManagerPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/KeyboardManagerPage.xaml.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Globalization; using System.IO.Abstractions; + using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml.cs index 691d8a0413..49d4cfce30 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseUtilsPage.xaml.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; + using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseWithoutBordersPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseWithoutBordersPage.xaml.cs index 1eb2cf5064..989bd9f0c6 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseWithoutBordersPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/MouseWithoutBordersPage.xaml.cs @@ -6,6 +6,7 @@ using System; using System.IO.Abstractions; using System.Threading.Tasks; using System.Windows.Input; + using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; @@ -15,6 +16,7 @@ using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Media; using Windows.ApplicationModel.DataTransfer; using WinRT; + using static Microsoft.PowerToys.Settings.UI.ViewModels.MouseWithoutBordersViewModel; namespace Microsoft.PowerToys.Settings.UI.Views diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml.cs index f33e7b0d4a..78e2fa948f 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using System.Windows; + using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml.cs index 81ba63fcd1..3e272e0eb6 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerLauncherPage.xaml.cs @@ -5,6 +5,7 @@ using System; using System.Collections.ObjectModel; using System.IO; + using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/ShellPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/ShellPage.xaml.cs index 4a5f66c11a..eac8029b2e 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/ShellPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/ShellPage.xaml.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; + using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Services; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/VideoConference.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/VideoConference.xaml.cs index 9cf7b71cd7..2768994933 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/VideoConference.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/VideoConference.xaml.cs @@ -5,6 +5,7 @@ using System; using System.Runtime.InteropServices; using System.Threading.Tasks; + using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; diff --git a/src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs index 7790460bb5..94787ae513 100644 --- a/src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs @@ -13,6 +13,7 @@ using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; using System.Timers; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/AlwaysOnTopViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/AlwaysOnTopViewModel.cs index 97409f376c..a45c66a1ef 100644 --- a/src/settings-ui/Settings.UI/ViewModels/AlwaysOnTopViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/AlwaysOnTopViewModel.cs @@ -6,6 +6,7 @@ using System; using System.Globalization; using System.Runtime.CompilerServices; using System.Text.Json; + using Common.UI; using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/ViewModels/AwakeViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/AwakeViewModel.cs index 7d9a48c628..2042015717 100644 --- a/src/settings-ui/Settings.UI/ViewModels/AwakeViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/AwakeViewModel.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.CompilerServices; + using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/CmdNotFoundViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/CmdNotFoundViewModel.cs index 458ed443d6..f0fd6a4ab8 100644 --- a/src/settings-ui/Settings.UI/ViewModels/CmdNotFoundViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/CmdNotFoundViewModel.cs @@ -9,6 +9,7 @@ using System.IO; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; + using global::PowerToys.GPOWrapper; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs index a1f57d4fc3..4f59ed6e03 100644 --- a/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/ColorPickerViewModel.cs @@ -9,6 +9,7 @@ using System.Globalization; using System.Linq; using System.Text.Json; using System.Timers; + using global::PowerToys.GPOWrapper; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/ViewModels/CropAndLockViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/CropAndLockViewModel.cs index e855174e25..9064cd9058 100644 --- a/src/settings-ui/Settings.UI/ViewModels/CropAndLockViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/CropAndLockViewModel.cs @@ -6,6 +6,7 @@ using System; using System.Globalization; using System.Runtime.CompilerServices; using System.Text.Json; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/DashboardListItem.cs b/src/settings-ui/Settings.UI/ViewModels/DashboardListItem.cs index c7c304388f..03843a8d8c 100644 --- a/src/settings-ui/Settings.UI/ViewModels/DashboardListItem.cs +++ b/src/settings-ui/Settings.UI/ViewModels/DashboardListItem.cs @@ -6,6 +6,7 @@ using System; using System.Collections.ObjectModel; using System.ComponentModel; using System.Runtime.CompilerServices; + using ManagedCommon; using Microsoft.UI; using Windows.UI; diff --git a/src/settings-ui/Settings.UI/ViewModels/DashboardModuleItem.cs b/src/settings-ui/Settings.UI/ViewModels/DashboardModuleItem.cs index 5032abe999..fe029e6619 100644 --- a/src/settings-ui/Settings.UI/ViewModels/DashboardModuleItem.cs +++ b/src/settings-ui/Settings.UI/ViewModels/DashboardModuleItem.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Runtime.CompilerServices; + using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.UI.Xaml; using Windows.UI; diff --git a/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs index 7884a0994f..388a9f84ec 100644 --- a/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/DashboardViewModel.cs @@ -8,6 +8,7 @@ using System.Collections.ObjectModel; using System.IO.Abstractions; using System.Linq; using System.Windows.Threading; + using global::PowerToys.GPOWrapper; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/EnvironmentVariablesViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/EnvironmentVariablesViewModel.cs index 7a7960cff7..636d88d1ca 100644 --- a/src/settings-ui/Settings.UI/ViewModels/EnvironmentVariablesViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/EnvironmentVariablesViewModel.cs @@ -5,6 +5,7 @@ using System; using System.Runtime.CompilerServices; using System.Threading; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/FancyZonesViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/FancyZonesViewModel.cs index 89d1c95ff3..2b01dc3cec 100644 --- a/src/settings-ui/Settings.UI/ViewModels/FancyZonesViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/FancyZonesViewModel.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.CompilerServices; + using Common.UI; using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/ViewModels/FileLocksmithViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/FileLocksmithViewModel.cs index c79a42d83c..19175dba9a 100644 --- a/src/settings-ui/Settings.UI/ViewModels/FileLocksmithViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/FileLocksmithViewModel.cs @@ -5,6 +5,7 @@ using System; using System.Globalization; using System.Text.Json; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/Flyout/AllAppsViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/Flyout/AllAppsViewModel.cs index a0cefee149..58d1f4f9ce 100644 --- a/src/settings-ui/Settings.UI/ViewModels/Flyout/AllAppsViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/Flyout/AllAppsViewModel.cs @@ -4,6 +4,7 @@ using System; using System.Collections.ObjectModel; + using global::PowerToys.GPOWrapper; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutMenuItem.cs b/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutMenuItem.cs index 8bf4482e78..8493834f0f 100644 --- a/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutMenuItem.cs +++ b/src/settings-ui/Settings.UI/ViewModels/Flyout/FlyoutMenuItem.cs @@ -5,6 +5,7 @@ using System; using System.ComponentModel; using System.Runtime.CompilerServices; + using ManagedCommon; namespace Microsoft.PowerToys.Settings.UI.ViewModels diff --git a/src/settings-ui/Settings.UI/ViewModels/Flyout/LauncherViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/Flyout/LauncherViewModel.cs index ad24c17428..c1d820c0d7 100644 --- a/src/settings-ui/Settings.UI/ViewModels/Flyout/LauncherViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/Flyout/LauncherViewModel.cs @@ -4,6 +4,7 @@ using System; using System.Collections.ObjectModel; + using global::PowerToys.GPOWrapper; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs index 4f6ff3cb38..25f8b6ef3f 100644 --- a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs @@ -14,6 +14,7 @@ using System.Reflection; using System.Runtime.CompilerServices; using System.Text.Json; using System.Threading.Tasks; + using global::PowerToys.GPOWrapper; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/ViewModels/HostsViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/HostsViewModel.cs index 235ae42bd6..c15d8f9306 100644 --- a/src/settings-ui/Settings.UI/ViewModels/HostsViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/HostsViewModel.cs @@ -5,6 +5,7 @@ using System; using System.Runtime.CompilerServices; using System.Threading; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/ImageResizerViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/ImageResizerViewModel.cs index c0ae29ed59..dcd8db95f1 100644 --- a/src/settings-ui/Settings.UI/ViewModels/ImageResizerViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/ImageResizerViewModel.cs @@ -7,6 +7,7 @@ using System.Collections.ObjectModel; using System.ComponentModel; using System.IO; using System.Linq; + using global::PowerToys.GPOWrapper; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs index 037761c5f8..822df0d6f1 100644 --- a/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/KeyboardManagerViewModel.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Windows.Input; + using global::PowerToys.GPOWrapper; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/ViewModels/MeasureToolViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/MeasureToolViewModel.cs index 5257c677c5..6c8ee50535 100644 --- a/src/settings-ui/Settings.UI/ViewModels/MeasureToolViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/MeasureToolViewModel.cs @@ -6,6 +6,7 @@ using System; using System.Globalization; using System.Runtime.CompilerServices; using System.Text.Json; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs index 2ad31341c2..5533a6f04f 100644 --- a/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs @@ -5,6 +5,7 @@ using System; using System.ComponentModel; using System.Runtime.CompilerServices; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/MouseWithoutBordersViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/MouseWithoutBordersViewModel.cs index bc9eb69c12..6e71752b30 100644 --- a/src/settings-ui/Settings.UI/ViewModels/MouseWithoutBordersViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/MouseWithoutBordersViewModel.cs @@ -13,6 +13,7 @@ using System.Runtime.CompilerServices; using System.Text.Json; using System.Threading; using System.Threading.Tasks; + using global::PowerToys.GPOWrapper; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/NewPlusViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/NewPlusViewModel.cs index 1a92af09da..11e3d3ee46 100644 --- a/src/settings-ui/Settings.UI/ViewModels/NewPlusViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/NewPlusViewModel.cs @@ -10,6 +10,7 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; using System.Windows; + using Common.UI; using global::PowerToys.GPOWrapper; using ManagedCommon; @@ -21,6 +22,7 @@ using Microsoft.PowerToys.Settings.UI.Library.Utilities; using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands; using Windows.ApplicationModel.VoiceCommands; using Windows.System; + using static Microsoft.PowerToys.Settings.UI.Helpers.ShellGetFolder; namespace Microsoft.PowerToys.Settings.UI.ViewModels diff --git a/src/settings-ui/Settings.UI/ViewModels/PeekViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PeekViewModel.cs index 4672d0dabf..b3e6483e6c 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PeekViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PeekViewModel.cs @@ -5,6 +5,7 @@ using System; using System.Globalization; using System.Text.Json; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/PluginAdditionalOptionViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PluginAdditionalOptionViewModel.cs index 02b83228c7..c77d2c0596 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PluginAdditionalOptionViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PluginAdditionalOptionViewModel.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.Globalization; using System.Runtime.CompilerServices; + using Microsoft.PowerToys.Settings.UI.Library; namespace Microsoft.PowerToys.Settings.UI.ViewModels diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs index 99ef36afb9..75097ae2de 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerAccentViewModel.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.CompilerServices; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Enumerations; diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerLauncherPluginViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerLauncherPluginViewModel.cs index c350222c35..f8cc3b46f1 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerLauncherPluginViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerLauncherPluginViewModel.cs @@ -8,6 +8,7 @@ using System.ComponentModel; using System.IO; using System.Linq; using System.Runtime.CompilerServices; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerLauncherViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerLauncherViewModel.cs index 9bb10b8d2f..ee0781ce91 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerLauncherViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerLauncherViewModel.cs @@ -10,6 +10,7 @@ using System.Linq; using System.Runtime.CompilerServices; using System.Text.Json; using System.Windows.Input; + using global::PowerToys.GPOWrapper; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerOcrViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerOcrViewModel.cs index a52924860b..3751f6a377 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerOcrViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerOcrViewModel.cs @@ -9,6 +9,7 @@ using System.Globalization; using System.Linq; using System.Text.Json; using System.Timers; + using Common.UI; using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerPreviewViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerPreviewViewModel.cs index fa494f6cd5..3c8904803f 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerPreviewViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerPreviewViewModel.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.CompilerServices; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/PowerRenameViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/PowerRenameViewModel.cs index 602c16cf6b..00616bd678 100644 --- a/src/settings-ui/Settings.UI/ViewModels/PowerRenameViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/PowerRenameViewModel.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Runtime.CompilerServices; + using global::PowerToys.GPOWrapper; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; diff --git a/src/settings-ui/Settings.UI/ViewModels/RegistryPreviewViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/RegistryPreviewViewModel.cs index 77a2e5c870..f6edc99524 100644 --- a/src/settings-ui/Settings.UI/ViewModels/RegistryPreviewViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/RegistryPreviewViewModel.cs @@ -5,6 +5,7 @@ using System; using System.Globalization; using System.Text.Json; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/ShellViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/ShellViewModel.cs index b222c4ddf9..f02127ea38 100644 --- a/src/settings-ui/Settings.UI/ViewModels/ShellViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/ShellViewModel.cs @@ -9,6 +9,7 @@ using System.IO.Abstractions; using System.Linq; using System.Threading.Tasks; using System.Windows.Input; + using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library.Helpers; using Microsoft.PowerToys.Settings.UI.Services; diff --git a/src/settings-ui/Settings.UI/ViewModels/ShortcutGuideViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/ShortcutGuideViewModel.cs index 1ec77c81dc..e98b62dea4 100644 --- a/src/settings-ui/Settings.UI/ViewModels/ShortcutGuideViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/ShortcutGuideViewModel.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.CompilerServices; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs index b3abc2a215..21d3a7eb92 100644 --- a/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/VideoConferenceViewModel.cs @@ -10,6 +10,7 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text.Json; using System.Threading.Tasks; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; diff --git a/src/settings-ui/Settings.UI/ViewModels/WorkspacesViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/WorkspacesViewModel.cs index b3cee17070..ff7ff1bf88 100644 --- a/src/settings-ui/Settings.UI/ViewModels/WorkspacesViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/WorkspacesViewModel.cs @@ -6,6 +6,7 @@ using System; using System.Globalization; using System.Runtime.CompilerServices; using System.Text.Json; + using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Helpers; From 2ef987e1d9af49e3627fcc168e9db3b4ad241f6b Mon Sep 17 00:00:00 2001 From: Davide Giacometti <25966642+davidegiacometti@users.noreply.github.com> Date: Fri, 18 Oct 2024 11:11:18 +0200 Subject: [PATCH 25/95] [Chore]Common Logger: Log missing Exception type (#35084) log type for exception and inner exception --- src/common/ManagedCommon/Logger.cs | 31 ++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/src/common/ManagedCommon/Logger.cs b/src/common/ManagedCommon/Logger.cs index 416a7aa042..bbc2637fd9 100644 --- a/src/common/ManagedCommon/Logger.cs +++ b/src/common/ManagedCommon/Logger.cs @@ -60,14 +60,29 @@ namespace ManagedCommon public static void LogError(string message, Exception ex) { - Log( - message + Environment.NewLine + - ex?.Message + Environment.NewLine + - "Inner exception: " + Environment.NewLine + - ex?.InnerException?.Message + Environment.NewLine + - "Stack trace: " + Environment.NewLine + - ex?.StackTrace, - Error); + if (ex == null) + { + LogError(message); + } + else + { + var exMessage = + message + Environment.NewLine + + ex.GetType() + ": " + ex.Message + Environment.NewLine; + + if (ex.InnerException != null) + { + exMessage += + "Inner exception: " + Environment.NewLine + + ex.InnerException.GetType() + ": " + ex.InnerException.Message + Environment.NewLine; + } + + exMessage += + "Stack trace: " + Environment.NewLine + + ex.StackTrace; + + Log(exMessage, Error); + } } public static void LogWarning(string message) From 2335501421c11bfbfb0ca4889ea35c23c0ad3042 Mon Sep 17 00:00:00 2001 From: octastylos-pseudodipteros <128853928+octastylos-pseudodipteros@users.noreply.github.com> Date: Fri, 18 Oct 2024 14:37:09 +0200 Subject: [PATCH 26/95] [PTRun][WebSearch]Fix title not displaying the last letter (#35176) --- .../Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs index 350f68896a..2021b965df 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.WebSearch/Main.cs @@ -72,7 +72,7 @@ namespace Community.PowerToys.Run.Plugin.WebSearch string arguments = "? "; results.Add(new Result { - Title = Properties.Resources.plugin_description.Remove(Description.Length - 1, 1), + Title = Properties.Resources.plugin_description, SubTitle = string.Format(CultureInfo.CurrentCulture, PluginInBrowserName, BrowserInfo.Name ?? BrowserInfo.MSEdgeName), QueryTextDisplay = string.Empty, IcoPath = _iconPath, From 14139affc52179d2a1cc76242455e1bb668c3a93 Mon Sep 17 00:00:00 2001 From: Davide Giacometti <25966642+davidegiacometti@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:09:29 +0200 Subject: [PATCH 27/95] [Settings][QuickAccent]Sort Languages ComboBox (#35462) sort languages --- .../ViewModels/GeneralViewModel.cs | 40 +++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs index 25f8b6ef3f..adbc6cd0d3 100644 --- a/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/GeneralViewModel.cs @@ -9,7 +9,6 @@ using System.Diagnostics; using System.Globalization; using System.IO; using System.IO.Abstractions; -using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text.Json; @@ -1083,25 +1082,50 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private void InitializeLanguages() { var lang = LanguageModel.LoadSetting(); - int i = 0; + var selectedLanguageIndex = 0; foreach (var item in langTagsAndIds) { - Languages.Add(new LanguageModel { Tag = item.Key, ResourceID = item.Value, Language = GetResourceString(item.Value) }); + var language = new LanguageModel { Tag = item.Key, ResourceID = item.Value, Language = GetResourceString(item.Value) }; + var index = GetLanguageIndex(language.Language, item.Key == string.Empty); + Languages.Insert(index, language); if (item.Key.Equals(lang, StringComparison.Ordinal)) { - _initLanguagesIndex = i; - LanguagesIndex = i; + selectedLanguageIndex = index; + } + else if (index <= selectedLanguageIndex) + { + selectedLanguageIndex++; } - - i++; } + + _initLanguagesIndex = selectedLanguageIndex; + LanguagesIndex = selectedLanguageIndex; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1309:Use ordinal string comparison", Justification = "Building a user facing list")] + private int GetLanguageIndex(string language, bool isDefault) + { + if (Languages.Count == 0 || isDefault) + { + return 0; + } + + for (var i = 1; i < Languages.Count; i++) + { + if (string.Compare(Languages[i].Language, language, StringComparison.CurrentCultureIgnoreCase) > 0) + { + return i; + } + } + + return Languages.Count; } private void NotifyLanguageChanged() { - OutGoingLanguageSettings outsettings = new OutGoingLanguageSettings(langTagsAndIds.ElementAt(LanguagesIndex).Key); + OutGoingLanguageSettings outsettings = new OutGoingLanguageSettings(Languages[_languagesIndex].Tag); SendConfigMSG(outsettings.ToString()); } From dd5cd2a5708c2f130f1b7d5ce7b2b050ddf6ad58 Mon Sep 17 00:00:00 2001 From: Ani <115020168+drawbyperpetual@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:34:09 +0200 Subject: [PATCH 28/95] [AdvancedPaste]Additional actions - Image to text, Paste as file (txt, png, html) (#35167) * [AdvancedPaste] Additional actions, including Image to text * Spellcheck issue * [AdvancedPaste] Paste as file and many other improvements * Fixed typo * Fixed typo * [AdvancedPaste] Improved paste window menu layout * [AdvancedPaste] Improved settings window layout * [AdvancedPaste] Removed AudioToText for the moment * Code cleanup * Minor fixes * Changed log-line with potentially sensitive info * Extra telemetry for AdvancedPaste * Added 'Hotkey' suffix to AdvancedPaste_Settings telemetry event --- src/common/interop/Constants.cpp | 4 + src/common/interop/Constants.h | 1 + src/common/interop/Constants.idl | 1 + src/common/interop/shared_constants.h | 2 + .../AdvancedPasteXAML/App.xaml.cs | 79 ++-- .../AdvancedPasteXAML/Controls/PromptBox.xaml | 6 +- .../Controls/PromptBox.xaml.cs | 95 ++-- .../PasteFormatsToHeightConverter.cs | 24 + .../AdvancedPasteXAML/MainWindow.xaml | 4 +- .../AdvancedPasteXAML/MainWindow.xaml.cs | 21 +- .../AdvancedPasteXAML/Pages/MainPage.xaml | 98 ++-- .../AdvancedPasteXAML/Pages/MainPage.xaml.cs | 13 +- .../AdvancedPaste/Helpers/ClipboardHelper.cs | 168 +++++-- .../AdvancedPaste/Helpers/IUserSettings.cs | 10 +- .../AdvancedPaste/Helpers/OcrHelpers.cs | 39 ++ .../AdvancedPaste/Helpers/UserSettings.cs | 49 +- .../AdvancedPaste/Models/ClipboardFormat.cs | 18 + .../AdvancedPaste/Models/ClipboardItem.cs | 13 +- .../Models/CustomActionActivatedEventArgs.cs | 6 +- .../Models/PasteActionException.cs | 11 + .../AdvancedPaste/Models/PasteActionSource.cs | 13 + .../AdvancedPaste/Models/PasteFormat.cs | 58 ++- .../Models/PasteFormatMetadataAttribute.cs | 23 + .../AdvancedPaste/Models/PasteFormats.cs | 36 +- .../Services/IPasteFormatExecutor.cs | 13 + .../Services/PasteFormatExecutor.cs | 253 ++++++++++ .../Strings/en-us/Resources.resw | 27 +- .../ViewModels/OptionsViewModel.cs | 435 ++++++++---------- .../AdvancedPasteModuleInterface/dllmain.cpp | 138 +++++- .../AdvancedPasteModuleInterface/trace.cpp | 61 ++- .../AdvancedPasteModuleInterface/trace.h | 4 +- .../AdvancedPasteAdditionalAction.cs | 39 ++ .../AdvancedPasteAdditionalActions.cs | 27 ++ .../AdvancedPasteCustomAction.cs | 69 +-- .../AdvancedPastePasteAsFileAction.cs | 56 +++ .../AdvancedPasteProperties.cs | 7 +- .../Settings.UI.Library/Helpers/Observable.cs | 8 +- .../IAdvancedPasteAction.cs | 12 + .../SettingsXAML/Views/AdvancedPaste.xaml | 65 ++- .../Settings.UI/Strings/en-us/Resources.resw | 18 + .../ViewModels/AdvancedPasteViewModel.cs | 35 +- 41 files changed, 1435 insertions(+), 624 deletions(-) create mode 100644 src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Converters/PasteFormatsToHeightConverter.cs create mode 100644 src/modules/AdvancedPaste/AdvancedPaste/Helpers/OcrHelpers.cs create mode 100644 src/modules/AdvancedPaste/AdvancedPaste/Models/ClipboardFormat.cs create mode 100644 src/modules/AdvancedPaste/AdvancedPaste/Models/PasteActionException.cs create mode 100644 src/modules/AdvancedPaste/AdvancedPaste/Models/PasteActionSource.cs create mode 100644 src/modules/AdvancedPaste/AdvancedPaste/Models/PasteFormatMetadataAttribute.cs create mode 100644 src/modules/AdvancedPaste/AdvancedPaste/Services/IPasteFormatExecutor.cs create mode 100644 src/modules/AdvancedPaste/AdvancedPaste/Services/PasteFormatExecutor.cs create mode 100644 src/settings-ui/Settings.UI.Library/AdvancedPasteAdditionalAction.cs create mode 100644 src/settings-ui/Settings.UI.Library/AdvancedPasteAdditionalActions.cs create mode 100644 src/settings-ui/Settings.UI.Library/AdvancedPastePasteAsFileAction.cs create mode 100644 src/settings-ui/Settings.UI.Library/IAdvancedPasteAction.cs diff --git a/src/common/interop/Constants.cpp b/src/common/interop/Constants.cpp index dc4641822b..7a01cd225a 100644 --- a/src/common/interop/Constants.cpp +++ b/src/common/interop/Constants.cpp @@ -63,6 +63,10 @@ namespace winrt::PowerToys::Interop::implementation { return CommonSharedConstants::ADVANCED_PASTE_JSON_MESSAGE; } + hstring Constants::AdvancedPasteAdditionalActionMessage() + { + return CommonSharedConstants::ADVANCED_PASTE_ADDITIONAL_ACTION_MESSAGE; + } hstring Constants::AdvancedPasteCustomActionMessage() { return CommonSharedConstants::ADVANCED_PASTE_CUSTOM_ACTION_MESSAGE; diff --git a/src/common/interop/Constants.h b/src/common/interop/Constants.h index b59cd3e62c..09a884e50b 100644 --- a/src/common/interop/Constants.h +++ b/src/common/interop/Constants.h @@ -19,6 +19,7 @@ namespace winrt::PowerToys::Interop::implementation static hstring AdvancedPasteShowUIMessage(); static hstring AdvancedPasteMarkdownMessage(); static hstring AdvancedPasteJsonMessage(); + static hstring AdvancedPasteAdditionalActionMessage(); static hstring AdvancedPasteCustomActionMessage(); static hstring ShowPowerOCRSharedEvent(); static hstring MouseJumpShowPreviewEvent(); diff --git a/src/common/interop/Constants.idl b/src/common/interop/Constants.idl index a681ee5a81..9ae489c62b 100644 --- a/src/common/interop/Constants.idl +++ b/src/common/interop/Constants.idl @@ -16,6 +16,7 @@ namespace PowerToys static String AdvancedPasteShowUIMessage(); static String AdvancedPasteMarkdownMessage(); static String AdvancedPasteJsonMessage(); + static String AdvancedPasteAdditionalActionMessage(); static String AdvancedPasteCustomActionMessage(); static String ShowPowerOCRSharedEvent(); static String MouseJumpShowPreviewEvent(); diff --git a/src/common/interop/shared_constants.h b/src/common/interop/shared_constants.h index 3f9c350b7b..c98b143104 100644 --- a/src/common/interop/shared_constants.h +++ b/src/common/interop/shared_constants.h @@ -32,6 +32,8 @@ namespace CommonSharedConstants const wchar_t ADVANCED_PASTE_JSON_MESSAGE[] = L"PasteJson"; + const wchar_t ADVANCED_PASTE_ADDITIONAL_ACTION_MESSAGE[] = L"AdditionalAction"; + const wchar_t ADVANCED_PASTE_CUSTOM_ACTION_MESSAGE[] = L"CustomAction"; // Path to the event used to show Color Picker diff --git a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/App.xaml.cs b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/App.xaml.cs index 3f990ef6fa..77f8551803 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/App.xaml.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/App.xaml.cs @@ -3,12 +3,16 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Reflection; using System.Threading; using System.Threading.Tasks; using AdvancedPaste.Helpers; +using AdvancedPaste.Models; +using AdvancedPaste.Services; using AdvancedPaste.Settings; using AdvancedPaste.ViewModels; using ManagedCommon; @@ -34,6 +38,13 @@ namespace AdvancedPaste { public IHost Host { get; private set; } + private static readonly Dictionary AdditionalActionIPCKeys = + typeof(PasteFormats).GetFields() + .Where(field => field.IsLiteral) + .Select(field => (Format: (PasteFormats)field.GetRawConstantValue(), field.GetCustomAttribute().IPCKey)) + .Where(field => field.IPCKey != null) + .ToDictionary(field => field.IPCKey, field => field.Format); + private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread(); private readonly OptionsViewModel viewModel; @@ -60,8 +71,10 @@ namespace AdvancedPaste Host = Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder().UseContentRoot(AppContext.BaseDirectory).ConfigureServices((context, services) => { - services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); }).Build(); viewModel = GetService(); @@ -111,35 +124,35 @@ namespace AdvancedPaste private void ProcessNamedPipe(string pipeName) { - void OnMessage(string message) => _dispatcherQueue.TryEnqueue(() => OnNamedPipeMessage(message)); + void OnMessage(string message) => _dispatcherQueue.TryEnqueue(async () => await OnNamedPipeMessage(message)); - Task.Run(async () => - { - var connectTimeout = TimeSpan.FromSeconds(10); - await NamedPipeProcessor.ProcessNamedPipeAsync(pipeName, connectTimeout, OnMessage, CancellationToken.None); - }); + Task.Run(async () => await NamedPipeProcessor.ProcessNamedPipeAsync(pipeName, connectTimeout: TimeSpan.FromSeconds(10), OnMessage, CancellationToken.None)); } - private void OnNamedPipeMessage(string message) + private async Task OnNamedPipeMessage(string message) { var messageParts = message.Split(); var messageType = messageParts.First(); if (messageType == PowerToys.Interop.Constants.AdvancedPasteShowUIMessage()) { - OnAdvancedPasteHotkey(); + await ShowWindow(); } else if (messageType == PowerToys.Interop.Constants.AdvancedPasteMarkdownMessage()) { - OnAdvancedPasteMarkdownHotkey(); + await viewModel.ExecutePasteFormatAsync(PasteFormats.Markdown, PasteActionSource.GlobalKeyboardShortcut); } else if (messageType == PowerToys.Interop.Constants.AdvancedPasteJsonMessage()) { - OnAdvancedPasteJsonHotkey(); + await viewModel.ExecutePasteFormatAsync(PasteFormats.Json, PasteActionSource.GlobalKeyboardShortcut); + } + else if (messageType == PowerToys.Interop.Constants.AdvancedPasteAdditionalActionMessage()) + { + await OnAdvancedPasteAdditionalActionHotkey(messageParts); } else if (messageType == PowerToys.Interop.Constants.AdvancedPasteCustomActionMessage()) { - OnAdvancedPasteCustomActionHotkey(messageParts); + await OnAdvancedPasteCustomActionHotkey(messageParts); } } @@ -148,24 +161,27 @@ namespace AdvancedPaste Logger.LogError("Unhandled exception", e.Exception); } - private void OnAdvancedPasteJsonHotkey() + private async Task OnAdvancedPasteAdditionalActionHotkey(string[] messageParts) { - viewModel.ReadClipboard(); - viewModel.ToJsonFunction(true); + if (messageParts.Length != 2) + { + Logger.LogWarning("Unexpected additional action message"); + } + else + { + if (!AdditionalActionIPCKeys.TryGetValue(messageParts[1], out PasteFormats pasteFormat)) + { + Logger.LogWarning($"Unexpected additional action type {messageParts[1]}"); + } + else + { + await ShowWindow(); + await viewModel.ExecutePasteFormatAsync(pasteFormat, PasteActionSource.GlobalKeyboardShortcut); + } + } } - private void OnAdvancedPasteMarkdownHotkey() - { - viewModel.ReadClipboard(); - viewModel.ToMarkdownFunction(true); - } - - private void OnAdvancedPasteHotkey() - { - ShowWindow(); - } - - private void OnAdvancedPasteCustomActionHotkey(string[] messageParts) + private async Task OnAdvancedPasteCustomActionHotkey(string[] messageParts) { if (messageParts.Length != 2) { @@ -179,16 +195,15 @@ namespace AdvancedPaste } else { - ShowWindow(); - viewModel.ReadClipboard(); - viewModel.ExecuteCustomActionWithPaste(customActionId); + await ShowWindow(); + await viewModel.ExecuteCustomActionAsync(customActionId, PasteActionSource.GlobalKeyboardShortcut); } } } - private void ShowWindow() + private async Task ShowWindow() { - viewModel.OnShow(); + await viewModel.OnShowAsync(); if (window is null) { diff --git a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Controls/PromptBox.xaml b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Controls/PromptBox.xaml index 27891312ac..2c0d9ea937 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Controls/PromptBox.xaml +++ b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Controls/PromptBox.xaml @@ -346,7 +346,7 @@ x:Name="InputTxtBox" HorizontalAlignment="Stretch" x:FieldModifier="public" - IsEnabled="{x:Bind ViewModel.IsClipboardDataText, Mode=OneWay}" + IsEnabled="{x:Bind ViewModel.ClipboardHasData, Mode=OneWay}" KeyDown="InputTxtBox_KeyDown" PlaceholderText="{x:Bind ViewModel.InputTxtBoxPlaceholderText, Mode=OneWay}" Style="{StaticResource CustomTextBoxStyle}" @@ -589,7 +589,7 @@ Background="Transparent" Visibility="{x:Bind ViewModel.IsCustomAIEnabled, Mode=OneWay, Converter={StaticResource BoolToInvertedVisibilityConverter}}"> - + @@ -638,7 +638,7 @@ FontWeight="SemiBold" Foreground="{ThemeResource SystemFillColorCriticalBrush}" Style="{StaticResource CaptionTextBlockStyle}" - Text="{x:Bind ViewModel.ApiErrorText, Mode=OneWay}" /> + Text="{x:Bind ViewModel.PasteOperationErrorText, Mode=OneWay}" /> (); + InitializeComponent(); ViewModel = App.GetService(); - ViewModel.CustomActionActivated += (_, e) => GenerateCustom(e.ForcePasteCustom); + ViewModel.PropertyChanged += ViewModel_PropertyChanged; + ViewModel.CustomActionActivated += ViewModel_CustomActionActivated; + } + + private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == nameof(ViewModel.Busy) || e.PropertyName == nameof(ViewModel.PasteOperationErrorText)) + { + var state = ViewModel.Busy ? "LoadingState" : string.IsNullOrEmpty(ViewModel.PasteOperationErrorText) ? "DefaultState" : "ErrorState"; + VisualStateManager.GoToState(this, state, true); + } + } + + private void ViewModel_CustomActionActivated(object sender, CustomActionActivatedEventArgs e) + { + Logger.LogTrace(); + + if (!e.PasteResult) + { + PreviewGrid.Width = InputTxtBox.ActualWidth; + PreviewFlyout.ShowAt(InputTxtBox); + } } private void Grid_Loaded(object sender, RoutedEventArgs e) @@ -68,48 +79,7 @@ namespace AdvancedPaste.Controls } [RelayCommand] - private void GenerateCustom() => GenerateCustom(false); - - private void GenerateCustom(bool forcePasteCustom) - { - Logger.LogTrace(); - - VisualStateManager.GoToState(this, "LoadingState", true); - string inputInstructions = ViewModel.Query; - ViewModel.SaveQuery(inputInstructions); - - var customFormatTask = ViewModel.GenerateCustomFunction(inputInstructions); - var delayTask = forcePasteCustom ? Task.Delay(MinTaskTime) : Task.CompletedTask; - Task.WhenAll(customFormatTask, delayTask) - .ContinueWith( - _ => - { - _dispatcherQueue.TryEnqueue(() => - { - ViewModel.CustomFormatResult = customFormatTask.Result; - - if (ViewModel.ApiRequestStatus == (int)HttpStatusCode.OK) - { - VisualStateManager.GoToState(this, "DefaultState", true); - if (_userSettings.ShowCustomPreview && !forcePasteCustom) - { - PreviewGrid.Width = InputTxtBox.ActualWidth; - PreviewFlyout.ShowAt(InputTxtBox); - } - else - { - ViewModel.PasteCustom(); - InputTxtBox.Text = string.Empty; - } - } - else - { - VisualStateManager.GoToState(this, "ErrorState", true); - } - }); - }, - TaskScheduler.Default); - } + private async Task GenerateCustomAsync() => await ViewModel.GenerateCustomFunctionAsync(PasteActionSource.PromptBox); [RelayCommand] private void Recall() @@ -127,29 +97,24 @@ namespace AdvancedPaste.Controls ClipboardHelper.SetClipboardTextContent(lastQuery.ClipboardData); } - private void InputTxtBox_KeyDown(object sender, Microsoft.UI.Xaml.Input.KeyRoutedEventArgs e) + private async void InputTxtBox_KeyDown(object sender, Microsoft.UI.Xaml.Input.KeyRoutedEventArgs e) { if (e.Key == Windows.System.VirtualKey.Enter && InputTxtBox.Text.Length > 0 && ViewModel.IsCustomAIEnabled) { - GenerateCustom(); + await GenerateCustomAsync(); } } private void PreviewPasteBtn_Click(object sender, RoutedEventArgs e) { ViewModel.PasteCustom(); - InputTxtBox.Text = string.Empty; } private void ThumbUpDown_Click(object sender, RoutedEventArgs e) { - if (sender is Button btn) + if (sender is Button btn && bool.TryParse(btn.CommandParameter as string, out bool result)) { - bool result; - if (bool.TryParse(btn.CommandParameter as string, out result)) - { - PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteCustomFormatOutputThumbUpDownEvent(result)); - } + PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteCustomFormatOutputThumbUpDownEvent(result)); } } diff --git a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Converters/PasteFormatsToHeightConverter.cs b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Converters/PasteFormatsToHeightConverter.cs new file mode 100644 index 0000000000..2061bdefc9 --- /dev/null +++ b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Converters/PasteFormatsToHeightConverter.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Data; + +namespace AdvancedPaste.Converters; + +public sealed partial class PasteFormatsToHeightConverter : IValueConverter +{ + private const int ItemHeight = 40; + + public int MaxItems { get; set; } = 5; + + public object Convert(object value, Type targetType, object parameter, string language) => + new GridLength(GetHeight((value is ICollection collection) ? collection.Count : (value is int intValue) ? intValue : 0)); + + public int GetHeight(int itemCount) => Math.Min(MaxItems, itemCount) * ItemHeight; + + public object ConvertBack(object value, Type targetType, object parameter, string language) => throw new NotImplementedException(); +} diff --git a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/MainWindow.xaml b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/MainWindow.xaml index 8aa111219c..33a0dec49f 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/MainWindow.xaml +++ b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/MainWindow.xaml @@ -8,9 +8,9 @@ xmlns:pages="using:AdvancedPaste.Pages" xmlns:winuiex="using:WinUIEx" Width="420" - Height="308" + Height="188" MinWidth="420" - MinHeight="308" + MinHeight="188" Closed="WindowEx_Closed" IsAlwaysOnTop="True" IsMaximizable="False" diff --git a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/MainWindow.xaml.cs b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/MainWindow.xaml.cs index 314e503ed8..8f675e4d4e 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/MainWindow.xaml.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/MainWindow.xaml.cs @@ -4,9 +4,13 @@ using System; +using System.Linq; +using AdvancedPaste.Converters; using AdvancedPaste.Helpers; +using AdvancedPaste.Models; using AdvancedPaste.Settings; using AdvancedPaste.ViewModels; + using ManagedCommon; using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; @@ -24,25 +28,32 @@ namespace AdvancedPaste public MainWindow() { - this.InitializeComponent(); + InitializeComponent(); _userSettings = App.GetService(); + var optionsViewModel = App.GetService(); var baseHeight = MinHeight; + var coreActionCount = PasteFormat.MetadataDict.Values.Count(metadata => metadata.IsCoreAction); void UpdateHeight() { - var trimmedCustomActionCount = optionsViewModel.IsPasteWithAIEnabled ? Math.Min(_userSettings.CustomActions.Count, 5) : 0; - Height = MinHeight = baseHeight + (trimmedCustomActionCount * 40); + double GetHeight(int maxCustomActionCount) => + baseHeight + + new PasteFormatsToHeightConverter().GetHeight(coreActionCount + _userSettings.AdditionalActions.Count) + + new PasteFormatsToHeightConverter() { MaxItems = maxCustomActionCount }.GetHeight(optionsViewModel.IsAIServiceEnabled ? _userSettings.CustomActions.Count : 0); + + MinHeight = GetHeight(1); + Height = GetHeight(5); } UpdateHeight(); - _userSettings.CustomActions.CollectionChanged += (_, _) => UpdateHeight(); + _userSettings.Changed += (_, _) => UpdateHeight(); optionsViewModel.PropertyChanged += (_, e) => { - if (e.PropertyName == nameof(optionsViewModel.IsPasteWithAIEnabled)) + if (e.PropertyName == nameof(optionsViewModel.IsAIServiceEnabled)) { UpdateHeight(); } diff --git a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Pages/MainPage.xaml b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Pages/MainPage.xaml index f45a222866..b4e99ebaed 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Pages/MainPage.xaml +++ b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Pages/MainPage.xaml @@ -16,8 +16,9 @@ + + - - - - - - - - - - - - - + @@ -166,9 +186,9 @@ BorderThickness="0,1,0,0" RowSpacing="4"> + - - + @@ -176,12 +196,13 @@ x:Name="PasteOptionsListView" Grid.Row="0" VerticalAlignment="Bottom" - IsEnabled="{x:Bind ViewModel.IsClipboardDataText, Mode=OneWay}" - IsItemClickEnabled="True" - ItemClick="ListView_Click" + IsItemClickEnabled="False" + ItemContainerStyle="{StaticResource PasteFormatListViewItemStyle}" ItemContainerTransitions="{x:Null}" ItemTemplate="{StaticResource PasteFormatTemplate}" ItemsSource="{x:Bind ViewModel.StandardPasteFormats, Mode=OneWay}" + ScrollViewer.VerticalScrollBarVisibility="Visible" + ScrollViewer.VerticalScrollMode="Auto" SelectionMode="None" TabIndex="1" /> @@ -196,9 +217,8 @@ x:Name="CustomActionsListView" Grid.Row="2" VerticalAlignment="Top" - IsEnabled="{x:Bind ViewModel.IsCustomAIEnabled, Mode=OneWay}" - IsItemClickEnabled="True" - ItemClick="ListView_Click" + IsItemClickEnabled="False" + ItemContainerStyle="{StaticResource PasteFormatListViewItemStyle}" ItemContainerTransitions="{x:Null}" ItemTemplate="{StaticResource PasteFormatTemplate}" ItemsSource="{x:Bind ViewModel.CustomActionPasteFormats, Mode=OneWay}" diff --git a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Pages/MainPage.xaml.cs b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Pages/MainPage.xaml.cs index ab25db1d7c..d3e4ff1829 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Pages/MainPage.xaml.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/Pages/MainPage.xaml.cs @@ -130,15 +130,15 @@ namespace AdvancedPaste.Pages } } - private void ListView_Click(object sender, ItemClickEventArgs e) + private async void ListView_Button_Click(object sender, RoutedEventArgs e) { - if (e.ClickedItem is PasteFormat format) + if (sender is Button { DataContext: PasteFormat format }) { - ViewModel.ExecutePasteFormat(format); + await ViewModel.ExecutePasteFormatAsync(format, PasteActionSource.ContextMenu); } } - private void KeyboardAccelerator_Invoked(Microsoft.UI.Xaml.Input.KeyboardAccelerator sender, Microsoft.UI.Xaml.Input.KeyboardAcceleratorInvokedEventArgs args) + private async void KeyboardAccelerator_Invoked(Microsoft.UI.Xaml.Input.KeyboardAccelerator sender, Microsoft.UI.Xaml.Input.KeyboardAcceleratorInvokedEventArgs args) { if (GetMainWindow()?.Visible is false) { @@ -171,7 +171,7 @@ namespace AdvancedPaste.Pages case VirtualKey.Number7: case VirtualKey.Number8: case VirtualKey.Number9: - ViewModel.ExecutePasteFormat(sender.Key); + await ViewModel.ExecutePasteFormatAsync(sender.Key); break; default: @@ -199,8 +199,7 @@ namespace AdvancedPaste.Pages } else if (item.Image is not null) { - RandomAccessStreamReference image = null; - image = await item.Item.Content.GetBitmapAsync(); + RandomAccessStreamReference image = await item.Item.Content.GetBitmapAsync(); ClipboardHelper.SetClipboardImageContent(image); } } diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Helpers/ClipboardHelper.cs b/src/modules/AdvancedPaste/AdvancedPaste/Helpers/ClipboardHelper.cs index 4c1bf5b24e..9ec1dd1618 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Helpers/ClipboardHelper.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/Helpers/ClipboardHelper.cs @@ -3,12 +3,16 @@ // See the LICENSE file in the project root for more information. using System; -using System.Threading; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; +using AdvancedPaste.Models; using ManagedCommon; -using Microsoft.UI.Xaml.Media.Imaging; using Windows.ApplicationModel.DataTransfer; +using Windows.Data.Html; +using Windows.Graphics.Imaging; +using Windows.Storage; using Windows.Storage.Streams; using Windows.System; @@ -16,6 +20,34 @@ namespace AdvancedPaste.Helpers { internal static class ClipboardHelper { + private static readonly HashSet ImageFileTypes = new(StringComparer.InvariantCultureIgnoreCase) { ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".tiff", ".ico", ".svg" }; + + private static readonly (string DataFormat, ClipboardFormat ClipboardFormat)[] DataFormats = + [ + (StandardDataFormats.Text, ClipboardFormat.Text), + (StandardDataFormats.Html, ClipboardFormat.Html), + (StandardDataFormats.Bitmap, ClipboardFormat.Image), + ]; + + internal static async Task GetAvailableClipboardFormatsAsync(DataPackageView clipboardData) + { + var availableClipboardFormats = DataFormats.Aggregate( + ClipboardFormat.None, + (result, formatPair) => clipboardData.Contains(formatPair.DataFormat) ? (result | formatPair.ClipboardFormat) : result); + + if (clipboardData.Contains(StandardDataFormats.StorageItems)) + { + var storageItems = await clipboardData.GetStorageItemsAsync(); + + if (storageItems.Count == 1 && storageItems.Single() is StorageFile file && ImageFileTypes.Contains(file.FileType)) + { + availableClipboardFormats |= ClipboardFormat.ImageFile; + } + } + + return availableClipboardFormats; + } + internal static void SetClipboardTextContent(string text) { Logger.LogTrace(); @@ -26,31 +58,45 @@ namespace AdvancedPaste.Helpers output.SetText(text); Clipboard.SetContentWithOptions(output, null); - // TODO(stefan): For some reason Flush() fails from time to time when directly activated via hotkey. - // Calling inside a loop makes it work. - bool flushed = false; - for (int i = 0; i < 5; i++) + Flush(); + } + } + + private static bool Flush() + { + // TODO(stefan): For some reason Flush() fails from time to time when directly activated via hotkey. + // Calling inside a loop makes it work. + const int maxAttempts = 5; + for (int i = 1; i <= maxAttempts; i++) + { + try { - if (flushed) + Task.Run(Clipboard.Flush).Wait(); + return true; + } + catch (Exception ex) + { + if (i == maxAttempts) { - break; - } - - try - { - Task.Run(() => - { - Clipboard.Flush(); - }).Wait(); - - flushed = true; - } - catch (Exception ex) - { - Logger.LogError("Clipboard.Flush() failed", ex); + Logger.LogError($"{nameof(Clipboard)}.{nameof(Flush)}() failed", ex); } } } + + return false; + } + + private static async Task FlushAsync() => await Task.Run(Flush); + + internal static async Task SetClipboardFileContentAsync(string fileName) + { + var storageFile = await StorageFile.GetFileFromPathAsync(fileName); + + DataPackage output = new(); + output.SetStorageItems([storageFile]); + Clipboard.SetContent(output); + + await FlushAsync(); } internal static void SetClipboardImageContent(RandomAccessStreamReference image) @@ -63,30 +109,7 @@ namespace AdvancedPaste.Helpers output.SetBitmap(image); Clipboard.SetContentWithOptions(output, null); - // TODO(stefan): For some reason Flush() fails from time to time when directly activated via hotkey. - // Calling inside a loop makes it work. - bool flushed = false; - for (int i = 0; i < 5; i++) - { - if (flushed) - { - break; - } - - try - { - Task.Run(() => - { - Clipboard.Flush(); - }).Wait(); - - flushed = true; - } - catch (Exception ex) - { - Logger.LogError("Clipboard.Flush() failed", ex); - } - } + Flush(); } } @@ -136,5 +159,58 @@ namespace AdvancedPaste.Helpers Logger.LogInfo("Paste sent"); } + + internal static async Task GetClipboardTextOrHtmlTextAsync(DataPackageView clipboardData) + { + if (clipboardData.Contains(StandardDataFormats.Text)) + { + return await clipboardData.GetTextAsync(); + } + else if (clipboardData.Contains(StandardDataFormats.Html)) + { + var html = await clipboardData.GetHtmlFormatAsync(); + return HtmlUtilities.ConvertToText(html); + } + else + { + return string.Empty; + } + } + + internal static async Task GetClipboardHtmlContentAsync(DataPackageView clipboardData) => + clipboardData.Contains(StandardDataFormats.Html) ? await clipboardData.GetHtmlFormatAsync() : string.Empty; + + internal static async Task GetClipboardImageContentAsync(DataPackageView clipboardData) + { + using var stream = await GetClipboardImageStreamAsync(clipboardData); + if (stream != null) + { + var decoder = await BitmapDecoder.CreateAsync(stream); + return await decoder.GetSoftwareBitmapAsync(); + } + + return null; + } + + private static async Task GetClipboardImageStreamAsync(DataPackageView clipboardData) + { + if (clipboardData.Contains(StandardDataFormats.StorageItems)) + { + var storageItems = await clipboardData.GetStorageItemsAsync(); + var file = storageItems.Count == 1 ? storageItems[0] as StorageFile : null; + if (file != null) + { + return await file.OpenReadAsync(); + } + } + + if (clipboardData.Contains(StandardDataFormats.Bitmap)) + { + var bitmap = await clipboardData.GetBitmapAsync(); + return await bitmap.OpenReadAsync(); + } + + return null; + } } } diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Helpers/IUserSettings.cs b/src/modules/AdvancedPaste/AdvancedPaste/Helpers/IUserSettings.cs index fd72f67262..49dbfda945 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Helpers/IUserSettings.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/Helpers/IUserSettings.cs @@ -2,8 +2,10 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.ObjectModel; +using System; +using System.Collections.Generic; +using AdvancedPaste.Models; using Microsoft.PowerToys.Settings.UI.Library; namespace AdvancedPaste.Settings @@ -16,6 +18,10 @@ namespace AdvancedPaste.Settings public bool CloseAfterLosingFocus { get; } - public ObservableCollection CustomActions { get; } + public IReadOnlyList CustomActions { get; } + + public IReadOnlyList AdditionalActions { get; } + + public event EventHandler Changed; } } diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Helpers/OcrHelpers.cs b/src/modules/AdvancedPaste/AdvancedPaste/Helpers/OcrHelpers.cs new file mode 100644 index 0000000000..25b46fd160 --- /dev/null +++ b/src/modules/AdvancedPaste/AdvancedPaste/Helpers/OcrHelpers.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Linq; +using System.Threading.Tasks; +using Windows.Globalization; +using Windows.Graphics.Imaging; +using Windows.Media.Ocr; +using Windows.System.UserProfile; + +namespace AdvancedPaste.Helpers; + +public static class OcrHelpers +{ + public static async Task ExtractTextAsync(SoftwareBitmap bitmap) + { + var ocrLanguage = GetOCRLanguage() ?? throw new InvalidOperationException("Unable to determine OCR language"); + + var ocrEngine = OcrEngine.TryCreateFromLanguage(ocrLanguage) ?? throw new InvalidOperationException("Unable to create OCR engine"); + var ocrResult = await ocrEngine.RecognizeAsync(bitmap); + + return ocrResult.Text; + } + + private static Language GetOCRLanguage() + { + var userLanguageTags = GlobalizationPreferences.Languages.ToList(); + + var languages = from language in OcrEngine.AvailableRecognizerLanguages + let tag = language.LanguageTag + where userLanguageTags.Contains(tag) + orderby userLanguageTags.IndexOf(tag) + select language; + + return languages.FirstOrDefault(); + } +} diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Helpers/UserSettings.cs b/src/modules/AdvancedPaste/AdvancedPaste/Helpers/UserSettings.cs index 0bfb50c898..fe60dd7f53 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Helpers/UserSettings.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/Helpers/UserSettings.cs @@ -3,11 +3,13 @@ // See the LICENSE file in the project root for more information. using System; -using System.Collections.ObjectModel; +using System.Collections.Generic; using System.IO.Abstractions; +using System.Linq; using System.Threading; using System.Threading.Tasks; +using AdvancedPaste.Models; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; @@ -20,6 +22,8 @@ namespace AdvancedPaste.Settings private readonly TaskScheduler _taskScheduler; private readonly IFileSystemWatcher _watcher; private readonly object _loadingSettingsLock = new(); + private readonly List _additionalActions; + private readonly List _customActions; private const string AdvancedPasteModuleName = "AdvancedPaste"; private const int MaxNumberOfRetry = 5; @@ -27,13 +31,17 @@ namespace AdvancedPaste.Settings private bool _disposedValue; private CancellationTokenSource _cancellationTokenSource; + public event EventHandler Changed; + public bool ShowCustomPreview { get; private set; } public bool SendPasteKeyCombination { get; private set; } public bool CloseAfterLosingFocus { get; private set; } - public ObservableCollection CustomActions { get; private set; } + public IReadOnlyList AdditionalActions => _additionalActions; + + public IReadOnlyList CustomActions => _customActions; public UserSettings() { @@ -42,8 +50,8 @@ namespace AdvancedPaste.Settings ShowCustomPreview = true; SendPasteKeyCombination = true; CloseAfterLosingFocus = false; - CustomActions = []; - + _additionalActions = []; + _customActions = []; _taskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); LoadSettingsFromJson(); @@ -88,18 +96,29 @@ namespace AdvancedPaste.Settings { void UpdateSettings() { - ShowCustomPreview = settings.Properties.ShowCustomPreview; - SendPasteKeyCombination = settings.Properties.SendPasteKeyCombination; - CloseAfterLosingFocus = settings.Properties.CloseAfterLosingFocus; + var properties = settings.Properties; - CustomActions.Clear(); - foreach (var customAction in settings.Properties.CustomActions.Value) - { - if (customAction.IsShown && customAction.IsValid) - { - CustomActions.Add(customAction); - } - } + ShowCustomPreview = properties.ShowCustomPreview; + SendPasteKeyCombination = properties.SendPasteKeyCombination; + CloseAfterLosingFocus = properties.CloseAfterLosingFocus; + + var sourceAdditionalActions = properties.AdditionalActions; + (PasteFormats Format, IAdvancedPasteAction[] Actions)[] additionalActionFormats = + [ + (PasteFormats.ImageToText, [sourceAdditionalActions.ImageToText]), + (PasteFormats.PasteAsTxtFile, [sourceAdditionalActions.PasteAsFile, sourceAdditionalActions.PasteAsFile.PasteAsTxtFile]), + (PasteFormats.PasteAsPngFile, [sourceAdditionalActions.PasteAsFile, sourceAdditionalActions.PasteAsFile.PasteAsPngFile]), + (PasteFormats.PasteAsHtmlFile, [sourceAdditionalActions.PasteAsFile, sourceAdditionalActions.PasteAsFile.PasteAsHtmlFile]) + ]; + + _additionalActions.Clear(); + _additionalActions.AddRange(additionalActionFormats.Where(tuple => tuple.Actions.All(action => action.IsShown)) + .Select(tuple => tuple.Format)); + + _customActions.Clear(); + _customActions.AddRange(properties.CustomActions.Value.Where(customAction => customAction.IsShown && customAction.IsValid)); + + Changed?.Invoke(this, EventArgs.Empty); } Task.Factory diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Models/ClipboardFormat.cs b/src/modules/AdvancedPaste/AdvancedPaste/Models/ClipboardFormat.cs new file mode 100644 index 0000000000..a63e79735e --- /dev/null +++ b/src/modules/AdvancedPaste/AdvancedPaste/Models/ClipboardFormat.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace AdvancedPaste.Models; + +[Flags] +public enum ClipboardFormat +{ + None, + Text = 1 << 0, + Html = 1 << 1, + Audio = 1 << 2, + Image = 1 << 3, + ImageFile = 1 << 4, +} diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Models/ClipboardItem.cs b/src/modules/AdvancedPaste/AdvancedPaste/Models/ClipboardItem.cs index 1ff749753e..3927949064 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Models/ClipboardItem.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/Models/ClipboardItem.cs @@ -5,14 +5,13 @@ using Microsoft.UI.Xaml.Media.Imaging; using Windows.ApplicationModel.DataTransfer; -namespace AdvancedPaste.Models +namespace AdvancedPaste.Models; + +public class ClipboardItem { - public class ClipboardItem - { - public string Content { get; set; } + public string Content { get; set; } - public ClipboardHistoryItem Item { get; set; } + public ClipboardHistoryItem Item { get; set; } - public BitmapImage Image { get; set; } - } + public BitmapImage Image { get; set; } } diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Models/CustomActionActivatedEventArgs.cs b/src/modules/AdvancedPaste/AdvancedPaste/Models/CustomActionActivatedEventArgs.cs index d321cb01f6..d4846e01be 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Models/CustomActionActivatedEventArgs.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/Models/CustomActionActivatedEventArgs.cs @@ -6,9 +6,9 @@ using System; namespace AdvancedPaste.Models; -public sealed class CustomActionActivatedEventArgs(string text, bool forcePasteCustom) : EventArgs +public sealed class CustomActionActivatedEventArgs(string text, bool pasteResult) : EventArgs { - public string Text { get; private set; } = text; + public string Text { get; private init; } = text; - public bool ForcePasteCustom { get; private set; } = forcePasteCustom; + public bool PasteResult { get; private init; } = pasteResult; } diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteActionException.cs b/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteActionException.cs new file mode 100644 index 0000000000..fed4e24c50 --- /dev/null +++ b/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteActionException.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace AdvancedPaste.Models; + +public sealed class PasteActionException(string message) : Exception(message) +{ +} diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteActionSource.cs b/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteActionSource.cs new file mode 100644 index 0000000000..bdfabfbcc3 --- /dev/null +++ b/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteActionSource.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace AdvancedPaste.Models; + +public enum PasteActionSource +{ + ContextMenu, + InAppKeyboardShortcut, + GlobalKeyboardShortcut, + PromptBox, +} diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteFormat.cs b/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteFormat.cs index 6f5103be3e..c38a54b843 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteFormat.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteFormat.cs @@ -2,38 +2,62 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using CommunityToolkit.Mvvm.ComponentModel; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; using Microsoft.PowerToys.Settings.UI.Library; namespace AdvancedPaste.Models; -public partial class PasteFormat : ObservableObject +[DebuggerDisplay("{Name} IsEnabled={IsEnabled} ShortcutText={ShortcutText}")] +public sealed class PasteFormat { - [ObservableProperty] - private string _shortcutText = string.Empty; + public static readonly IReadOnlyDictionary MetadataDict = + typeof(PasteFormats).GetFields() + .Where(field => field.IsLiteral) + .ToDictionary(field => (PasteFormats)field.GetRawConstantValue(), field => field.GetCustomAttribute()); - [ObservableProperty] - private string _toolTip = string.Empty; - - public PasteFormat() + private PasteFormat(PasteFormats format, ClipboardFormat clipboardFormats, bool isAIServiceEnabled) { + Format = format; + IsEnabled = SupportsClipboardFormats(clipboardFormats) && (isAIServiceEnabled || !Metadata.RequiresAIService); } - public PasteFormat(AdvancedPasteCustomAction customAction, string shortcutText) + public PasteFormat(PasteFormats format, ClipboardFormat clipboardFormats, bool isAIServiceEnabled, Func resourceLoader) + : this(format, clipboardFormats, isAIServiceEnabled) + { + Name = Metadata.ResourceId == null ? string.Empty : resourceLoader(Metadata.ResourceId); + Prompt = string.Empty; + } + + public PasteFormat(AdvancedPasteCustomAction customAction, ClipboardFormat clipboardFormats, bool isAIServiceEnabled) + : this(PasteFormats.Custom, clipboardFormats, isAIServiceEnabled) { - IconGlyph = "\uE945"; Name = customAction.Name; Prompt = customAction.Prompt; - Format = PasteFormats.Custom; - ShortcutText = shortcutText; - ToolTip = customAction.Prompt; } - public string IconGlyph { get; init; } + public PasteFormatMetadataAttribute Metadata => MetadataDict[Format]; - public string Name { get; init; } + public string IconGlyph => Metadata.IconGlyph; - public PasteFormats Format { get; init; } + public string Name { get; private init; } - public string Prompt { get; init; } = string.Empty; + public PasteFormats Format { get; private init; } + + public string Prompt { get; private init; } + + public bool IsEnabled { get; private init; } + + public double Opacity => IsEnabled ? 1 : 0.5; + + public string ToolTip => string.IsNullOrEmpty(Prompt) ? $"{Name} ({ShortcutText})" : Prompt; + + public string Query => string.IsNullOrEmpty(Prompt) ? Name : Prompt; + + public string ShortcutText { get; set; } = string.Empty; + + public bool SupportsClipboardFormats(ClipboardFormat clipboardFormats) => (clipboardFormats & Metadata.SupportedClipboardFormats) != ClipboardFormat.None; } diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteFormatMetadataAttribute.cs b/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteFormatMetadataAttribute.cs new file mode 100644 index 0000000000..cb3a8a954e --- /dev/null +++ b/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteFormatMetadataAttribute.cs @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace AdvancedPaste.Models; + +[AttributeUsage(AttributeTargets.Field)] +public sealed class PasteFormatMetadataAttribute : Attribute +{ + public bool IsCoreAction { get; init; } + + public string ResourceId { get; init; } + + public string IconGlyph { get; init; } + + public bool RequiresAIService { get; init; } + + public ClipboardFormat SupportedClipboardFormats { get; init; } + + public string IPCKey { get; init; } +} diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteFormats.cs b/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteFormats.cs index ba48fe6586..fe710d5410 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteFormats.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/Models/PasteFormats.cs @@ -2,13 +2,33 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace AdvancedPaste.Models +using Microsoft.PowerToys.Settings.UI.Library; + +namespace AdvancedPaste.Models; + +public enum PasteFormats { - public enum PasteFormats - { - PlainText, - Markdown, - Json, - Custom, - } + [PasteFormatMetadata(IsCoreAction = true, ResourceId = "PasteAsPlainText", IconGlyph = "\uE8E9", RequiresAIService = false, SupportedClipboardFormats = ClipboardFormat.Text)] + PlainText, + + [PasteFormatMetadata(IsCoreAction = true, ResourceId = "PasteAsMarkdown", IconGlyph = "\ue8a5", RequiresAIService = false, SupportedClipboardFormats = ClipboardFormat.Text)] + Markdown, + + [PasteFormatMetadata(IsCoreAction = true, ResourceId = "PasteAsJson", IconGlyph = "\uE943", RequiresAIService = false, SupportedClipboardFormats = ClipboardFormat.Text)] + Json, + + [PasteFormatMetadata(IsCoreAction = false, ResourceId = "ImageToText", IconGlyph = "\uE91B", RequiresAIService = false, SupportedClipboardFormats = ClipboardFormat.Image | ClipboardFormat.ImageFile, IPCKey = AdvancedPasteAdditionalActions.PropertyNames.ImageToText)] + ImageToText, + + [PasteFormatMetadata(IsCoreAction = false, ResourceId = "PasteAsTxtFile", IconGlyph = "\uE8D2", RequiresAIService = false, SupportedClipboardFormats = ClipboardFormat.Text | ClipboardFormat.Html, IPCKey = AdvancedPastePasteAsFileAction.PropertyNames.PasteAsTxtFile)] + PasteAsTxtFile, + + [PasteFormatMetadata(IsCoreAction = false, ResourceId = "PasteAsPngFile", IconGlyph = "\uE8B9", RequiresAIService = false, SupportedClipboardFormats = ClipboardFormat.Image | ClipboardFormat.ImageFile, IPCKey = AdvancedPastePasteAsFileAction.PropertyNames.PasteAsPngFile)] + PasteAsPngFile, + + [PasteFormatMetadata(IsCoreAction = false, ResourceId = "PasteAsHtmlFile", IconGlyph = "\uF6FA", RequiresAIService = false, SupportedClipboardFormats = ClipboardFormat.Html, IPCKey = AdvancedPastePasteAsFileAction.PropertyNames.PasteAsHtmlFile)] + PasteAsHtmlFile, + + [PasteFormatMetadata(IsCoreAction = false, IconGlyph = "\uE945", RequiresAIService = true, SupportedClipboardFormats = ClipboardFormat.Text)] + Custom, } diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Services/IPasteFormatExecutor.cs b/src/modules/AdvancedPaste/AdvancedPaste/Services/IPasteFormatExecutor.cs new file mode 100644 index 0000000000..e0bb39ab7c --- /dev/null +++ b/src/modules/AdvancedPaste/AdvancedPaste/Services/IPasteFormatExecutor.cs @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Threading.Tasks; +using AdvancedPaste.Models; + +namespace AdvancedPaste.Services; + +public interface IPasteFormatExecutor +{ + Task ExecutePasteFormatAsync(PasteFormat pasteFormat, PasteActionSource source); +} diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Services/PasteFormatExecutor.cs b/src/modules/AdvancedPaste/AdvancedPaste/Services/PasteFormatExecutor.cs new file mode 100644 index 0000000000..f77cf8ef99 --- /dev/null +++ b/src/modules/AdvancedPaste/AdvancedPaste/Services/PasteFormatExecutor.cs @@ -0,0 +1,253 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Globalization; +using System.IO; +using System.Net; +using System.Threading.Tasks; +using AdvancedPaste.Helpers; +using AdvancedPaste.Models; +using ManagedCommon; +using Microsoft.PowerToys.Telemetry; +using Windows.ApplicationModel.DataTransfer; +using Windows.Graphics.Imaging; +using Windows.Storage.Streams; + +namespace AdvancedPaste.Services; + +public sealed class PasteFormatExecutor(AICompletionsHelper aiHelper) : IPasteFormatExecutor +{ + private readonly AICompletionsHelper _aiHelper = aiHelper; + + public async Task ExecutePasteFormatAsync(PasteFormat pasteFormat, PasteActionSource source) + { + if (!pasteFormat.IsEnabled) + { + return null; + } + + WriteTelemetry(pasteFormat.Format, source); + + return await ExecutePasteFormatCoreAsync(pasteFormat, Clipboard.GetContent()); + } + + private async Task ExecutePasteFormatCoreAsync(PasteFormat pasteFormat, DataPackageView clipboardData) + { + switch (pasteFormat.Format) + { + case PasteFormats.PlainText: + ToPlainText(clipboardData); + return null; + + case PasteFormats.Markdown: + ToMarkdown(clipboardData); + return null; + + case PasteFormats.Json: + ToJson(clipboardData); + return null; + + case PasteFormats.ImageToText: + await ImageToTextAsync(clipboardData); + return null; + + case PasteFormats.PasteAsTxtFile: + await ToTxtFileAsync(clipboardData); + return null; + + case PasteFormats.PasteAsPngFile: + await ToPngFileAsync(clipboardData); + return null; + + case PasteFormats.PasteAsHtmlFile: + await ToHtmlFileAsync(clipboardData); + return null; + + case PasteFormats.Custom: + return await ToCustomAsync(pasteFormat.Prompt, clipboardData); + + default: + throw new ArgumentException($"Unknown paste format {pasteFormat.Format}", nameof(pasteFormat)); + } + } + + private static void WriteTelemetry(PasteFormats format, PasteActionSource source) + { + switch (source) + { + case PasteActionSource.ContextMenu: + PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteFormatClickedEvent(format)); + break; + + case PasteActionSource.InAppKeyboardShortcut: + PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteInAppKeyboardShortcutEvent(format)); + break; + + case PasteActionSource.GlobalKeyboardShortcut: + case PasteActionSource.PromptBox: + break; // no telemetry yet for these sources + + default: + throw new ArgumentOutOfRangeException(nameof(format)); + } + } + + private void ToPlainText(DataPackageView clipboardData) + { + Logger.LogTrace(); + SetClipboardTextContent(MarkdownHelper.PasteAsPlainTextFromClipboard(clipboardData)); + } + + private void ToMarkdown(DataPackageView clipboardData) + { + Logger.LogTrace(); + SetClipboardTextContent(MarkdownHelper.ToMarkdown(clipboardData)); + } + + private void ToJson(DataPackageView clipboardData) + { + Logger.LogTrace(); + SetClipboardTextContent(JsonHelper.ToJsonFromXmlOrCsv(clipboardData)); + } + + private async Task ImageToTextAsync(DataPackageView clipboardData) + { + Logger.LogTrace(); + + var bitmap = await ClipboardHelper.GetClipboardImageContentAsync(clipboardData); + var text = await OcrHelpers.ExtractTextAsync(bitmap); + SetClipboardTextContent(text); + } + + private async Task ToPngFileAsync(DataPackageView clipboardData) + { + Logger.LogTrace(); + + var clipboardBitmap = await ClipboardHelper.GetClipboardImageContentAsync(clipboardData); + + using var pngStream = new InMemoryRandomAccessStream(); + var encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, pngStream); + encoder.SetSoftwareBitmap(clipboardBitmap); + await encoder.FlushAsync(); + + await SetClipboardFileContentAsync(pngStream.AsStreamForRead(), "png"); + } + + private async Task ToTxtFileAsync(DataPackageView clipboardData) + { + Logger.LogTrace(); + + var text = await ClipboardHelper.GetClipboardTextOrHtmlTextAsync(clipboardData); + await SetClipboardFileContentAsync(text, "txt"); + } + + private async Task ToHtmlFileAsync(DataPackageView clipboardData) + { + Logger.LogTrace(); + + var cfHtml = await ClipboardHelper.GetClipboardHtmlContentAsync(clipboardData); + var html = RemoveHtmlMetadata(cfHtml); + + await SetClipboardFileContentAsync(html, "html"); + } + + /// + /// Removes leading CF_HTML metadata from HTML clipboard data. + /// See: https://learn.microsoft.com/en-us/windows/win32/dataxchg/html-clipboard-format + /// + private static string RemoveHtmlMetadata(string cfHtml) + { + int? GetIntTagValue(string tagName) + { + var tagNameWithColon = tagName + ":"; + int tagStartPos = cfHtml.IndexOf(tagNameWithColon, StringComparison.InvariantCulture); + + const int tagValueLength = 10; + return tagStartPos != -1 && int.TryParse(cfHtml.AsSpan(tagStartPos + tagNameWithColon.Length, tagValueLength), CultureInfo.InvariantCulture, out int result) ? result : null; + } + + var startFragmentIndex = GetIntTagValue("StartFragment"); + var endFragmentIndex = GetIntTagValue("EndFragment"); + + return (startFragmentIndex == null || endFragmentIndex == null) ? cfHtml : cfHtml[startFragmentIndex.Value..endFragmentIndex.Value]; + } + + private static async Task SetClipboardFileContentAsync(string data, string fileExtension) + { + if (string.IsNullOrEmpty(data)) + { + throw new ArgumentException($"Empty value in {nameof(SetClipboardFileContentAsync)}", nameof(data)); + } + + var path = GetPasteAsFileTempFilePath(fileExtension); + + await File.WriteAllTextAsync(path, data); + await ClipboardHelper.SetClipboardFileContentAsync(path); + } + + private static async Task SetClipboardFileContentAsync(Stream stream, string fileExtension) + { + var path = GetPasteAsFileTempFilePath(fileExtension); + + using var fileStream = File.Create(path); + await stream.CopyToAsync(fileStream); + + await ClipboardHelper.SetClipboardFileContentAsync(path); + } + + private static string GetPasteAsFileTempFilePath(string fileExtension) + { + var prefix = ResourceLoaderInstance.ResourceLoader.GetString("PasteAsFile_FilePrefix"); + var timestamp = DateTime.Now.ToString("yyyyMMddHHmmss", CultureInfo.InvariantCulture); + + return Path.Combine(Path.GetTempPath(), $"{prefix}{timestamp}.{fileExtension}"); + } + + private async Task ToCustomAsync(string prompt, DataPackageView clipboardData) + { + Logger.LogTrace(); + + if (string.IsNullOrWhiteSpace(prompt)) + { + return string.Empty; + } + + if (!clipboardData.Contains(StandardDataFormats.Text)) + { + Logger.LogWarning("Clipboard does not contain text data"); + return string.Empty; + } + + var currentClipboardText = await clipboardData.GetTextAsync(); + + if (string.IsNullOrWhiteSpace(currentClipboardText)) + { + Logger.LogWarning("Clipboard has no usable text data"); + return string.Empty; + } + + var aiResponse = await Task.Run(() => _aiHelper.AIFormatString(prompt, currentClipboardText)); + + return aiResponse.ApiRequestStatus == (int)HttpStatusCode.OK + ? aiResponse.Response + : throw new PasteActionException(TranslateErrorText(aiResponse.ApiRequestStatus)); + } + + private void SetClipboardTextContent(string content) + { + if (!string.IsNullOrEmpty(content)) + { + ClipboardHelper.SetClipboardTextContent(content); + } + } + + private static string TranslateErrorText(int apiRequestStatus) => (HttpStatusCode)apiRequestStatus switch + { + HttpStatusCode.TooManyRequests => ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyTooManyRequests"), + HttpStatusCode.Unauthorized => ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyUnauthorized"), + HttpStatusCode.OK => string.Empty, + _ => ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyError") + apiRequestStatus.ToString(CultureInfo.InvariantCulture), + }; +} diff --git a/src/modules/AdvancedPaste/AdvancedPaste/Strings/en-us/Resources.resw b/src/modules/AdvancedPaste/AdvancedPaste/Strings/en-us/Resources.resw index dd2720732c..0ec81547e2 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/Strings/en-us/Resources.resw +++ b/src/modules/AdvancedPaste/AdvancedPaste/Strings/en-us/Resources.resw @@ -120,9 +120,12 @@ AI can make mistakes. - - Clipboard data is not text + + Clipboard does not contain any usable formats + + Clipboard data is not text + To custom with AI is not enabled @@ -135,6 +138,9 @@ OpenAI request failed with status code: + + An error occurred during the paste operation + Clipboard history @@ -151,7 +157,7 @@ Privacy - Connecting to AI services and generating output.. + Generating output... Paste as JSON @@ -162,6 +168,18 @@ Paste as plain text + + Image to text + + + Paste as .txt file + + + Paste as .png file + + + Paste as .html file + Paste @@ -228,4 +246,7 @@ Ctrl + + PowerToys_Paste_ + \ No newline at end of file diff --git a/src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs b/src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs index 4d59e327af..f2e461e47f 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/ViewModels/OptionsViewModel.cs @@ -3,21 +3,20 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Globalization; using System.Linq; -using System.Net; using System.Threading.Tasks; using AdvancedPaste.Helpers; using AdvancedPaste.Models; +using AdvancedPaste.Services; using AdvancedPaste.Settings; using Common.UI; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; -using Microsoft.PowerToys.Telemetry; using Microsoft.UI.Xaml; using Microsoft.Win32; using Windows.ApplicationModel.DataTransfer; @@ -28,67 +27,66 @@ using DispatcherQueue = Microsoft.UI.Dispatching.DispatcherQueue; namespace AdvancedPaste.ViewModels { - public partial class OptionsViewModel : ObservableObject, IDisposable + public sealed partial class OptionsViewModel : ObservableObject, IDisposable { private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread(); private readonly DispatcherTimer _clipboardTimer; private readonly IUserSettings _userSettings; - private readonly AICompletionsHelper aiHelper; - private readonly App app = App.Current as App; - private readonly PasteFormat[] _allStandardPasteFormats; + private readonly IPasteFormatExecutor _pasteFormatExecutor; + private readonly AICompletionsHelper _aiHelper; public DataPackageView ClipboardData { get; set; } [ObservableProperty] - [NotifyPropertyChangedFor(nameof(InputTxtBoxPlaceholderText))] - [NotifyPropertyChangedFor(nameof(GeneralErrorText))] [NotifyPropertyChangedFor(nameof(IsCustomAIEnabled))] - private bool _isClipboardDataText; + [NotifyPropertyChangedFor(nameof(ClipboardHasData))] + [NotifyPropertyChangedFor(nameof(InputTxtBoxPlaceholderText))] + [NotifyPropertyChangedFor(nameof(AIDisabledErrorText))] + private ClipboardFormat _availableClipboardFormats; [ObservableProperty] private bool _clipboardHistoryEnabled; [ObservableProperty] - [NotifyPropertyChangedFor(nameof(InputTxtBoxPlaceholderText))] - [NotifyPropertyChangedFor(nameof(GeneralErrorText))] - [NotifyPropertyChangedFor(nameof(IsPasteWithAIEnabled))] + [NotifyPropertyChangedFor(nameof(AIDisabledErrorText))] + [NotifyPropertyChangedFor(nameof(IsAIServiceEnabled))] [NotifyPropertyChangedFor(nameof(IsCustomAIEnabled))] private bool _isAllowedByGPO; [ObservableProperty] - [NotifyPropertyChangedFor(nameof(ApiErrorText))] - private int _apiRequestStatus; + private string _pasteOperationErrorText; [ObservableProperty] private string _query = string.Empty; private bool _pasteFormatsDirty; + [ObservableProperty] + private bool _busy; + public ObservableCollection StandardPasteFormats { get; } = []; public ObservableCollection CustomActionPasteFormats { get; } = []; - public bool IsPasteWithAIEnabled => IsAllowedByGPO && aiHelper.IsAIEnabled; + public bool IsAIServiceEnabled => IsAllowedByGPO && _aiHelper.IsAIEnabled; - public bool IsCustomAIEnabled => IsPasteWithAIEnabled && IsClipboardDataText; + public bool IsCustomAIEnabled => IsAIServiceEnabled && ClipboardHasText; + + public bool ClipboardHasData => AvailableClipboardFormats != ClipboardFormat.None; + + private bool ClipboardHasText => AvailableClipboardFormats.HasFlag(ClipboardFormat.Text); + + private bool Visible => GetMainWindow()?.Visible is true; public event EventHandler CustomActionActivated; - public OptionsViewModel(IUserSettings userSettings) + public OptionsViewModel(AICompletionsHelper aiHelper, IUserSettings userSettings, IPasteFormatExecutor pasteFormatExecutor) { - aiHelper = new AICompletionsHelper(); + _aiHelper = aiHelper; _userSettings = userSettings; + _pasteFormatExecutor = pasteFormatExecutor; - ApiRequestStatus = (int)HttpStatusCode.OK; - - _allStandardPasteFormats = - [ - new PasteFormat { IconGlyph = "\uE8E9", Name = ResourceLoaderInstance.ResourceLoader.GetString("PasteAsPlainText"), Format = PasteFormats.PlainText }, - new PasteFormat { IconGlyph = "\ue8a5", Name = ResourceLoaderInstance.ResourceLoader.GetString("PasteAsMarkdown"), Format = PasteFormats.Markdown }, - new PasteFormat { IconGlyph = "\uE943", Name = ResourceLoaderInstance.ResourceLoader.GetString("PasteAsJson"), Format = PasteFormats.Json }, - ]; - - GeneratedResponses = new ObservableCollection(); + GeneratedResponses = []; GeneratedResponses.CollectionChanged += (s, e) => { OnPropertyChanged(nameof(HasMultipleResponses)); @@ -96,28 +94,31 @@ namespace AdvancedPaste.ViewModels }; ClipboardHistoryEnabled = IsClipboardHistoryEnabled(); - ReadClipboard(); UpdateOpenAIKey(); _clipboardTimer = new() { Interval = TimeSpan.FromSeconds(1) }; _clipboardTimer.Tick += ClipboardTimer_Tick; _clipboardTimer.Start(); RefreshPasteFormats(); - _userSettings.CustomActions.CollectionChanged += (_, _) => EnqueueRefreshPasteFormats(); + _userSettings.Changed += (_, _) => EnqueueRefreshPasteFormats(); PropertyChanged += (_, e) => { - if (e.PropertyName == nameof(Query) || e.PropertyName == nameof(IsPasteWithAIEnabled)) + string[] dirtyingProperties = [nameof(Query), nameof(IsAIServiceEnabled), nameof(IsCustomAIEnabled), nameof(AvailableClipboardFormats)]; + + if (dirtyingProperties.Contains(e.PropertyName)) { EnqueueRefreshPasteFormats(); } }; } - private void ClipboardTimer_Tick(object sender, object e) + private static MainWindow GetMainWindow() => (App.Current as App)?.GetMainWindow(); + + private async void ClipboardTimer_Tick(object sender, object e) { - if (app.GetMainWindow()?.Visible is true) + if (Visible) { - ReadClipboard(); + await ReadClipboardAsync(); UpdateAllowedByGPO(); } } @@ -137,10 +138,12 @@ namespace AdvancedPaste.ViewModels }); } + private PasteFormat CreatePasteFormat(PasteFormats format) => new(format, AvailableClipboardFormats, IsAIServiceEnabled, ResourceLoaderInstance.ResourceLoader.GetString); + + private PasteFormat CreatePasteFormat(AdvancedPasteCustomAction customAction) => new(customAction, AvailableClipboardFormats, IsAIServiceEnabled); + private void RefreshPasteFormats() { - bool Filter(string text) => text.Contains(Query, StringComparison.CurrentCultureIgnoreCase); - var ctrlString = ResourceLoaderInstance.ResourceLoader.GetString("CtrlKey"); int shortcutNum = 0; @@ -150,28 +153,33 @@ namespace AdvancedPaste.ViewModels return shortcutNum <= 9 ? $"{ctrlString}+{shortcutNum}" : string.Empty; } - StandardPasteFormats.Clear(); - foreach (var format in _allStandardPasteFormats) + IEnumerable FilterAndSort(IEnumerable pasteFormats) => + from pasteFormat in pasteFormats + let comparison = StringComparison.CurrentCultureIgnoreCase + where pasteFormat.Name.Contains(Query, comparison) || pasteFormat.Prompt.Contains(Query, comparison) + orderby pasteFormat.IsEnabled descending + select pasteFormat; + + void UpdateFormats(ObservableCollection collection, IEnumerable pasteFormats) { - if (Filter(format.Name)) + collection.Clear(); + + foreach (var format in FilterAndSort(pasteFormats)) { - format.ShortcutText = GetNextShortcutText(); - format.ToolTip = $"{format.Name} ({format.ShortcutText})"; - StandardPasteFormats.Add(format); + if (format.IsEnabled) + { + format.ShortcutText = GetNextShortcutText(); + } + + collection.Add(format); } } - CustomActionPasteFormats.Clear(); - if (IsPasteWithAIEnabled) - { - foreach (var customAction in _userSettings.CustomActions) - { - if (Filter(customAction.Name) || Filter(customAction.Prompt)) - { - CustomActionPasteFormats.Add(new PasteFormat(customAction, GetNextShortcutText())); - } - } - } + UpdateFormats(StandardPasteFormats, Enum.GetValues() + .Where(format => PasteFormat.MetadataDict[format].IsCoreAction || _userSettings.AdditionalActions.Contains(format)) + .Select(CreatePasteFormat)); + + UpdateFormats(CustomActionPasteFormats, IsAIServiceEnabled ? _userSettings.CustomActions.Select(CreatePasteFormat) : []); } public void Dispose() @@ -180,26 +188,34 @@ namespace AdvancedPaste.ViewModels GC.SuppressFinalize(this); } - public void ReadClipboard() + public async Task ReadClipboardAsync() { + if (Busy) + { + return; + } + ClipboardData = Clipboard.GetContent(); - IsClipboardDataText = ClipboardData.Contains(StandardDataFormats.Text); + AvailableClipboardFormats = await ClipboardHelper.GetAvailableClipboardFormatsAsync(ClipboardData); } - public void OnShow() + public async Task OnShowAsync() { - ReadClipboard(); + PasteOperationErrorText = string.Empty; + Query = string.Empty; + + await ReadClipboardAsync(); if (UpdateOpenAIKey()) { - app.GetMainWindow()?.StartLoading(); + GetMainWindow()?.StartLoading(); _dispatcherQueue.TryEnqueue(() => { - app.GetMainWindow()?.FinishLoading(aiHelper.IsAIEnabled); + GetMainWindow()?.FinishLoading(_aiHelper.IsAIEnabled); OnPropertyChanged(nameof(InputTxtBoxPlaceholderText)); - OnPropertyChanged(nameof(GeneralErrorText)); - OnPropertyChanged(nameof(IsPasteWithAIEnabled)); + OnPropertyChanged(nameof(AIDisabledErrorText)); + OnPropertyChanged(nameof(IsAIServiceEnabled)); OnPropertyChanged(nameof(IsCustomAIEnabled)); }); } @@ -209,7 +225,7 @@ namespace AdvancedPaste.ViewModels } // List to store generated responses - public ObservableCollection GeneratedResponses { get; set; } = new ObservableCollection(); + public ObservableCollection GeneratedResponses { get; set; } = []; // Index to keep track of the current response private int _currentResponseIndex; @@ -228,30 +244,20 @@ namespace AdvancedPaste.ViewModels } } - public bool HasMultipleResponses - { - get => GeneratedResponses.Count > 1; - } + public bool HasMultipleResponses => GeneratedResponses.Count > 1; public string CurrentIndexDisplay => $"{CurrentResponseIndex + 1}/{GeneratedResponses.Count}"; public string InputTxtBoxPlaceholderText + => ResourceLoaderInstance.ResourceLoader.GetString(ClipboardHasData ? "CustomFormatTextBox/PlaceholderText" : "ClipboardEmptyWarning"); + + public string AIDisabledErrorText { get { - app.GetMainWindow().ClearInputText(); - - return IsClipboardDataText ? ResourceLoaderInstance.ResourceLoader.GetString("CustomFormatTextBox/PlaceholderText") : GeneralErrorText; - } - } - - public string GeneralErrorText - { - get - { - if (!IsClipboardDataText) + if (!ClipboardHasText) { - return ResourceLoaderInstance.ResourceLoader.GetString("ClipboardDataTypeMismatchWarning"); + return ResourceLoaderInstance.ResourceLoader.GetString("ClipboardDataNotTextWarning"); } if (!IsAllowedByGPO) @@ -259,7 +265,7 @@ namespace AdvancedPaste.ViewModels return ResourceLoaderInstance.ResourceLoader.GetString("OpenAIGpoDisabled"); } - if (!aiHelper.IsAIEnabled) + if (!_aiHelper.IsAIEnabled) { return ResourceLoaderInstance.ResourceLoader.GetString("OpenAINotConfigured"); } @@ -270,17 +276,6 @@ namespace AdvancedPaste.ViewModels } } - public string ApiErrorText - { - get => (HttpStatusCode)ApiRequestStatus switch - { - HttpStatusCode.TooManyRequests => ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyTooManyRequests"), - HttpStatusCode.Unauthorized => ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyUnauthorized"), - HttpStatusCode.OK => string.Empty, - _ => ResourceLoaderInstance.ResourceLoader.GetString("OpenAIApiKeyError") + ApiRequestStatus.ToString(CultureInfo.InvariantCulture), - }; - } - [ObservableProperty] private string _customFormatResult; @@ -289,9 +284,17 @@ namespace AdvancedPaste.ViewModels { var text = GeneratedResponses.ElementAtOrDefault(CurrentResponseIndex); - if (text != null) + if (!string.IsNullOrEmpty(text)) { - PasteCustomFunction(text); + ClipboardHelper.SetClipboardTextContent(text); + HideWindow(); + + if (_userSettings.SendPasteKeyCombination) + { + ClipboardHelper.SendPasteKeyCombination(); + } + + Query = string.Empty; } } @@ -320,190 +323,120 @@ namespace AdvancedPaste.ViewModels public void OpenSettings() { SettingsDeepLink.OpenSettings(SettingsDeepLink.SettingsWindow.AdvancedPaste, true); - (App.Current as App).GetMainWindow().Close(); + GetMainWindow()?.Close(); } - private void SetClipboardContentAndHideWindow(string content) + internal async Task ExecutePasteFormatAsync(PasteFormats format, PasteActionSource source) { - if (!string.IsNullOrEmpty(content)) - { - ClipboardHelper.SetClipboardTextContent(content); - } - - if (app.GetMainWindow() != null) - { - Windows.Win32.Foundation.HWND hwnd = (Windows.Win32.Foundation.HWND)app.GetMainWindow().GetWindowHandle(); - Windows.Win32.PInvoke.ShowWindow(hwnd, Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD.SW_HIDE); - } + await ReadClipboardAsync(); + await ExecutePasteFormatAsync(CreatePasteFormat(format), source); } - internal void ToPlainTextFunction() + internal async Task ExecutePasteFormatAsync(PasteFormat pasteFormat, PasteActionSource source) { - try - { - Logger.LogTrace(); - - string outputString = MarkdownHelper.PasteAsPlainTextFromClipboard(ClipboardData); - - SetClipboardContentAndHideWindow(outputString); - - if (_userSettings.SendPasteKeyCombination) - { - ClipboardHelper.SendPasteKeyCombination(); - } - } - catch - { - } - } - - internal void ToMarkdownFunction(bool pasteAlways = false) - { - try - { - Logger.LogTrace(); - - string outputString = MarkdownHelper.ToMarkdown(ClipboardData); - - SetClipboardContentAndHideWindow(outputString); - - if (pasteAlways || _userSettings.SendPasteKeyCombination) - { - ClipboardHelper.SendPasteKeyCombination(); - } - } - catch - { - } - } - - internal void ToJsonFunction(bool pasteAlways = false) - { - try - { - Logger.LogTrace(); - - string jsonText = JsonHelper.ToJsonFromXmlOrCsv(ClipboardData); - - SetClipboardContentAndHideWindow(jsonText); - - if (pasteAlways || _userSettings.SendPasteKeyCombination) - { - ClipboardHelper.SendPasteKeyCombination(); - } - } - catch - { - } - } - - internal void ExecutePasteFormat(VirtualKey key) - { - var index = key - VirtualKey.Number1; - var pasteFormat = StandardPasteFormats.ElementAtOrDefault(index) ?? CustomActionPasteFormats.ElementAtOrDefault(index - StandardPasteFormats.Count); - - if (pasteFormat != null) - { - ExecutePasteFormat(pasteFormat); - PowerToysTelemetry.Log.WriteEvent(new Telemetry.AdvancedPasteInAppKeyboardShortcutEvent(pasteFormat.Format)); - } - } - - internal void ExecutePasteFormat(PasteFormat pasteFormat) - { - if (!IsClipboardDataText || (pasteFormat.Format == PasteFormats.Custom && !IsCustomAIEnabled)) + if (Busy) { + Logger.LogWarning($"Execution of {pasteFormat.Format} from {source} suppressed as busy"); return; } - switch (pasteFormat.Format) + if (!pasteFormat.IsEnabled) { - case PasteFormats.PlainText: - ToPlainTextFunction(); - break; + var resourceId = pasteFormat.SupportsClipboardFormats(AvailableClipboardFormats) ? "PasteError" : "ClipboardEmptyWarning"; + PasteOperationErrorText = ResourceLoaderInstance.ResourceLoader.GetString(resourceId); + return; + } - case PasteFormats.Markdown: - ToMarkdownFunction(); - break; + Busy = true; + PasteOperationErrorText = string.Empty; + Query = pasteFormat.Query; - case PasteFormats.Json: - ToJsonFunction(); - break; + if (pasteFormat.Format == PasteFormats.Custom) + { + SaveQuery(Query); + } - case PasteFormats.Custom: - Query = pasteFormat.Prompt; - CustomActionActivated?.Invoke(this, new CustomActionActivatedEventArgs(pasteFormat.Prompt, false)); - break; + try + { + // Minimum time to show busy spinner for AI actions when triggered by global keyboard shortcut. + var aiActionMinTaskTime = TimeSpan.FromSeconds(2); + var delayTask = (Visible && source == PasteActionSource.GlobalKeyboardShortcut) ? Task.Delay(aiActionMinTaskTime) : Task.CompletedTask; + var aiOutput = await _pasteFormatExecutor.ExecutePasteFormatAsync(pasteFormat, source); + + await delayTask; + + if (pasteFormat.Format != PasteFormats.Custom) + { + HideWindow(); + + if (source == PasteActionSource.GlobalKeyboardShortcut || _userSettings.SendPasteKeyCombination) + { + ClipboardHelper.SendPasteKeyCombination(); + } + } + else + { + var pasteResult = source == PasteActionSource.GlobalKeyboardShortcut || !_userSettings.ShowCustomPreview; + + GeneratedResponses.Add(aiOutput); + CurrentResponseIndex = GeneratedResponses.Count - 1; + CustomActionActivated?.Invoke(this, new CustomActionActivatedEventArgs(pasteFormat.Prompt, pasteResult)); + + if (pasteResult) + { + PasteCustom(); + } + } + } + catch (Exception ex) + { + Logger.LogError("Error executing paste format", ex); + PasteOperationErrorText = ex is PasteActionException ? ex.Message : ResourceLoaderInstance.ResourceLoader.GetString("PasteError"); + } + + Busy = false; + } + + internal async Task ExecutePasteFormatAsync(VirtualKey key) + { + var pasteFormat = StandardPasteFormats.Concat(CustomActionPasteFormats) + .Where(pasteFormat => pasteFormat.IsEnabled) + .ElementAtOrDefault(key - VirtualKey.Number1); + + if (pasteFormat != null) + { + await ExecutePasteFormatAsync(pasteFormat, PasteActionSource.InAppKeyboardShortcut); } } - internal void ExecuteCustomActionWithPaste(int customActionId) + internal async Task ExecuteCustomActionAsync(int customActionId, PasteActionSource source) { Logger.LogTrace(); + await ReadClipboardAsync(); + var customAction = _userSettings.CustomActions.FirstOrDefault(customAction => customAction.Id == customActionId); if (customAction != null) { - Query = customAction.Prompt; - CustomActionActivated?.Invoke(this, new CustomActionActivatedEventArgs(customAction.Prompt, true)); + await ExecutePasteFormatAsync(CreatePasteFormat(customAction), source); } } - internal async Task GenerateCustomFunction(string inputInstructions) + internal async Task GenerateCustomFunctionAsync(PasteActionSource triggerSource) { - Logger.LogTrace(); - - if (string.IsNullOrWhiteSpace(inputInstructions) || !IsCustomAIEnabled) - { - return string.Empty; - } - - if (!IsClipboardDataText) - { - Logger.LogWarning("Clipboard does not contain text data"); - return string.Empty; - } - - string currentClipboardText = await Task.Run(async () => - { - try - { - string text = await ClipboardData.GetTextAsync() as string; - return text; - } - catch (Exception) - { - // Couldn't get text from the clipboard. Resume with empty text. - return string.Empty; - } - }); - - if (string.IsNullOrWhiteSpace(currentClipboardText)) - { - Logger.LogWarning("Clipboard has no usable text data"); - return string.Empty; - } - - var aiResponse = await Task.Run(() => aiHelper.AIFormatString(inputInstructions, currentClipboardText)); - - string aiOutput = aiResponse.Response; - ApiRequestStatus = aiResponse.ApiRequestStatus; - - GeneratedResponses.Add(aiOutput); - CurrentResponseIndex = GeneratedResponses.Count - 1; - return aiOutput; + AdvancedPasteCustomAction customAction = new() { Name = "Default", Prompt = Query }; + await ExecutePasteFormatAsync(CreatePasteFormat(customAction), triggerSource); } - internal void PasteCustomFunction(string text) + private void HideWindow() { - Logger.LogTrace(); + var mainWindow = GetMainWindow(); - SetClipboardContentAndHideWindow(text); - - if (_userSettings.SendPasteKeyCombination) + if (mainWindow != null) { - ClipboardHelper.SendPasteKeyCombination(); + Windows.Win32.Foundation.HWND hwnd = (Windows.Win32.Foundation.HWND)mainWindow.GetWindowHandle(); + Windows.Win32.PInvoke.ShowWindow(hwnd, Windows.Win32.UI.WindowsAndMessaging.SHOW_WINDOW_CMD.SW_HIDE); } } @@ -524,11 +457,7 @@ namespace AdvancedPaste.ViewModels return; } - string currentClipboardText = Task.Run(async () => - { - string text = await clipboardData.GetTextAsync() as string; - return text; - }).Result; + var currentClipboardText = Task.Run(async () => await clipboardData.GetTextAsync()).Result; var queryData = new CustomQuery { @@ -536,13 +465,13 @@ namespace AdvancedPaste.ViewModels ClipboardData = currentClipboardText, }; - SettingsUtils utils = new SettingsUtils(); + SettingsUtils utils = new(); utils.SaveSettings(queryData.ToString(), Constants.AdvancedPasteModuleName, Constants.LastQueryJsonFileName); } internal CustomQuery LoadPreviousQuery() { - SettingsUtils utils = new SettingsUtils(); + SettingsUtils utils = new(); var query = utils.GetSettings(Constants.AdvancedPasteModuleName, Constants.LastQueryJsonFileName); return query; } @@ -572,9 +501,9 @@ namespace AdvancedPaste.ViewModels if (IsAllowedByGPO) { - var oldKey = aiHelper.GetKey(); + var oldKey = _aiHelper.GetKey(); var newKey = AICompletionsHelper.LoadOpenAIKey(); - aiHelper.SetOpenAIKey(newKey); + _aiHelper.SetOpenAIKey(newKey); return newKey != oldKey; } diff --git a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/dllmain.cpp b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/dllmain.cpp index 649342bc2f..4da030d6ba 100644 --- a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/dllmain.cpp +++ b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/dllmain.cpp @@ -42,6 +42,7 @@ namespace { const wchar_t JSON_KEY_PROPERTIES[] = L"properties"; const wchar_t JSON_KEY_CUSTOM_ACTIONS[] = L"custom-actions"; + const wchar_t JSON_KEY_ADDITIONAL_ACTIONS[] = L"additional-actions"; const wchar_t JSON_KEY_SHORTCUT[] = L"shortcut"; const wchar_t JSON_KEY_IS_SHOWN[] = L"isShown"; const wchar_t JSON_KEY_ID[] = L"id"; @@ -73,7 +74,6 @@ private: HANDLE m_hProcess; - std::thread create_pipe_thread; std::unique_ptr m_write_pipe; // Time to wait for process to close after sending WM_CLOSE signal @@ -86,8 +86,18 @@ private: Hotkey m_paste_as_markdown_hotkey{}; Hotkey m_paste_as_json_hotkey{}; - std::vector m_custom_action_hotkeys; - std::vector m_custom_action_ids; + template + struct ActionData + { + Id id; + Hotkey hotkey; + }; + + using AdditionalAction = ActionData; + std::vector m_additional_actions; + + using CustomAction = ActionData; + std::vector m_custom_actions; bool m_preview_custom_format_output = true; @@ -166,6 +176,34 @@ private: open_ai_key_exists(); } + static std::wstring kebab_to_pascal_case(const std::wstring& kebab_str) + { + std::wstring result; + bool capitalize_next = true; + + for (const auto ch : kebab_str) + { + if (ch == L'-') + { + capitalize_next = true; + } + else + { + if (capitalize_next) + { + result += std::towupper(ch); + capitalize_next = false; + } + else + { + result += ch; + } + } + } + + return result; + } + bool migrate_data_and_remove_data_file(Hotkey& old_paste_as_plain_hotkey) { const wchar_t OLD_JSON_KEY_ACTIVATION_SHORTCUT[] = L"ActivationShortcut"; @@ -197,6 +235,39 @@ private: return false; } + void process_additional_action(const winrt::hstring& actionName, const winrt::Windows::Data::Json::IJsonValue& actionValue) + { + if (actionValue.ValueType() != winrt::Windows::Data::Json::JsonValueType::Object) + { + return; + } + + const auto action = actionValue.GetObjectW(); + + if (!action.GetNamedBoolean(JSON_KEY_IS_SHOWN, false)) + { + return; + } + + if (action.HasKey(JSON_KEY_SHORTCUT)) + { + const AdditionalAction additionalAction + { + actionName.c_str(), + parse_single_hotkey(action.GetNamedObject(JSON_KEY_SHORTCUT)) + }; + + m_additional_actions.push_back(additionalAction); + } + else + { + for (const auto& [subActionName, subAction] : action) + { + process_additional_action(subActionName, subAction); + } + } + } + void parse_hotkeys(PowerToysSettings::PowerToyValues& settings) { auto settingsObject = settings.get_raw_json(); @@ -239,13 +310,23 @@ private: *hotkey = parse_single_hotkey(keyName, settingsObject); } - m_custom_action_hotkeys.clear(); - m_custom_action_ids.clear(); + m_additional_actions.clear(); + m_custom_actions.clear(); if (settingsObject.HasKey(JSON_KEY_PROPERTIES)) { const auto propertiesObject = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES); + if (propertiesObject.HasKey(JSON_KEY_ADDITIONAL_ACTIONS)) + { + const auto additionalActions = propertiesObject.GetNamedObject(JSON_KEY_ADDITIONAL_ACTIONS); + + for (const auto& [actionName, additionalAction] : additionalActions) + { + process_additional_action(actionName, additionalAction); + } + } + if (propertiesObject.HasKey(JSON_KEY_CUSTOM_ACTIONS)) { const auto customActions = propertiesObject.GetNamedObject(JSON_KEY_CUSTOM_ACTIONS).GetNamedArray(JSON_KEY_VALUE); @@ -257,8 +338,13 @@ private: if (object.GetNamedBoolean(JSON_KEY_IS_SHOWN, false)) { - m_custom_action_hotkeys.push_back(parse_single_hotkey(object.GetNamedObject(JSON_KEY_SHORTCUT))); - m_custom_action_ids.push_back(static_cast(object.GetNamedNumber(JSON_KEY_ID))); + const CustomAction customActionData + { + static_cast(object.GetNamedNumber(JSON_KEY_ID)), + parse_single_hotkey(object.GetNamedObject(JSON_KEY_SHORTCUT)) + }; + + m_custom_actions.push_back(customActionData); } } } @@ -331,7 +417,7 @@ private: return; } - create_pipe_thread = std::thread([&] { start_named_pipe_server(pipe_name.value()); }); + std::thread create_pipe_thread ([&]{ start_named_pipe_server(pipe_name.value()); }); launch_process(pipe_name.value()); create_pipe_thread.join(); } @@ -730,12 +816,19 @@ public: m_preview_custom_format_output = settingsObject.GetNamedObject(JSON_KEY_PROPERTIES).GetNamedObject(JSON_KEY_SHOW_CUSTOM_PREVIEW).GetNamedBoolean(JSON_KEY_VALUE); } + std::unordered_map additionalActionMap; + for (const auto& action : m_additional_actions) + { + additionalActionMap[kebab_to_pascal_case(action.id)] = action.hotkey; + } + // order of args matter Trace::AdvancedPaste_SettingsTelemetry(m_paste_as_plain_hotkey, m_advanced_paste_ui_hotkey, m_paste_as_markdown_hotkey, m_paste_as_json_hotkey, - m_preview_custom_format_output); + m_preview_custom_format_output, + additionalActionMap); // If you don't need to do any custom processing of the settings, proceed // to persists the values calling: @@ -825,11 +918,24 @@ public: return true; } - const auto custom_action_index = hotkeyId - NUM_DEFAULT_HOTKEYS; - if (custom_action_index < m_custom_action_ids.size()) + const auto additional_action_index = hotkeyId - NUM_DEFAULT_HOTKEYS; + if (additional_action_index < m_additional_actions.size()) { - const auto id = m_custom_action_ids.at(custom_action_index); + const auto& id = m_additional_actions.at(additional_action_index).id; + + Logger::trace(L"Starting additional action id={}", id); + + Trace::AdvancedPaste_Invoked(std::format(L"{}Direct", kebab_to_pascal_case(id))); + + send_named_pipe_message(CommonSharedConstants::ADVANCED_PASTE_ADDITIONAL_ACTION_MESSAGE, id); + return true; + } + + const auto custom_action_index = additional_action_index - m_additional_actions.size(); + if (custom_action_index < m_custom_actions.size()) + { + const auto id = m_custom_actions.at(custom_action_index).id; Logger::trace(L"Starting custom action id={}", id); @@ -844,7 +950,7 @@ public: virtual size_t get_hotkeys(Hotkey* hotkeys, size_t buffer_size) override { - const size_t num_hotkeys = NUM_DEFAULT_HOTKEYS + m_custom_action_hotkeys.size(); + const size_t num_hotkeys = NUM_DEFAULT_HOTKEYS + m_additional_actions.size() + m_custom_actions.size(); if (hotkeys && buffer_size >= num_hotkeys) { @@ -852,9 +958,11 @@ public: m_advanced_paste_ui_hotkey, m_paste_as_markdown_hotkey, m_paste_as_json_hotkey }; - std::copy(default_hotkeys.begin(), default_hotkeys.end(), hotkeys); - std::copy(m_custom_action_hotkeys.begin(), m_custom_action_hotkeys.end(), hotkeys + NUM_DEFAULT_HOTKEYS); + + const auto get_action_hotkey = [](const auto& action) { return action.hotkey; }; + std::transform(m_additional_actions.begin(), m_additional_actions.end(), hotkeys + NUM_DEFAULT_HOTKEYS, get_action_hotkey); + std::transform(m_custom_actions.begin(), m_custom_actions.end(), hotkeys + NUM_DEFAULT_HOTKEYS + m_additional_actions.size(), get_action_hotkey); } return num_hotkeys; diff --git a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.cpp b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.cpp index 87d610682f..0ae92a7187 100644 --- a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.cpp +++ b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.cpp @@ -58,45 +58,44 @@ void Trace::AdvancedPaste_SettingsTelemetry(const PowertoyModuleIface::Hotkey& p const PowertoyModuleIface::Hotkey& advancedPasteUIHotkey, const PowertoyModuleIface::Hotkey& pasteMarkdownHotkey, const PowertoyModuleIface::Hotkey& pasteJsonHotkey, - const bool preview_custom_format_output) noexcept + const bool preview_custom_format_output, + const std::unordered_map& additionalActionsHotkeys) noexcept { - std::wstring pastePlainHotkeyStr = - std::wstring(pastePlainHotkey.win ? L"Win + " : L"") + - std::wstring(pastePlainHotkey.ctrl ? L"Ctrl + " : L"") + - std::wstring(pastePlainHotkey.shift ? L"Shift + " : L"") + - std::wstring(pastePlainHotkey.alt ? L"Alt + " : L"") + - std::wstring(L"VK ") + std::to_wstring(pastePlainHotkey.key); + const auto getHotKeyStr = [](const PowertoyModuleIface::Hotkey& hotKey) + { + return std::wstring(hotKey.win ? L"Win + " : L"") + + std::wstring(hotKey.ctrl ? L"Ctrl + " : L"") + + std::wstring(hotKey.shift ? L"Shift + " : L"") + + std::wstring(hotKey.alt ? L"Alt + " : L"") + + std::wstring(L"VK ") + std::to_wstring(hotKey.key); + }; - std::wstring advancedPasteUIHotkeyStr = - std::wstring(advancedPasteUIHotkey.win ? L"Win + " : L"") + - std::wstring(advancedPasteUIHotkey.ctrl ? L"Ctrl + " : L"") + - std::wstring(advancedPasteUIHotkey.shift ? L"Shift + " : L"") + - std::wstring(advancedPasteUIHotkey.alt ? L"Alt + " : L"") + - std::wstring(L"VK ") + std::to_wstring(advancedPasteUIHotkey.key); + std::vector hotkeyStrs; + const auto getHotkeyCStr = [&](const PowertoyModuleIface::Hotkey& hotkey) + { + hotkeyStrs.push_back(getHotKeyStr(hotkey)); // Probably unnecessary, but offers protection against the macro TraceLoggingWideString expanding to something that would invalidate the pointer + return hotkeyStrs.back().c_str(); + }; - std::wstring pasteMarkdownHotkeyStr = - std::wstring(pasteMarkdownHotkey.win ? L"Win + " : L"") + - std::wstring(pasteMarkdownHotkey.ctrl ? L"Ctrl + " : L"") + - std::wstring(pasteMarkdownHotkey.shift ? L"Shift + " : L"") + - std::wstring(pasteMarkdownHotkey.alt ? L"Alt + " : L"") + - std::wstring(L"VK ") + std::to_wstring(pasteMarkdownHotkey.key); - - std::wstring pasteJsonHotkeyStr = - std::wstring(pasteJsonHotkey.win ? L"Win + " : L"") + - std::wstring(pasteJsonHotkey.ctrl ? L"Ctrl + " : L"") + - std::wstring(pasteJsonHotkey.shift ? L"Shift + " : L"") + - std::wstring(pasteJsonHotkey.alt ? L"Alt + " : L"") + - std::wstring(L"VK ") + std::to_wstring(pasteJsonHotkey.key); + const auto getAdditionalActionHotkeyCStr = [&](const std::wstring& name) + { + const auto it = additionalActionsHotkeys.find(name); + return it != additionalActionsHotkeys.end() ? getHotkeyCStr(it->second) : L""; + }; TraceLoggingWrite( g_hProvider, "AdvancedPaste_Settings", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE), - TraceLoggingWideString(pastePlainHotkeyStr.c_str(), "PastePlainHotkey"), - TraceLoggingWideString(advancedPasteUIHotkeyStr.c_str(), "AdvancedPasteUIHotkey"), - TraceLoggingWideString(pasteMarkdownHotkeyStr.c_str(), "PasteMarkdownHotkey"), - TraceLoggingWideString(pasteJsonHotkeyStr.c_str(), "PasteJsonHotkey"), - TraceLoggingBoolean(preview_custom_format_output, "ShowCustomPreview") + TraceLoggingWideString(getHotkeyCStr(pastePlainHotkey), "PastePlainHotkey"), + TraceLoggingWideString(getHotkeyCStr(advancedPasteUIHotkey), "AdvancedPasteUIHotkey"), + TraceLoggingWideString(getHotkeyCStr(pasteMarkdownHotkey), "PasteMarkdownHotkey"), + TraceLoggingWideString(getHotkeyCStr(pasteJsonHotkey), "PasteJsonHotkey"), + TraceLoggingBoolean(preview_custom_format_output, "ShowCustomPreview"), + TraceLoggingWideString(getAdditionalActionHotkeyCStr(L"ImageToText"), "ImageToTextHotkey"), + TraceLoggingWideString(getAdditionalActionHotkeyCStr(L"PasteAsTxtFile"), "PasteAsTxtFileHotkey"), + TraceLoggingWideString(getAdditionalActionHotkeyCStr(L"PasteAsPngFile"), "PasteAsPngFileHotkey"), + TraceLoggingWideString(getAdditionalActionHotkeyCStr(L"PasteAsHtmlFile"), "PasteAsHtmlFileHotkey") ); } diff --git a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.h b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.h index d64d1cd874..7c0504d58e 100644 --- a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.h +++ b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.h @@ -1,5 +1,6 @@ #pragma once #include +#include class Trace { @@ -21,5 +22,6 @@ public: const PowertoyModuleIface::Hotkey& advancedPasteUIHotkey, const PowertoyModuleIface::Hotkey& pasteMarkdownHotkey, const PowertoyModuleIface::Hotkey& pasteJsonHotkey, - const bool preview_custom_format_output) noexcept; + const bool preview_custom_format_output, + const std::unordered_map& additionalActionsHotkeys) noexcept; }; diff --git a/src/settings-ui/Settings.UI.Library/AdvancedPasteAdditionalAction.cs b/src/settings-ui/Settings.UI.Library/AdvancedPasteAdditionalAction.cs new file mode 100644 index 0000000000..7a6fd3081a --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/AdvancedPasteAdditionalAction.cs @@ -0,0 +1,39 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Text.Json.Serialization; + +using Microsoft.PowerToys.Settings.UI.Library.Helpers; + +namespace Microsoft.PowerToys.Settings.UI.Library; + +public sealed partial class AdvancedPasteAdditionalAction : Observable, IAdvancedPasteAction +{ + private HotkeySettings _shortcut = new(); + private bool _isShown = true; + + [JsonPropertyName("shortcut")] + public HotkeySettings Shortcut + { + get => _shortcut; + set + { + if (_shortcut != value) + { + // We null-coalesce here rather than outside this branch as we want to raise PropertyChanged when the setter is called + // with null; the ShortcutControl depends on this. + _shortcut = value ?? new(); + + OnPropertyChanged(); + } + } + } + + [JsonPropertyName("isShown")] + public bool IsShown + { + get => _isShown; + set => Set(ref _isShown, value); + } +} diff --git a/src/settings-ui/Settings.UI.Library/AdvancedPasteAdditionalActions.cs b/src/settings-ui/Settings.UI.Library/AdvancedPasteAdditionalActions.cs new file mode 100644 index 0000000000..ce26962b02 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/AdvancedPasteAdditionalActions.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using System.Text.Json.Serialization; + +namespace Microsoft.PowerToys.Settings.UI.Library; + +public sealed class AdvancedPasteAdditionalActions +{ + public static class PropertyNames + { + public const string ImageToText = "image-to-text"; + public const string PasteAsFile = "paste-as-file"; + } + + [JsonPropertyName(PropertyNames.ImageToText)] + public AdvancedPasteAdditionalAction ImageToText { get; init; } = new(); + + [JsonPropertyName(PropertyNames.PasteAsFile)] + public AdvancedPastePasteAsFileAction PasteAsFile { get; init; } = new(); + + [JsonIgnore] + public IEnumerable AllActions => new IAdvancedPasteAction[] { ImageToText, PasteAsFile }.Concat(PasteAsFile.SubActions); +} diff --git a/src/settings-ui/Settings.UI.Library/AdvancedPasteCustomAction.cs b/src/settings-ui/Settings.UI.Library/AdvancedPasteCustomAction.cs index 585663026b..f3bb4431ca 100644 --- a/src/settings-ui/Settings.UI.Library/AdvancedPasteCustomAction.cs +++ b/src/settings-ui/Settings.UI.Library/AdvancedPasteCustomAction.cs @@ -3,14 +3,13 @@ // See the LICENSE file in the project root for more information. using System; -using System.ComponentModel; -using System.Runtime.CompilerServices; -using System.Text.Json; using System.Text.Json.Serialization; +using Microsoft.PowerToys.Settings.UI.Library.Helpers; + namespace Microsoft.PowerToys.Settings.UI.Library; -public sealed class AdvancedPasteCustomAction : INotifyPropertyChanged, ICloneable +public sealed class AdvancedPasteCustomAction : Observable, IAdvancedPasteAction, ICloneable { private int _id; private string _name = string.Empty; @@ -25,14 +24,7 @@ public sealed class AdvancedPasteCustomAction : INotifyPropertyChanged, ICloneab public int Id { get => _id; - set - { - if (_id != value) - { - _id = value; - OnPropertyChanged(); - } - } + set => Set(ref _id, value); } [JsonPropertyName("name")] @@ -41,10 +33,8 @@ public sealed class AdvancedPasteCustomAction : INotifyPropertyChanged, ICloneab get => _name; set { - if (_name != value) + if (Set(ref _name, value)) { - _name = value; - OnPropertyChanged(); UpdateIsValid(); } } @@ -56,10 +46,8 @@ public sealed class AdvancedPasteCustomAction : INotifyPropertyChanged, ICloneab get => _prompt; set { - if (_prompt != value) + if (Set(ref _prompt, value)) { - _prompt = value; - OnPropertyChanged(); UpdateIsValid(); } } @@ -86,62 +74,30 @@ public sealed class AdvancedPasteCustomAction : INotifyPropertyChanged, ICloneab public bool IsShown { get => _isShown; - set - { - if (_isShown != value) - { - _isShown = value; - OnPropertyChanged(); - } - } + set => Set(ref _isShown, value); } [JsonIgnore] public bool CanMoveUp { get => _canMoveUp; - set - { - if (_canMoveUp != value) - { - _canMoveUp = value; - OnPropertyChanged(); - } - } + set => Set(ref _canMoveUp, value); } [JsonIgnore] public bool CanMoveDown { get => _canMoveDown; - set - { - if (_canMoveDown != value) - { - _canMoveDown = value; - OnPropertyChanged(); - } - } + set => Set(ref _canMoveDown, value); } [JsonIgnore] public bool IsValid { get => _isValid; - private set - { - if (_isValid != value) - { - _isValid = value; - OnPropertyChanged(); - } - } + private set => Set(ref _isValid, value); } - public event PropertyChangedEventHandler PropertyChanged; - - public string ToJsonString() => JsonSerializer.Serialize(this); - public object Clone() { AdvancedPasteCustomAction clone = new(); @@ -160,11 +116,6 @@ public sealed class AdvancedPasteCustomAction : INotifyPropertyChanged, ICloneab CanMoveDown = other.CanMoveDown; } - private void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - private HotkeySettings GetShortcutClone() { object shortcut = null; diff --git a/src/settings-ui/Settings.UI.Library/AdvancedPastePasteAsFileAction.cs b/src/settings-ui/Settings.UI.Library/AdvancedPastePasteAsFileAction.cs new file mode 100644 index 0000000000..979e967d4a --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/AdvancedPastePasteAsFileAction.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; +using System.Text.Json.Serialization; + +using Microsoft.PowerToys.Settings.UI.Library.Helpers; + +namespace Microsoft.PowerToys.Settings.UI.Library; + +public sealed class AdvancedPastePasteAsFileAction : Observable, IAdvancedPasteAction +{ + public static class PropertyNames + { + public const string PasteAsTxtFile = "paste-as-txt-file"; + public const string PasteAsPngFile = "paste-as-png-file"; + public const string PasteAsHtmlFile = "paste-as-html-file"; + } + + private AdvancedPasteAdditionalAction _pasteAsTxtFile = new(); + private AdvancedPasteAdditionalAction _pasteAsPngFile = new(); + private AdvancedPasteAdditionalAction _pasteAsHtmlFile = new(); + private bool _isShown = true; + + [JsonPropertyName("isShown")] + public bool IsShown + { + get => _isShown; + set => Set(ref _isShown, value); + } + + [JsonPropertyName(PropertyNames.PasteAsTxtFile)] + public AdvancedPasteAdditionalAction PasteAsTxtFile + { + get => _pasteAsTxtFile; + init => Set(ref _pasteAsTxtFile, value); + } + + [JsonPropertyName(PropertyNames.PasteAsPngFile)] + public AdvancedPasteAdditionalAction PasteAsPngFile + { + get => _pasteAsPngFile; + init => Set(ref _pasteAsPngFile, value); + } + + [JsonPropertyName(PropertyNames.PasteAsHtmlFile)] + public AdvancedPasteAdditionalAction PasteAsHtmlFile + { + get => _pasteAsHtmlFile; + init => Set(ref _pasteAsHtmlFile, value); + } + + [JsonIgnore] + public IEnumerable SubActions => [PasteAsTxtFile, PasteAsPngFile, PasteAsHtmlFile]; +} diff --git a/src/settings-ui/Settings.UI.Library/AdvancedPasteProperties.cs b/src/settings-ui/Settings.UI.Library/AdvancedPasteProperties.cs index 8e6ebb8238..8322302ddf 100644 --- a/src/settings-ui/Settings.UI.Library/AdvancedPasteProperties.cs +++ b/src/settings-ui/Settings.UI.Library/AdvancedPasteProperties.cs @@ -22,6 +22,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library PasteAsMarkdownShortcut = new(); PasteAsJsonShortcut = new(); CustomActions = new(); + AdditionalActions = new(); ShowCustomPreview = true; SendPasteKeyCombination = true; CloseAfterLosingFocus = false; @@ -51,7 +52,11 @@ namespace Microsoft.PowerToys.Settings.UI.Library [JsonPropertyName("custom-actions")] [CmdConfigureIgnoreAttribute] - public AdvancedPasteCustomActions CustomActions { get; set; } + public AdvancedPasteCustomActions CustomActions { get; init; } + + [JsonPropertyName("additional-actions")] + [CmdConfigureIgnoreAttribute] + public AdvancedPasteAdditionalActions AdditionalActions { get; init; } public override string ToString() => JsonSerializer.Serialize(this); diff --git a/src/settings-ui/Settings.UI.Library/Helpers/Observable.cs b/src/settings-ui/Settings.UI.Library/Helpers/Observable.cs index a77099b4b8..79b4e9d2ee 100644 --- a/src/settings-ui/Settings.UI.Library/Helpers/Observable.cs +++ b/src/settings-ui/Settings.UI.Library/Helpers/Observable.cs @@ -11,17 +11,19 @@ namespace Microsoft.PowerToys.Settings.UI.Library.Helpers { public event PropertyChangedEventHandler PropertyChanged; - protected void Set(ref T storage, T value, [CallerMemberName] string propertyName = null) + protected bool Set(ref T storage, T value, [CallerMemberName] string propertyName = null) { if (Equals(storage, value)) { - return; + return false; } storage = value; OnPropertyChanged(propertyName); + + return true; } - protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + protected void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } diff --git a/src/settings-ui/Settings.UI.Library/IAdvancedPasteAction.cs b/src/settings-ui/Settings.UI.Library/IAdvancedPasteAction.cs new file mode 100644 index 0000000000..4c31557010 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/IAdvancedPasteAction.cs @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; + +namespace Microsoft.PowerToys.Settings.UI.Library; + +public interface IAdvancedPasteAction : INotifyPropertyChanged +{ + public bool IsShown { get; } +} diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPaste.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPaste.xaml index 1598beecc8..1d7205de96 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPaste.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/AdvancedPaste.xaml @@ -24,6 +24,18 @@ ms-appx:///Assets/Settings/Modules/APDialog.light.png + + + + + + @@ -129,6 +141,7 @@ AllowDisable="True" HotkeySettings="{x:Bind Path=ViewModel.PasteAsJsonShortcut, Mode=TwoWay}" /> + @@ -202,6 +215,56 @@ IsTabStop="{x:Bind ViewModel.IsConflictingCopyShortcut, Mode=OneWay}" Severity="Warning" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index afd694d95f..ed662f87d0 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -725,6 +725,9 @@ Actions + + Additional actions + Current Key Remappings @@ -2046,6 +2049,21 @@ Made with 💗 by Microsoft and the PowerToys community. Paste as Custom with AI directly + + Image to text + + + Paste as file + + + Paste as .txt file + + + Paste as .png file + + + Paste as .html file + OpenAI API key: diff --git a/src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs index 94787ae513..a0533d8d63 100644 --- a/src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/AdvancedPasteViewModel.cs @@ -38,6 +38,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private readonly object _delayedActionLock = new object(); private readonly AdvancedPasteSettings _advancedPasteSettings; + private readonly AdvancedPasteAdditionalActions _additionalActions; private readonly ObservableCollection _customActions; private Timer _delayedTimer; @@ -69,6 +70,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels _advancedPasteSettings = advancedPasteSettingsRepository.SettingsConfig; + _additionalActions = _advancedPasteSettings.Properties.AdditionalActions; _customActions = _advancedPasteSettings.Properties.CustomActions.Value; InitializeEnabledValue(); @@ -81,6 +83,11 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels _delayedTimer.Elapsed += DelayedTimer_Tick; _delayedTimer.AutoReset = false; + foreach (var action in _additionalActions.AllActions) + { + action.PropertyChanged += OnAdditionalActionPropertyChanged; + } + foreach (var customAction in _customActions) { customAction.PropertyChanged += OnCustomActionPropertyChanged; @@ -143,6 +150,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels public ObservableCollection CustomActions => _customActions; + public AdvancedPasteAdditionalActions AdditionalActions => _additionalActions; + private bool OpenAIKeyExists() { PasswordVault vault = new PasswordVault(); @@ -336,12 +345,16 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels } } - public bool IsConflictingCopyShortcut - { - get => _customActions.Select(customAction => customAction.Shortcut) - .Concat([PasteAsPlainTextShortcut, AdvancedPasteUIShortcut, PasteAsMarkdownShortcut, PasteAsJsonShortcut]) - .Any(hotkey => WarnHotkeys.Contains(hotkey.ToString())); - } + public bool IsConflictingCopyShortcut => + _customActions.Select(customAction => customAction.Shortcut) + .Concat([PasteAsPlainTextShortcut, AdvancedPasteUIShortcut, PasteAsMarkdownShortcut, PasteAsJsonShortcut]) + .Any(hotkey => WarnHotkeys.Contains(hotkey.ToString())); + + public bool IsAdditionalActionConflictingCopyShortcut => + _additionalActions.AllActions + .OfType() + .Select(additionalAction => additionalAction.Shortcut) + .Any(hotkey => WarnHotkeys.Contains(hotkey.ToString())); private void DelayedTimer_Tick(object sender, EventArgs e) { @@ -461,6 +474,16 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels NotifySettingsChanged(); } + private void OnAdditionalActionPropertyChanged(object sender, PropertyChangedEventArgs e) + { + SaveAndNotifySettings(); + + if (e.PropertyName == nameof(AdvancedPasteAdditionalAction.Shortcut)) + { + OnPropertyChanged(nameof(IsAdditionalActionConflictingCopyShortcut)); + } + } + private void OnCustomActionPropertyChanged(object sender, PropertyChangedEventArgs e) { if (typeof(AdvancedPasteCustomAction).GetProperty(e.PropertyName).GetCustomAttribute() == null) From e99b52fb35275d3259b41fb0c775cc081856c8f8 Mon Sep 17 00:00:00 2001 From: PesBandi <127593627+PesBandi@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:51:57 +0200 Subject: [PATCH 29/95] [PTRun][UnitConverter]Preserve more significant digits (#35073) * [PTRun][UnitConverter]Preserve more significant digits * Use StringComparison.OrdinalIgnoreCase --- .../UnitHandlerTests.cs | 49 +++---------------- .../Main.cs | 4 +- .../UnitConversionResult.cs | 20 ++++++-- .../UnitHandler.cs | 21 +------- 4 files changed, 28 insertions(+), 66 deletions(-) diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/UnitHandlerTests.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/UnitHandlerTests.cs index 0789d29a3c..bab0199b27 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/UnitHandlerTests.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter.UnitTest/UnitHandlerTests.cs @@ -57,8 +57,8 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter.UnitTest var convertModel = new ConvertModel(1, "parsec", "nanometer"); var result = UnitHandler.Convert(convertModel).Single(); var str = result.ToString(System.Globalization.CultureInfo.InvariantCulture); - Assert.AreEqual(3.0857000000000004E+25, result.ConvertedValue); - Assert.AreEqual("3.0857e+25 nanometer", str); + Assert.AreEqual(3.08567758128E+25, result.ConvertedValue); + Assert.AreEqual("3.08567758128E+25 nanometer", str); } [TestMethod] @@ -67,8 +67,8 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter.UnitTest var convertModel = new ConvertModel(1, "nanometer", "parsec"); var result = UnitHandler.Convert(convertModel).Single(); var str = result.ToString(System.Globalization.CultureInfo.InvariantCulture); - Assert.AreEqual(3.2408000000000005E-26, result.ConvertedValue); - Assert.AreEqual("3.2408e-26 parsec", str); + Assert.AreEqual(3.240779289666357E-26, result.ConvertedValue); + Assert.AreEqual("3.2407792896664E-26 parsec", str); } [TestMethod] @@ -79,46 +79,13 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter.UnitTest Assert.AreEqual(0, results.Count()); } - [TestMethod] - public void RoundZero() - { - double result = UnitHandler.Round(0.0); - Assert.AreEqual(0, result); - } - [TestMethod] public void RoundNormalValue() { - double result = UnitHandler.Round(3.141592653589793); - Assert.AreEqual(3.1416, result); - } - - [TestMethod] - public void RoundSmallValue() - { - double result = UnitHandler.Round(1.23456789012345E-16); - Assert.AreEqual(1.2346E-16, result); - } - - [TestMethod] - public void RoundBigValue() - { - double result = UnitHandler.Round(1234567890123456.0); - Assert.AreEqual(1234600000000000.0, result); - } - - [TestMethod] - public void RoundNegativeValue() - { - double result = UnitHandler.Round(-3.141592653589793); - Assert.AreEqual(-3.1416, result); - } - - [TestMethod] - public void RoundNinesValue() - { - double result = UnitHandler.Round(999999999999.9998); - Assert.AreEqual(1000000000000.0, result); + var convertModel = new ConvertModel(3.14159265358979323, "stone", "kg"); + var result = UnitHandler.Convert(convertModel).Single(); + var str = result.ToString(System.Globalization.CultureInfo.InvariantCulture); + Assert.AreEqual("19.950018128979… kg", str); } } } diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs index 1d471420b4..bc4c00d7a7 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs @@ -72,7 +72,7 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter { try { - Clipboard.SetText(result.ConvertedValue.ToString(UnitConversionResult.Format, CultureInfo.CurrentCulture)); + Clipboard.SetText(result.ConvertedValue.ToString(UnitConversionResult.CopyFormat, CultureInfo.CurrentCulture)); ret = true; } catch (ExternalException) @@ -104,7 +104,7 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter { try { - Clipboard.SetText(result.ConvertedValue.ToString(UnitConversionResult.Format, CultureInfo.CurrentCulture)); + Clipboard.SetText(result.ConvertedValue.ToString(UnitConversionResult.CopyFormat, CultureInfo.CurrentCulture)); ret = true; } catch (ExternalException) diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitConversionResult.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitConversionResult.cs index 9c5c94c69e..576d677387 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitConversionResult.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitConversionResult.cs @@ -2,13 +2,16 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using System; using UnitsNet; namespace Community.PowerToys.Run.Plugin.UnitConverter { public class UnitConversionResult { - public static string Format { get; set; } = "g14"; + public static string TitleFormat { get; set; } = "G14"; + + public static string CopyFormat { get; set; } = "R"; public double ConvertedValue { get; } @@ -23,14 +26,25 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter QuantityInfo = quantityInfo; } - public string ToString(System.IFormatProvider provider = null) + public string ToString(IFormatProvider provider = null) { if (provider == null) { provider = System.Globalization.CultureInfo.CurrentCulture; } - return ConvertedValue.ToString(Format, provider) + " " + UnitName; + // Check if the formatted number matches the original value. If they differ, some + // decimal places where cut off, and therefore we add an ellipsis. + string formatted = ConvertedValue.ToString(TitleFormat, provider); + + if (double.TryParse(formatted, provider, out double parsedNumber) && + Math.Abs(ConvertedValue - parsedNumber) > double.Epsilon && + !formatted.Contains('E', StringComparison.OrdinalIgnoreCase)) + { + return formatted + "… " + UnitName; + } + + return formatted + " " + UnitName; } } } diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitHandler.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitHandler.cs index d8c0dce3c6..fe6b4634dd 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitHandler.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/UnitHandler.cs @@ -11,8 +11,6 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter { public static class UnitHandler { - private static readonly int _roundingSignificantDigits = 4; - private static readonly QuantityInfo[] _included = new QuantityInfo[] { Acceleration.Info, @@ -59,23 +57,6 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter return null; } - /// - /// Rounds the value to the predefined number of significant digits. - /// - /// Value to be rounded - public static double Round(double value) - { - if (value == 0.0D) - { - return 0; - } - - var power = Math.Floor(Math.Log10(Math.Abs(value))); - var exponent = Math.Pow(10, power); - var rounded = Math.Round(value / exponent, _roundingSignificantDigits) * exponent; - return rounded; - } - /// /// Given parsed ConvertModel, computes result. (E.g "1 foot in cm"). /// @@ -106,7 +87,7 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter if (!double.IsNaN(convertedValue)) { - UnitConversionResult result = new UnitConversionResult(Round(convertedValue), convertModel.ToUnit, quantityInfo); + UnitConversionResult result = new UnitConversionResult(convertedValue, convertModel.ToUnit, quantityInfo); results.Add(result); } } From 8fb45b5a55331feb6aa7410d1fde8d5c333443be Mon Sep 17 00:00:00 2001 From: Dave Rayment Date: Fri, 18 Oct 2024 14:52:47 +0100 Subject: [PATCH 30/95] [Peek] Improve folder enumeration (#35076) * Improve folder enumeration. Fix initial display of folder details. Simplify UnsupportedFilePreviewer. Use Progress method for reporting progress. #35008 * Remove unused using. * Apply suggestions from code review Co-authored-by: Davide Giacometti <25966642+davidegiacometti@users.noreply.github.com> * Implement PR review suggestions, including fix for InaccessibleFiles not being skipped. * Folder enumeration avoids Reparse Points. Moved enumeration options to field. * Remove unused using. * Make folder enumeration options static...again. --- .../UnsupportedFilePreviewer.cs | 252 +++++++----------- 1 file changed, 93 insertions(+), 159 deletions(-) diff --git a/src/modules/peek/Peek.FilePreviewer/Previewers/UnsupportedFilePreviewer/UnsupportedFilePreviewer.cs b/src/modules/peek/Peek.FilePreviewer/Previewers/UnsupportedFilePreviewer/UnsupportedFilePreviewer.cs index 8644422f9f..d287eb41bf 100644 --- a/src/modules/peek/Peek.FilePreviewer/Previewers/UnsupportedFilePreviewer/UnsupportedFilePreviewer.cs +++ b/src/modules/peek/Peek.FilePreviewer/Previewers/UnsupportedFilePreviewer/UnsupportedFilePreviewer.cs @@ -5,13 +5,13 @@ using System; using System.Globalization; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using ManagedCommon; using Microsoft.UI.Dispatching; -using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Media.Imaging; using Peek.Common.Extensions; using Peek.Common.Helpers; @@ -22,27 +22,42 @@ using Windows.Foundation; namespace Peek.FilePreviewer.Previewers { - public partial class UnsupportedFilePreviewer : ObservableObject, IUnsupportedFilePreviewer, IDisposable + public partial class UnsupportedFilePreviewer : ObservableObject, IUnsupportedFilePreviewer { - private static readonly EnumerationOptions _fileEnumOptions = new() { MatchType = MatchType.Win32, AttributesToSkip = 0, IgnoreInaccessible = true }; - private static readonly EnumerationOptions _directoryEnumOptions = new() { MatchType = MatchType.Win32, AttributesToSkip = FileAttributes.ReparsePoint, IgnoreInaccessible = true }; - private readonly DispatcherTimer _folderSizeDispatcherTimer = new(); - private ulong _folderSize; + /// + /// The number of files to scan between updates when calculating folder size. + /// + private const int FolderEnumerationChunkSize = 100; + + /// + /// The maximum view updates per second when enumerating a folder's contents. + /// + private const int MaxUpdateFps = 15; + + /// + /// The icon to display when a file or folder's thumbnail or icon could not be retrieved. + /// + private static readonly SvgImageSource DefaultIcon = new(new Uri("ms-appx:///Assets/Peek/DefaultFileIcon.svg")); + + /// + /// The options to use for the folder size enumeration. We recurse through all files and all subfolders. + /// + private static readonly EnumerationOptions FolderEnumerationOptions; [ObservableProperty] - private UnsupportedFilePreviewData preview = new UnsupportedFilePreviewData(); + private UnsupportedFilePreviewData preview = new(); [ObservableProperty] private PreviewState state; + static UnsupportedFilePreviewer() + { + FolderEnumerationOptions = new() { RecurseSubdirectories = true, AttributesToSkip = FileAttributes.ReparsePoint }; + } + public UnsupportedFilePreviewer(IFileSystemItem file) { - _folderSizeDispatcherTimer.Interval = TimeSpan.FromMilliseconds(500); - _folderSizeDispatcherTimer.Tick += FolderSizeDispatcherTimer_Tick; - Item = file; - Preview.FileName = file.Name; - Preview.DateModified = file.DateModified?.ToString(CultureInfo.CurrentCulture); Dispatcher = DispatcherQueue.GetForCurrentThread(); } @@ -50,41 +65,40 @@ namespace Peek.FilePreviewer.Previewers private DispatcherQueue Dispatcher { get; } - private Task? IconPreviewTask { get; set; } - - private Task? DisplayInfoTask { get; set; } - - public void Dispose() - { - _folderSizeDispatcherTimer.Tick -= FolderSizeDispatcherTimer_Tick; - GC.SuppressFinalize(this); - } - - public Task GetPreviewSizeAsync(CancellationToken cancellationToken) - { - Size? size = new Size(680, 500); - var previewSize = new PreviewSize { MonitorSize = size, UseEffectivePixels = true }; - return Task.FromResult(previewSize); - } + public Task GetPreviewSizeAsync(CancellationToken cancellationToken) => + Task.FromResult(new PreviewSize { MonitorSize = new Size(680, 500), UseEffectivePixels = true }); public async Task LoadPreviewAsync(CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); - - State = PreviewState.Loading; - - IconPreviewTask = LoadIconPreviewAsync(cancellationToken); - DisplayInfoTask = LoadDisplayInfoAsync(cancellationToken); - - await Task.WhenAll(IconPreviewTask, DisplayInfoTask); - - if (HasFailedLoadingPreview()) + try { - State = PreviewState.Error; + await Dispatcher.RunOnUiThread(async () => + { + Preview.FileName = Item.Name; + Preview.DateModified = Item.DateModified?.ToString(CultureInfo.CurrentCulture); + + State = PreviewState.Loaded; + + await LoadIconPreviewAsync(cancellationToken); + }); + + var progress = new Progress(update => + { + Dispatcher.TryEnqueue(() => + { + Preview.FileSize = update; + }); + }); + + await LoadDisplayInfoAsync(progress, cancellationToken); } - else + catch (OperationCanceledException) { - State = PreviewState.Loaded; + } + catch (Exception ex) + { + Logger.LogError("UnsupportedFilePreviewer error.", ex); + State = PreviewState.Error; } } @@ -97,139 +111,59 @@ namespace Peek.FilePreviewer.Previewers }); } - public async Task LoadIconPreviewAsync(CancellationToken cancellationToken) + private async Task LoadIconPreviewAsync(CancellationToken cancellationToken) { - bool isIconValid = false; - - var isTaskSuccessful = await TaskExtension.RunSafe(async () => - { - cancellationToken.ThrowIfCancellationRequested(); - await Dispatcher.RunOnUiThread(async () => - { - cancellationToken.ThrowIfCancellationRequested(); - - var iconBitmap = await ThumbnailHelper.GetThumbnailAsync(Item.Path, cancellationToken) - ?? await ThumbnailHelper.GetIconAsync(Item.Path, cancellationToken); - - cancellationToken.ThrowIfCancellationRequested(); - - isIconValid = iconBitmap != null; - - Preview.IconPreview = iconBitmap ?? new SvgImageSource(new Uri("ms-appx:///Assets/Peek/DefaultFileIcon.svg")); - }); - }); - - return isIconValid && isTaskSuccessful; + Preview.IconPreview = await ThumbnailHelper.GetThumbnailAsync(Item.Path, cancellationToken) ?? + await ThumbnailHelper.GetIconAsync(Item.Path, cancellationToken) ?? + DefaultIcon; } - public async Task LoadDisplayInfoAsync(CancellationToken cancellationToken) + private async Task LoadDisplayInfoAsync(IProgress sizeProgress, CancellationToken cancellationToken) { - bool isDisplayValid = false; + string type = await Item.GetContentTypeAsync(); - var isTaskSuccessful = await TaskExtension.RunSafe(async () => + Dispatcher.TryEnqueue(() => Preview.FileType = type); + + if (Item is FolderItem folderItem) { - cancellationToken.ThrowIfCancellationRequested(); - - var type = await Task.Run(Item.GetContentTypeAsync); - - cancellationToken.ThrowIfCancellationRequested(); - - isDisplayValid = type != null; - - var readableFileSize = string.Empty; - - if (Item is FileItem) - { - readableFileSize = ReadableStringHelper.BytesToReadableString(Item.FileSizeBytes); - } - else if (Item is FolderItem) - { - ComputeFolderSize(cancellationToken); - } - - await Dispatcher.RunOnUiThread(() => - { - Preview.FileSize = readableFileSize; - Preview.FileType = type; - return Task.CompletedTask; - }); - }); - - return isDisplayValid && isTaskSuccessful; - } - - private bool HasFailedLoadingPreview() - { - var isLoadingIconPreviewSuccessful = IconPreviewTask?.Result ?? false; - var isLoadingDisplayInfoSuccessful = DisplayInfoTask?.Result ?? false; - - return !isLoadingIconPreviewSuccessful || !isLoadingDisplayInfoSuccessful; - } - - private void ComputeFolderSize(CancellationToken cancellationToken) - { - Task.Run( - async () => - { - try - { - // Special folders like recycle bin don't have a path - if (string.IsNullOrWhiteSpace(Item.Path)) - { - return; - } - - await Dispatcher.RunOnUiThread(_folderSizeDispatcherTimer.Start); - GetDirectorySize(new DirectoryInfo(Item.Path), cancellationToken); - } - catch (OperationCanceledException) - { - } - catch (Exception ex) - { - Logger.LogError("Failed to calculate folder size", ex); - } - finally - { - await Dispatcher.RunOnUiThread(_folderSizeDispatcherTimer.Stop); - } - - // If everything went well, ensure the UI is updated - await Dispatcher.RunOnUiThread(UpdateFolderSize); - }, - cancellationToken); - } - - private void GetDirectorySize(DirectoryInfo directory, CancellationToken cancellationToken) - { - var files = directory.GetFiles("*", _fileEnumOptions); - for (var i = 0; i < files.Length; i++) - { - cancellationToken.ThrowIfCancellationRequested(); - - var f = files[i]; - if (f.Length > 0) - { - _folderSize += Convert.ToUInt64(f.Length); - } + await Task.Run(() => CalculateFolderSizeWithProgress(Item.Path, sizeProgress, cancellationToken), cancellationToken); } - - var directories = directory.GetDirectories("*", _directoryEnumOptions); - for (var i = 0; i < directories.Length; i++) + else { - cancellationToken.ThrowIfCancellationRequested(); - GetDirectorySize(directories[i], cancellationToken); + ReportProgress(sizeProgress, Item.FileSizeBytes); } } - private void UpdateFolderSize() + private void CalculateFolderSizeWithProgress(string path, IProgress progress, CancellationToken cancellationToken) { - Preview.FileSize = ReadableStringHelper.BytesToReadableString(_folderSize); + ulong folderSize = 0; + TimeSpan updateInterval = TimeSpan.FromMilliseconds(1000 / MaxUpdateFps); + DateTime nextUpdate = DateTime.UtcNow + updateInterval; + + var files = new DirectoryInfo(path).EnumerateFiles("*", FolderEnumerationOptions); + + foreach (var chunk in files.Chunk(FolderEnumerationChunkSize)) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (DateTime.Now >= nextUpdate) + { + ReportProgress(progress, folderSize); + nextUpdate = DateTime.UtcNow + updateInterval; + } + + foreach (var file in chunk) + { + folderSize += (ulong)file.Length; + } + } + + ReportProgress(progress, folderSize); } - private void FolderSizeDispatcherTimer_Tick(object? sender, object e) + private void ReportProgress(IProgress progress, ulong size) { - UpdateFolderSize(); + progress.Report(ReadableStringHelper.BytesToReadableString(size)); } } } From 37eaf10bf090873e565d754a7fad38c55147d987 Mon Sep 17 00:00:00 2001 From: Heiko <61519853+htcfreek@users.noreply.github.com> Date: Fri, 18 Oct 2024 16:02:25 +0200 Subject: [PATCH 31/95] [Settings > New+ page] Fix enabled state of template backup info bar (#35235) Update NewPlusPage.xaml --- .../Settings.UI/SettingsXAML/Views/NewPlusPage.xaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml index f232227b05..ef8b9c68e8 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml @@ -60,9 +60,9 @@ From 5cba82f9290af1f04c2679079a66adcda7be98a4 Mon Sep 17 00:00:00 2001 From: Michael Clayton Date: Fri, 18 Oct 2024 17:32:08 +0100 Subject: [PATCH 32/95] [MWB][CQ]Refactoring "Common" classes - Common.Log.cs (#35156) * [MouseWithoutBorders] - split "Common.Log.cs" into "Logger.cs" * [MouseWithoutBorders] - fix references to Logger * [MouseWithoutBorders] - add MouseWithoutBorders.UnitTests * [MouseWithoutBorders] - fixing broken tests * [MouseWithoutBorders] - fixing spelling * [MouseWithoutBorders] - fixing spelling * [MouseWithoutBorders] - fixing resource filename casing * [MouseWithoutBorders] - fixing resource filename casing * [MouseWithoutBorders] - fixing resource filename casing * [MouseWithoutBorders] - fixing resource filename casing * [MouseWithoutBorders] - fixed compile error * [MouseWithoutBorders] - fixed failing test * [MouseWithoutBorders] - fixed failing build * [MouseWithoutBorders] - ignore flakey test --- .github/actions/spell-check/expect.txt | 1 + PowerToys.sln | 15 + .../App/Class/Common.Clipboard.cs | 137 +++--- .../App/Class/Common.DragDrop.cs | 47 +- .../App/Class/Common.Encryption.cs | 10 +- .../App/Class/Common.Event.cs | 25 +- .../App/Class/Common.Helper.cs | 38 +- .../App/Class/Common.InitAndCleanup.cs | 21 +- .../App/Class/Common.Launch.cs | 37 +- .../App/Class/Common.Log.cs | 384 +-------------- .../App/Class/Common.MachineStuff.cs | 33 +- .../App/Class/Common.Receiver.cs | 21 +- .../App/Class/Common.Service.cs | 13 +- .../App/Class/Common.ShutdownWithPowerToys.cs | 4 +- .../App/Class/Common.WinAPI.cs | 41 +- .../MouseWithoutBorders/App/Class/Common.cs | 99 ++-- .../App/Class/CustomCursor.cs | 10 +- .../App/Class/Extensions.cs | 4 +- .../App/Class/IClipboardHelper.cs | 15 +- .../App/Class/InputHook.cs | 15 +- .../App/Class/InputSimulation.cs | 17 +- .../MouseWithoutBorders/App/Class/Program.cs | 46 +- .../MouseWithoutBorders/App/Class/Setting.cs | 9 +- .../App/Class/SocketStuff.cs | 233 ++++----- .../App/Class/TcpServer.cs | 8 +- .../MouseWithoutBorders/App/Core/Logger.cs | 447 ++++++++++++++++++ .../App/Form/Settings/SettingsForm.cs | 10 +- .../App/Form/Settings/SetupPage3a.cs | 3 +- .../App/Form/frmInputCallback.cs | 3 +- .../MouseWithoutBorders/App/Form/frmMatrix.cs | 8 +- .../MouseWithoutBorders/App/Form/frmScreen.cs | 33 +- .../App/Helper/FormHelper.cs | 10 +- .../App/Properties/AssemblyInfo.cs | 7 + .../Core/Logger.PrivateDump.expected.txt | 404 ++++++++++++++++ .../Core/Logger.PrivateDump.original.txt | 405 ++++++++++++++++ .../Core/LoggerTests.cs | 161 +++++++ .../MouseWithoutBorders.UnitTests.csproj | 30 ++ 37 files changed, 1965 insertions(+), 839 deletions(-) create mode 100644 src/modules/MouseWithoutBorders/App/Core/Logger.cs create mode 100644 src/modules/MouseWithoutBorders/App/Properties/AssemblyInfo.cs create mode 100644 src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/Logger.PrivateDump.expected.txt create mode 100644 src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/Logger.PrivateDump.original.txt create mode 100644 src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/LoggerTests.cs create mode 100644 src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/MouseWithoutBorders.UnitTests.csproj diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index 83689c8071..eeb735f825 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -1911,6 +1911,7 @@ XFile XIncrement XLoc XNamespace +Xoshiro XPels XPixel xplorer diff --git a/PowerToys.sln b/PowerToys.sln index e91c2e3907..1113ab5530 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -622,6 +622,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesLauncher", "src\m EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesWindowArranger", "src\modules\Workspaces\WorkspacesWindowArranger\WorkspacesWindowArranger.vcxproj", "{37D07516-4185-43A4-924F-3C7A5D95ECF6}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseWithoutBorders.UnitTests", "src\modules\MouseWithoutBorders\MouseWithoutBorders.UnitTests\MouseWithoutBorders.UnitTests.csproj", "{66614C26-314C-4B91-9071-76133422CFEF}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -2734,6 +2736,18 @@ Global {37D07516-4185-43A4-924F-3C7A5D95ECF6}.Release|x64.Build.0 = Release|x64 {37D07516-4185-43A4-924F-3C7A5D95ECF6}.Release|x86.ActiveCfg = Release|x64 {37D07516-4185-43A4-924F-3C7A5D95ECF6}.Release|x86.Build.0 = Release|x64 + {66614C26-314C-4B91-9071-76133422CFEF}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {66614C26-314C-4B91-9071-76133422CFEF}.Debug|ARM64.Build.0 = Debug|ARM64 + {66614C26-314C-4B91-9071-76133422CFEF}.Debug|x64.ActiveCfg = Debug|x64 + {66614C26-314C-4B91-9071-76133422CFEF}.Debug|x64.Build.0 = Debug|x64 + {66614C26-314C-4B91-9071-76133422CFEF}.Debug|x86.ActiveCfg = Debug|x64 + {66614C26-314C-4B91-9071-76133422CFEF}.Debug|x86.Build.0 = Debug|x64 + {66614C26-314C-4B91-9071-76133422CFEF}.Release|ARM64.ActiveCfg = Release|ARM64 + {66614C26-314C-4B91-9071-76133422CFEF}.Release|ARM64.Build.0 = Release|ARM64 + {66614C26-314C-4B91-9071-76133422CFEF}.Release|x64.ActiveCfg = Release|x64 + {66614C26-314C-4B91-9071-76133422CFEF}.Release|x64.Build.0 = Release|x64 + {66614C26-314C-4B91-9071-76133422CFEF}.Release|x86.ActiveCfg = Release|x64 + {66614C26-314C-4B91-9071-76133422CFEF}.Release|x86.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -2962,6 +2976,7 @@ Global {367D7543-7DBA-4381-99F1-BF6142A996C4} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {37D07516-4185-43A4-924F-3C7A5D95ECF6} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} + {66614C26-314C-4B91-9071-76133422CFEF} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs index e476ec3554..68f700c3d4 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.Clipboard.cs @@ -28,6 +28,7 @@ using Microsoft.PowerToys.Telemetry; // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; +using MouseWithoutBorders.Core; using MouseWithoutBorders.Exceptions; using SystemClipboard = System.Windows.Forms.Clipboard; @@ -83,8 +84,8 @@ namespace MouseWithoutBorders internal static bool CheckClipboardEx(ByteArrayOrString data, bool isFilePath) { - LogDebug($"{nameof(CheckClipboardEx)}: ShareClipboard = {Setting.Values.ShareClipboard}, TransferFile = {Setting.Values.TransferFile}, data = {data}."); - LogDebug($"{nameof(CheckClipboardEx)}: {nameof(Setting.Values.OneWayClipboardMode)} = {Setting.Values.OneWayClipboardMode}."); + Logger.LogDebug($"{nameof(CheckClipboardEx)}: ShareClipboard = {Setting.Values.ShareClipboard}, TransferFile = {Setting.Values.TransferFile}, data = {data}."); + Logger.LogDebug($"{nameof(CheckClipboardEx)}: {nameof(Setting.Values.OneWayClipboardMode)} = {Setting.Values.OneWayClipboardMode}."); if (!Setting.Values.ShareClipboard) { @@ -98,7 +99,7 @@ namespace MouseWithoutBorders if (GetTick() - LastClipboardEventTime < 1000) { - LogDebug("GetTick() - lastClipboardEventTime < 1000"); + Logger.LogDebug("GetTick() - lastClipboardEventTime < 1000"); LastClipboardEventTime = GetTick(); return false; } @@ -130,7 +131,7 @@ namespace MouseWithoutBorders { if (lastClipboardObject is string lastStringData && lastStringData.Equals(stringData, StringComparison.OrdinalIgnoreCase)) { - LogDebug("CheckClipboardEx: Same string data."); + Logger.LogDebug("CheckClipboardEx: Same string data."); return false; } } @@ -139,11 +140,11 @@ namespace MouseWithoutBorders if (isFilePath) { - Common.LogDebug("Clipboard contains FileDropList"); + Logger.LogDebug("Clipboard contains FileDropList"); if (!Setting.Values.TransferFile) { - Common.LogDebug("TransferFile option is unchecked."); + Logger.LogDebug("TransferFile option is unchecked."); return false; } @@ -155,7 +156,7 @@ namespace MouseWithoutBorders { if (File.Exists(filePath) && new FileInfo(filePath).Length <= MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT) { - LogDebug("Clipboard contains: " + filePath); + Logger.LogDebug("Clipboard contains: " + filePath); LastDragDropFile = filePath; SendClipboardBeat(); SetToggleIcon(new int[TOGGLE_ICONS_SIZE] { ICON_BIG_CLIPBOARD, -1, ICON_BIG_CLIPBOARD, -1 }); @@ -164,7 +165,7 @@ namespace MouseWithoutBorders { if (Directory.Exists(filePath)) { - LogDebug("Clipboard contains a directory: " + filePath); + Logger.LogDebug("Clipboard contains a directory: " + filePath); LastDragDropFile = filePath; SendClipboardBeat(); } @@ -172,7 +173,7 @@ namespace MouseWithoutBorders { LastDragDropFile = filePath + " - File too big (greater than 100MB), please drag and drop the file instead!"; SendClipboardBeat(); - Log("Clipboard: File too big: " + filePath); + Logger.Log("Clipboard: File too big: " + filePath); } SetToggleIcon(new int[TOGGLE_ICONS_SIZE] { ICON_ERROR, -1, ICON_ERROR, -1 }); @@ -180,7 +181,7 @@ namespace MouseWithoutBorders } else { - Log("CheckClipboardEx: File not found: " + filePath); + Logger.Log("CheckClipboardEx: File not found: " + filePath); } }); } @@ -194,7 +195,7 @@ namespace MouseWithoutBorders s.Write(texts, 0, texts.Length); } - Common.LogDebug("Plain/Zip = " + texts.Length.ToString(CultureInfo.CurrentCulture) + "/" + + Logger.LogDebug("Plain/Zip = " + texts.Length.ToString(CultureInfo.CurrentCulture) + "/" + ms.Length.ToString(CultureInfo.CurrentCulture)); LastClipboardData = ms.GetBuffer(); @@ -206,20 +207,20 @@ namespace MouseWithoutBorders { if (lastClipboardObject is byte[] lastByteData && Enumerable.SequenceEqual(lastByteData, byteData)) { - LogDebug("CheckClipboardEx: Same byte[] data."); + Logger.LogDebug("CheckClipboardEx: Same byte[] data."); return false; } } HasSwitchedMachineSinceLastCopy = false; - Common.LogDebug("Clipboard contains image"); + Logger.LogDebug("Clipboard contains image"); IsClipboardDataImage = true; LastClipboardData = byteData; } else { - LogDebug("*** Clipboard contains something else!"); + Logger.LogDebug("*** Clipboard contains something else!"); return false; } @@ -243,7 +244,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); } return false; @@ -334,7 +335,7 @@ namespace MouseWithoutBorders ProcessPackage(data, tcp); if (++unexpectedCount > 100) { - Log("ReceiveClipboardDataUsingTCP: unexpectedCount > 100!"); + Logger.Log("ReceiveClipboardDataUsingTCP: unexpectedCount > 100!"); done = true; } @@ -363,7 +364,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log("ReceiveClipboardDataUsingTCP: " + e.Message); + Logger.Log("ReceiveClipboardDataUsingTCP: " + e.Message); } } @@ -396,7 +397,7 @@ namespace MouseWithoutBorders { if (Sk == null) { - Log("ConnectAndGetData: Sk == null!"); + Logger.Log("ConnectAndGetData: Sk == null!"); return; } @@ -404,7 +405,7 @@ namespace MouseWithoutBorders TcpClient clipboardTcpClient = null; string postAct = (string)postAction; - LogDebug("ConnectAndGetData.postAction: " + postAct); + Logger.LogDebug("ConnectAndGetData.postAction: " + postAct); ClipboardPostAction clipboardPostAct = postAct.Contains("mspaint,") ? ClipboardPostAction.Mspaint : postAct.Equals("desktop", StringComparison.OrdinalIgnoreCase) ? ClipboardPostAction.Desktop @@ -418,7 +419,7 @@ namespace MouseWithoutBorders if (!IsConnectedByAClientSocketTo(remoteMachine)) { - Log($"No potential inbound connection from {MachineName} to {remoteMachine}, ask for a push back instead."); + Logger.Log($"No potential inbound connection from {MachineName} to {remoteMachine}, ask for a push back instead."); ID machineId = MachinePool.ResolveID(remoteMachine); if (machineId != ID.NONE) @@ -436,7 +437,7 @@ namespace MouseWithoutBorders } else { - Log($"Unable to resolve {remoteMachine} to its long IP."); + Logger.Log($"Unable to resolve {remoteMachine} to its long IP."); } return; @@ -448,7 +449,7 @@ namespace MouseWithoutBorders } catch (ThreadAbortException) { - Common.Log("The current thread is being aborted (1)."); + Logger.Log("The current thread is being aborted (1)."); if (clipboardTcpClient != null && clipboardTcpClient.Connected) { clipboardTcpClient.Client.Close(); @@ -458,7 +459,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Common.Log(e); + Logger.Log(e); Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_BIG_CLIPBOARD, @@ -513,7 +514,7 @@ namespace MouseWithoutBorders if ((rv = deStream.ReadEx(header, 0, header.Length)) < header.Length) { - Common.Log("Reading header failed: " + rv.ToString(CultureInfo.CurrentCulture)); + Logger.Log("Reading header failed: " + rv.ToString(CultureInfo.CurrentCulture)); Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_BIG_CLIPBOARD, @@ -523,12 +524,12 @@ namespace MouseWithoutBorders } fileName = Common.GetStringU(header).Replace("\0", string.Empty); - Common.LogDebug("Header: " + fileName); + Logger.LogDebug("Header: " + fileName); string[] headers = fileName.Split(Star); if (headers.Length < 2 || !long.TryParse(headers[0], out long dataSize)) { - Common.Log(string.Format( + Logger.Log(string.Format( CultureInfo.CurrentCulture, "Reading header failed: {0}:{1}", headers.Length, @@ -543,7 +544,7 @@ namespace MouseWithoutBorders fileName = headers[1]; - Common.LogDebug(string.Format( + Logger.LogDebug(string.Format( CultureInfo.CurrentCulture, "Receiving {0}:{1} from {2}...", Path.GetFileName(fileName), @@ -593,7 +594,7 @@ namespace MouseWithoutBorders m = new FileStream(tempFile, FileMode.Create); } - Common.Log("==> " + tempFile); + Logger.Log("==> " + tempFile); } ShowToolTip( @@ -643,7 +644,7 @@ namespace MouseWithoutBorders if (m != null && fileName != null) { m.Flush(); - Common.LogDebug(m.Length.ToString(CultureInfo.CurrentCulture) + " bytes received."); + Logger.LogDebug(m.Length.ToString(CultureInfo.CurrentCulture) + " bytes received."); Common.LastClipboardEventTime = Common.GetTick(); string toolTipText = null; string sizeText = m.Length >= 1024 @@ -738,7 +739,7 @@ namespace MouseWithoutBorders } catch (ThreadAbortException) { - Common.Log("The current thread is being aborted (3)."); + Logger.Log("The current thread is being aborted (3)."); s.Close(); if (m != null) @@ -754,11 +755,11 @@ namespace MouseWithoutBorders if (e is IOException) { string log = $"{nameof(ReceiveAndProcessClipboardData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; - Common.Log(log); + Logger.Log(log); } else { - Common.Log(e); + Logger.Log(e); } Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] @@ -805,15 +806,15 @@ namespace MouseWithoutBorders NetworkStream ns = new(s); enStream = Common.GetEncryptedStream(ns); Common.SendOrReceiveARandomDataBlockPerInitialIV(enStream); - LogDebug($"{nameof(ShakeHand)}: Writing header package."); + Logger.LogDebug($"{nameof(ShakeHand)}: Writing header package."); enStream.Write(package.Bytes, 0, PACKAGE_SIZE_EX); - LogDebug($"{nameof(ShakeHand)}: Sent: clientPush={clientPushData}, postAction={postAction}."); + Logger.LogDebug($"{nameof(ShakeHand)}: Sent: clientPush={clientPushData}, postAction={postAction}."); deStream = Common.GetDecryptedStream(ns); Common.SendOrReceiveARandomDataBlockPerInitialIV(deStream, false); - LogDebug($"{nameof(ShakeHand)}: Reading header package."); + Logger.LogDebug($"{nameof(ShakeHand)}: Reading header package."); int bytesReceived = deStream.ReadEx(buf, 0, Common.PACKAGE_SIZE_EX); package.Bytes = buf; @@ -826,41 +827,41 @@ namespace MouseWithoutBorders { name = remoteName = package.MachineName; - Common.LogDebug($"{nameof(ShakeHand)}: Connection from {name}:{package.Src}"); + Logger.LogDebug($"{nameof(ShakeHand)}: Connection from {name}:{package.Src}"); if (Common.MachinePool.ResolveID(name) == package.Src && Common.IsConnectedTo(package.Src)) { clientPushData = package.Type == PackageType.ClipboardPush; postAction = package.PostAction; handShaken = true; - LogDebug($"{nameof(ShakeHand)}: Received: clientPush={clientPushData}, postAction={postAction}."); + Logger.LogDebug($"{nameof(ShakeHand)}: Received: clientPush={clientPushData}, postAction={postAction}."); } else { - Common.LogDebug($"{nameof(ShakeHand)}: No active connection to the machine: {name}."); + Logger.LogDebug($"{nameof(ShakeHand)}: No active connection to the machine: {name}."); } } else { - Common.LogDebug($"{nameof(ShakeHand)}: Unexpected package type: {package.Type}."); + Logger.LogDebug($"{nameof(ShakeHand)}: Unexpected package type: {package.Type}."); } } else { - Common.LogDebug($"{nameof(ShakeHand)}: BytesTransferred != PACKAGE_SIZE_EX: {bytesReceived}"); + Logger.LogDebug($"{nameof(ShakeHand)}: BytesTransferred != PACKAGE_SIZE_EX: {bytesReceived}"); } if (!handShaken) { string msg = $"Clipboard connection rejected: {name}:{remoteName}/{package.Src}\r\n\r\nMake sure you run the same version in all machines."; - Common.Log(msg); + Logger.Log(msg); Common.ShowToolTip(msg, 3000, ToolTipIcon.Warning); Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_BIG_CLIPBOARD, -1, -1, -1 }); } } catch (ThreadAbortException) { - Common.Log($"{nameof(ShakeHand)}: The current thread is being aborted."); + Logger.Log($"{nameof(ShakeHand)}: The current thread is being aborted."); s.Close(); } catch (Exception e) @@ -868,11 +869,11 @@ namespace MouseWithoutBorders if (e is IOException) { string log = $"{nameof(ShakeHand)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; - Common.Log(log); + Logger.Log(log); } else { - Common.Log(e); + Logger.Log(e); } Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] @@ -906,7 +907,7 @@ namespace MouseWithoutBorders Common.DoSomethingInUIThread(() => Common.MainForm.ChangeIcon(Common.ICON_SMALL_CLIPBOARD)); System.Net.IPAddress ip = GetConnectedClientSocketIPAddressFor(remoteMachine); - Common.LogDebug($"{nameof(ConnectToRemoteClipboardSocket)}Connecting to {remoteMachine}:{ip}:{sk.TcpPort}..."); + Logger.LogDebug($"{nameof(ConnectToRemoteClipboardSocket)}Connecting to {remoteMachine}:{ip}:{sk.TcpPort}..."); if (ip != null) { @@ -917,7 +918,7 @@ namespace MouseWithoutBorders clipboardTcpClient.Connect(remoteMachine, sk.TcpPort); } - Common.LogDebug($"Connected from {clipboardTcpClient.Client.LocalEndPoint}. Getting data..."); + Logger.LogDebug($"Connected from {clipboardTcpClient.Client.LocalEndPoint}. Getting data..."); return clipboardTcpClient; } else @@ -930,7 +931,7 @@ namespace MouseWithoutBorders { if (data == null || data.Length <= 0) { - Common.Log("data is null or empty!"); + Logger.Log("data is null or empty!"); return; } @@ -988,28 +989,28 @@ namespace MouseWithoutBorders if (txt.StartsWith("RTF", StringComparison.CurrentCultureIgnoreCase)) { - Common.LogDebug(((double)tmp.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of RTF <-"); + Logger.LogDebug(((double)tmp.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of RTF <-"); data1.SetData(DataFormats.Rtf, tmp); } else if (txt.StartsWith("HTM", StringComparison.CurrentCultureIgnoreCase)) { - Common.LogDebug(((double)tmp.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of HTM <-"); + Logger.LogDebug(((double)tmp.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of HTM <-"); data1.SetData(DataFormats.Html, tmp); } else if (txt.StartsWith("TXT", StringComparison.CurrentCultureIgnoreCase)) { - Common.LogDebug(((double)tmp.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of TXT <-"); + Logger.LogDebug(((double)tmp.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of TXT <-"); data1.SetData(DataFormats.UnicodeText, tmp); } else { if (textTypeCount == 0) { - Common.LogDebug(((double)txt.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of UNI <-"); + Logger.LogDebug(((double)txt.Length / 1024).ToString("0.00", CultureInfo.InvariantCulture) + "KB of UNI <-"); data1.SetData(DataFormats.UnicodeText, txt); } - Common.Log("Invalid clipboard format received!"); + Logger.Log("Invalid clipboard format received!"); } textTypeCount++; @@ -1037,26 +1038,26 @@ namespace MouseWithoutBorders SystemClipboard.SetFileDropList(filePaths); return true; }, - (log) => Common.TelemetryLogTrace( + (log) => Logger.TelemetryLogTrace( log, SeverityLevel.Information), () => Common.LastClipboardEventTime = Common.GetTick()); } catch (ExternalException e) { - Common.Log(e); + Logger.Log(e); } catch (ThreadStateException e) { - Common.Log(e); + Logger.Log(e); } catch (ArgumentNullException e) { - Common.Log(e); + Logger.Log(e); } catch (ArgumentException e) { - Common.Log(e); + Logger.Log(e); } }); } @@ -1074,20 +1075,20 @@ namespace MouseWithoutBorders SystemClipboard.SetImage(image); return true; }, - (log) => Common.TelemetryLogTrace(log, SeverityLevel.Information), + (log) => Logger.TelemetryLogTrace(log, SeverityLevel.Information), () => Common.LastClipboardEventTime = Common.GetTick()); } catch (ExternalException e) { - Common.Log(e); + Logger.Log(e); } catch (ThreadStateException e) { - Common.Log(e); + Logger.Log(e); } catch (ArgumentNullException e) { - Common.Log(e); + Logger.Log(e); } }); } @@ -1105,20 +1106,20 @@ namespace MouseWithoutBorders SystemClipboard.SetText(text); return true; }, - (log) => Common.TelemetryLogTrace(log, SeverityLevel.Information), + (log) => Logger.TelemetryLogTrace(log, SeverityLevel.Information), () => Common.LastClipboardEventTime = Common.GetTick()); } catch (ExternalException e) { - Common.Log(e); + Logger.Log(e); } catch (ThreadStateException e) { - Common.Log(e); + Logger.Log(e); } catch (ArgumentNullException e) { - Common.Log(e); + Logger.Log(e); } }); } @@ -1134,15 +1135,15 @@ namespace MouseWithoutBorders catch (ExternalException e) { string dataFormats = string.Join(",", dataObject.GetFormats()); - Common.Log($"{e.Message}: {dataFormats}"); + Logger.Log($"{e.Message}: {dataFormats}"); } catch (ThreadStateException e) { - Common.Log(e); + Logger.Log(e); } catch (ArgumentNullException e) { - Common.Log(e); + Logger.Log(e); } }); } diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.DragDrop.cs b/src/modules/MouseWithoutBorders/App/Class/Common.DragDrop.cs index 11d9e5776d..f3ef0f1395 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.DragDrop.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.DragDrop.cs @@ -20,6 +20,7 @@ using Microsoft.PowerToys.Telemetry; // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; +using MouseWithoutBorders.Core; namespace MouseWithoutBorders { @@ -73,12 +74,12 @@ namespace MouseWithoutBorders MouseDown = true; DragMachine = desMachineID; dropMachineID = ID.NONE; - LogDebug("DragDropStep01: MouseDown"); + Logger.LogDebug("DragDropStep01: MouseDown"); } else if (wParam == WM_LBUTTONUP) { MouseDown = false; - LogDebug("DragDropStep01: MouseUp"); + Logger.LogDebug("DragDropStep01: MouseUp"); } if (wParam == WM_RBUTTONUP && IsDropping) @@ -92,7 +93,7 @@ namespace MouseWithoutBorders { if (desMachineID == MachineID) { - LogDebug("DragDropStep02: SendCheckExplorerDragDrop sent to myself"); + Logger.LogDebug("DragDropStep02: SendCheckExplorerDragDrop sent to myself"); DoSomethingInUIThread(() => { _ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_CHECK_EXPLORER_DRAG_DROP, (IntPtr)0, (IntPtr)0); @@ -101,7 +102,7 @@ namespace MouseWithoutBorders else { SendCheckExplorerDragDrop(); - LogDebug("DragDropStep02: SendCheckExplorerDragDrop sent"); + Logger.LogDebug("DragDropStep02: SendCheckExplorerDragDrop sent"); } } @@ -114,11 +115,11 @@ namespace MouseWithoutBorders if (package.Des == MachineID || package.Des == ID.ALL) { - LogDebug("DragDropStep03: ExplorerDragDrop Received."); + Logger.LogDebug("DragDropStep03: ExplorerDragDrop Received."); dropMachineID = package.Src; // Drop machine is the machine that sent ExplorerDragDrop if (MouseDown || IsDropping) { - LogDebug("DragDropStep03: Mouse is down, check if dragging...sending WM_CHECK_EXPLORER_DRAG_DROP to myself..."); + Logger.LogDebug("DragDropStep03: Mouse is down, check if dragging...sending WM_CHECK_EXPLORER_DRAG_DROP to myself..."); DoSomethingInUIThread(() => { _ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_CHECK_EXPLORER_DRAG_DROP, (IntPtr)0, (IntPtr)0); @@ -150,12 +151,12 @@ namespace MouseWithoutBorders { if (dragDropStep05ExCalledByIpc > 0) { - LogDebug("DragDropStep04: DragDropStep05ExCalledByIpc."); + Logger.LogDebug("DragDropStep04: DragDropStep05ExCalledByIpc."); break; } _ = NativeMethods.GetCursorPos(ref p); - LogDebug("DragDropStep04: Moving Mouse Without Borders Helper to (" + p.X.ToString(CultureInfo.CurrentCulture) + ", " + p.Y.ToString(CultureInfo.CurrentCulture) + ")"); + Logger.LogDebug("DragDropStep04: Moving Mouse Without Borders Helper to (" + p.X.ToString(CultureInfo.CurrentCulture) + ", " + p.Y.ToString(CultureInfo.CurrentCulture) + ")"); _ = NativeMethods.SetWindowPos(h, NativeMethods.HWND_TOPMOST, p.X - 100 + i, p.Y - 100 + i, 200, 200, 0); _ = NativeMethods.SendMessage(h, 0x000F, IntPtr.Zero, IntPtr.Zero); // WM_PAINT Thread.Sleep(20); @@ -166,20 +167,20 @@ namespace MouseWithoutBorders } else { - LogDebug("DragDropStep04: Mouse without Borders Helper not found!"); + Logger.LogDebug("DragDropStep04: Mouse without Borders Helper not found!"); } } else { - LogDebug("DragDropStep04: IsDropping == true, skip checking"); + Logger.LogDebug("DragDropStep04: IsDropping == true, skip checking"); } - LogDebug("DragDropStep04: Got WM_CHECK_EXPLORER_DRAG_DROP, done with processing jump to DragDropStep05..."); + Logger.LogDebug("DragDropStep04: Got WM_CHECK_EXPLORER_DRAG_DROP, done with processing jump to DragDropStep05..."); } internal static void DragDropStep05Ex(string dragFileName) { - LogDebug("DragDropStep05 called."); + Logger.LogDebug("DragDropStep05 called."); _ = Interlocked.Exchange(ref dragDropStep05ExCalledByIpc, 1); @@ -204,21 +205,21 @@ namespace MouseWithoutBorders } DragDropStep06(); - LogDebug("DragDropStep05: File dragging: " + dragFileName); + Logger.LogDebug("DragDropStep05: File dragging: " + dragFileName); _ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_HIDE_DD_HELPER, (IntPtr)1, (IntPtr)0); } else { - LogDebug("DragDropStep05: File not found: [" + dragFileName + "]"); + Logger.LogDebug("DragDropStep05: File not found: [" + dragFileName + "]"); _ = NativeMethods.PostMessage(MainForm.Handle, NativeMethods.WM_HIDE_DD_HELPER, (IntPtr)0, (IntPtr)0); } - LogDebug("DragDropStep05: WM_HIDE_DDHelper sent"); + Logger.LogDebug("DragDropStep05: WM_HIDE_DDHelper sent"); }); } else { - LogDebug("DragDropStep05: IsDropping == true, change drop machine..."); + Logger.LogDebug("DragDropStep05: IsDropping == true, change drop machine..."); IsDropping = false; MainFormVisible = true; // WM_HIDE_DRAG_DROP SendDropBegin(); // To dropMachineID set in DragDropStep03 @@ -230,7 +231,7 @@ namespace MouseWithoutBorders internal static void DragDropStep06() { IsDragging = true; - LogDebug("DragDropStep06: SendClipboardBeatDragDrop"); + Logger.LogDebug("DragDropStep06: SendClipboardBeatDragDrop"); SendClipboardBeatDragDrop(); SendDropBegin(); } @@ -238,7 +239,7 @@ namespace MouseWithoutBorders internal static void DragDropStep08(DATA package) { GetNameOfMachineWithClipboardData(package); - LogDebug("DragDropStep08: ClipboardDragDrop Received. machine with drag file was set"); + Logger.LogDebug("DragDropStep08: ClipboardDragDrop Received. machine with drag file was set"); } internal static void DragDropStep08_2(DATA package) @@ -247,7 +248,7 @@ namespace MouseWithoutBorders { IsDropping = true; dropMachineID = MachineID; - LogDebug("DragDropStep08_2: ClipboardDragDropOperation Received. IsDropping set"); + Logger.LogDebug("DragDropStep08_2: ClipboardDragDropOperation Received. IsDropping set"); } } @@ -278,7 +279,7 @@ namespace MouseWithoutBorders internal static void DragDropStep10() { - LogDebug("DragDropStep10: Hide the form and get data..."); + Logger.LogDebug("DragDropStep10: Hide the form and get data..."); IsDropping = false; IsDragging = false; LastIDWithClipboardData = ID.NONE; @@ -294,7 +295,7 @@ namespace MouseWithoutBorders internal static void DragDropStep11() { - LogDebug("DragDropStep11: Mouse drag coming back, canceling drag/drop"); + Logger.LogDebug("DragDropStep11: Mouse drag coming back, canceling drag/drop"); SendClipboardBeatDragDropEnd(); IsDropping = false; IsDragging = false; @@ -306,7 +307,7 @@ namespace MouseWithoutBorders internal static void DragDropStep12() { - LogDebug("DragDropStep12: ClipboardDragDropEnd received"); + Logger.LogDebug("DragDropStep12: ClipboardDragDropEnd received"); IsDropping = false; LastIDWithClipboardData = ID.NONE; @@ -374,7 +375,7 @@ namespace MouseWithoutBorders internal static void SendDropBegin() { - LogDebug("SendDropBegin..."); + Logger.LogDebug("SendDropBegin..."); SendPackage(dropMachineID, PackageType.ClipboardDragDropOperation); } diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Encryption.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Encryption.cs index d557a8223c..1293a4ef39 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.Encryption.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.Encryption.cs @@ -18,6 +18,8 @@ using System.Linq; using System.Security.Cryptography; using System.Threading.Tasks; +using MouseWithoutBorders.Core; + namespace MouseWithoutBorders { internal partial class Common @@ -25,7 +27,9 @@ namespace MouseWithoutBorders #pragma warning disable SYSLIB0021 private static AesCryptoServiceProvider symAl; #pragma warning restore SYSLIB0021 - private static string myKey; +#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter + internal static string myKey; +#pragma warning restore SA1307 private static uint magicNumber; private static Random ran = new(); // Used for non encryption related functionality. internal const int SymAlBlockSize = 16; @@ -106,7 +110,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); } } @@ -189,7 +193,7 @@ namespace MouseWithoutBorders hashValue = hash.ComputeHash(hashValue); } - Common.LogDebug(string.Format(CultureInfo.CurrentCulture, "magic: {0},{1},{2}", hashValue[0], hashValue[1], hashValue[^1])); + Logger.LogDebug(string.Format(CultureInfo.CurrentCulture, "magic: {0},{1},{2}", hashValue[0], hashValue[1], hashValue[^1])); hash.Clear(); return (uint)((hashValue[0] << 23) + (hashValue[1] << 16) + (hashValue[^1] << 8) + hashValue[2]); } diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Event.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Event.cs index 7674ba2296..e50e87b8f9 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.Event.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.Event.cs @@ -18,6 +18,7 @@ using System.Threading.Tasks; // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; +using MouseWithoutBorders.Core; using MouseWithoutBorders.Form; namespace MouseWithoutBorders @@ -26,8 +27,10 @@ namespace MouseWithoutBorders { private static readonly DATA KeybdPackage = new(); private static readonly DATA MousePackage = new(); - private static ulong inputEventCount; - private static ulong invalidPackageCount; +#pragma warning disable SA1307 // Accessible fields should begin with upper-case names + internal static ulong inputEventCount; + internal static ulong invalidPackageCount; +#pragma warning restore SA1307 internal static int MOVE_MOUSE_RELATIVE = 100000; internal static int XY_BY_PIXEL = 300000; @@ -73,7 +76,7 @@ namespace MouseWithoutBorders { HasSwitchedMachineSinceLastCopy = true; - Common.LogDebug(string.Format( + Logger.LogDebug(string.Format( CultureInfo.CurrentCulture, "***** Host Machine: newDesMachineIdEx set = [{0}]. Mouse is now at ({1},{2})", newDesMachineIdEx, @@ -117,7 +120,7 @@ namespace MouseWithoutBorders if (actualLastPos != Common.LastPos) { - Common.LogDebug($"Mouse cursor has moved unexpectedly: Expected: {Common.LastPos}, actual: {actualLastPos}."); + Logger.LogDebug($"Mouse cursor has moved unexpectedly: Expected: {Common.LastPos}, actual: {actualLastPos}."); Common.LastPos = actualLastPos; } } @@ -138,7 +141,7 @@ namespace MouseWithoutBorders } catch (Exception ex) { - Log(ex); + Logger.Log(ex); } } @@ -149,11 +152,11 @@ namespace MouseWithoutBorders internal static void PrepareToSwitchToMachine(ID newDesMachineID, Point desMachineXY) { - LogDebug($"PrepareToSwitchToMachine: newDesMachineID = {newDesMachineID}, desMachineXY = {desMachineXY}"); + Logger.LogDebug($"PrepareToSwitchToMachine: newDesMachineID = {newDesMachineID}, desMachineXY = {desMachineXY}"); if (((GetTick() - lastJump < 100) && (GetTick() - lastJump > 0)) || desMachineID == ID.ALL) { - LogDebug("PrepareToSwitchToMachine: lastJump"); + Logger.LogDebug("PrepareToSwitchToMachine: lastJump"); return; } @@ -163,7 +166,7 @@ namespace MouseWithoutBorders if (!IsConnectedTo(newDesMachineID)) {// Connection lost, cancel switching - LogDebug("No active connection found for " + newDesMachineName); + Logger.LogDebug("No active connection found for " + newDesMachineName); // ShowToolTip("No active connection found for [" + newDesMachineName + "]!", 500); } @@ -198,7 +201,7 @@ namespace MouseWithoutBorders // Change des machine if (desMachineID != newDesMachineID) { - LogDebug("MouseEvent: Switching to new machine:" + newDesMachineName); + Logger.LogDebug("MouseEvent: Switching to new machine:" + newDesMachineName); // Ask current machine to hide the Mouse cursor if (newDesMachineID != ID.ALL && desMachineID != MachineID) @@ -246,7 +249,7 @@ namespace MouseWithoutBorders PaintCount = 0; if (desMachineID != newDesMachineID) { - LogDebug("KeybdEvent: Switching to new machine..."); + Logger.LogDebug("KeybdEvent: Switching to new machine..."); DesMachineID = newDesMachineID; } @@ -265,7 +268,7 @@ namespace MouseWithoutBorders } catch (Exception ex) { - Log(ex); + Logger.Log(ex); } } } diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Helper.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Helper.cs index 12ee1810a8..ccbec57508 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.Helper.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.Helper.cs @@ -22,7 +22,7 @@ using System.Windows.Forms; // using Microsoft.Win32; using MouseWithoutBorders.Class; - +using MouseWithoutBorders.Core; using static System.Windows.Forms.Control; namespace MouseWithoutBorders @@ -61,7 +61,7 @@ namespace MouseWithoutBorders Process p = Process.GetCurrentProcess(); string procInfo = $"{p.PrivateMemorySize64 / 1024 / 1024}MB, {p.TotalProcessorTime}, {Environment.ProcessorCount}."; string threadStacks = $"{procInfo} {Thread.DumpThreadsStack()}"; - Common.TelemetryLogTrace(threadStacks, SeverityLevel.Error); + Logger.TelemetryLogTrace(threadStacks, SeverityLevel.Error); break; } @@ -95,7 +95,7 @@ namespace MouseWithoutBorders Common.SwitchLocation.Count--; // When we want to move mouse by pixels, we add 300k to x and y (search for XY_BY_PIXEL for other related code). - Common.LogDebug($"+++++ Moving mouse to {Common.SwitchLocation.X}, {Common.SwitchLocation.Y}"); + Logger.LogDebug($"+++++ Moving mouse to {Common.SwitchLocation.X}, {Common.SwitchLocation.Y}"); // MaxXY = 65535 so 100k is safe. if (Common.SwitchLocation.X > XY_BY_PIXEL - 100000 || Common.SwitchLocation.Y > XY_BY_PIXEL - 100000) @@ -119,16 +119,16 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); } signalHelperToExit = false; - LogDebug("^^^Helper Thread exiting...^^^"); + Logger.LogDebug("^^^Helper Thread exiting...^^^"); } internal static void MainFormDotEx(bool bCheckTS) { - LogDebug("***** MainFormDotEx:"); + Logger.LogDebug("***** MainFormDotEx:"); if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { @@ -178,7 +178,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); } } } @@ -188,7 +188,7 @@ namespace MouseWithoutBorders internal static void MainForm3Pixels() { - LogDebug("***** MainFormDotLarge:"); + Logger.LogDebug("***** MainFormDotLarge:"); DoSomethingInUIThread( () => @@ -254,7 +254,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); } } @@ -277,7 +277,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); _ = Common.SendMessageToHelper(SharedConst.QUIT_CMD, IntPtr.Zero, IntPtr.Zero); } @@ -291,7 +291,7 @@ namespace MouseWithoutBorders if (!Common.IpcChannelCreated) { - TelemetryLogTrace($"{nameof(Common.IpcChannelCreated)} = {Common.IpcChannelCreated}. {GetStackTrace(new StackTrace())}", SeverityLevel.Warning); + Logger.TelemetryLogTrace($"{nameof(Common.IpcChannelCreated)} = {Common.IpcChannelCreated}. {Logger.GetStackTrace(new StackTrace())}", SeverityLevel.Warning); return; } @@ -317,12 +317,12 @@ namespace MouseWithoutBorders var processes = Process.GetProcessesByName(HelperProcessName); if (processes?.Length == 0) { - Log("Unable to start helper process."); + Logger.Log("Unable to start helper process."); Common.ShowToolTip("Error starting Mouse Without Borders Helper, clipboard sharing will not work!", 5000, ToolTipIcon.Error); } else { - Log("Helper process started."); + Logger.Log("Helper process started."); } } else @@ -330,11 +330,11 @@ namespace MouseWithoutBorders var processes = Process.GetProcessesByName(HelperProcessName); if (processes?.Length > 0) { - Log("Helper process found running."); + Logger.Log("Helper process found running."); } else { - Log("Invalid helper process found running."); + Logger.Log("Invalid helper process found running."); Common.ShowToolTip("Error finding Mouse Without Borders Helper, clipboard sharing will not work!", 5000, ToolTipIcon.Error); } } @@ -354,7 +354,7 @@ namespace MouseWithoutBorders if (log) { - Common.LogDebug($"SendMessageToHelper: HelperWindow={h}, Return={rv}, msg={msg}, w={wparam.ToInt32()}, l={lparam.ToInt32()}, Post={!wait}"); + Logger.LogDebug($"SendMessageToHelper: HelperWindow={h}, Return={rv}, msg={msg}, w={wparam.ToInt32()}, l={lparam.ToInt32()}, Post={!wait}"); } return rv; @@ -424,7 +424,7 @@ namespace MouseWithoutBorders log += "Last 10 trace messages:\r\n"; - log += string.Join(Environment.NewLine, LogCounter.Select(item => $"({item.Value}): {item.Key}").Take(10)); + log += string.Join(Environment.NewLine, Logger.LogCounter.Select(item => $"({item.Value}): {item.Key}").Take(10)); log += "\r\n============================================================================================================================="; @@ -447,7 +447,7 @@ namespace MouseWithoutBorders Setting.Values.Username = Program.User; } - Common.LogDebug("[Username] = " + Setting.Values.Username); + Logger.LogDebug("[Username] = " + Setting.Values.Username); } return !string.IsNullOrEmpty(Setting.Values.Username); @@ -490,7 +490,7 @@ Please use the keyboard and Mouse from the SAW device. } catch (Exception e) { - Log(e); + Logger.Log(e); } } } diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.InitAndCleanup.cs b/src/modules/MouseWithoutBorders/App/Class/Common.InitAndCleanup.cs index 2c7f271b61..da52a60db2 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.InitAndCleanup.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.InitAndCleanup.cs @@ -19,6 +19,7 @@ using System.Threading; // using Microsoft.Win32; using MouseWithoutBorders.Class; +using MouseWithoutBorders.Core; using MouseWithoutBorders.Form; using Windows.UI.Input.Preview.Injection; @@ -61,7 +62,7 @@ namespace MouseWithoutBorders } catch (Exception ex) { - Common.Log(ex); + Logger.Log(ex); Common.MachinePool.Clear(); } } @@ -84,7 +85,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); } } @@ -102,13 +103,13 @@ namespace MouseWithoutBorders { Common.KeyCorrupted = true; Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey(); - Common.Log(e.Message); + Logger.Log(e.Message); } catch (CryptographicException e) { Common.KeyCorrupted = true; Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey(); - Common.Log(e.Message); + Logger.Log(e.Message); } try @@ -123,7 +124,7 @@ namespace MouseWithoutBorders catch (EntryPointNotFoundException) { NativeMethods.InjectMouseInputAvailable = false; - Common.Log($"{nameof(NativeMethods.InjectMouseInputAvailable)} = false"); + Logger.Log($"{nameof(NativeMethods.InjectMouseInputAvailable)} = false"); } bool dummy = Setting.Values.DrawMouseEx; @@ -149,7 +150,7 @@ namespace MouseWithoutBorders if (e.Mode is PowerModes.Resume or PowerModes.Suspend) { - Common.TelemetryLogTrace($"{nameof(SystemEvents_PowerModeChanged)}: {e.Mode}", SeverityLevel.Information); + Logger.TelemetryLogTrace($"{nameof(SystemEvents_PowerModeChanged)}: {e.Mode}", SeverityLevel.Information); LastResumeSuspendTime = DateTime.UtcNow; SwitchToMultipleMode(false, true); } @@ -206,7 +207,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); } } @@ -230,13 +231,13 @@ namespace MouseWithoutBorders VK.RCONTROL, VK.RMENU, VK.RWIN, VK.SHIFT, VK.MENU, VK.CONTROL, }; - LogDebug("***** ReleaseAllKeys has been called! *****:"); + Logger.LogDebug("***** ReleaseAllKeys has been called! *****:"); foreach (VK vk in keys) { if ((NativeMethods.GetAsyncKeyState((IntPtr)vk) & 0x8000) != 0) { - LogDebug(vk.ToString() + " is down, release it..."); + Logger.LogDebug(vk.ToString() + " is down, release it..."); Hook?.ResetLastSwitchKeys(); // Sticky key can turn ALL PC mode on (CtrlCtrlCtrl) kd.wVk = (int)vk; InputSimulation.SendKey(kd); @@ -247,7 +248,7 @@ namespace MouseWithoutBorders private static void NetworkChange_NetworkAvailabilityChanged(object sender, NetworkAvailabilityEventArgs e) { - LogDebug("NetworkAvailabilityEventArgs.IsAvailable: " + e.IsAvailable.ToString(CultureInfo.InvariantCulture)); + Logger.LogDebug("NetworkAvailabilityEventArgs.IsAvailable: " + e.IsAvailable.ToString(CultureInfo.InvariantCulture)); Common.WndProcCounter++; ScheduleReopenSocketsDueToNetworkChanges(!e.IsAvailable); } diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Launch.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Launch.cs index 80ffe9fd84..5704e6119d 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.Launch.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.Launch.cs @@ -19,6 +19,7 @@ using System.Security.Principal; // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; +using MouseWithoutBorders.Core; namespace MouseWithoutBorders { @@ -44,17 +45,17 @@ namespace MouseWithoutBorders { dwSessionId = (uint)Process.GetCurrentProcess().SessionId; uint rv = NativeMethods.WTSQueryUserToken(dwSessionId, ref hUserToken); - LogDebug("WTSQueryUserToken returned " + rv.ToString(CultureInfo.CurrentCulture)); + Logger.LogDebug("WTSQueryUserToken returned " + rv.ToString(CultureInfo.CurrentCulture)); if (rv == 0) { - LogDebug($"WTSQueryUserToken failed with: {Marshal.GetLastWin32Error()}."); + Logger.LogDebug($"WTSQueryUserToken failed with: {Marshal.GetLastWin32Error()}."); return false; } if (!NativeMethods.DuplicateToken(hUserToken, (int)NativeMethods.SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, ref hUserTokenDup)) { - TelemetryLogTrace($"DuplicateToken Failed! {GetStackTrace(new StackTrace())}", SeverityLevel.Warning); + Logger.TelemetryLogTrace($"DuplicateToken Failed! {Logger.GetStackTrace(new StackTrace())}", SeverityLevel.Warning); _ = NativeMethods.CloseHandle(hUserToken); _ = NativeMethods.CloseHandle(hUserTokenDup); return false; @@ -70,7 +71,7 @@ namespace MouseWithoutBorders } else { - Log("ImpersonateLoggedOnUser Failed!"); + Logger.Log("ImpersonateLoggedOnUser Failed!"); _ = NativeMethods.CloseHandle(hUserToken); _ = NativeMethods.CloseHandle(hUserTokenDup); return false; @@ -78,7 +79,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Common.Log(e); + Logger.Log(e); return false; } } @@ -102,7 +103,7 @@ namespace MouseWithoutBorders int dwSessionId; IntPtr hUserToken = IntPtr.Zero, hUserTokenDup = IntPtr.Zero; - Common.LogDebug("CreateProcessInInputDesktopSession called, launching " + commandLineWithArg + " on " + desktop); + Logger.LogDebug("CreateProcessInInputDesktopSession called, launching " + commandLineWithArg + " on " + desktop); try { @@ -122,7 +123,7 @@ namespace MouseWithoutBorders if (!NativeMethods.DuplicateTokenEx(hUserToken, NativeMethods.MAXIMUM_ALLOWED, ref sa, (int)NativeMethods.SECURITY_IMPERSONATION_LEVEL.SecurityIdentification, (int)NativeMethods.TOKEN_TYPE.TokenPrimary, ref hUserTokenDup)) { lastError = Marshal.GetLastWin32Error(); - Common.Log(string.Format(CultureInfo.CurrentCulture, "DuplicateTokenEx error: {0} Token does not have the privilege.", lastError)); + Logger.Log(string.Format(CultureInfo.CurrentCulture, "DuplicateTokenEx error: {0} Token does not have the privilege.", lastError)); _ = NativeMethods.CloseHandle(hUserToken); return 0; } @@ -138,7 +139,7 @@ namespace MouseWithoutBorders if (!rv) { - Log("ConvertStringSidToSid failed"); + Logger.Log("ConvertStringSidToSid failed"); _ = NativeMethods.CloseHandle(hUserToken); _ = NativeMethods.CloseHandle(hUserTokenDup); return 0; @@ -151,7 +152,7 @@ namespace MouseWithoutBorders if (!rv) { - Log("SetTokenInformation failed"); + Logger.Log("SetTokenInformation failed"); _ = NativeMethods.CloseHandle(hUserToken); _ = NativeMethods.CloseHandle(hUserTokenDup); return 0; @@ -185,7 +186,7 @@ namespace MouseWithoutBorders // GetLastError should be 0 int iResultOfCreateProcessAsUser = Marshal.GetLastWin32Error(); - LogDebug("CreateProcessAsUser returned " + iResultOfCreateProcessAsUser.ToString(CultureInfo.CurrentCulture)); + Logger.LogDebug("CreateProcessAsUser returned " + iResultOfCreateProcessAsUser.ToString(CultureInfo.CurrentCulture)); // Close handles task _ = NativeMethods.CloseHandle(hUserToken); @@ -195,7 +196,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Common.Log(e); + Logger.Log(e); return 0; } } @@ -223,19 +224,19 @@ namespace MouseWithoutBorders { if ((p = Process.GetProcessById(processId)) == null) { - Log("Process exited!"); + Logger.Log("Process exited!"); break; } } catch (ArgumentException) { - Log("GetProcessById.ArgumentException"); + Logger.Log("GetProcessById.ArgumentException"); break; } if ((!p.HasExited && p.PrivateMemorySize64 > limitedMem) || (++sec > (wait / 1000))) { - Log(string.Format(CultureInfo.CurrentCulture, "Process log (mem): {0}, {1}", sec, p.PrivateMemorySize64)); + Logger.Log(string.Format(CultureInfo.CurrentCulture, "Process log (mem): {0}, {1}", sec, p.PrivateMemorySize64)); return false; } @@ -248,11 +249,11 @@ namespace MouseWithoutBorders if ((p = Process.GetProcessById(processId)) == null) { - Log("Process exited!"); + Logger.Log("Process exited!"); } else if (NativeMethods.WaitForSingleObject(p.Handle, wait) != NativeMethods.WAIT_OBJECT_0 && killIfTimedOut) { - Log("Process log (time)."); + Logger.Log("Process log (time)."); TerminateProcessTree(p.Handle, (uint)processId, -1); return false; } @@ -287,12 +288,12 @@ namespace MouseWithoutBorders } catch (InvalidOperationException e) { - Log(e); + Logger.Log(e); continue; } catch (Win32Exception e) { - Log(e); + Logger.Log(e); continue; } } diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Log.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Log.cs index 02e65d3891..df7518fdb5 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.Log.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.Log.cs @@ -25,6 +25,7 @@ using System.Windows.Forms; // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; +using MouseWithoutBorders.Core; using MouseWithoutBorders.Exceptions; namespace MouseWithoutBorders @@ -81,7 +82,7 @@ namespace MouseWithoutBorders internal static string DumpThreadsStack() { string stack = "\r\nMANAGED THREADS: " + threads.Count.ToString(CultureInfo.InvariantCulture) + "\r\n"; - stack += Common.GetStackTrace(new StackTrace()); + stack += Logger.GetStackTrace(new StackTrace()); return stack; } @@ -115,385 +116,4 @@ namespace MouseWithoutBorders internal System.Threading.ThreadState ThreadState => thread.ThreadState; } - - internal partial class Common - { - private static readonly string[] AllLogs = new string[MAX_LOG]; - private static readonly object AllLogsLock = new(); - private static readonly ConcurrentDictionary LogCounter = new(); - private static readonly int[] RepeatedLogIndexSelection = new[] { 1, 3, 10, 50, 100 }; - private const int MAX_LOG = 10000; - private static int allLogsIndex; - - private const int MaxLogExceptionPerHour = 1000; - private static int lastHour; - private static int exceptionCount; - - internal static void TelemetryLogTrace(string log, SeverityLevel severityLevel, bool flush = false) - { - int logCount = LogCounter.AddOrUpdate(log, 1, (key, value) => value + 1); - Common.Log(log); - } - - internal static void Log(Exception e) - { - if (e is not KnownException) - { - string exText = e.ToString(); - - Log($"!Exception!: {exText}"); - - if (DateTime.UtcNow.Hour != lastHour) - { - lastHour = DateTime.UtcNow.Hour; - exceptionCount = 0; - } - - if (exceptionCount < MaxLogExceptionPerHour) - { - exceptionCount++; - } - else if (exceptionCount != short.MaxValue) - { - exceptionCount = short.MaxValue; - } - } - } - - private const string HeaderSENT = - "Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},Ie{12},Ni{13}"; - - private const string HeaderRECEIVED = - "Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},In{12},Ni{13},Pc{14}/{15}"; - - internal static void LogDebug(string log, bool clearLog = false) - { -#if DEBUG - Log(log, clearLog); -#endif - } - - internal static void Log(string log, bool clearLog = false) - { - log = DateTime.Now.ToString("MM/dd HH:mm:ss.fff", CultureInfo.InvariantCulture) + $"({Thread.CurrentThread.ManagedThreadId})" + log; - - ManagedCommon.Logger.LogInfo(log); - lock (AllLogsLock) - { - if (clearLog) - { - allLogsIndex = 0; - } - - AllLogs[allLogsIndex] = log; - allLogsIndex = (allLogsIndex + 1) % MAX_LOG; - } - } - - internal static void LogDebug(string format, params object[] args) - { -#if DEBUG - Log(format, args); -#endif - } - - internal static void Log(string format, params object[] args) - { - Common.Log(string.Format(CultureInfo.InvariantCulture, format, args)); - } - - private static PackageMonitor p1; - private static PackageMonitor p2; - - [Conditional("DEBUG")] - internal static void LogAll() - { - string log; - - if (!p1.Equals(PackageSent)) - { - log = string.Format( - CultureInfo.CurrentCulture, - "SENT:" + HeaderSENT, - PackageSent.Heartbeat, - PackageSent.Keyboard, - PackageSent.Mouse, - PackageSent.Hello, - PackageSent.Matrix, - PackageSent.ClipboardText, - PackageSent.ClipboardImage, - PackageSent.ByeBye, - PackageSent.Clipboard, - PackageSent.ClipboardDragDrop, - PackageSent.ClipboardDragDropEnd, - PackageSent.ExplorerDragDrop, - inputEventCount, - PackageSent.Nil); - Log(log); - p1 = PackageSent; // Copy data - } - - if (!p2.Equals(PackageReceived)) - { - log = string.Format( - CultureInfo.CurrentCulture, - "RECEIVED:" + HeaderRECEIVED, - PackageReceived.Heartbeat, - PackageReceived.Keyboard, - PackageReceived.Mouse, - PackageReceived.Hello, - PackageReceived.Matrix, - PackageReceived.ClipboardText, - PackageReceived.ClipboardImage, - PackageReceived.ByeBye, - PackageReceived.Clipboard, - PackageReceived.ClipboardDragDrop, - PackageReceived.ClipboardDragDropEnd, - PackageReceived.ExplorerDragDrop, - invalidPackageCount, - PackageReceived.Nil, - processedPackageCount, - skippedPackageCount); - Log(log); - p2 = PackageReceived; - } - } - - internal static void GenerateLog() - { - int l = Setting.Values.DumpObjectsLevel; - if (l is > 0 and < 10) - { - Common.DumpObjects(l); - } - } - - private static List myThreads; - - internal static void DumpObjects(int level) - { - try - { - string logFile = Path.Combine(Common.RunWithNoAdminRight ? Path.GetTempPath() : Path.GetDirectoryName(Application.ExecutablePath), "MagicMouse.log"); - - StringBuilder sb = new(1000000); - string log; - - myThreads = new List(); - - foreach (ProcessThread t in Process.GetCurrentProcess().Threads) - { - myThreads.Add(t); - } - - _ = PrivateDump(sb, AllLogs, "[Program logs]\r\n===============\r\n", 0, level, false); - _ = PrivateDump(sb, new Common(), "[Other Logs]\r\n===============\r\n", 0, level, false); - - log = string.Format( - CultureInfo.CurrentCulture, - "{0} {1}\r\n{2}\r\n\r\n{3}", - Application.ProductName, - Application.ProductVersion, - "Private Mem: " + (Process.GetCurrentProcess().PrivateMemorySize64 / 1024).ToString(CultureInfo.CurrentCulture) + "KB", - sb.ToString()); - - if (!string.IsNullOrEmpty(myKey)) - { - log = log.Replace(MyKey, GetDebugInfo(MyKey)); - } - - log += Thread.DumpThreadsStack(); - log += $"\r\nCurrent process session: {Process.GetCurrentProcess().SessionId}, active console session: {NativeMethods.WTSGetActiveConsoleSessionId()}."; - - File.WriteAllText(logFile, log); - - if (RunOnLogonDesktop || RunOnScrSaverDesktop) - { - _ = MessageBox.Show("Dump file created: " + logFile, Application.ProductName); - } - else - { - ShowToolTip("Dump file created: " + logFile + " and placed in the Clipboard.", 10000); - Clipboard.SetText(logFile); - } - } - catch (Exception e) - { - _ = MessageBox.Show(e.Message + "\r\n" + e.StackTrace, Application.ProductName); - } - } - - private static object GetFieldValue(object obj, string fieldName) - { - FieldInfo fi; - Type t; - - t = obj.GetType(); - fi = t.GetField(fieldName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); - return fi?.GetValue(obj); - } - - private static bool PrivateDump(StringBuilder sb, object obj, string objName, int level, int maxLevel, bool stop) - { - Type t; - string padStr = string.Empty; - string[] strArr; - string objString; - - if (obj == null || (maxLevel >= 0 && level >= maxLevel) || obj is Cursor) - { - return false; - } - - for (int i = 0; i < level; i++) - { - padStr += i < level - 1 ? "-" : padStr += string.Empty; - } - - objString = obj.ToString(); - t = obj.GetType(); - strArr = new string[7]; - strArr[0] = padStr; - strArr[1] = objName; - - // strArr[2] = " "; - // strArr[3] = t.FullName; - strArr[4] = " = "; - strArr[5] = objName.Equals("myKey", StringComparison.OrdinalIgnoreCase) ? GetDebugInfo(objString) - : objName.Equals("lastClipboardObject", StringComparison.OrdinalIgnoreCase) ? string.Empty - : objString.Replace("System.Windows.Forms.", string.Empty).Replace("System.Net.Sockets.", string.Empty).Replace("System.Security.Cryptography.", string.Empty).Replace("System.Threading.", string.Empty) - .Replace("System.ComponentModel.", string.Empty).Replace("System.Runtime.", string.Empty).Replace("System.Drawing.", string.Empty).Replace("System.Object", "O").Replace("System.Diagnostics.", string.Empty) - .Replace("System.Collections.", string.Empty).Replace("System.Drawing.", string.Empty).Replace("System.Int", string.Empty).Replace("System.EventHandler.", string.Empty); - strArr[6] = "\r\n"; - _ = sb.Append(string.Concat(strArr).Replace(Common.BinaryName, "MM")); - - if (stop || t.IsPrimitive) - { - return false; - } - - DumpType(padStr, sb, obj, level, t, maxLevel); - return true; - } - - private static void DumpType(string initialStr, StringBuilder sb, object obj, int level, System.Type t, int maxLevel) - { - int i; - bool stop; - if (t == typeof(System.Delegate)) - { - return; - } - - FieldInfo[] fi; - string type; - - if (obj is MouseWithoutBorders.PackageType or string or AddressFamily or ID or IPAddress - ) - { - return; - } - - type = obj.GetType().ToString(); - - if (type.EndsWith("type", StringComparison.CurrentCultureIgnoreCase) || type.Contains("Cryptography") - || type.EndsWith("AsyncEventBits", StringComparison.CurrentCultureIgnoreCase)) - { - return; - } - - stop = obj == null || obj is MouseWithoutBorders.DATA || obj.GetType().BaseType == typeof(ValueType) - || obj.GetType().Namespace.Contains("System.Windows"); - fi = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); - - foreach (FieldInfo f in fi) - { - if (f.GetValue(obj) != AllLogs) - { - _ = PrivateDump(sb, f.GetValue(obj), f.Name, level + 1, maxLevel, stop); - } - } - - if (obj is Dictionary>) - { - Dictionary> d = obj as Dictionary>; - - foreach (string k in d.Keys) - { - if (d.TryGetValue(k, out List l)) - { - foreach (IPAddress ip in l) - { - _ = PrivateDump(sb, ip, "[" + k + "]", level + 1, maxLevel, false); - } - } - } - } - - if (obj is System.Array) - { - try - { - if (obj is MachineInf[]) - { - MachineInf[] os = (MachineInf[])obj; - - for (i = 0; i < os.GetLength(0); i++) - { - _ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false); - } - } - else if (obj is int[] || obj is uint[]) - { - int[] os = (int[])obj; - - for (i = 0; i < os.GetLength(0); i++) - { - _ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false); - } - } - else if (obj is short[] || obj is ushort[]) - { - short[] os = (short[])obj; - - for (i = 0; i < os.GetLength(0); i++) - { - _ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false); - } - } - else if (obj is TcpClient[] || obj is IPAddress[] || obj is TcpSk[] || obj is string[] - || obj is TcpServer[] - || obj is ProcessThread[] || obj is Thread[]) - { - object[] os = (object[])obj; - - for (i = 0; i < os.GetLength(0); i++) - { - _ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false); - } - } - else - { - _ = PrivateDump(sb, obj.GetType().ToString() + ": N/A", obj.GetType().ToString(), level + 1, maxLevel, false); - } - } - catch (Exception) - { - } - } - } - - internal static string GetStackTrace(StackTrace st) - { - string rv = string.Empty; - - for (int i = 0; i < st.FrameCount; i++) - { - StackFrame sf = st.GetFrame(i); - rv += sf.GetMethod() + " <= "; - } - - return rv; - } - } } diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.MachineStuff.cs b/src/modules/MouseWithoutBorders/App/Class/Common.MachineStuff.cs index d2f79e931f..2088fa0b8f 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.MachineStuff.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.MachineStuff.cs @@ -22,6 +22,7 @@ using Microsoft.PowerToys.Telemetry; // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; +using MouseWithoutBorders.Core; namespace MouseWithoutBorders { @@ -390,7 +391,7 @@ namespace MouseWithoutBorders // THIS LOGIC IS THE SAME FOR Move*(int x, int y) METHODS. if (newDesMachineIdEx != desMachineID) { - LogDebug("Move Right"); + Logger.LogDebug("Move Right"); if (!Setting.Values.MoveMouseRelatively) { @@ -529,7 +530,7 @@ namespace MouseWithoutBorders if (newDesMachineIdEx != desMachineID) { - LogDebug("Move Left"); + Logger.LogDebug("Move Left"); return !Setting.Values.MoveMouseRelatively ? newDesMachineIdEx == MachineID @@ -598,7 +599,7 @@ namespace MouseWithoutBorders if (newDesMachineIdEx != desMachineID) { - LogDebug("Move Up"); + Logger.LogDebug("Move Up"); return !Setting.Values.MoveMouseRelatively ? newDesMachineIdEx == MachineID @@ -668,7 +669,7 @@ namespace MouseWithoutBorders if (newDesMachineIdEx != desMachineID) { - LogDebug("Move Down"); + Logger.LogDebug("Move Down"); return !Setting.Values.MoveMouseRelatively ? newDesMachineIdEx == MachineID @@ -696,7 +697,7 @@ namespace MouseWithoutBorders rv = true; } - LogDebug("<><><><><>>><><><<><><><><><><><><><><>><><><><><><><><><><><" + inf.Name); + Logger.LogDebug("<><><><><>>><><><<><><><><><><><><><><>><><><><><><><><><><><" + inf.Name); } } @@ -740,7 +741,7 @@ namespace MouseWithoutBorders if (machineInfo.Name.Equals(DesMachineName, StringComparison.OrdinalIgnoreCase)) { - LogDebug("AddToMachinePool: Des ID updated: " + DesMachineID.ToString() + "/" + package.Src.ToString()); + Logger.LogDebug("AddToMachinePool: Des ID updated: " + DesMachineID.ToString() + "/" + package.Src.ToString()); newDesMachineID = desMachineID = package.Src; } @@ -754,7 +755,7 @@ namespace MouseWithoutBorders } else { - LogDebug("AddToMachinePool: could not add a new machine: " + name); + Logger.LogDebug("AddToMachinePool: could not add a new machine: " + name); return "The 5th machine"; } } @@ -781,14 +782,14 @@ namespace MouseWithoutBorders Common.ReopenSockets(true); Common.SendMachineMatrix(); - LogDebug("Machine added: " + name + "/" + package.Src.ToString()); + Logger.LogDebug("Machine added: " + name + "/" + package.Src.ToString()); UpdateClientSockets("AddToMachinePool"); return name; } internal static void UpdateClientSockets(string logHeader) { - LogDebug("UpdateClientSockets: " + logHeader); + Logger.LogDebug("UpdateClientSockets: " + logHeader); Sk?.UpdateTCPClients(); } @@ -802,13 +803,13 @@ namespace MouseWithoutBorders internal static void ShowSetupForm(bool reopenSockets = false) { - Common.LogDebug("========== BEGIN THE SETUP EXPERIENCE ==========", true); + Logger.LogDebug("========== BEGIN THE SETUP EXPERIENCE ==========", true); Setting.Values.MyKey = Common.MyKey = Common.CreateRandomKey(); Common.GeneratedKey = true; if (Process.GetCurrentProcess().SessionId != NativeMethods.WTSGetActiveConsoleSessionId()) { - Common.Log("Not physical console session."); + Logger.Log("Not physical console session."); _ = MessageBox.Show( "Please run the program in the physical console session.\r\nThe program does not work in a remote desktop or virtual machine session.", Application.ProductName, @@ -948,7 +949,7 @@ namespace MouseWithoutBorders { bool twoRow = !Setting.Values.MatrixOneRow; string[] connectedMachines = twoRow ? MachineMatrix : MachineMatrix.Select(m => IsConnectedTo(IdFromName(m)) ? m : string.Empty).ToArray(); - LogDebug($"Matrix: {string.Join(",", MachineMatrix)}, Connected: {string.Join(",", connectedMachines)}"); + Logger.LogDebug($"Matrix: {string.Join(",", MachineMatrix)}, Connected: {string.Join(",", connectedMachines)}"); return connectedMachines; } @@ -981,7 +982,7 @@ namespace MouseWithoutBorders SkSend(package, null, false); - LogDebug($"matrixIncludedMachine sent: [{i + 1}]:[{MachineMatrix[i]}]"); + Logger.LogDebug($"matrixIncludedMachine sent: [{i + 1}]:[{MachineMatrix[i]}]"); } } @@ -992,7 +993,7 @@ namespace MouseWithoutBorders if (i is > 0 and <= MAX_MACHINE) { - LogDebug($"matrixIncludedMachine: [{i}]:[{matrixIncludedMachine}]"); + Logger.LogDebug($"matrixIncludedMachine: [{i}]:[{matrixIncludedMachine}]"); MachineMatrix[i - 1] = matrixIncludedMachine; @@ -1011,7 +1012,7 @@ namespace MouseWithoutBorders } else { - LogDebug("Invalid machine Matrix package!"); + Logger.LogDebug("Invalid machine Matrix package!"); } } @@ -1080,7 +1081,7 @@ namespace MouseWithoutBorders if (!created) { - TelemetryLogTrace($"Second instance found: {eventName}.", SeverityLevel.Warning, true); + Logger.TelemetryLogTrace($"Second instance found: {eventName}.", SeverityLevel.Warning, true); CurrentProcess.KillProcess(true); } } diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Receiver.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Receiver.cs index b4d2af42f4..5b17801450 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.Receiver.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.Receiver.cs @@ -20,6 +20,7 @@ using System.Windows.Forms; // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; +using MouseWithoutBorders.Core; [module: SuppressMessage("Microsoft.Reliability", "CA2002:DoNotLockOnObjectsWithWeakIdentity", Scope = "member", Target = "MouseWithoutBorders.Common.#PreProcess(MouseWithoutBorders.DATA)", Justification = "Dotnet port with style preservation")] @@ -30,8 +31,10 @@ namespace MouseWithoutBorders private static readonly uint QUEUE_SIZE = 50; private static readonly int[] RecentProcessedPackageIDs = new int[QUEUE_SIZE]; private static int recentProcessedPackageIndex; - private static long processedPackageCount; - private static long skippedPackageCount; +#pragma warning disable SA1307 // Accessible fields should begin with upper-case letter + internal static long processedPackageCount; + internal static long skippedPackageCount; +#pragma warning restore SA1307 internal static long JustGotAKey { get; set; } @@ -45,12 +48,12 @@ namespace MouseWithoutBorders } Common.InvalidPackageCount++; - Common.Log("Invalid packages received!"); + Logger.Log("Invalid packages received!"); return false; } else if (package.Type == 0) { - Common.Log("Got an unknown package!"); + Logger.Log("Got an unknown package!"); return false; } else if (package.Type is not PackageType.ClipboardText and not PackageType.ClipboardImage @@ -157,7 +160,7 @@ namespace MouseWithoutBorders { HasSwitchedMachineSinceLastCopy = true; - Common.LogDebug(string.Format( + Logger.LogDebug(string.Format( CultureInfo.CurrentCulture, "***** Controlled Machine: newDesMachineIdEx set = [{0}]. Mouse is now at ({1},{2})", newDesMachineIdEx, @@ -191,7 +194,7 @@ namespace MouseWithoutBorders break; case PackageType.NextMachine: - LogDebug("PackageType.NextMachine received!"); + Logger.LogDebug("PackageType.NextMachine received!"); if (IsSwitchingByMouseEnabled()) { @@ -334,7 +337,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); } }); } @@ -395,7 +398,7 @@ namespace MouseWithoutBorders else { // We should never get to this point! - Common.Log("Invalid package received!"); + Logger.Log("Invalid package received!"); return; } } @@ -421,7 +424,7 @@ namespace MouseWithoutBorders private static void SignalBigClipboardData() { - LogDebug("SignalBigClipboardData"); + Logger.LogDebug("SignalBigClipboardData"); SetToggleIcon(new int[TOGGLE_ICONS_SIZE] { ICON_BIG_CLIPBOARD, -1, ICON_BIG_CLIPBOARD, -1 }); } } diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.Service.cs b/src/modules/MouseWithoutBorders/App/Class/Common.Service.cs index 1b65270fd0..a5a71baf5c 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.Service.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.Service.cs @@ -21,6 +21,7 @@ using System.Windows.Forms; // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; +using MouseWithoutBorders.Core; [module: SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Scope = "member", Target = "MouseWithoutBorders.Common.#StartMouseWithoutBordersService()", Justification = "Dotnet port with style preservation")] @@ -39,7 +40,7 @@ namespace MouseWithoutBorders return; } - Log($"{nameof(StartMouseWithoutBordersService)}: {GetStackTrace(new StackTrace())}."); + Logger.Log($"{nameof(StartMouseWithoutBordersService)}: {Logger.GetStackTrace(new StackTrace())}."); Task task = Task.Run(() => { @@ -49,13 +50,13 @@ namespace MouseWithoutBorders { if (DateTime.UtcNow - lastStartServiceTime < TimeSpan.FromSeconds(5)) { - Log($"{nameof(StartMouseWithoutBordersService)}: Aborted."); + Logger.Log($"{nameof(StartMouseWithoutBordersService)}: Aborted."); return; } foreach (Process pp in ps) { - Common.Log(string.Format(CultureInfo.InvariantCulture, "Killing process MouseWithoutBordersSvc {0}.", pp.Id)); + Logger.Log(string.Format(CultureInfo.InvariantCulture, "Killing process MouseWithoutBordersSvc {0}.", pp.Id)); pp.KillProcess(); } } @@ -65,7 +66,7 @@ namespace MouseWithoutBorders try { - Log("Starting " + service.ServiceName); + Logger.Log("Starting " + service.ServiceName); } catch (Exception) { @@ -104,7 +105,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); // ERROR_SERVICE_ALREADY_RUNNING if (!(shownErrMessage || ((e?.InnerException as Win32Exception)?.NativeErrorCode == 1056))) @@ -153,7 +154,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Common.Log($"{nameof(StartServiceAndSendLogoffSignal)}: {e.Message}"); + Logger.Log($"{nameof(StartServiceAndSendLogoffSignal)}: {e.Message}"); } } } diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.ShutdownWithPowerToys.cs b/src/modules/MouseWithoutBorders/App/Class/Common.ShutdownWithPowerToys.cs index 96b605c5b1..8a562e3b70 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.ShutdownWithPowerToys.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.ShutdownWithPowerToys.cs @@ -7,6 +7,8 @@ using System.Diagnostics; using ManagedCommon; +using Logger = MouseWithoutBorders.Core.Logger; + namespace MouseWithoutBorders { internal class ShutdownWithPowerToys @@ -22,7 +24,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } } } diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.WinAPI.cs b/src/modules/MouseWithoutBorders/App/Class/Common.WinAPI.cs index 775945cc34..722590ef81 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.WinAPI.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.WinAPI.cs @@ -21,6 +21,7 @@ using System.Windows.Forms; // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; +using MouseWithoutBorders.Core; namespace MouseWithoutBorders { @@ -52,19 +53,19 @@ namespace MouseWithoutBorders { // For logging only _ = NativeMethods.GetDpiForMonitor(hMonitor, 0, out uint dpiX, out uint dpiY); - Log(string.Format(CultureInfo.CurrentCulture, "MONITOR: ({0}, {1}, {2}, {3}). DPI: ({4}, {5})", mi.rcMonitor.Left, mi.rcMonitor.Top, mi.rcMonitor.Right, mi.rcMonitor.Bottom, dpiX, dpiY)); + Logger.Log(string.Format(CultureInfo.CurrentCulture, "MONITOR: ({0}, {1}, {2}, {3}). DPI: ({4}, {5})", mi.rcMonitor.Left, mi.rcMonitor.Top, mi.rcMonitor.Right, mi.rcMonitor.Bottom, dpiX, dpiY)); } catch (DllNotFoundException) { - Common.Log("GetDpiForMonitor is unsupported in Windows 7 and lower."); + Logger.Log("GetDpiForMonitor is unsupported in Windows 7 and lower."); } catch (EntryPointNotFoundException) { - Common.Log("GetDpiForMonitor is unsupported in Windows 7 and lower."); + Logger.Log("GetDpiForMonitor is unsupported in Windows 7 and lower."); } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } if (mi.rcMonitor.Left == 0 && mi.rcMonitor.Top == 0 && mi.rcMonitor.Right != 0 && mi.rcMonitor.Bottom != 0) @@ -116,7 +117,7 @@ namespace MouseWithoutBorders { try { - Common.LogDebug("==================== GetScreenConfig started"); + Logger.LogDebug("==================== GetScreenConfig started"); newDesktopBounds = new MyRectangle(); newPrimaryScreenBounds = new MyRectangle(); newDesktopBounds.Left = newPrimaryScreenBounds.Left = Screen.PrimaryScreen.Bounds.Left; @@ -124,7 +125,7 @@ namespace MouseWithoutBorders newDesktopBounds.Right = newPrimaryScreenBounds.Right = Screen.PrimaryScreen.Bounds.Right; newDesktopBounds.Bottom = newPrimaryScreenBounds.Bottom = Screen.PrimaryScreen.Bounds.Bottom; - Common.Log(string.Format( + Logger.Log(string.Format( CultureInfo.CurrentCulture, "logon = {0} PrimaryScreenBounds = {1},{2},{3},{4} desktopBounds = {5},{6},{7},{8}", Common.RunOnLogonDesktop, @@ -162,7 +163,7 @@ namespace MouseWithoutBorders Interlocked.Exchange(ref desktopBounds, newDesktopBounds); Interlocked.Exchange(ref primaryScreenBounds, newPrimaryScreenBounds); - Common.Log(string.Format( + Logger.Log(string.Format( CultureInfo.CurrentCulture, "logon = {0} PrimaryScreenBounds = {1},{2},{3},{4} desktopBounds = {5},{6},{7},{8}", Common.RunOnLogonDesktop, @@ -175,11 +176,11 @@ namespace MouseWithoutBorders Common.DesktopBounds.Right, Common.DesktopBounds.Bottom)); - Common.Log("==================== GetScreenConfig ended"); + Logger.Log("==================== GetScreenConfig ended"); } catch (Exception e) { - Log(e); + Logger.Log(e); } } @@ -265,7 +266,7 @@ namespace MouseWithoutBorders { if (!Common.RunWithNoAdminRight) { - LogDebug("*** Starting on active Desktop: " + desktopToRunMouseWithoutBordersOn); + Logger.LogDebug("*** Starting on active Desktop: " + desktopToRunMouseWithoutBordersOn); StartMouseWithoutBordersService(desktopToRunMouseWithoutBordersOn); } } @@ -282,19 +283,19 @@ namespace MouseWithoutBorders while (NativeMethods.WTSGetActiveConsoleSessionId() == 0xFFFFFFFF && waitCount > 0) { waitCount--; - LogDebug("The session is detached/attached."); + Logger.LogDebug("The session is detached/attached."); Thread.Sleep(500); } string myDesktop = GetMyDesktop(); activeDesktop = GetInputDesktop(); - LogDebug("*** Active Desktop = " + activeDesktop); - LogDebug("*** My Desktop = " + myDesktop); + Logger.LogDebug("*** Active Desktop = " + activeDesktop); + Logger.LogDebug("*** My Desktop = " + myDesktop); if (myDesktop.Equals(activeDesktop, StringComparison.OrdinalIgnoreCase)) { - LogDebug("*** Active Desktop == My Desktop (TS session)"); + Logger.LogDebug("*** Active Desktop == My Desktop (TS session)"); } if (!activeDesktop.Equals("winlogon", StringComparison.OrdinalIgnoreCase) && @@ -307,25 +308,25 @@ namespace MouseWithoutBorders } catch (Exception e) { - Common.Log($"{nameof(CheckForDesktopSwitchEvent)}: {e}"); + Logger.Log($"{nameof(CheckForDesktopSwitchEvent)}: {e}"); } } else { if (!myDesktop.Equals(activeDesktop, StringComparison.OrdinalIgnoreCase)) { - Log("*** Active Desktop <> My Desktop"); + Logger.Log("*** Active Desktop <> My Desktop"); } uint sid = NativeMethods.WTSGetActiveConsoleSessionId(); if (Process.GetProcessesByName(Common.BinaryName).Any(p => (uint)p.SessionId == sid)) { - Log("Found MouseWithoutBorders on the active session!"); + Logger.Log("Found MouseWithoutBorders on the active session!"); } else { - Log("MouseWithoutBorders not found on the active session!"); + Logger.Log("MouseWithoutBorders not found on the active session!"); StartMMService(null); } } @@ -333,7 +334,7 @@ namespace MouseWithoutBorders if (!myDesktop.Equals("winlogon", StringComparison.OrdinalIgnoreCase) && !myDesktop.Equals("default", StringComparison.OrdinalIgnoreCase)) { - LogDebug("*** Desktop inactive, exiting: " + myDesktop); + Logger.LogDebug("*** Desktop inactive, exiting: " + myDesktop); Setting.Values.LastX = JUST_GOT_BACK_FROM_SCREEN_SAVER; if (cleanupIfExit) { @@ -346,7 +347,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); } } diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.cs b/src/modules/MouseWithoutBorders/App/Class/Common.cs index 325f84ca7d..1caeb47b2c 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.cs @@ -29,6 +29,7 @@ using Microsoft.PowerToys.Settings.UI.Library; // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; +using MouseWithoutBorders.Core; using MouseWithoutBorders.Exceptions; // Log is enough @@ -78,7 +79,7 @@ namespace MouseWithoutBorders { internal partial class Common { - private Common() + internal Common() { } @@ -259,7 +260,7 @@ namespace MouseWithoutBorders { if (SocketMutex != null) { - LogDebug("SOCKET MUTEX BEGIN RELEASE."); + Logger.LogDebug("SOCKET MUTEX BEGIN RELEASE."); try { @@ -269,14 +270,14 @@ namespace MouseWithoutBorders catch (ApplicationException e) { // The current thread does not own the mutex, the thread acquired it will own it. - TelemetryLogTrace($"{nameof(ReleaseSocketMutex)}: {e.Message}. {Thread.CurrentThread.ManagedThreadId}/{UIThreadID}.", SeverityLevel.Warning); + Logger.TelemetryLogTrace($"{nameof(ReleaseSocketMutex)}: {e.Message}. {Thread.CurrentThread.ManagedThreadId}/{UIThreadID}.", SeverityLevel.Warning); } - LogDebug("SOCKET MUTEX RELEASED."); + Logger.LogDebug("SOCKET MUTEX RELEASED."); } else { - LogDebug("SOCKET MUTEX NULL."); + Logger.LogDebug("SOCKET MUTEX NULL."); } } @@ -284,7 +285,7 @@ namespace MouseWithoutBorders { if (SocketMutex != null) { - LogDebug("SOCKET MUTEX BEGIN WAIT."); + Logger.LogDebug("SOCKET MUTEX BEGIN WAIT."); int waitTimeout = 60000; // TcpListener.Stop may take very long to complete for some reason. int socketMutexBalance = int.MinValue; @@ -302,14 +303,14 @@ namespace MouseWithoutBorders if (!acquireMutex) { Process[] ps = Process.GetProcessesByName(Common.BinaryName); - TelemetryLogTrace($"Balance: {socketMutexBalance}, Active: {IsMyDesktopActive()}, Sid/Console: {Process.GetCurrentProcess().SessionId}/{NativeMethods.WTSGetActiveConsoleSessionId()}, Desktop/Input: {GetMyDesktop()}/{GetInputDesktop()}, count: {ps?.Length}.", SeverityLevel.Warning); + Logger.TelemetryLogTrace($"Balance: {socketMutexBalance}, Active: {IsMyDesktopActive()}, Sid/Console: {Process.GetCurrentProcess().SessionId}/{NativeMethods.WTSGetActiveConsoleSessionId()}, Desktop/Input: {GetMyDesktop()}/{GetInputDesktop()}, count: {ps?.Length}.", SeverityLevel.Warning); } - LogDebug("SOCKET MUTEX ENDED."); + Logger.LogDebug("SOCKET MUTEX ENDED."); } else { - LogDebug("SOCKET MUTEX NULL."); + Logger.LogDebug("SOCKET MUTEX NULL."); } } @@ -318,7 +319,7 @@ namespace MouseWithoutBorders internal static bool ExecuteAndTrace(string actionName, Action action, TimeSpan timeout, bool restart = false) { bool rv = true; - LogDebug(actionName); + Logger.LogDebug(actionName); bool done = false; BlockingUI = true; @@ -343,12 +344,12 @@ namespace MouseWithoutBorders } } - TelemetryLogTrace($"[{actionName}] took more than {(long)timeout.TotalSeconds}, restarting the process.", SeverityLevel.Warning, true); + Logger.TelemetryLogTrace($"[{actionName}] took more than {(long)timeout.TotalSeconds}, restarting the process.", SeverityLevel.Warning, true); string desktop = Common.GetMyDesktop(); oneInstanceCheck?.Close(); _ = Process.Start(Application.ExecutablePath, desktop); - LogDebug($"Started on desktop {desktop}"); + Logger.LogDebug($"Started on desktop {desktop}"); Process.GetCurrentProcess().KillProcess(true); }, @@ -379,7 +380,7 @@ namespace MouseWithoutBorders if (!restart) { - TelemetryLogTrace($"[{actionName}] took more than {(long)timeout.TotalSeconds}: {(long)timer.Elapsed.TotalSeconds}.", SeverityLevel.Warning); + Logger.TelemetryLogTrace($"[{actionName}] took more than {(long)timeout.TotalSeconds}: {(long)timer.Elapsed.TotalSeconds}.", SeverityLevel.Warning); } } } @@ -445,7 +446,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); } finally { @@ -457,7 +458,7 @@ namespace MouseWithoutBorders catch (Exception e) { done = true; - Log(e); + Logger.Log(e); } while (blocking && !done) @@ -508,11 +509,11 @@ namespace MouseWithoutBorders internal static void SendNextMachine(ID hostMachine, ID nextMachine, Point requestedXY) { - LogDebug($"SendNextMachine: Host machine: {hostMachine}, Next machine: {nextMachine}, Requested XY: {requestedXY}"); + Logger.LogDebug($"SendNextMachine: Host machine: {hostMachine}, Next machine: {nextMachine}, Requested XY: {requestedXY}"); if (GetTick() - lastSendNextMachine < 100) { - LogDebug("Machine switching in progress."); // "Move Mouse relatively" mode, slow machine/network, quick/busy hand. + Logger.LogDebug("Machine switching in progress."); // "Move Mouse relatively" mode, slow machine/network, quick/busy hand. return; } @@ -529,7 +530,7 @@ namespace MouseWithoutBorders SkSend(package, null, false); - LogDebug("SendNextMachine done."); + Logger.LogDebug("SendNextMachine done."); } private static ulong lastInputEventCount; @@ -603,7 +604,7 @@ namespace MouseWithoutBorders private static void SendByeBye() { - LogDebug($"{nameof(SendByeBye)}"); + Logger.LogDebug($"{nameof(SendByeBye)}"); SendPackage(ID.ALL, PackageType.ByeBye); } @@ -629,7 +630,7 @@ namespace MouseWithoutBorders internal static void SetToggleIcon(int[] toggleIcons) { - Common.LogDebug($"{nameof(SetToggleIcon)}: {toggleIcons?.FirstOrDefault()}"); + Logger.LogDebug($"{nameof(SetToggleIcon)}: {toggleIcons?.FirstOrDefault()}"); Common.toggleIcons = toggleIcons; toggleIconsIndex = 0; } @@ -651,7 +652,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); return null; } } @@ -666,7 +667,7 @@ namespace MouseWithoutBorders InputSimulation.SendKey(new KEYBDDATA() { wVk = (int)VK.SNAPSHOT }); InputSimulation.SendKey(new KEYBDDATA() { dwFlags = (int)Common.LLKHF.UP, wVk = (int)VK.SNAPSHOT }); - Common.LogDebug("PrepareScreenCapture: SNAPSHOT simulated."); + Logger.LogDebug("PrepareScreenCapture: SNAPSHOT simulated."); _ = NativeMethods.MoveWindow( (IntPtr)NativeMethods.FindWindow(null, Common.HELPER_FORM_TEXT), @@ -680,7 +681,7 @@ namespace MouseWithoutBorders } else { - Common.Log("PrepareScreenCapture: Validation failed."); + Logger.Log("PrepareScreenCapture: Validation failed."); } }); } @@ -761,7 +762,7 @@ namespace MouseWithoutBorders } else { - Log(tip); + Logger.Log(tip); } } }); @@ -934,7 +935,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); t.BackingSocket = null; // To be removed at CloseAnUnusedSocket() updateClientSockets = true; } @@ -945,7 +946,7 @@ namespace MouseWithoutBorders if (!connected && data.Des != ID.ALL) { - LogDebug("********** No active connection found for the remote machine! **********" + data.Des.ToString()); + Logger.LogDebug("********** No active connection found for the remote machine! **********" + data.Des.ToString()); if (data.Des == ID.NONE || RemoveDeadMachines(data.Des)) { @@ -965,7 +966,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); } #if DEBUG @@ -1006,7 +1007,7 @@ namespace MouseWithoutBorders { if ((t.Status != SocketStatus.Connected && t.BirthTime < GetTick() - SocketStuff.CONNECT_TIMEOUT) || t.BackingSocket == null) { - LogDebug("CloseAnUnusedSocket: " + t.MachineName + ":" + t.MachineId + "|" + t.Status.ToString()); + Logger.LogDebug("CloseAnUnusedSocket: " + t.MachineName + ":" + t.MachineId + "|" + t.Status.ToString()); tobeRemoved = t; if (t.BackingSocket != null) @@ -1017,7 +1018,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); } } @@ -1048,7 +1049,7 @@ namespace MouseWithoutBorders { if (t.Status == SocketStatus.Connected) { - LogDebug("AtLeastOneSocketConnected returning true: " + t.MachineName); + Logger.LogDebug("AtLeastOneSocketConnected returning true: " + t.MachineName); return true; } } @@ -1056,7 +1057,7 @@ namespace MouseWithoutBorders } } - LogDebug("AtLeastOneSocketConnected returning false."); + Logger.LogDebug("AtLeastOneSocketConnected returning false."); return false; } @@ -1074,7 +1075,7 @@ namespace MouseWithoutBorders { if (!t.IsClient && t.Status == SocketStatus.Connected) { - LogDebug("AtLeastOneServerSocketConnected returning true: " + t.MachineName); + Logger.LogDebug("AtLeastOneServerSocketConnected returning true: " + t.MachineName); return t.BackingSocket; } } @@ -1082,7 +1083,7 @@ namespace MouseWithoutBorders } } - LogDebug("AtLeastOneServerSocketConnected returning false."); + Logger.LogDebug("AtLeastOneServerSocketConnected returning false."); return null; } @@ -1119,7 +1120,7 @@ namespace MouseWithoutBorders { if (TestSend(t)) { - LogDebug($"{nameof(AtLeastOneSocketEstablished)} returning true: {t.MachineName}"); + Logger.LogDebug($"{nameof(AtLeastOneSocketEstablished)} returning true: {t.MachineName}"); return true; } } @@ -1128,7 +1129,7 @@ namespace MouseWithoutBorders } } - LogDebug($"{nameof(AtLeastOneSocketEstablished)} returning false."); + Logger.LogDebug($"{nameof(AtLeastOneSocketEstablished)} returning false."); return false; } @@ -1209,7 +1210,7 @@ namespace MouseWithoutBorders if (machineCt < 2 && Common.Settings != null && (Common.Settings.GetCurrentPage() is SetupPage1 || Common.Settings.GetCurrentPage() is SetupPage2b)) { Common.MachineMatrix = new string[Common.MAX_MACHINE] { Common.MachineName.Trim(), desMachine, string.Empty, string.Empty }; - Common.LogDebug("UpdateSetupMachineMatrix: " + string.Join(",", Common.MachineMatrix)); + Logger.LogDebug("UpdateSetupMachineMatrix: " + string.Join(",", Common.MachineMatrix)); Common.DoSomethingInUIThread( () => @@ -1240,7 +1241,7 @@ namespace MouseWithoutBorders catch (Exception e) { Sk = null; - Log(e); + Logger.Log(e); } if (Sk != null) @@ -1325,7 +1326,7 @@ namespace MouseWithoutBorders }); } - LogDebug("GetMyStorageDir: " + st); + Logger.LogDebug("GetMyStorageDir: " + st); // Delete old files. foreach (FileInfo fi in new DirectoryInfo(st).GetFiles()) @@ -1340,7 +1341,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Log(e); + Logger.Log(e); if (string.IsNullOrEmpty(st) || !st.Contains(Common.BinaryName)) { @@ -1358,11 +1359,11 @@ namespace MouseWithoutBorders try { machine_Name = Dns.GetHostName(); - LogDebug("GetHostName = " + machine_Name); + Logger.LogDebug("GetHostName = " + machine_Name); } catch (Exception e) { - Log(e); + Logger.Log(e); if (string.IsNullOrEmpty(machine_Name)) { @@ -1377,7 +1378,7 @@ namespace MouseWithoutBorders Common.MachineName = machine_Name.Trim(); - LogDebug($"========== {nameof(GetMachineName)} ended!"); + Logger.LogDebug($"========== {nameof(GetMachineName)} ended!"); } private static string GetNetworkName(NetworkInterface networkInterface) @@ -1405,7 +1406,7 @@ namespace MouseWithoutBorders } catch (ObjectDisposedException e) { - Log($"{nameof(GetRemoteStringIP)}: The socket could have been disposed by other threads, error: {e.Message}"); + Logger.Log($"{nameof(GetRemoteStringIP)}: The socket could have been disposed by other threads, error: {e.Message}"); if (throwException) { @@ -1416,7 +1417,7 @@ namespace MouseWithoutBorders } catch (SocketException e) { - Log($"{nameof(GetRemoteStringIP)}: {e.Message}"); + Logger.Log($"{nameof(GetRemoteStringIP)}: {e.Message}"); if (throwException) { @@ -1466,7 +1467,7 @@ namespace MouseWithoutBorders internal static void MoveMouseToCenter() { - LogDebug("+++++ MoveMouseToCenter"); + Logger.LogDebug("+++++ MoveMouseToCenter"); InputSimulation.MoveMouse( Common.PrimaryScreenBounds.Left + ((Common.PrimaryScreenBounds.Right - Common.PrimaryScreenBounds.Left) / 2), Common.PrimaryScreenBounds.Top + ((Common.PrimaryScreenBounds.Bottom - Common.PrimaryScreenBounds.Top) / 2)); @@ -1482,7 +1483,7 @@ namespace MouseWithoutBorders { _ = NativeMethods.SetCursorPos(Common.LastPos.X, Common.LastPos.Y); _ = NativeMethods.GetCursorPos(ref Common.lastPos); - LogDebug($"+++++ HideMouseCursor, byHideMouseMessage = {byHideMouseMessage}"); + Logger.LogDebug($"+++++ HideMouseCursor, byHideMouseMessage = {byHideMouseMessage}"); } CustomCursor.ShowFakeMouseCursor(int.MinValue, int.MinValue); @@ -1493,7 +1494,7 @@ namespace MouseWithoutBorders int length = NativeMethods.GetWindowTextLength(hWnd); StringBuilder sb = new(length + 1); int rv = NativeMethods.GetWindowText(hWnd, sb, sb.Capacity); - LogDebug("GetWindowText returned " + rv.ToString(CultureInfo.CurrentCulture)); + Logger.LogDebug("GetWindowText returned " + rv.ToString(CultureInfo.CurrentCulture)); return sb.ToString(); } @@ -1536,14 +1537,14 @@ namespace MouseWithoutBorders if (read != toRead) { - Common.LogDebug("Stream has no more data after reading {0} bytes.", read); + Logger.LogDebug("Stream has no more data after reading {0} bytes.", read); } } } catch (IOException e) { string log = $"{nameof(SendOrReceiveARandomDataBlockPerInitialIV)}: Exception {(send ? "writing" : "reading")} to the socket stream: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; - Common.Log(log); + Logger.Log(log); if (e.InnerException is not (SocketException or ObjectDisposedException)) { diff --git a/src/modules/MouseWithoutBorders/App/Class/CustomCursor.cs b/src/modules/MouseWithoutBorders/App/Class/CustomCursor.cs index d1e56f7f8a..dc90471d4c 100644 --- a/src/modules/MouseWithoutBorders/App/Class/CustomCursor.cs +++ b/src/modules/MouseWithoutBorders/App/Class/CustomCursor.cs @@ -16,6 +16,8 @@ using System.Drawing.Imaging; using System.Runtime.InteropServices; using System.Windows.Forms; +using MouseWithoutBorders.Core; + // Disable the warning to preserve original code #pragma warning disable CA1716 namespace MouseWithoutBorders.Class @@ -107,12 +109,12 @@ namespace MouseWithoutBorders.Class } catch (NullReferenceException) { - Common.Log($"{nameof(Common.MouseCursorForm)} has been set to null by another thread."); + Logger.Log($"{nameof(Common.MouseCursorForm)} has been set to null by another thread."); Common.MouseCursorForm = new FrmMouseCursor(); } catch (ObjectDisposedException) { - Common.Log($"{nameof(Common.MouseCursorForm)} has been disposed."); + Logger.Log($"{nameof(Common.MouseCursorForm)} has been disposed."); Common.MouseCursorForm = new FrmMouseCursor(); } }, @@ -134,11 +136,11 @@ namespace MouseWithoutBorders.Class } catch (NullReferenceException) { - Common.Log($"{nameof(Common.MouseCursorForm)} has already been set to null by another thread!"); + Logger.Log($"{nameof(Common.MouseCursorForm)} has already been set to null by another thread!"); } catch (ObjectDisposedException) { - Common.Log($"{nameof(Common.MouseCursorForm)} has already been disposed!"); + Logger.Log($"{nameof(Common.MouseCursorForm)} has already been disposed!"); } Common.MouseCursorForm = null; diff --git a/src/modules/MouseWithoutBorders/App/Class/Extensions.cs b/src/modules/MouseWithoutBorders/App/Class/Extensions.cs index 904a14a3a5..0103c3e4e3 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Extensions.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Extensions.cs @@ -6,6 +6,8 @@ using System.ComponentModel; using System.Diagnostics; using System.IO; +using MouseWithoutBorders.Core; + // Disable the warning to preserve original code #pragma warning disable CA1716 namespace MouseWithoutBorders.Class @@ -42,7 +44,7 @@ namespace MouseWithoutBorders.Class catch (Win32Exception e) { string log = $"The process {processName} (PID={processId}) could not be terminated, error: {e.Message}"; - Common.TelemetryLogTrace(log, SeverityLevel.Error); + Logger.TelemetryLogTrace(log, SeverityLevel.Error); Common.ShowToolTip(log, 5000); if (!keepTrying) diff --git a/src/modules/MouseWithoutBorders/App/Class/IClipboardHelper.cs b/src/modules/MouseWithoutBorders/App/Class/IClipboardHelper.cs index c88ac5a6da..ecaeef77db 100644 --- a/src/modules/MouseWithoutBorders/App/Class/IClipboardHelper.cs +++ b/src/modules/MouseWithoutBorders/App/Class/IClipboardHelper.cs @@ -23,8 +23,9 @@ using StreamJsonRpc; #if !MM_HELPER using MouseWithoutBorders.Class; - +using MouseWithoutBorders.Core; #endif + using SystemClipboard = System.Windows.Forms.Clipboard; namespace MouseWithoutBorders @@ -127,7 +128,7 @@ namespace MouseWithoutBorders { public void SendLog(string log) { - Common.LogDebug("FROM HELPER: " + log); + Logger.LogDebug("FROM HELPER: " + log); if (!string.IsNullOrEmpty(log)) { @@ -143,7 +144,7 @@ namespace MouseWithoutBorders } else if (log.StartsWith("Trace:", StringComparison.InvariantCulture)) { - Common.TelemetryLogTrace(log, SeverityLevel.Information); + Logger.TelemetryLogTrace(log, SeverityLevel.Information); } } } @@ -197,7 +198,7 @@ WellKnownSidType.AuthenticatedUserSid, null); #if MM_HELPER _ = e; #else - Common.Log(e); + Logger.Log(e); #endif } }, @@ -247,7 +248,7 @@ WellKnownSidType.AuthenticatedUserSid, null); { Common.IpcChannelCreated = false; Common.ShowToolTip("Error setting up clipboard sharing, clipboard sharing will not work!", 5000, ToolTipIcon.Error); - Common.Log(e); + Logger.Log(e); } } #else @@ -263,7 +264,7 @@ WellKnownSidType.AuthenticatedUserSid, null); } catch (Exception e) { - Logger.LogEvent(e.Message, EventLogEntryType.Error); + EventLogger.LogEvent(e.Message, EventLogEntryType.Error); } return null; @@ -272,7 +273,7 @@ WellKnownSidType.AuthenticatedUserSid, null); } - internal static class Logger + internal static class EventLogger { #if MM_HELPER private const string EventSourceName = "MouseWithoutBordersHelper"; diff --git a/src/modules/MouseWithoutBorders/App/Class/InputHook.cs b/src/modules/MouseWithoutBorders/App/Class/InputHook.cs index 88306a4720..59b5974a80 100644 --- a/src/modules/MouseWithoutBorders/App/Class/InputHook.cs +++ b/src/modules/MouseWithoutBorders/App/Class/InputHook.cs @@ -19,6 +19,7 @@ using System.Runtime.InteropServices; using System.Windows.Forms; using Microsoft.PowerToys.Settings.UI.Library; +using MouseWithoutBorders.Core; [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.InputHook.#MouseHookProc(System.Int32,System.Int32,System.IntPtr)", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "MouseWithoutBorders.InputHook.#ProcessKeyEx(System.Int32,System.Int32)", Justification = "Dotnet port with style preservation")] @@ -117,7 +118,7 @@ namespace MouseWithoutBorders.Class if (hMouseHook == 0) { le = Marshal.GetLastWin32Error(); - Common.Log("Error installing Mouse hook: " + le.ToString(CultureInfo.CurrentCulture)); + Logger.Log("Error installing Mouse hook: " + le.ToString(CultureInfo.CurrentCulture)); er = true; Stop(); } @@ -133,7 +134,7 @@ namespace MouseWithoutBorders.Class if (hKeyboardHook == 0) { le = Marshal.GetLastWin32Error(); - Common.Log("Error installing keyboard hook: " + le.ToString(CultureInfo.CurrentCulture)); + Logger.Log("Error installing keyboard hook: " + le.ToString(CultureInfo.CurrentCulture)); er = true; Stop(); } @@ -166,7 +167,7 @@ namespace MouseWithoutBorders.Class int errorCode = Marshal.GetLastWin32Error(); // throw new Win32Exception(errorCode); - Common.Log("Exception uninstalling Mouse hook, error code: " + errorCode.ToString(CultureInfo.CurrentCulture)); + Logger.Log("Exception uninstalling Mouse hook, error code: " + errorCode.ToString(CultureInfo.CurrentCulture)); } } @@ -179,7 +180,7 @@ namespace MouseWithoutBorders.Class int errorCode = Marshal.GetLastWin32Error(); // throw new Win32Exception(errorCode); - Common.Log("Exception uninstalling keyboard hook, error code: " + errorCode.ToString(CultureInfo.CurrentCulture)); + Logger.Log("Exception uninstalling keyboard hook, error code: " + errorCode.ToString(CultureInfo.CurrentCulture)); } } } @@ -234,7 +235,7 @@ namespace MouseWithoutBorders.Class { if (wParam == Common.WM_LBUTTONUP && SkipMouseUpCount > 0) { - Common.LogDebug($"{nameof(SkipMouseUpCount)}: {SkipMouseUpCount}."); + Logger.LogDebug($"{nameof(SkipMouseUpCount)}: {SkipMouseUpCount}."); SkipMouseUpCount--; rv = NativeMethods.CallNextHookEx(hMouseHook, nCode, wParam, lParam); return rv; @@ -326,7 +327,7 @@ namespace MouseWithoutBorders.Class } catch (Exception e) { - Common.Log(e); + Logger.Log(e); rv = NativeMethods.CallNextHookEx(hMouseHook, nCode, wParam, lParam); } @@ -455,7 +456,7 @@ namespace MouseWithoutBorders.Class break; default: - Common.LogDebug("X"); + Logger.LogDebug("X"); return ProcessHotKeys(vkCode, hookCallbackKeybdData); } } diff --git a/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs b/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs index 5ff39f8adc..bca6e4b28f 100644 --- a/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs +++ b/src/modules/MouseWithoutBorders/App/Class/InputSimulation.cs @@ -17,6 +17,7 @@ using System.ServiceProcess; using System.Threading.Tasks; using Microsoft.PowerToys.Settings.UI.Library; +using MouseWithoutBorders.Core; using Windows.UI.Input.Preview.Injection; using static MouseWithoutBorders.Class.NativeMethods; @@ -152,7 +153,7 @@ namespace MouseWithoutBorders.Class } log += "*"; // ((Keys)kd.wVk).ToString(CultureInfo.InvariantCulture); - Common.LogDebug(log); + Logger.LogDebug(log); } // Md.X, Md.Y is from 0 to 65535 @@ -174,7 +175,7 @@ namespace MouseWithoutBorders.Class if (md.dwFlags != Common.WM_MOUSEMOVE) { - Common.LogDebug($"InputSimulation.SendMouse: x = {md.X}, y = {md.Y}, WheelDelta = {md.WheelDelta}, dwFlags = {md.dwFlags}."); + Logger.LogDebug($"InputSimulation.SendMouse: x = {md.X}, y = {md.Y}, WheelDelta = {md.WheelDelta}, dwFlags = {md.dwFlags}."); } switch (md.dwFlags) @@ -242,7 +243,7 @@ namespace MouseWithoutBorders.Class mouse_input.mi.dx = (int)dx; mouse_input.mi.dy = (int)dy; - Common.LogDebug($"InputSimulation.MoveMouseEx: x = {x}, y = {y}."); + Logger.LogDebug($"InputSimulation.MoveMouseEx: x = {x}, y = {y}."); mouse_input.mi.dwFlags |= (int)(NativeMethods.MOUSEEVENTF.MOVE | NativeMethods.MOUSEEVENTF.ABSOLUTE); @@ -264,7 +265,7 @@ namespace MouseWithoutBorders.Class mouse_input.mi.mouseData = 0; mouse_input.mi.dwFlags = (int)(NativeMethods.MOUSEEVENTF.MOVE | NativeMethods.MOUSEEVENTF.ABSOLUTE); - Common.LogDebug($"InputSimulation.MoveMouse: x = {x}, y = {y}."); + Logger.LogDebug($"InputSimulation.MoveMouse: x = {x}, y = {y}."); Common.DoSomethingInTheInputSimulationThread(() => { @@ -285,7 +286,7 @@ namespace MouseWithoutBorders.Class mouse_input.mi.mouseData = 0; mouse_input.mi.dwFlags = (int)NativeMethods.MOUSEEVENTF.MOVE; - Common.LogDebug($"InputSimulation.MoveMouseRelative: x = {dx}, y = {dy}."); + Logger.LogDebug($"InputSimulation.MoveMouseRelative: x = {dx}, y = {dy}."); Common.DoSomethingInTheInputSimulationThread(() => { @@ -309,7 +310,7 @@ namespace MouseWithoutBorders.Class InputHook.SkipMouseUpCount++; _ = SendInputEx(input); - Common.LogDebug("MouseUp() called"); + Logger.LogDebug("MouseUp() called"); }); } @@ -338,7 +339,7 @@ namespace MouseWithoutBorders.Class input.mi.dwFlags = (int)NativeMethods.MOUSEEVENTF.LEFTUP; _ = SendInputEx(input); - Common.LogDebug("MouseClick() called"); + Logger.LogDebug("MouseClick() called"); Thread.Sleep(200); } finally @@ -450,7 +451,7 @@ namespace MouseWithoutBorders.Class eatKey = true; Common.ReleaseAllKeys(); uint rv = NativeMethods.LockWorkStation(); - Common.LogDebug("LockWorkStation returned " + rv.ToString(CultureInfo.CurrentCulture)); + Logger.LogDebug("LockWorkStation returned " + rv.ToString(CultureInfo.CurrentCulture)); } break; diff --git a/src/modules/MouseWithoutBorders/App/Class/Program.cs b/src/modules/MouseWithoutBorders/App/Class/Program.cs index 025c6b9b88..91ed60874f 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Program.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Program.cs @@ -34,6 +34,8 @@ using Microsoft.PowerToys.Telemetry; using Newtonsoft.Json; using StreamJsonRpc; +using Logger = MouseWithoutBorders.Core.Logger; + [module: SuppressMessage("Microsoft.MSInternal", "CA904:DeclareTypesInMicrosoftOrSystemNamespace", Scope = "namespace", Target = "MouseWithoutBorders", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Design", "CA1014:MarkAssembliesWithClsCompliant", Justification = "Dotnet port with style preservation")] [module: SuppressMessage("Microsoft.Globalization", "CA1304:SpecifyCultureInfo", Scope = "member", Target = "MouseWithoutBorders.Program.#Main()", MessageId = "System.String.ToLower", Justification = "Dotnet port with style preservation")] @@ -53,11 +55,11 @@ namespace MouseWithoutBorders.Class private static void Main() { ManagedCommon.Logger.InitializeLogger("\\MouseWithoutBorders\\Logs"); - Common.Log(Application.ProductName + " Started!"); + Logger.Log(Application.ProductName + " Started!"); if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredMouseWithoutBordersEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) { - Common.Log("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator."); + Logger.Log("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator."); return; } @@ -81,9 +83,9 @@ namespace MouseWithoutBorders.Class } User = WindowsIdentity.GetCurrent().Name; - Common.LogDebug("*** Started as " + User); + Logger.LogDebug("*** Started as " + User); - Common.Log(Environment.CommandLine); + Logger.Log(Environment.CommandLine); bool serviceMode = firstArg == ServiceModeArg; @@ -99,8 +101,8 @@ namespace MouseWithoutBorders.Class } catch (Exception ex) { - Common.Log("Couldn't start the service. Will try to continue as not a service."); - Common.Log(ex); + Logger.Log("Couldn't start the service. Will try to continue as not a service."); + Logger.Log(ex); ShowServiceModeErrorTooltip = true; serviceMode = false; Setting.Values.UseService = false; @@ -121,7 +123,7 @@ namespace MouseWithoutBorders.Class { if (Common.CheckSecondInstance(Common.RunWithNoAdminRight)) { - Common.Log("*** Second instance, exiting..."); + Logger.Log("*** Second instance, exiting..."); return; } @@ -130,16 +132,16 @@ namespace MouseWithoutBorders.Class if (firstArg.Equals("winlogon", StringComparison.OrdinalIgnoreCase)) { // Executed by service, running on logon desktop - Common.Log("*** Running on " + firstArg + " desktop"); + Logger.Log("*** Running on " + firstArg + " desktop"); Common.RunOnLogonDesktop = true; } else if (args[1].Trim().Equals("default", StringComparison.OrdinalIgnoreCase)) { - Common.Log("*** Running on " + firstArg + " desktop"); + Logger.Log("*** Running on " + firstArg + " desktop"); } else if (args[1].Equals(myDesktop, StringComparison.OrdinalIgnoreCase)) { - Common.Log("*** Running on " + myDesktop); + Logger.Log("*** Running on " + myDesktop); if (myDesktop.Equals("Screen-saver", StringComparison.OrdinalIgnoreCase)) { Common.RunOnScrSaverDesktop = true; @@ -151,7 +153,7 @@ namespace MouseWithoutBorders.Class { if (Common.CheckSecondInstance(true)) { - Common.Log("*** Second instance, exiting..."); + Logger.Log("*** Second instance, exiting..."); return; } } @@ -165,10 +167,10 @@ namespace MouseWithoutBorders.Class } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } - Common.Log(Environment.OSVersion.ToString()); + Logger.Log(Environment.OSVersion.ToString()); // Environment.OSVersion is unreliable from 6.2 and up, so just forcefully call the APIs and log the exception unsupported by Windows: int setProcessDpiAwarenessResult = -1; @@ -176,31 +178,31 @@ namespace MouseWithoutBorders.Class try { setProcessDpiAwarenessResult = NativeMethods.SetProcessDpiAwareness(2); - Common.Log(string.Format(CultureInfo.InvariantCulture, "SetProcessDpiAwareness: {0}.", setProcessDpiAwarenessResult)); + Logger.Log(string.Format(CultureInfo.InvariantCulture, "SetProcessDpiAwareness: {0}.", setProcessDpiAwarenessResult)); } catch (DllNotFoundException) { - Common.Log("SetProcessDpiAwareness is unsupported in Windows 7 and lower."); + Logger.Log("SetProcessDpiAwareness is unsupported in Windows 7 and lower."); } catch (EntryPointNotFoundException) { - Common.Log("SetProcessDpiAwareness is unsupported in Windows 7 and lower."); + Logger.Log("SetProcessDpiAwareness is unsupported in Windows 7 and lower."); } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } try { if (setProcessDpiAwarenessResult != 0) { - Common.Log(string.Format(CultureInfo.InvariantCulture, "SetProcessDPIAware: {0}.", NativeMethods.SetProcessDPIAware())); + Logger.Log(string.Format(CultureInfo.InvariantCulture, "SetProcessDPIAware: {0}.", NativeMethods.SetProcessDPIAware())); } } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } System.Threading.Thread mainUIThread = Thread.CurrentThread; @@ -226,7 +228,7 @@ namespace MouseWithoutBorders.Class } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } } @@ -397,14 +399,14 @@ namespace MouseWithoutBorders.Class { if (pp.Id != me.Id) { - Common.Log(string.Format(CultureInfo.InvariantCulture, "Killing process {0}.", pp.Id)); + Logger.Log(string.Format(CultureInfo.InvariantCulture, "Killing process {0}.", pp.Id)); pp.KillProcess(); } } } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } Common.StartMouseWithoutBordersService(); diff --git a/src/modules/MouseWithoutBorders/App/Class/Setting.cs b/src/modules/MouseWithoutBorders/App/Class/Setting.cs index 6ce32bbc2f..a2eb300b0c 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Setting.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Setting.cs @@ -29,6 +29,7 @@ using Microsoft.PowerToys.Settings.UI.Library.Utilities; // 2023- Included in PowerToys. // using Microsoft.Win32; +using MouseWithoutBorders.Core; using Settings.UI.Library.Attributes; [module: SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "MouseWithoutBorders.Properties.Setting.Values.#LoadIntSetting(System.String,System.Int32)", Justification = "Dotnet port with style preservation")] @@ -133,7 +134,7 @@ namespace MouseWithoutBorders.Class } catch (IOException ex) { - Logger.LogEvent($"Failed to read settings: {ex.Message}", System.Diagnostics.EventLogEntryType.Error); + EventLogger.LogEvent($"Failed to read settings: {ex.Message}", System.Diagnostics.EventLogEntryType.Error); } PauseInstantSaving = false; @@ -168,7 +169,7 @@ namespace MouseWithoutBorders.Class } catch (IOException ex) { - Logger.LogEvent($"Failed to write settings: {ex.Message}", System.Diagnostics.EventLogEntryType.Error); + EventLogger.LogEvent($"Failed to write settings: {ex.Message}", System.Diagnostics.EventLogEntryType.Error); } if (saved) @@ -198,7 +199,7 @@ namespace MouseWithoutBorders.Class } catch (Exception ex) { - Logger.LogEvent($"Failed to update settings: {ex.Message}", System.Diagnostics.EventLogEntryType.Error); + EventLogger.LogEvent($"Failed to update settings: {ex.Message}", System.Diagnostics.EventLogEntryType.Error); } }); @@ -442,7 +443,7 @@ namespace MouseWithoutBorders.Class { if (_properties.SecurityKey.Value.Length != 0) { - Common.LogDebug("GETSECKEY: Key was already loaded/set: " + _properties.SecurityKey.Value); + Logger.LogDebug("GETSECKEY: Key was already loaded/set: " + _properties.SecurityKey.Value); return _properties.SecurityKey.Value; } else diff --git a/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs b/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs index 57600cde95..a6cf02e348 100644 --- a/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs +++ b/src/modules/MouseWithoutBorders/App/Class/SocketStuff.cs @@ -17,6 +17,7 @@ using System.Security.Cryptography; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; +using MouseWithoutBorders.Core; // // Socket code. @@ -174,12 +175,12 @@ namespace MouseWithoutBorders.Class internal SocketStuff(int port, bool byUser) { - Common.LogDebug("SocketStuff started."); + Logger.LogDebug("SocketStuff started."); bASE_PORT = port; Common.Ran = new Random(); - Common.LogDebug("Validating session..."); + Logger.LogDebug("Validating session..."); if (Common.CurrentProcess.SessionId != NativeMethods.WTSGetActiveConsoleSessionId()) { @@ -198,12 +199,12 @@ namespace MouseWithoutBorders.Class } Common.MMSleep(1); - Common.Log("Not physical console session."); + Logger.Log("Not physical console session."); throw new NotPhysicalConsoleException("Not physical console session."); } - Common.LogDebug("Creating socket list and mutex..."); + Logger.LogDebug("Creating socket list and mutex..."); try { @@ -235,7 +236,7 @@ namespace MouseWithoutBorders.Class } catch (AbandonedMutexException e) { - Common.TelemetryLogTrace($"{nameof(SocketStuff)}: {e.Message}", SeverityLevel.Warning); + Logger.TelemetryLogTrace($"{nameof(SocketStuff)}: {e.Message}", SeverityLevel.Warning); } Common.GetScreenConfig(); @@ -280,7 +281,7 @@ namespace MouseWithoutBorders.Class Common.GetMachineName(); // IPs might have been changed Common.UpdateMachineTimeAndID(); - Common.LogDebug("Creating sockets..."); + Logger.LogDebug("Creating sockets..."); openSocketErr = CreateSocket(); @@ -303,7 +304,7 @@ namespace MouseWithoutBorders.Class // It is reasonable to give a try on restarting MwB processes in other sessions. if (restartCount++ < 5 && Common.IsMyDesktopActive() && !Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { - Common.TelemetryLogTrace("Restarting the service dues to WSAEADDRINUSE.", SeverityLevel.Warning); + Logger.TelemetryLogTrace("Restarting the service dues to WSAEADDRINUSE.", SeverityLevel.Warning); Program.StartService(); Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET; } @@ -367,11 +368,11 @@ namespace MouseWithoutBorders.Class "Closing sockets", () => { - Common.LogDebug($"Closing socket [{skMessageServer?.Name}]."); + Logger.LogDebug($"Closing socket [{skMessageServer?.Name}]."); skMessageServer?.Close(); // The original ones, not the socket instances produced by the accept() method. skMessageServer = null; - Common.LogDebug($"Closing socket [{skClipboardServer?.Name}]."); + Logger.LogDebug($"Closing socket [{skClipboardServer?.Name}]."); skClipboardServer?.Close(); skClipboardServer = null; try @@ -383,7 +384,7 @@ namespace MouseWithoutBorders.Class if (TcpSockets != null) { - Common.LogDebug("********** Closing Sockets: " + TcpSockets.Count.ToString(CultureInfo.InvariantCulture)); + Logger.LogDebug("********** Closing Sockets: " + TcpSockets.Count.ToString(CultureInfo.InvariantCulture)); List notClosedSockets = new(); @@ -412,16 +413,16 @@ namespace MouseWithoutBorders.Class catch (SocketException e) { string log = $"Socket.Close error: {e.GetType()}/{e.Message}. This is expected when the socket is already closed by remote host."; - Common.Log(log); + Logger.Log(log); } catch (ObjectDisposedException e) { string log = $"Socket.Close error: {e.GetType()}/{e.Message}. This is expected when the socket is already disposed."; - Common.Log(log); + Logger.Log(log); } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } // If there was an error closing the socket: @@ -435,12 +436,12 @@ namespace MouseWithoutBorders.Class TcpSockets = notClosedSockets; } - Common.LogDebug("********** Sockets Closed: " + c.ToString(CultureInfo.CurrentCulture)); + Logger.LogDebug("********** Sockets Closed: " + c.ToString(CultureInfo.CurrentCulture)); } } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } }, TimeSpan.FromSeconds(3), @@ -448,7 +449,7 @@ namespace MouseWithoutBorders.Class } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } finally { @@ -463,7 +464,7 @@ namespace MouseWithoutBorders.Class } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } } } @@ -477,16 +478,16 @@ namespace MouseWithoutBorders.Class } catch (SocketException e) { - Common.Log(e); + Logger.Log(e); return e; } catch (Exception e) { - Common.Log(e); + Logger.Log(e); return e; } - Common.LogDebug("=================================================="); + Logger.LogDebug("=================================================="); return null; } @@ -497,7 +498,7 @@ namespace MouseWithoutBorders.Class if (tcp.BackingSocket == null || !tcp.BackingSocket.Connected || encryptedStream == null) { string log = $"{nameof(TcpSendData)}: The socket is no longer connected, it could have been closed by the remote host."; - Common.Log(log); + Logger.Log(log); throw new ExpectedSocketException(log); } @@ -516,7 +517,7 @@ namespace MouseWithoutBorders.Class catch (IOException e) { string log = $"{nameof(TcpSendData)}: Exception writing to the socket: {tcp.MachineName}: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; - Common.Log(log); + Logger.Log(log); throw e.InnerException is SocketException se ? new ExpectedSocketException(se) : new ExpectedSocketException(log); } @@ -533,7 +534,7 @@ namespace MouseWithoutBorders.Class if (magic != (Common.MagicNumber & 0xFFFF0000)) { - Common.Log("Magic number invalid!"); + Logger.Log("Magic number invalid!"); buf[0] = (byte)PackageType.Invalid; } @@ -544,7 +545,7 @@ namespace MouseWithoutBorders.Class if (buf[1] != checksum) { - Common.Log("Checksum invalid!"); + Logger.Log("Checksum invalid!"); buf[0] = (byte)PackageType.Invalid; } @@ -559,7 +560,7 @@ namespace MouseWithoutBorders.Class if (tcp.BackingSocket == null || !tcp.BackingSocket.Connected || decryptedStream == null) { string log = $"{nameof(TcpReceiveData)}: The socket is no longer connected, it could have been closed by the remote host."; - Common.Log(log); + Logger.Log(log); throw new ExpectedSocketException(log); } @@ -597,7 +598,7 @@ namespace MouseWithoutBorders.Class catch (IOException e) { string log = $"{nameof(TcpReceiveData)}: Exception reading from the socket: {tcp.MachineName}: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; - Common.Log(log); + Logger.Log(log); throw e.InnerException is SocketException se ? new ExpectedSocketException(se) : new ExpectedSocketException(log); } @@ -668,7 +669,7 @@ namespace MouseWithoutBorders.Class int rv = TcpSendData(tcp, dataAsBytes); if (rv < dataAsBytes.Length) { - Common.Log("TcpSend error! Length of sent data is unexpected."); + Logger.Log("TcpSend error! Length of sent data is unexpected."); UpdateTcpSockets(tcp, SocketStatus.SendError); throw new SocketException((int)SocketStatus.SendError); } @@ -683,7 +684,7 @@ namespace MouseWithoutBorders.Class TcpListener server = param as TcpListener; do { - Common.LogDebug("TCPServerThread: Waiting for request..."); + Logger.LogDebug("TCPServerThread: Waiting for request..."); Socket s = server.AcceptSocket(); _ = Task.Run(() => @@ -694,7 +695,7 @@ namespace MouseWithoutBorders.Class } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } }); } @@ -703,22 +704,22 @@ namespace MouseWithoutBorders.Class catch (InvalidOperationException e) { string log = $"TCPServerThread.AcceptSocket: The server socket could have been closed. {e.Message}"; - Common.Log(log); + Logger.Log(log); } catch (SocketException e) { if (e.ErrorCode == (int)SocketError.Interrupted) { - Common.Log("TCPServerThread.AcceptSocket: A blocking socket call was canceled."); + Logger.Log("TCPServerThread.AcceptSocket: A blocking socket call was canceled."); } else { - Common.Log(e); + Logger.Log(e); } } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } } @@ -734,7 +735,7 @@ namespace MouseWithoutBorders.Class } catch (SocketException e) { - Common.Log($"{nameof(GetMachineNameFromSocket)}: {e.Message}"); + Logger.Log($"{nameof(GetMachineNameFromSocket)}: {e.Message}"); return stringIP; } @@ -745,7 +746,7 @@ namespace MouseWithoutBorders.Class if (dotPos > 0) { - Common.LogDebug("Removing domain part from the full machine name: {0}.", name); + Logger.LogDebug("Removing domain part from the full machine name: {0}.", name); name = name[..dotPos]; } } @@ -756,7 +757,7 @@ namespace MouseWithoutBorders.Class private void AddSocket(Socket s) { string machineName = GetMachineNameFromSocket(s); - Common.Log($"New connection from client: [{machineName}]."); + Logger.Log($"New connection from client: [{machineName}]."); TcpSk tcp = AddTcpSocket(false, s, SocketStatus.Connecting, machineName); StartNewTcpServer(tcp, machineName); } @@ -772,7 +773,7 @@ namespace MouseWithoutBorders.Class } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } } @@ -789,13 +790,13 @@ namespace MouseWithoutBorders.Class return; } - Common.LogDebug("!!!!! UpdateTCPClients !!!!!"); + Logger.LogDebug("!!!!! UpdateTCPClients !!!!!"); try { if (Common.MachineMatrix != null) { - Common.LogDebug("MachineMatrix = " + string.Join(", ", Common.MachineMatrix)); + Logger.LogDebug("MachineMatrix = " + string.Join(", ", Common.MachineMatrix)); foreach (string st in Common.MachineMatrix) { @@ -809,7 +810,7 @@ namespace MouseWithoutBorders.Class if (found) { - Common.LogDebug(machineName + " is already connected! ^^^^^^^^^^^^^^^^^^^^^"); + Logger.LogDebug(machineName + " is already connected! ^^^^^^^^^^^^^^^^^^^^^"); continue; } @@ -820,7 +821,7 @@ namespace MouseWithoutBorders.Class } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } } @@ -883,7 +884,7 @@ namespace MouseWithoutBorders.Class Socket dummySocket = new(AddressFamily.Unspecified, SocketType.Stream, ProtocolType.Tcp); TcpSk dummyTcp = AddTcpSocket(true, dummySocket, SocketStatus.Resolving, machineName); - Common.LogDebug("Connecting to: " + machineName); + Logger.LogDebug("Connecting to: " + machineName); if (!string.IsNullOrEmpty(Setting.Values.Name2IP)) { @@ -911,7 +912,7 @@ namespace MouseWithoutBorders.Class { useName2IP = true; - Common.LogDebug("Using both user-defined Name-to-IP mappings and DNS result for " + machineName); + Logger.LogDebug("Using both user-defined Name-to-IP mappings and DNS result for " + machineName); Common.ShowToolTip("Using both user-defined Name-to-IP mappings and DNS result for " + machineName, 3000, ToolTipIcon.Info, false); @@ -941,7 +942,7 @@ namespace MouseWithoutBorders.Class Common.ShowToolTip(e.Message + ": " + machineName, 10000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); - Common.Log($"{nameof(StartNewTcpClient)}.{nameof(Dns.GetHostEntry)}: {e.Message}"); + Logger.Log($"{nameof(StartNewTcpClient)}.{nameof(Dns.GetHostEntry)}: {e.Message}"); } UpdateTcpSockets(dummyTcp, SocketStatus.NA); @@ -967,7 +968,7 @@ namespace MouseWithoutBorders.Class } } - Common.LogDebug(machineName + ipLog); + Logger.LogDebug(machineName + ipLog); } if (validAddresses.Count > 0) @@ -983,7 +984,7 @@ namespace MouseWithoutBorders.Class { if (IsBadIP(machineName, ip)) { - Common.Log($"Skip bad IP address: {ip}"); + Logger.Log($"Skip bad IP address: {ip}"); continue; } @@ -998,18 +999,18 @@ namespace MouseWithoutBorders.Class } else { - Common.Log($"DNS information of machine not matched: {machineName} => {ip} => {hn}."); + Logger.Log($"DNS information of machine not matched: {machineName} => {ip} => {hn}."); AddBadIP(machineName, ip); } } catch (SocketException se) { - Common.Log($"{nameof(StartNewTcpClient)}: DNS information of machine not matched: {machineName} => {ip} => {se.Message}."); + Logger.Log($"{nameof(StartNewTcpClient)}: DNS information of machine not matched: {machineName} => {ip} => {se.Message}."); AddBadIP(machineName, ip); } catch (ArgumentException ae) { - Common.Log($"{nameof(StartNewTcpClient)}: DNS information of machine not matched: {machineName} => {ip} => {ae.Message}."); + Logger.Log($"{nameof(StartNewTcpClient)}: DNS information of machine not matched: {machineName} => {ip} => {ae.Message}."); AddBadIP(machineName, ip); } } @@ -1030,7 +1031,7 @@ namespace MouseWithoutBorders.Class } else { - Common.Log("Cannot resolve IPv4 Addresses of machine: " + machineName); + Logger.Log("Cannot resolve IPv4 Addresses of machine: " + machineName); if (!useName2IP) { @@ -1058,7 +1059,7 @@ namespace MouseWithoutBorders.Class if (!remoteIPv4Addresses.Any()) { - Common.Log($"No IPv4 resolved from the remote machine: {machineName}."); + Logger.Log($"No IPv4 resolved from the remote machine: {machineName}."); return true; } @@ -1066,7 +1067,7 @@ namespace MouseWithoutBorders.Class if (localIPv4Addresses.Count == 0) { - Common.Log($"No IPv4 resolved from the local machine: {Common.MachineName}"); + Logger.Log($"No IPv4 resolved from the local machine: {Common.MachineName}"); return true; } @@ -1085,7 +1086,7 @@ namespace MouseWithoutBorders.Class } } - Common.Log($"Skip machine not in the same network: {machineName}."); + Logger.Log($"Skip machine not in the same network: {machineName}."); return false; } @@ -1105,7 +1106,7 @@ namespace MouseWithoutBorders.Class } catch (Exception e) { - Common.Log(e); + Logger.Log(e); return Enumerable.Empty(); } } @@ -1123,13 +1124,13 @@ namespace MouseWithoutBorders.Class if (Common.IsConnectedByAClientSocketTo(machineName)) { - Common.LogDebug(machineName + " is already connected by another client socket."); + Logger.LogDebug(machineName + " is already connected by another client socket."); return; } if (Common.IsConnectingByAClientSocketTo(machineName, ip)) { - Common.LogDebug($"{machineName}:{ip} is already being connected by another client socket."); + Logger.LogDebug($"{machineName}:{ip} is already being connected by another client socket."); return; } @@ -1138,7 +1139,7 @@ namespace MouseWithoutBorders.Class // Update the other server socket's machine name based on this corresponding client socket. UpdateTcpSockets(tcp, SocketStatus.Connecting); - Common.LogDebug(string.Format(CultureInfo.CurrentCulture, "=====> Connecting to: {0}:{1}", machineName, ip.ToString())); + Logger.LogDebug(string.Format(CultureInfo.CurrentCulture, "=====> Connecting to: {0}:{1}", machineName, ip.ToString())); long timeoutLeft; @@ -1151,7 +1152,7 @@ namespace MouseWithoutBorders.Class catch (ObjectDisposedException) { // When user reconnects. - Common.LogDebug($"tcpClient.Connect: The socket has already been disposed: {machineName}:{ip}"); + Logger.LogDebug($"tcpClient.Connect: The socket has already been disposed: {machineName}:{ip}"); return; } catch (SocketException e) @@ -1160,13 +1161,13 @@ namespace MouseWithoutBorders.Class if (timeoutLeft > 0) { - Common.LogDebug($"tcpClient.Connect: {timeoutLeft}: {e.Message}"); + Logger.LogDebug($"tcpClient.Connect: {timeoutLeft}: {e.Message}"); Thread.Sleep(1000); continue; } else { - Common.Log($"tcpClient.Connect: Unable to connect after a timeout: {machineName}:{ip} : {e.Message}"); + Logger.Log($"tcpClient.Connect: Unable to connect after a timeout: {machineName}:{ip} : {e.Message}"); string message = $"Connection timed out: {machineName}:{ip}"; @@ -1181,14 +1182,14 @@ namespace MouseWithoutBorders.Class } while (true); - Common.LogDebug($"=====> Connected: {tcpClient.Client.LocalEndPoint} => {machineName}: {ip}"); + Logger.LogDebug($"=====> Connected: {tcpClient.Client.LocalEndPoint} => {machineName}: {ip}"); // Sending/Receiving packages MainTCPRoutine(tcp, machineName, true); } catch (ObjectDisposedException e) { - Common.Log($"{nameof(StartNewTcpClientThread)}: The socket could have been closed/disposed due to machine switch: {e.Message}"); + Logger.Log($"{nameof(StartNewTcpClientThread)}: The socket could have been closed/disposed due to machine switch: {e.Message}"); } catch (SocketException e) { @@ -1201,12 +1202,12 @@ namespace MouseWithoutBorders.Class } else { - Common.TelemetryLogTrace($"{nameof(StartNewTcpClientThread)}: Error: {e.Message} on the IP Address: {localIP}", SeverityLevel.Error); + Logger.TelemetryLogTrace($"{nameof(StartNewTcpClientThread)}: Error: {e.Message} on the IP Address: {localIP}", SeverityLevel.Error); } } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } } @@ -1230,7 +1231,7 @@ namespace MouseWithoutBorders.Class if (e is ExpectedSocketException se && se.ShouldReconnect) { Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET; - Common.Log($"MainTCPRoutine: {nameof(FlagReopenSocketIfNeeded)}"); + Logger.Log($"MainTCPRoutine: {nameof(FlagReopenSocketIfNeeded)}"); } } @@ -1254,7 +1255,7 @@ namespace MouseWithoutBorders.Class if (currentSocket == null) { - Common.LogDebug($"{nameof(MainTCPRoutine)}: The socket could have been closed/disposed by other threads."); + Logger.LogDebug($"{nameof(MainTCPRoutine)}: The socket could have been closed/disposed by other threads."); return; } @@ -1283,21 +1284,21 @@ namespace MouseWithoutBorders.Class strIP = Common.GetRemoteStringIP(currentSocket, true); remoteMachine = string.IsNullOrEmpty(machineName) ? GetMachineNameFromSocket(currentSocket) : machineName; - Common.LogDebug($"MainTCPRoutine: Remote machineName/IP = {remoteMachine}/{strIP}"); + Logger.LogDebug($"MainTCPRoutine: Remote machineName/IP = {remoteMachine}/{strIP}"); } catch (ObjectDisposedException e) { Common.PleaseReopenSocket = Common.REOPEN_WHEN_WSAECONNRESET; UpdateTcpSockets(currentTcp, SocketStatus.ForceClosed); currentSocket.Close(); - Common.Log($"{nameof(MainTCPRoutine)}: The socket could have been closed/disposed by other threads: {e.Message}"); + Logger.Log($"{nameof(MainTCPRoutine)}: The socket could have been closed/disposed by other threads: {e.Message}"); } catch (Exception e) { UpdateTcpSockets(currentTcp, SocketStatus.ForceClosed); FlagReopenSocketIfNeeded(e); currentSocket.Close(); - Common.Log(e); + Logger.Log(e); } int errCount = 0; @@ -1314,7 +1315,7 @@ namespace MouseWithoutBorders.Class errCount++; string log = $"{nameof(MainTCPRoutine)}.TcpReceive error, invalid package from {remoteMachine}: {receivedCount}"; - Common.Log(log); + Logger.Log(log); if (receivedCount > 0) { @@ -1369,7 +1370,7 @@ namespace MouseWithoutBorders.Class if (++packageCount >= 10) { // Common.ShowToolTip("Invalid Security Key from " + remoteMachine, 5000); - Common.Log("More than 10 invalid packages received!"); + Logger.Log("More than 10 invalid packages received!"); package.Type = PackageType.Invalid; @@ -1393,7 +1394,7 @@ namespace MouseWithoutBorders.Class if (!remoteMachine.Equals(claimedMachineName, StringComparison.Ordinal)) { - Common.LogDebug($"DNS.RemoteMachineName({remoteMachine}) <> Claimed.MachineName({claimedMachineName}), using the claimed machine name."); + Logger.LogDebug($"DNS.RemoteMachineName({remoteMachine}) <> Claimed.MachineName({claimedMachineName}), using the claimed machine name."); remoteMachine = claimedMachineName; currentTcp.MachineName = remoteMachine; } @@ -1401,7 +1402,7 @@ namespace MouseWithoutBorders.Class // Double check to avoid a redundant client socket. if (isClient && Common.IsConnectedByAClientSocketTo(remoteMachine)) { - Common.LogDebug("=====> Duplicate connected client socket for: " + remoteMachine + ":" + strIP + " is being removed."); + Logger.LogDebug("=====> Duplicate connected client socket for: " + remoteMachine + ":" + strIP + " is being removed."); UpdateTcpSockets(currentTcp, SocketStatus.ForceClosed); currentSocket.Close(); return; @@ -1409,7 +1410,7 @@ namespace MouseWithoutBorders.Class if (remoteMachine.Equals(Common.MachineName, StringComparison.OrdinalIgnoreCase)) { - Common.LogDebug("Connected to/from local socket: " + strIP + (isClient ? "-Client" : "-Server")); + Logger.LogDebug("Connected to/from local socket: " + strIP + (isClient ? "-Client" : "-Server")); UpdateTcpSockets(currentTcp, SocketStatus.NA); Common.MMSleep(1); currentSocket.Close(); @@ -1421,7 +1422,7 @@ namespace MouseWithoutBorders.Class currentTcp.MachineId = (uint)remoteID; currentTcp.Status = SocketStatus.Connected; UpdateTcpSockets(currentTcp, SocketStatus.Connected); - Common.LogDebug("))))))))))))))) Machine got trusted: " + remoteMachine + ":" + strIP + ", Is client: " + isClient); + Logger.LogDebug("))))))))))))))) Machine got trusted: " + remoteMachine + ":" + strIP + ", Is client: " + isClient); if (Math.Abs(Common.GetTick() - Common.LastReconnectByHotKeyTime) < 5000) { @@ -1432,11 +1433,11 @@ namespace MouseWithoutBorders.Class if (Common.MachinePool.TryFindMachineByName(remoteMachine, out MachineInf machineInfo)) { - Common.LogDebug("Machine updated: " + remoteMachine + "/" + remoteID.ToString()); + Logger.LogDebug("Machine updated: " + remoteMachine + "/" + remoteID.ToString()); if (machineInfo.Name.Equals(Common.DesMachineName, StringComparison.OrdinalIgnoreCase)) { - Common.LogDebug("Des ID updated: " + Common.DesMachineID.ToString() + + Logger.LogDebug("Des ID updated: " + Common.DesMachineID.ToString() + "/" + remoteID.ToString()); Common.NewDesMachineID = Common.DesMachineID = remoteID; } @@ -1446,7 +1447,7 @@ namespace MouseWithoutBorders.Class } else { - Common.LogDebug("New machine connected: {0}.", remoteMachine); + Logger.LogDebug("New machine connected: {0}.", remoteMachine); if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop) { @@ -1461,7 +1462,7 @@ namespace MouseWithoutBorders.Class } else { - Common.LogDebug("Invalid ACK from " + remoteMachine); + Logger.LogDebug("Invalid ACK from " + remoteMachine); UpdateTcpSockets(currentTcp, SocketStatus.InvalidKey); string remoteEP = currentSocket.RemoteEndPoint.ToString(); @@ -1500,7 +1501,7 @@ namespace MouseWithoutBorders.Class } else { - Common.Log(string.Format( + Logger.Log(string.Format( CultureInfo.CurrentCulture, "Unexpected package, size = {0}, type = {1}", receivedCount, @@ -1514,12 +1515,12 @@ namespace MouseWithoutBorders.Class if (lastRemoteMachineID != (long)remoteID) { _ = Interlocked.Exchange(ref lastRemoteMachineID, (long)remoteID); - Common.LogDebug($"MainTCPRoutine: Remote machine = {strIP}/{lastRemoteMachineID}"); + Logger.LogDebug($"MainTCPRoutine: Remote machine = {strIP}/{lastRemoteMachineID}"); } if (package.Type == PackageType.HandshakeAck) { - Common.LogDebug("Skipping the rest of the Handshake packages."); + Logger.LogDebug("Skipping the rest of the Handshake packages."); } else { @@ -1533,7 +1534,7 @@ namespace MouseWithoutBorders.Class UpdateTcpSockets(currentTcp, SocketStatus.Error); FlagReopenSocketIfNeeded(e); currentSocket.Close(); - Common.Log(e); + Logger.Log(e); break; } } @@ -1550,7 +1551,7 @@ namespace MouseWithoutBorders.Class do { - Common.LogDebug("SendClipboardData: Waiting for request..."); + Logger.LogDebug("SendClipboardData: Waiting for request..."); Socket s = null; try @@ -1559,25 +1560,25 @@ namespace MouseWithoutBorders.Class } catch (InvalidOperationException e) { - Common.Log($"The clipboard socket could have been closed. {e.Message}"); + Logger.Log($"The clipboard socket could have been closed. {e.Message}"); break; } catch (SocketException e) { if (e.ErrorCode == (int)SocketError.Interrupted) { - Common.Log("server.AcceptSocket: A blocking socket call was canceled."); + Logger.Log("server.AcceptSocket: A blocking socket call was canceled."); continue; } else { - Common.Log(e); + Logger.Log(e); break; } } catch (Exception e) { - Common.Log(e); + Logger.Log(e); break; } @@ -1595,7 +1596,7 @@ namespace MouseWithoutBorders.Class } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } } } @@ -1607,7 +1608,7 @@ namespace MouseWithoutBorders.Class try { string remoteEndPoint = s.RemoteEndPoint.ToString(); - Common.LogDebug("SendClipboardData: Request accepted: " + s.LocalEndPoint.ToString() + "/" + remoteEndPoint); + Logger.LogDebug("SendClipboardData: Request accepted: " + s.LocalEndPoint.ToString() + "/" + remoteEndPoint); Common.IsDropping = false; Common.IsDragging = false; Common.DragMachine = (ID)1; @@ -1623,7 +1624,7 @@ namespace MouseWithoutBorders.Class } else { - Common.LogDebug($"{nameof(SendOrReceiveClipboardData)}: Clipboard connection accepted: " + remoteEndPoint); + Logger.LogDebug($"{nameof(SendOrReceiveClipboardData)}: Clipboard connection accepted: " + remoteEndPoint); Common.SetToggleIcon(new int[Common.TOGGLE_ICONS_SIZE] { Common.ICON_SMALL_CLIPBOARD, -1, -1, -1 }); } @@ -1638,7 +1639,7 @@ namespace MouseWithoutBorders.Class } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } } @@ -1699,17 +1700,17 @@ namespace MouseWithoutBorders.Class catch (IOException e) { string log = $"{nameof(SendClipboardData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; - Common.Log(log); + Logger.Log(log); } catch (SocketException e) { string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the connection is closed by the remote host."; - Common.Log(log); + Logger.Log(log); } catch (ObjectDisposedException e) { string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the socket is disposed by a machine switch for ex.."; - Common.Log(log); + Logger.Log(log); } } else if (!Common.IsClipboardDataImage && Common.LastClipboardData != null) @@ -1725,7 +1726,7 @@ namespace MouseWithoutBorders.Class { ecStream.Write(header, 0, header.Length); _ = SendData(s, ecStream, data); - Common.LogDebug("Text sent: " + data.Length.ToString(CultureInfo.CurrentCulture)); + Logger.LogDebug("Text sent: " + data.Length.ToString(CultureInfo.CurrentCulture)); } s.Close(CLOSE_TIMEOUT); @@ -1733,17 +1734,17 @@ namespace MouseWithoutBorders.Class catch (IOException e) { string log = $"{nameof(SendClipboardData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; - Common.Log(log); + Logger.Log(log); } catch (SocketException e) { string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the connection is closed by the remote host."; - Common.Log(log); + Logger.Log(log); } catch (ObjectDisposedException e) { string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the socket is disposed by a machine switch for ex.."; - Common.Log(log); + Logger.Log(log); } } else if (Common.LastClipboardData != null && Common.LastClipboardData.Length > 0) @@ -1756,28 +1757,28 @@ namespace MouseWithoutBorders.Class { ecStream.Write(header, 0, header.Length); _ = SendData(s, ecStream, data); - Common.LogDebug("Image sent: " + data.Length.ToString(CultureInfo.CurrentCulture)); + Logger.LogDebug("Image sent: " + data.Length.ToString(CultureInfo.CurrentCulture)); s.Close(CLOSE_TIMEOUT); } catch (IOException e) { string log = $"{nameof(SendClipboardData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; - Common.Log(log); + Logger.Log(log); } catch (SocketException e) { string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the connection is closed by the remote host."; - Common.Log(log); + Logger.Log(log); } catch (ObjectDisposedException e) { string log = $"{nameof(SendClipboardData)}: {e.GetType()}/{e.Message}. This is expected when the socket is disposed by a machine switch for ex.."; - Common.Log(log); + Logger.Log(log); } } else { - Common.Log("No data available in clipboard or LastDragDropFile!"); + Logger.Log("No data available in clipboard or LastDragDropFile!"); s.Close(); } } @@ -1809,7 +1810,7 @@ namespace MouseWithoutBorders.Class ecStream.Flush(); - Common.LogDebug("File sent: " + fileName); + Logger.LogDebug("File sent: " + fileName); } return true; @@ -1819,11 +1820,11 @@ namespace MouseWithoutBorders.Class if (e is IOException) { string log = $"{nameof(SendFileEx)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; - Common.Log(log); + Logger.Log(log); } else { - Common.Log(e); + Logger.Log(e); } Common.ShowToolTip(e.Message, 1000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); @@ -1879,7 +1880,7 @@ namespace MouseWithoutBorders.Class } ecStream.Flush(); - Common.LogDebug("Data sent: " + data.Length.ToString(CultureInfo.InvariantCulture)); + Logger.LogDebug("Data sent: " + data.Length.ToString(CultureInfo.InvariantCulture)); r = true; } catch (Exception e) @@ -1887,11 +1888,11 @@ namespace MouseWithoutBorders.Class if (e is IOException) { string log = $"{nameof(SendData)}: Exception accessing the socket: {e.InnerException?.GetType()}/{e.Message}. (This is expected when the remote machine closes the connection during desktop switch or reconnection.)"; - Common.Log(log); + Logger.Log(log); } else { - Common.Log(e); + Logger.Log(e); } Common.ShowToolTip(e.Message, 1000, ToolTipIcon.Warning, Setting.Values.ShowClipNetStatus); @@ -1960,7 +1961,7 @@ namespace MouseWithoutBorders.Class Common.UpdateMachineTimeAndID(); Common.PleaseReopenSocket = Common.REOPEN_WHEN_HOTKEY; - Common.TelemetryLogTrace("MachineID conflict.", SeverityLevel.Information); + Logger.TelemetryLogTrace("MachineID conflict.", SeverityLevel.Information); } else { @@ -1971,7 +1972,7 @@ namespace MouseWithoutBorders.Class foreach (TcpSk t in tobeRemovedSockets) { t.Status = SocketStatus.ForceClosed; - Common.LogDebug($"Closing duplicated socket {t.MachineName}: {t.Address}"); + Logger.LogDebug($"Closing duplicated socket {t.MachineName}: {t.Address}"); } } @@ -2001,12 +2002,12 @@ namespace MouseWithoutBorders.Class catch (SocketException e) { string log = $"{nameof(UpdateTcpSockets)}: {e.GetType()}/{e.Message}. This is expected when the connection is closed by the remote host."; - Common.Log(log); + Logger.Log(log); } catch (ObjectDisposedException e) { string log = $"{nameof(UpdateTcpSockets)}: {e.GetType()}/{e.Message}. This is expected when the socket is disposed by a machine switch for ex.."; - Common.Log(log); + Logger.Log(log); } } } @@ -2048,7 +2049,7 @@ namespace MouseWithoutBorders.Class } else { - Common.Log("UpdateTcpSockets.Exception: Socket not found!"); + Logger.Log("UpdateTcpSockets.Exception: Socket not found!"); } foreach (TcpSk t in toBeRemoved) @@ -2060,7 +2061,7 @@ namespace MouseWithoutBorders.Class } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } } diff --git a/src/modules/MouseWithoutBorders/App/Class/TcpServer.cs b/src/modules/MouseWithoutBorders/App/Class/TcpServer.cs index f7ea7ba094..ffff1ce76b 100644 --- a/src/modules/MouseWithoutBorders/App/Class/TcpServer.cs +++ b/src/modules/MouseWithoutBorders/App/Class/TcpServer.cs @@ -11,6 +11,8 @@ using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; +using MouseWithoutBorders.Core; + // // TCP Server implementation. // @@ -33,7 +35,7 @@ namespace MouseWithoutBorders.Class internal TcpServer(int port, ParameterizedThreadStart job) { - Common.Log($"TCP listening on port: {port}"); + Logger.Log($"TCP listening on port: {port}"); Name = port.ToString(CultureInfo.CurrentCulture); server = TcpListener.Create(port); StartServer(job); @@ -79,7 +81,7 @@ namespace MouseWithoutBorders.Class } else { - Common.TelemetryLogTrace($"Error listening on: {server.LocalEndpoint}: {e.ErrorCode}/{e.Message}", SeverityLevel.Error); + Logger.TelemetryLogTrace($"Error listening on: {server.LocalEndpoint}: {e.ErrorCode}/{e.Message}", SeverityLevel.Error); throw; } } @@ -99,7 +101,7 @@ namespace MouseWithoutBorders.Class } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } } diff --git a/src/modules/MouseWithoutBorders/App/Core/Logger.cs b/src/modules/MouseWithoutBorders/App/Core/Logger.cs new file mode 100644 index 0000000000..8d4c42e373 --- /dev/null +++ b/src/modules/MouseWithoutBorders/App/Core/Logger.cs @@ -0,0 +1,447 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Net; +using System.Net.Sockets; +using System.Reflection; +using System.Text; +using System.Windows.Forms; + +// +// Logging. +// +// +// 2008 created by Truong Do (ductdo). +// 2009-... modified by Truong Do (TruongDo). +// 2023- Included in PowerToys. +// +using MouseWithoutBorders.Class; +using MouseWithoutBorders.Exceptions; + +namespace MouseWithoutBorders.Core; + +internal static class Logger +{ + internal static readonly string[] AllLogs = new string[MAX_LOG]; + private static readonly object AllLogsLock = new(); + internal static readonly ConcurrentDictionary LogCounter = new(); + private const int MAX_LOG = 10000; + private static int allLogsIndex; + + private const int MaxLogExceptionPerHour = 1000; + private static int lastHour; + private static int exceptionCount; + + internal static void TelemetryLogTrace(string log, SeverityLevel severityLevel, bool flush = false) + { + int logCount = LogCounter.AddOrUpdate(log, 1, (key, value) => value + 1); + Logger.Log(log); + } + + internal static void Log(Exception e) + { + if (e is not KnownException) + { + string exText = e.ToString(); + + Log($"!Exception!: {exText}"); + + if (DateTime.UtcNow.Hour != lastHour) + { + lastHour = DateTime.UtcNow.Hour; + exceptionCount = 0; + } + + if (exceptionCount < MaxLogExceptionPerHour) + { + exceptionCount++; + } + else if (exceptionCount != short.MaxValue) + { + exceptionCount = short.MaxValue; + } + } + } + + private const string HeaderSENT = + "Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},Ie{12},Ni{13}"; + + private const string HeaderRECEIVED = + "Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},In{12},Ni{13},Pc{14}/{15}"; + + internal static void LogDebug(string log, bool clearLog = false) + { +#if DEBUG + Log(log, clearLog); +#endif + } + + internal static void Log(string log, bool clearLog = false) + { + log = DateTime.Now.ToString("MM/dd HH:mm:ss.fff", CultureInfo.InvariantCulture) + $"({Thread.CurrentThread.ManagedThreadId})" + log; + + ManagedCommon.Logger.LogInfo(log); + lock (AllLogsLock) + { + if (clearLog) + { + allLogsIndex = 0; + } + + AllLogs[allLogsIndex] = log; + allLogsIndex = (allLogsIndex + 1) % MAX_LOG; + } + } + + internal static void LogDebug(string format, params object[] args) + { +#if DEBUG + Logger.Log(format, args); +#endif + } + + private static void Log(string format, params object[] args) + { + Logger.Log(string.Format(CultureInfo.InvariantCulture, format, args)); + } + + private static PackageMonitor lastPackageSent; + private static PackageMonitor lastPackageReceived; + + [Conditional("DEBUG")] + internal static void LogAll() + { + string log; + + if (!lastPackageSent.Equals(Common.PackageSent)) + { + log = string.Format( + CultureInfo.CurrentCulture, + "SENT:" + HeaderSENT, + Common.PackageSent.Heartbeat, + Common.PackageSent.Keyboard, + Common.PackageSent.Mouse, + Common.PackageSent.Hello, + Common.PackageSent.Matrix, + Common.PackageSent.ClipboardText, + Common.PackageSent.ClipboardImage, + Common.PackageSent.ByeBye, + Common.PackageSent.Clipboard, + Common.PackageSent.ClipboardDragDrop, + Common.PackageSent.ClipboardDragDropEnd, + Common.PackageSent.ExplorerDragDrop, + Common.inputEventCount, + Common.PackageSent.Nil); + Log(log); + lastPackageSent = Common.PackageSent; // Copy data + } + + if (!lastPackageReceived.Equals(Common.PackageReceived)) + { + log = string.Format( + CultureInfo.CurrentCulture, + "RECEIVED:" + HeaderRECEIVED, + Common.PackageReceived.Heartbeat, + Common.PackageReceived.Keyboard, + Common.PackageReceived.Mouse, + Common.PackageReceived.Hello, + Common.PackageReceived.Matrix, + Common.PackageReceived.ClipboardText, + Common.PackageReceived.ClipboardImage, + Common.PackageReceived.ByeBye, + Common.PackageReceived.Clipboard, + Common.PackageReceived.ClipboardDragDrop, + Common.PackageReceived.ClipboardDragDropEnd, + Common.PackageReceived.ExplorerDragDrop, + Common.invalidPackageCount, + Common.PackageReceived.Nil, + Common.processedPackageCount, + Common.skippedPackageCount); + Log(log); + lastPackageReceived = Common.PackageReceived; + } + } + + internal static void GenerateLog() + { + int l = Setting.Values.DumpObjectsLevel; + if (l is > 0 and < 10) + { + Logger.DumpObjects(l); + } + } + + private static List myThreads; + + internal static void DumpObjects(int level) + { + try + { + string logFile = Path.Combine(Common.RunWithNoAdminRight ? Path.GetTempPath() : Path.GetDirectoryName(Application.ExecutablePath), "MagicMouse.log"); + + StringBuilder sb = new(1000000); + string log; + + myThreads = new List(); + + foreach (ProcessThread t in Process.GetCurrentProcess().Threads) + { + myThreads.Add(t); + } + + _ = Logger.PrivateDump(sb, AllLogs, "[Program logs]\r\n===============\r\n", 0, level, false); + _ = Logger.PrivateDump(sb, new Common(), "[Other Logs]\r\n===============\r\n", 0, level, false); + sb.AppendLine("[Logger]\r\n==============="); + Logger.DumpType(sb, typeof(Logger), 0, level); + + log = string.Format( + CultureInfo.CurrentCulture, + "{0} {1}\r\n{2}\r\n\r\n{3}", + Application.ProductName, + Application.ProductVersion, + "Private Mem: " + (Process.GetCurrentProcess().PrivateMemorySize64 / 1024).ToString(CultureInfo.CurrentCulture) + "KB", + sb.ToString()); + + if (!string.IsNullOrEmpty(Common.myKey)) + { + log = log.Replace(Common.MyKey, Common.GetDebugInfo(Common.MyKey)); + } + + log += Thread.DumpThreadsStack(); + log += $"\r\nCurrent process session: {Process.GetCurrentProcess().SessionId}, active console session: {NativeMethods.WTSGetActiveConsoleSessionId()}."; + + File.WriteAllText(logFile, log); + + if (Common.RunOnLogonDesktop || Common.RunOnScrSaverDesktop) + { + _ = MessageBox.Show("Dump file created: " + logFile, Application.ProductName); + } + else + { + Common.ShowToolTip("Dump file created: " + logFile + " and placed in the Clipboard.", 10000); + Clipboard.SetText(logFile); + } + } + catch (Exception e) + { + _ = MessageBox.Show(e.Message + "\r\n" + e.StackTrace, Application.ProductName); + } + } + + internal static bool PrivateDump(StringBuilder sb, object obj, string objName, int level, int maxLevel, bool stop) + { + Type t; + string padStr = string.Empty; + string[] strArr; + string objString; + + if (obj == null || (maxLevel >= 0 && level >= maxLevel) || obj is Cursor) + { + return false; + } + + for (int i = 0; i < level; i++) + { + padStr += i < level - 1 ? "-" : padStr += string.Empty; + } + + objString = obj.ToString(); + t = obj.GetType(); + strArr = new string[7]; + strArr[0] = padStr; + strArr[1] = objName; + + // strArr[2] = " "; + // strArr[3] = t.FullName; + strArr[4] = " = "; + strArr[5] = objName.Equals("myKey", StringComparison.OrdinalIgnoreCase) + ? Common.GetDebugInfo(objString) + : objName.Equals("lastClipboardObject", StringComparison.OrdinalIgnoreCase) + ? string.Empty + : objString + .Replace("System.Windows.Forms.", string.Empty) + .Replace("System.Net.Sockets.", string.Empty) + .Replace("System.Security.Cryptography.", string.Empty) + .Replace("System.Threading.", string.Empty) + .Replace("System.ComponentModel.", string.Empty) + .Replace("System.Runtime.", string.Empty) + .Replace("System.Drawing.", string.Empty) + .Replace("System.Object", "O") + .Replace("System.Diagnostics.", string.Empty) + .Replace("System.Collections.", string.Empty) + .Replace("System.Drawing.", string.Empty) + .Replace("System.Int", string.Empty) + .Replace("System.EventHandler.", string.Empty); + strArr[6] = "\r\n"; + _ = sb.Append(string.Concat(strArr).Replace(Common.BinaryName, "MM")); + + if (stop || t.IsPrimitive) + { + return false; + } + + Logger.DumpObject(sb, obj, level, t, maxLevel); + return true; + } + + internal static void DumpObject(StringBuilder sb, object obj, int level, Type t, int maxLevel) + { + int i; + bool stop; + if (t == typeof(Delegate)) + { + return; + } + + FieldInfo[] fi; + string type; + + if (obj is PackageType or string or AddressFamily or ID or IPAddress) + { + return; + } + + type = obj.GetType().ToString(); + + if (type.EndsWith("type", StringComparison.CurrentCultureIgnoreCase) || type.Contains("Cryptography") + || type.EndsWith("AsyncEventBits", StringComparison.CurrentCultureIgnoreCase)) + { + return; + } + + stop = obj == null || obj is DATA || obj.GetType().BaseType == typeof(ValueType) + || obj.GetType().Namespace.Contains("System.Windows"); + fi = t.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + + foreach (FieldInfo f in fi) + { + if (f.GetValue(obj) != AllLogs) + { + _ = PrivateDump(sb, f.GetValue(obj), f.Name, level + 1, maxLevel, stop); + } + } + + if (obj is Dictionary>) + { + Dictionary> d = obj as Dictionary>; + + foreach (string k in d.Keys) + { + if (d.TryGetValue(k, out List l)) + { + foreach (IPAddress ip in l) + { + _ = PrivateDump(sb, ip, "[" + k + "]", level + 1, maxLevel, false); + } + } + } + } + + if (obj is Array) + { + try + { + if (obj is MachineInf[]) + { + MachineInf[] os = (MachineInf[])obj; + + for (i = 0; i < os.GetLength(0); i++) + { + _ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false); + } + } + else if (obj is int[] || obj is uint[]) + { + int[] os = (int[])obj; + + for (i = 0; i < os.GetLength(0); i++) + { + _ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false); + } + } + else if (obj is short[] || obj is ushort[]) + { + short[] os = (short[])obj; + + for (i = 0; i < os.GetLength(0); i++) + { + _ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false); + } + } + else if (obj is TcpClient[] || obj is IPAddress[] || obj is TcpSk[] || obj is string[] + || obj is TcpServer[] + || obj is ProcessThread[] || obj is Thread[]) + { + object[] os = (object[])obj; + + for (i = 0; i < os.GetLength(0); i++) + { + _ = PrivateDump(sb, os[i], "[" + i + "]", level + 1, maxLevel, false); + } + } + else + { + _ = PrivateDump(sb, obj.GetType().ToString() + ": N/A", obj.GetType().ToString(), level + 1, maxLevel, false); + } + } + catch (Exception) + { + } + } + } + + internal static void DumpType(StringBuilder sb, Type typeToDump, int level, int maxLevel) + { + if ((typeToDump == typeof(Delegate)) + || (typeToDump == typeof(PackageType)) + || (typeToDump == typeof(string)) + || (typeToDump == typeof(AddressFamily)) + || (typeToDump == typeof(ID)) + || (typeToDump == typeof(IPAddress))) + { + return; + } + + var typeFullName = typeToDump.ToString(); + if (typeFullName.EndsWith("type", StringComparison.CurrentCultureIgnoreCase) + || typeFullName.Contains("Cryptography") + || typeFullName.EndsWith("AsyncEventBits", StringComparison.CurrentCultureIgnoreCase)) + { + return; + } + + var stop = (typeToDump == null) + || (typeToDump == typeof(DATA)) + || (typeToDump.BaseType == typeof(ValueType)) + || typeToDump.Namespace.Contains("System.Windows"); + + var fieldInfos = typeToDump.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static); + foreach (var fieldInfo in fieldInfos) + { + if (fieldInfo.GetValue(null) != AllLogs) + { + _ = Logger.PrivateDump(sb, fieldInfo.GetValue(null), fieldInfo.Name, level + 1, maxLevel, stop); + } + } + } + + internal static string GetStackTrace(StackTrace st) + { + string rv = string.Empty; + + for (int i = 0; i < st.FrameCount; i++) + { + StackFrame sf = st.GetFrame(i); + rv += sf.GetMethod() + " <= "; + } + + return rv; + } +} diff --git a/src/modules/MouseWithoutBorders/App/Form/Settings/SettingsForm.cs b/src/modules/MouseWithoutBorders/App/Form/Settings/SettingsForm.cs index c8977b782a..768e0bdadc 100644 --- a/src/modules/MouseWithoutBorders/App/Form/Settings/SettingsForm.cs +++ b/src/modules/MouseWithoutBorders/App/Form/Settings/SettingsForm.cs @@ -5,7 +5,7 @@ using System; using System.Drawing; using System.Windows.Forms; - +using MouseWithoutBorders.Core; using MouseWithoutBorders.Form.Settings; namespace MouseWithoutBorders @@ -23,9 +23,9 @@ namespace MouseWithoutBorders toolTipManual.ToolTipTitle = Application.ProductName; Text = Application.ProductName; - Common.LogDebug("FIRST RUN, SHOWING THE FIRST SETUP PAGE."); + Logger.LogDebug("FIRST RUN, SHOWING THE FIRST SETUP PAGE."); - Common.LogDebug($"{nameof(Common.RunWithNoAdminRight)} = {Common.RunWithNoAdminRight}"); + Logger.LogDebug($"{nameof(Common.RunWithNoAdminRight)} = {Common.RunWithNoAdminRight}"); if (Common.RunWithNoAdminRight) { @@ -43,7 +43,7 @@ namespace MouseWithoutBorders if (_currentPage != null) { - Common.LogDebug(_currentPage.Name + " closing."); + Logger.LogDebug(_currentPage.Name + " closing."); _currentPage.OnPageClosing(); } @@ -67,7 +67,7 @@ namespace MouseWithoutBorders if (page != null) { - Common.LogDebug("GOING TO NEXT PAGE: " + page.Name); + Logger.LogDebug("GOING TO NEXT PAGE: " + page.Name); page.BackColor = Color.Transparent; page.NextPage += PageNextPage; page.Location = contentPanel.Location; diff --git a/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage3a.cs b/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage3a.cs index bd08682585..4c8c3cd2eb 100644 --- a/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage3a.cs +++ b/src/modules/MouseWithoutBorders/App/Form/Settings/SetupPage3a.cs @@ -7,6 +7,7 @@ using System.Drawing; using System.Windows.Forms; using MouseWithoutBorders.Class; +using MouseWithoutBorders.Core; using MouseWithoutBorders.Properties; namespace MouseWithoutBorders @@ -42,7 +43,7 @@ namespace MouseWithoutBorders private void ShowStatus(string status) { labelStatus.Text = status; - Common.Log(status); + Logger.Log(status); } public override void OnPageClosing() diff --git a/src/modules/MouseWithoutBorders/App/Form/frmInputCallback.cs b/src/modules/MouseWithoutBorders/App/Form/frmInputCallback.cs index fdb76ed901..edd35035ad 100644 --- a/src/modules/MouseWithoutBorders/App/Form/frmInputCallback.cs +++ b/src/modules/MouseWithoutBorders/App/Form/frmInputCallback.cs @@ -16,6 +16,7 @@ using System.Windows.Forms; // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; +using MouseWithoutBorders.Core; [module: SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Scope = "member", Target = "MouseWithoutBorders.frmInputCallback.#InstallKeyboardAndMouseHook()", Justification = "Dotnet port with style preservation")] @@ -65,7 +66,7 @@ namespace MouseWithoutBorders Common.Hook.MouseEvent += new InputHook.MouseEvHandler(Common.MouseEvent); Common.Hook.KeyboardEvent += new InputHook.KeybdEvHandler(Common.KeybdEvent); - Common.Log("(((((Keyboard/Mouse hooks installed/reinstalled!)))))"); + Logger.Log("(((((Keyboard/Mouse hooks installed/reinstalled!)))))"); /* The hook is called in the context of the thread that installed it. * The call is made by sending a message to the thread that installed the hook. * Therefore, the thread that installed the hook must have a message loop!!! diff --git a/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs b/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs index d00215e01a..fff0199069 100644 --- a/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs +++ b/src/modules/MouseWithoutBorders/App/Form/frmMatrix.cs @@ -21,7 +21,7 @@ using Microsoft.PowerToys.Telemetry; // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; - +using MouseWithoutBorders.Core; using Timer = System.Windows.Forms.Timer; [module: SuppressMessage("Microsoft.Globalization", "CA1300:SpecifyMessageBoxOptions", Scope = "member", Target = "MouseWithoutBorders.frmMatrix.#buttonOK_Click(System.Object,System.EventArgs)", Justification = "Dotnet port with style preservation")] @@ -151,7 +151,7 @@ namespace MouseWithoutBorders if (Common.MachineMatrix != null && Common.MachineMatrix.Length == Common.MAX_MACHINE) { - Common.LogDebug("LoadMachines: Machine Matrix: " + Setting.Values.MachineMatrixString); + Logger.LogDebug("LoadMachines: Machine Matrix: " + Setting.Values.MachineMatrixString); for (int i = 0; i < Common.MAX_MACHINE; i++) { @@ -329,8 +329,8 @@ namespace MouseWithoutBorders } catch (Exception ee) { - Common.Log(ee); - Common.Log(rv.ToString(CultureInfo.CurrentCulture)); + Logger.Log(ee); + Logger.Log(rv.ToString(CultureInfo.CurrentCulture)); } } diff --git a/src/modules/MouseWithoutBorders/App/Form/frmScreen.cs b/src/modules/MouseWithoutBorders/App/Form/frmScreen.cs index bf79bef841..0df3454d80 100644 --- a/src/modules/MouseWithoutBorders/App/Form/frmScreen.cs +++ b/src/modules/MouseWithoutBorders/App/Form/frmScreen.cs @@ -22,6 +22,7 @@ using Microsoft.PowerToys.Telemetry; // 2023- Included in PowerToys. // using MouseWithoutBorders.Class; +using MouseWithoutBorders.Core; using MouseWithoutBorders.Properties; using Timer = System.Windows.Forms.Timer; @@ -81,7 +82,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } } @@ -232,7 +233,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } } @@ -273,7 +274,7 @@ namespace MouseWithoutBorders { BackColor = Color.White; Opacity = 0.15; - Common.Log(ex); + Logger.Log(ex); } helperTimer = new System.Windows.Forms.Timer(); @@ -355,7 +356,7 @@ namespace MouseWithoutBorders if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && !Common.GetUserName()) { // While Windows 8 is hybrid-shutting down, user name would be empty (as returned from the .Net API), we should not do anything in this case. - Common.LogDebug("No active user."); + Logger.LogDebug("No active user."); Thread.Sleep(1000); busy = false; return; @@ -432,7 +433,7 @@ namespace MouseWithoutBorders if (!Common.AtLeastOneSocketEstablished()) { Common.GetMachineName(); - Common.LogDebug("Common.pleaseReopenSocket: " + Common.PleaseReopenSocket.ToString(CultureInfo.InvariantCulture)); + Logger.LogDebug("Common.pleaseReopenSocket: " + Common.PleaseReopenSocket.ToString(CultureInfo.InvariantCulture)); Common.ReopenSockets(false); Common.NewDesMachineID = Common.DesMachineID = Common.MachineID; } @@ -545,7 +546,7 @@ namespace MouseWithoutBorders if (count % 20 == 0) { - Common.LogAll(); + Logger.LogAll(); // Need to review this code on why it is needed (moved from MoveToMyNeighbourIfNeeded(...)) for (int i = 0; i < Common.MachineMatrix.Length; i++) @@ -579,14 +580,14 @@ namespace MouseWithoutBorders if (!Common.RunOnLogonDesktop && !Common.RunOnScrSaverDesktop && Common.IsMyDesktopActive() && (rv = Common.SendMessageToHelper(0x400, IntPtr.Zero, IntPtr.Zero)) <= 0) { - Common.TelemetryLogTrace($"{Common.HELPER_FORM_TEXT} not found: {rv}", SeverityLevel.Warning); + Logger.TelemetryLogTrace($"{Common.HELPER_FORM_TEXT} not found: {rv}", SeverityLevel.Warning); } } } } catch (Exception ex) { - Common.Log(ex); + Logger.Log(ex); } finally { @@ -678,7 +679,7 @@ namespace MouseWithoutBorders p.Dispose(); #endif - Common.LogDebug($"Changing icon to {iconCode}."); + Logger.LogDebug($"Changing icon to {iconCode}."); if (NotifyIcon.Icon != null) { @@ -690,13 +691,13 @@ namespace MouseWithoutBorders } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } } internal void MenuAllPC_Click(object sender, EventArgs e) { - Common.LogDebug("menuAllPC_Click"); + Logger.LogDebug("menuAllPC_Click"); Common.SwitchToMultipleMode(MenuAllPC.Checked, true); CurIcon = MenuAllPC.Checked ? Common.ICON_ALL : Common.ICON_ONE; ChangeIcon(CurIcon); @@ -801,7 +802,7 @@ namespace MouseWithoutBorders if (h.ToInt32() > 0) { - Common.LogDebug("Hide Mouse Without Borders Helper."); + Logger.LogDebug("Hide Mouse Without Borders Helper."); // Common.ShowWindow(h, 1); _ = NativeMethods.ShowWindow(h, 0); @@ -813,7 +814,7 @@ namespace MouseWithoutBorders break; case NativeMethods.WM_CHECK_EXPLORER_DRAG_DROP: - Common.LogDebug("Got WM_CHECK_EXPLORER_DRAG_DROP!"); + Logger.LogDebug("Got WM_CHECK_EXPLORER_DRAG_DROP!"); Common.DragDropStep04(); break; @@ -825,7 +826,7 @@ namespace MouseWithoutBorders break; case WM_QUERYENDSESSION: - Common.LogDebug("WM_QUERYENDSESSION..."); + Logger.LogDebug("WM_QUERYENDSESSION..."); Common.StartServiceAndSendLogoffSignal(); break; @@ -993,7 +994,7 @@ namespace MouseWithoutBorders } catch (Exception e) { - Common.Log(e); + Logger.Log(e); } } @@ -1210,7 +1211,7 @@ namespace MouseWithoutBorders private void MenuGenDumpFile_Click(object sender, EventArgs e) { - Common.GenerateLog(); + Logger.GenerateLog(); } private void MainMenu_Opening(object sender, CancelEventArgs e) diff --git a/src/modules/MouseWithoutBorders/App/Helper/FormHelper.cs b/src/modules/MouseWithoutBorders/App/Helper/FormHelper.cs index f36bd0292a..969ba9db37 100644 --- a/src/modules/MouseWithoutBorders/App/Helper/FormHelper.cs +++ b/src/modules/MouseWithoutBorders/App/Helper/FormHelper.cs @@ -100,7 +100,7 @@ namespace MouseWithoutBorders if (Process.GetCurrentProcess().SessionId != NativeMethods.WTSGetActiveConsoleSessionId()) { - Logger.LogEvent(Application.ProductName + " cannot be used in a remote desktop or virtual machine session."); + EventLogger.LogEvent(Application.ProductName + " cannot be used in a remote desktop or virtual machine session."); } else { @@ -135,7 +135,7 @@ namespace MouseWithoutBorders } catch (Exception ex) { - Logger.LogEvent("FormHelper_DragEnter: " + ex.Message, EventLogEntryType.Error); + EventLogger.LogEvent("FormHelper_DragEnter: " + ex.Message, EventLogEntryType.Error); QuitDueToCommunicationError(); } } @@ -164,11 +164,11 @@ namespace MouseWithoutBorders { try { - Logger.LogEvent(log, EventLogEntryType.Warning); + EventLogger.LogEvent(log, EventLogEntryType.Warning); } catch (Exception e) { - Logger.LogEvent(log + " ==> SendLog Exception: " + e.Message, EventLogEntryType.Warning); + EventLogger.LogEvent(log + " ==> SendLog Exception: " + e.Message, EventLogEntryType.Warning); } } @@ -343,7 +343,7 @@ namespace MouseWithoutBorders } catch (Exception ex) { - Logger.LogEvent("WM_DRAWCLIPBOARD: " + ex.Message, EventLogEntryType.Error); + EventLogger.LogEvent("WM_DRAWCLIPBOARD: " + ex.Message, EventLogEntryType.Error); QuitDueToCommunicationError(); } diff --git a/src/modules/MouseWithoutBorders/App/Properties/AssemblyInfo.cs b/src/modules/MouseWithoutBorders/App/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..b031b89509 --- /dev/null +++ b/src/modules/MouseWithoutBorders/App/Properties/AssemblyInfo.cs @@ -0,0 +1,7 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("MouseWithoutBorders.UnitTests")] diff --git a/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/Logger.PrivateDump.expected.txt b/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/Logger.PrivateDump.expected.txt new file mode 100644 index 0000000000..1a20c67b31 --- /dev/null +++ b/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/Logger.PrivateDump.expected.txt @@ -0,0 +1,404 @@ +[Program logs] +=============== + = System.String[] +[Other Logs] +=============== + = MouseWithoutBorders.Common +lastClipboardEventTime = 0 +clipboardCopiedTime = 0 +Comma = System.Char[] +--System.Char[] = System.Char[]: N/A +Star = System.Char[] +--System.Char[] = System.Char[]: N/A +NullSeparator = System.Char[] +--System.Char[] = System.Char[]: N/A +k__BackingField = NONE +k__BackingField = 0 +k__BackingField = False +lastClipboardObject = +k__BackingField = False +ClipboardThreadOldLock = O +screenWidth = 0 +screenHeight = 0 +lastX = 0 +lastY = 0 +mainFormVisible = True +runOnLogonDesktop = False +runOnScrSaverDesktop = False +toggleIconsIndex = 0 +EvSwitch = EventWaitHandle +lastPos = {X=0,Y=0} +--x = 0 +--y = 0 +--Empty = {X=0,Y=0} +switchCount = 0 +lastReconnectByHotKeyTime = 0 +tcpPort = 0 +secondOpenSocketTry = False +binaryName = MM +k__BackingField = 0 +k__BackingField = False +k__BackingField = False +socketMutexBalance = 0 +k__BackingField = False +k__BackingField = 0 +k__BackingField = 0 +InputSimulationLock = O +lastSendNextMachine = 0 +lastInputEventCount = 0 +lastRealInputEventCount = 0 +minSendTime = 9223372036854775807 +avgSendTime = 0 +maxSendTime = 0 +totalSendCount = 0 +totalSendTime = 0 +isDragging = False +dragDropStep05ExCalledByIpc = 0 +isDropping = False +dragMachine = NONE +k__BackingField = False +magicNumber = 0 +ran = System.Random +--_impl = System.Random+XoshiroImpl +----_s0 = ???????????? +----_s1 = ???????????? +----_s2 = ???????????? +----_s3 = ???????????? +--k__BackingField = System.Random+ThreadSafeRandom +InitialIV = ???????????? +k__BackingField = False +k__BackingField = False +LegalKeyDictionary = Concurrent.ConcurrentDictionary`2[System.String,System.Byte[]] +--_tables = Concurrent.ConcurrentDictionary`2+Tables[System.String,System.Byte[]] +----_comparer = Generic.NonRandomizedStringEqualityComparer+OrdinalIgnoreCaseComparer +----_buckets = Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,System.Byte[]][] +------System.Collections.Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,System.Byte[]][] = Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,System.Byte[]][]: N/A +----_fastModBucketsMultiplier = 498560650640798693 +----_locks = O[] +------System.Object[] = O[]: N/A +----_countPerLock = 32[] +------[0] = 0 +------[1] = 0 +------[2] = 0 +------[3] = 0 +------[4] = 0 +------[5] = 0 +------[6] = 0 +------[7] = 0 +------[8] = 0 +------[9] = 0 +------[10] = 0 +------[11] = 0 +------[12] = 0 +------[13] = 0 +------[14] = 0 +------[15] = 0 +--_budget = 2 +--_growLockArray = True +--_comparerIsDefaultForClasses = False +KeybdPackage = MouseWithoutBorders.DATA +--Type = 0 +--Id = 0 +--Src = NONE +--Des = NONE +--DateTime = 0 +--Kd = MouseWithoutBorders.KEYBDDATA +--Md = MouseWithoutBorders.MOUSEDATA +--Machine1 = NONE +--Machine2 = NONE +--Machine3 = NONE +--Machine4 = NONE +--PostAction = Other +--machineNameP1 = 0 +--machineNameP2 = 0 +--machineNameP3 = 0 +--machineNameP4 = 0 +MousePackage = MouseWithoutBorders.DATA +--Type = 0 +--Id = 0 +--Src = NONE +--Des = NONE +--DateTime = 0 +--Kd = MouseWithoutBorders.KEYBDDATA +--Md = MouseWithoutBorders.MOUSEDATA +--Machine1 = NONE +--Machine2 = NONE +--Machine3 = NONE +--Machine4 = NONE +--PostAction = Other +--machineNameP1 = 0 +--machineNameP2 = 0 +--machineNameP3 = 0 +--machineNameP4 = 0 +inputEventCount = 0 +invalidPackageCount = 0 +MOVE_MOUSE_RELATIVE = 100000 +XY_BY_PIXEL = 300000 +k__BackingField = 0 +actualLastPos = {X=0,Y=0} +--x = 0 +--y = 0 +--Empty = {X=0,Y=0} +myLastX = 0 +myLastY = 0 +signalHelperToExit = False +signalWatchDogToExit = False +WndProcCounter = 0 +initDone = False +REOPEN_WHEN_WSAECONNRESET = -10054 +REOPEN_WHEN_HOTKEY = -10055 +PleaseReopenSocket = 0 +ReopenSocketDueToReadError = False +k__BackingField = ???????????? +--_dateData = ???????????? +--MinValue = 01/01/0001 00:00:00 +--MaxValue = 31/12/9999 23:59:59 +--UnixEpoch = 01/01/1970 00:00:00 +lastReleaseAllKeysCall = 0 +McMatrixLock = O +desMachineID = NONE +DesMachineName = +newDesMachineID = NONE +newDesMachineIdEx = NONE +dropMachineID = NONE +lastJump = ???????????? +desktopBounds = MouseWithoutBorders.MyRectangle +--Left = 0 +--Top = 0 +--Right = 0 +--Bottom = 0 +primaryScreenBounds = MouseWithoutBorders.MyRectangle +--Left = 0 +--Top = 0 +--Right = 0 +--Bottom = 0 +SwitchLocation = MouseWithoutBorders.Class.MouseLocation +--k__BackingField = 0 +--k__BackingField = 0 +--k__BackingField = 0 +PackageSent = MouseWithoutBorders.PackageMonitor +--Keyboard = 0 +--Mouse = 0 +--Heartbeat = 0 +--ByeBye = 0 +--Hello = 0 +--Matrix = 0 +--ClipboardText = 0 +--ClipboardImage = 0 +--Clipboard = 0 +--ClipboardDragDrop = 0 +--ClipboardDragDropEnd = 0 +--ClipboardAsk = 0 +--ExplorerDragDrop = 0 +--Nil = 0 +PackageReceived = MouseWithoutBorders.PackageMonitor +--Keyboard = 0 +--Mouse = 0 +--Heartbeat = 0 +--ByeBye = 0 +--Hello = 0 +--Matrix = 0 +--ClipboardText = 0 +--ClipboardImage = 0 +--Clipboard = 0 +--ClipboardDragDrop = 0 +--ClipboardDragDropEnd = 0 +--ClipboardAsk = 0 +--ExplorerDragDrop = 0 +--Nil = 0 +PackageID = 0 +QUEUE_SIZE = 50 +RecentProcessedPackageIDs = 32[] +--[0] = 0 +--[1] = 0 +--[2] = 0 +--[3] = 0 +--[4] = 0 +--[5] = 0 +--[6] = 0 +--[7] = 0 +--[8] = 0 +--[9] = 0 +--[10] = 0 +--[11] = 0 +--[12] = 0 +--[13] = 0 +--[14] = 0 +--[15] = 0 +--[16] = 0 +--[17] = 0 +--[18] = 0 +--[19] = 0 +--[20] = 0 +--[21] = 0 +--[22] = 0 +--[23] = 0 +--[24] = 0 +--[25] = 0 +--[26] = 0 +--[27] = 0 +--[28] = 0 +--[29] = 0 +--[30] = 0 +--[31] = 0 +--[32] = 0 +--[33] = 0 +--[34] = 0 +--[35] = 0 +--[36] = 0 +--[37] = 0 +--[38] = 0 +--[39] = 0 +--[40] = 0 +--[41] = 0 +--[42] = 0 +--[43] = 0 +--[44] = 0 +--[45] = 0 +--[46] = 0 +--[47] = 0 +--[48] = 0 +--[49] = 0 +recentProcessedPackageIndex = 0 +processedPackageCount = 0 +skippedPackageCount = 0 +k__BackingField = 0 +lastXY = {X=0,Y=0} +--x = 0 +--y = 0 +--Empty = {X=0,Y=0} +shownErrMessage = False +lastStartServiceTime = ???????????? +--_dateData = ???????????? +--MinValue = 01/01/0001 00:00:00 +--MaxValue = 31/12/9999 23:59:59 +--UnixEpoch = 01/01/1970 00:00:00 +SensitivePoints = Generic.List`1[Point] +--_items = Point[] +----System.Drawing.Point[] = Point[]: N/A +--_size = 0 +--_version = 0 +--s_emptyArray = Point[] +----System.Drawing.Point[] = Point[]: N/A +p = {X=0,Y=0} +--x = 0 +--y = 0 +--Empty = {X=0,Y=0} +k__BackingField = False +BIG_CLIPBOARD_DATA_TIMEOUT = 30000 +MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP = 1048576 +MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT = 104857600 +TEXT_HEADER_SIZE = 12 +DATA_SIZE = 48 +TEXT_TYPE_SEP = {4CFF57F7-BEDD-43d5-AE8F-27A61E886F2F} +TOGGLE_ICONS_SIZE = 4 +ICON_ONE = 0 +ICON_ALL = 1 +ICON_SMALL_CLIPBOARD = 2 +ICON_BIG_CLIPBOARD = 3 +ICON_ERROR = 4 +JUST_GOT_BACK_FROM_SCREEN_SAVER = 9999 +NETWORK_STREAM_BUF_SIZE = 1048576 +SymAlBlockSize = 16 +PW_LENGTH = 16 +HELPER_FORM_TEXT = Mouse without Borders Helper +HelperProcessName = PowerToys.MouseWithoutBordersHelper +MAX_MACHINE = 4 +MAX_SOCKET = 8 +HEARTBEAT_TIMEOUT = 1500000 +SKIP_PIXELS = 1 +JUMP_PIXELS = 2 +PACKAGE_SIZE = 32 +PACKAGE_SIZE_EX = 64 +WP_PACKAGE_SIZE = 6 +KEYEVENTF_KEYDOWN = 1 +KEYEVENTF_KEYUP = 2 +WH_MOUSE = 7 +WH_KEYBOARD = 2 +WH_MOUSE_LL = 14 +WH_KEYBOARD_LL = 13 +WM_MOUSEMOVE = 512 +WM_LBUTTONDOWN = 513 +WM_RBUTTONDOWN = 516 +WM_MBUTTONDOWN = 519 +WM_XBUTTONDOWN = 523 +WM_LBUTTONUP = 514 +WM_RBUTTONUP = 517 +WM_MBUTTONUP = 520 +WM_XBUTTONUP = 524 +WM_LBUTTONDBLCLK = 515 +WM_RBUTTONDBLCLK = 518 +WM_MBUTTONDBLCLK = 521 +WM_MOUSEWHEEL = 522 +WM_KEYDOWN = 256 +WM_KEYUP = 257 +WM_SYSKEYDOWN = 260 +WM_SYSKEYUP = 261 +[Logger Logs] +=============== +AllLogsLock = O +LogCounter = Concurrent.ConcurrentDictionary`2[System.String,32] +--_tables = Concurrent.ConcurrentDictionary`2+Tables[System.String,32] +----_comparer = Generic.NonRandomizedStringEqualityComparer+OrdinalComparer +----_buckets = Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,32][] +------System.Collections.Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,System.Int32][] = Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,32][]: N/A +----_fastModBucketsMultiplier = 498560650640798693 +----_locks = O[] +------System.Object[] = O[]: N/A +----_countPerLock = 32[] +------[0] = 0 +------[1] = 0 +------[2] = 0 +------[3] = 0 +------[4] = 0 +------[5] = 0 +------[6] = 0 +------[7] = 0 +------[8] = 0 +------[9] = 0 +------[10] = 0 +------[11] = 0 +------[12] = 0 +------[13] = 0 +------[14] = 0 +------[15] = 0 +--_budget = 2 +--_growLockArray = True +--_comparerIsDefaultForClasses = False +allLogsIndex = 0 +lastHour = 0 +exceptionCount = 0 +lastPackageSent = MouseWithoutBorders.PackageMonitor +--Keyboard = 0 +--Mouse = 0 +--Heartbeat = 0 +--ByeBye = 0 +--Hello = 0 +--Matrix = 0 +--ClipboardText = 0 +--ClipboardImage = 0 +--Clipboard = 0 +--ClipboardDragDrop = 0 +--ClipboardDragDropEnd = 0 +--ClipboardAsk = 0 +--ExplorerDragDrop = 0 +--Nil = 0 +lastPackageReceived = MouseWithoutBorders.PackageMonitor +--Keyboard = 0 +--Mouse = 0 +--Heartbeat = 0 +--ByeBye = 0 +--Hello = 0 +--Matrix = 0 +--ClipboardText = 0 +--ClipboardImage = 0 +--Clipboard = 0 +--ClipboardDragDrop = 0 +--ClipboardDragDropEnd = 0 +--ClipboardAsk = 0 +--ExplorerDragDrop = 0 +--Nil = 0 +MAX_LOG = 10000 +MaxLogExceptionPerHour = 1000 +HeaderSENT = Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},Ie{12},Ni{13} +HeaderRECEIVED = Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},In{12},Ni{13},Pc{14}/{15} diff --git a/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/Logger.PrivateDump.original.txt b/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/Logger.PrivateDump.original.txt new file mode 100644 index 0000000000..04346ea04a --- /dev/null +++ b/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/Logger.PrivateDump.original.txt @@ -0,0 +1,405 @@ +[Other Logs] +=============== + = MouseWithoutBorders.Common +lastClipboardEventTime = 0 +clipboardCopiedTime = 0 +Comma = System.Char[] +--System.Char[] = System.Char[]: N/A +Star = System.Char[] +--System.Char[] = System.Char[]: N/A +NullSeparator = System.Char[] +--System.Char[] = System.Char[]: N/A +k__BackingField = NONE +k__BackingField = 0 +k__BackingField = False +lastClipboardObject = +k__BackingField = False +ClipboardThreadOldLock = O +screenWidth = 0 +screenHeight = 0 +lastX = 0 +lastY = 0 +mainFormVisible = True +runOnLogonDesktop = False +runOnScrSaverDesktop = False +toggleIconsIndex = 0 +EvSwitch = EventWaitHandle +lastPos = {X=0,Y=0} +--x = 0 +--y = 0 +--Empty = {X=0,Y=0} +switchCount = 0 +lastReconnectByHotKeyTime = 0 +tcpPort = 0 +secondOpenSocketTry = False +binaryName = MM +k__BackingField = 0 +k__BackingField = False +k__BackingField = False +socketMutexBalance = 0 +k__BackingField = False +k__BackingField = 0 +k__BackingField = 0 +InputSimulationLock = O +lastSendNextMachine = 0 +lastInputEventCount = 0 +lastRealInputEventCount = 0 +minSendTime = 9223372036854775807 +avgSendTime = 0 +maxSendTime = 0 +totalSendCount = 0 +totalSendTime = 0 +isDragging = False +dragDropStep05ExCalledByIpc = 0 +isDropping = False +dragMachine = NONE +k__BackingField = False +magicNumber = 0 +ran = System.Random +--_impl = System.Random+XoshiroImpl +----_s0 = 7284351989338469498 +----_s1 = 15479097190878625043 +----_s2 = 10963166711904013490 +----_s3 = 8600427501468926680 +--k__BackingField = System.Random+ThreadSafeRandom +InitialIV = 99999999999999999999 +k__BackingField = False +k__BackingField = False +LegalKeyDictionary = Concurrent.ConcurrentDictionary`2[System.String,System.Byte[]] +--_tables = Concurrent.ConcurrentDictionary`2+Tables[System.String,System.Byte[]] +----_comparer = Generic.NonRandomizedStringEqualityComparer+OrdinalIgnoreCaseComparer +----_buckets = Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,System.Byte[]][] +------System.Collections.Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,System.Byte[]][] = Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,System.Byte[]][]: N/A +----_fastModBucketsMultiplier = 498560650640798693 +----_locks = O[] +------System.Object[] = O[]: N/A +----_countPerLock = 32[] +------[0] = 0 +------[1] = 0 +------[2] = 0 +------[3] = 0 +------[4] = 0 +------[5] = 0 +------[6] = 0 +------[7] = 0 +------[8] = 0 +------[9] = 0 +------[10] = 0 +------[11] = 0 +------[12] = 0 +------[13] = 0 +------[14] = 0 +------[15] = 0 +--_budget = 2 +--_growLockArray = True +--_comparerIsDefaultForClasses = False +KeybdPackage = MouseWithoutBorders.DATA +--Type = 0 +--Id = 0 +--Src = NONE +--Des = NONE +--DateTime = 0 +--Kd = MouseWithoutBorders.KEYBDDATA +--Md = MouseWithoutBorders.MOUSEDATA +--Machine1 = NONE +--Machine2 = NONE +--Machine3 = NONE +--Machine4 = NONE +--PostAction = Other +--machineNameP1 = 0 +--machineNameP2 = 0 +--machineNameP3 = 0 +--machineNameP4 = 0 +MousePackage = MouseWithoutBorders.DATA +--Type = 0 +--Id = 0 +--Src = NONE +--Des = NONE +--DateTime = 0 +--Kd = MouseWithoutBorders.KEYBDDATA +--Md = MouseWithoutBorders.MOUSEDATA +--Machine1 = NONE +--Machine2 = NONE +--Machine3 = NONE +--Machine4 = NONE +--PostAction = Other +--machineNameP1 = 0 +--machineNameP2 = 0 +--machineNameP3 = 0 +--machineNameP4 = 0 +inputEventCount = 0 +invalidPackageCount = 0 +MOVE_MOUSE_RELATIVE = 100000 +XY_BY_PIXEL = 300000 +k__BackingField = 0 +actualLastPos = {X=0,Y=0} +--x = 0 +--y = 0 +--Empty = {X=0,Y=0} +myLastX = 0 +myLastY = 0 +signalHelperToExit = False +signalWatchDogToExit = False +WndProcCounter = 0 +initDone = False +REOPEN_WHEN_WSAECONNRESET = -10054 +REOPEN_WHEN_HOTKEY = -10055 +PleaseReopenSocket = 0 +ReopenSocketDueToReadError = False +k__BackingField = 26/09/2024 20:35:51 +--_dateData = 5250315815941007823 +--MinValue = 01/01/0001 00:00:00 +--MaxValue = 31/12/9999 23:59:59 +--UnixEpoch = 01/01/1970 00:00:00 +lastReleaseAllKeysCall = 0 +AllLogsLock = O +LogCounter = Concurrent.ConcurrentDictionary`2[System.String,32] +--_tables = Concurrent.ConcurrentDictionary`2+Tables[System.String,32] +----_comparer = Generic.NonRandomizedStringEqualityComparer+OrdinalComparer +----_buckets = Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,32][] +------System.Collections.Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,System.Int32][] = Concurrent.ConcurrentDictionary`2+VolatileNode[System.String,32][]: N/A +----_fastModBucketsMultiplier = 498560650640798693 +----_locks = O[] +------System.Object[] = O[]: N/A +----_countPerLock = 32[] +------[0] = 0 +------[1] = 0 +------[2] = 0 +------[3] = 0 +------[4] = 0 +------[5] = 0 +------[6] = 0 +------[7] = 0 +------[8] = 0 +------[9] = 0 +------[10] = 0 +------[11] = 0 +------[12] = 0 +------[13] = 0 +------[14] = 0 +------[15] = 0 +--_budget = 2 +--_growLockArray = True +--_comparerIsDefaultForClasses = False +RepeatedLogIndexSelection = 32[] +--[0] = 1 +--[1] = 3 +--[2] = 10 +--[3] = 50 +--[4] = 100 +allLogsIndex = 0 +lastHour = 0 +exceptionCount = 0 +p1 = MouseWithoutBorders.PackageMonitor +--Keyboard = 0 +--Mouse = 0 +--Heartbeat = 0 +--ByeBye = 0 +--Hello = 0 +--Matrix = 0 +--ClipboardText = 0 +--ClipboardImage = 0 +--Clipboard = 0 +--ClipboardDragDrop = 0 +--ClipboardDragDropEnd = 0 +--ClipboardAsk = 0 +--ExplorerDragDrop = 0 +--Nil = 0 +p2 = MouseWithoutBorders.PackageMonitor +--Keyboard = 0 +--Mouse = 0 +--Heartbeat = 0 +--ByeBye = 0 +--Hello = 0 +--Matrix = 0 +--ClipboardText = 0 +--ClipboardImage = 0 +--Clipboard = 0 +--ClipboardDragDrop = 0 +--ClipboardDragDropEnd = 0 +--ClipboardAsk = 0 +--ExplorerDragDrop = 0 +--Nil = 0 +McMatrixLock = O +desMachineID = NONE +DesMachineName = +newDesMachineID = NONE +newDesMachineIdEx = NONE +dropMachineID = NONE +lastJump = 63862983351362 +desktopBounds = MouseWithoutBorders.MyRectangle +--Left = 0 +--Top = 0 +--Right = 0 +--Bottom = 0 +primaryScreenBounds = MouseWithoutBorders.MyRectangle +--Left = 0 +--Top = 0 +--Right = 0 +--Bottom = 0 +SwitchLocation = MouseWithoutBorders.Class.MouseLocation +--k__BackingField = 0 +--k__BackingField = 0 +--k__BackingField = 0 +PackageSent = MouseWithoutBorders.PackageMonitor +--Keyboard = 0 +--Mouse = 0 +--Heartbeat = 0 +--ByeBye = 0 +--Hello = 0 +--Matrix = 0 +--ClipboardText = 0 +--ClipboardImage = 0 +--Clipboard = 0 +--ClipboardDragDrop = 0 +--ClipboardDragDropEnd = 0 +--ClipboardAsk = 0 +--ExplorerDragDrop = 0 +--Nil = 0 +PackageReceived = MouseWithoutBorders.PackageMonitor +--Keyboard = 0 +--Mouse = 0 +--Heartbeat = 0 +--ByeBye = 0 +--Hello = 0 +--Matrix = 0 +--ClipboardText = 0 +--ClipboardImage = 0 +--Clipboard = 0 +--ClipboardDragDrop = 0 +--ClipboardDragDropEnd = 0 +--ClipboardAsk = 0 +--ExplorerDragDrop = 0 +--Nil = 0 +PackageID = 0 +QUEUE_SIZE = 50 +RecentProcessedPackageIDs = 32[] +--[0] = 0 +--[1] = 0 +--[2] = 0 +--[3] = 0 +--[4] = 0 +--[5] = 0 +--[6] = 0 +--[7] = 0 +--[8] = 0 +--[9] = 0 +--[10] = 0 +--[11] = 0 +--[12] = 0 +--[13] = 0 +--[14] = 0 +--[15] = 0 +--[16] = 0 +--[17] = 0 +--[18] = 0 +--[19] = 0 +--[20] = 0 +--[21] = 0 +--[22] = 0 +--[23] = 0 +--[24] = 0 +--[25] = 0 +--[26] = 0 +--[27] = 0 +--[28] = 0 +--[29] = 0 +--[30] = 0 +--[31] = 0 +--[32] = 0 +--[33] = 0 +--[34] = 0 +--[35] = 0 +--[36] = 0 +--[37] = 0 +--[38] = 0 +--[39] = 0 +--[40] = 0 +--[41] = 0 +--[42] = 0 +--[43] = 0 +--[44] = 0 +--[45] = 0 +--[46] = 0 +--[47] = 0 +--[48] = 0 +--[49] = 0 +recentProcessedPackageIndex = 0 +processedPackageCount = 0 +skippedPackageCount = 0 +k__BackingField = 0 +lastXY = {X=0,Y=0} +--x = 0 +--y = 0 +--Empty = {X=0,Y=0} +shownErrMessage = False +lastStartServiceTime = 26/09/2024 20:35:51 +--_dateData = 5250315815941010854 +--MinValue = 01/01/0001 00:00:00 +--MaxValue = 31/12/9999 23:59:59 +--UnixEpoch = 01/01/1970 00:00:00 +SensitivePoints = Generic.List`1[Point] +--_items = Point[] +----System.Drawing.Point[] = Point[]: N/A +--_size = 0 +--_version = 0 +--s_emptyArray = Point[] +----System.Drawing.Point[] = Point[]: N/A +p = {X=0,Y=0} +--x = 0 +--y = 0 +--Empty = {X=0,Y=0} +k__BackingField = False +BIG_CLIPBOARD_DATA_TIMEOUT = 30000 +MAX_CLIPBOARD_DATA_SIZE_CAN_BE_SENT_INSTANTLY_TCP = 1048576 +MAX_CLIPBOARD_FILE_SIZE_CAN_BE_SENT = 104857600 +TEXT_HEADER_SIZE = 12 +DATA_SIZE = 48 +TEXT_TYPE_SEP = {4CFF57F7-BEDD-43d5-AE8F-27A61E886F2F} +TOGGLE_ICONS_SIZE = 4 +ICON_ONE = 0 +ICON_ALL = 1 +ICON_SMALL_CLIPBOARD = 2 +ICON_BIG_CLIPBOARD = 3 +ICON_ERROR = 4 +JUST_GOT_BACK_FROM_SCREEN_SAVER = 9999 +NETWORK_STREAM_BUF_SIZE = 1048576 +SymAlBlockSize = 16 +PW_LENGTH = 16 +HELPER_FORM_TEXT = Mouse without Borders Helper +HelperProcessName = PowerToys.MouseWithoutBordersHelper +MAX_LOG = 10000 +MaxLogExceptionPerHour = 1000 +HeaderSENT = Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},Ie{12},Ni{13} +HeaderRECEIVED = Be{0},Ke{1},Mo{2},He{3},Mx{4},Tx{5},Im{6},By{7},Cl{8},Dr{9},De{10},Ed{11},In{12},Ni{13},Pc{14}/{15} +MAX_MACHINE = 4 +MAX_SOCKET = 8 +HEARTBEAT_TIMEOUT = 1500000 +SKIP_PIXELS = 1 +JUMP_PIXELS = 2 +PACKAGE_SIZE = 32 +PACKAGE_SIZE_EX = 64 +WP_PACKAGE_SIZE = 6 +KEYEVENTF_KEYDOWN = 1 +KEYEVENTF_KEYUP = 2 +WH_MOUSE = 7 +WH_KEYBOARD = 2 +WH_MOUSE_LL = 14 +WH_KEYBOARD_LL = 13 +WM_MOUSEMOVE = 512 +WM_LBUTTONDOWN = 513 +WM_RBUTTONDOWN = 516 +WM_MBUTTONDOWN = 519 +WM_XBUTTONDOWN = 523 +WM_LBUTTONUP = 514 +WM_RBUTTONUP = 517 +WM_MBUTTONUP = 520 +WM_XBUTTONUP = 524 +WM_LBUTTONDBLCLK = 515 +WM_RBUTTONDBLCLK = 518 +WM_MBUTTONDBLCLK = 521 +WM_MOUSEWHEEL = 522 +WM_KEYDOWN = 256 +WM_KEYUP = 257 +WM_SYSKEYDOWN = 260 +WM_SYSKEYUP = 261 \ No newline at end of file diff --git a/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/LoggerTests.cs b/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/LoggerTests.cs new file mode 100644 index 0000000000..be960b9a9b --- /dev/null +++ b/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/Core/LoggerTests.cs @@ -0,0 +1,161 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Concurrent; +using System.Globalization; +using System.Reflection; +using System.Text; + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MouseWithoutBorders.Core; + +namespace MouseWithoutBorders.UnitTests.Core; + +public static class LoggerTests +{ + [TestClass] + public sealed class PrivateDumpTests + { + /* + [TestMethod] + public void PrivateDumpShouldGenerateExpectedOutput() + { + // this was used to create the contents of "Logger.PrivateDump.original.txt" + // when the "Core.Logger" class was "Common" in "Common.Log.cs" + + // PrivateDump throws an ArgumentNullException if this is null + Common.BinaryName = "MyBinary.dll"; + + // magic number from Settings.cs + var dumpObjectsLevel = 6; + + // copied from DumpObjects in Common.Log.cs + var sb = new StringBuilder(1000000); + var result = Common.PrivateDump(sb, new Common(), "[Other Logs]\r\n===============\r\n", 0, dumpObjectsLevel, false); + var output = sb.ToString(); + } + */ + + [TestMethod] + [Ignore( + "This test relies on internal details of the dotnet platform and is sensitive to " + + "the specific version of dotnet being used. As a result it's likely to fail if the " + + "\"expected\" result was generated with a different version to the version used to " + + "run the test, so we're going to ignore it in the CI build process.")] + public void PrivateDumpShouldGenerateExpectedOutput() + { + static string NormalizeLog(string log) + { + var lines = log.Split("\r\n"); + + // some parts of the PrivateDump output are impossible to reproduce - + // e.g. random numbers, system timestamps, thread ids, etc, so we'll mask them + var maskPrefixes = new string[] + { + "----_s0 = ", + "----_s1 = ", + "----_s2 = ", + "----_s3 = ", + "k__BackingField = ", + "--_dateData = ", + "lastJump = ", + "lastStartServiceTime = ", + "InitialIV = ", + }; + for (var i = 0; i < lines.Length; i++) + { + foreach (var maskPrefix in maskPrefixes) + { + if (lines[i].StartsWith(maskPrefix, StringComparison.InvariantCulture)) + { + // replace the trailing text with "?" characters + lines[i] = string.Concat( + lines[i].AsSpan(0, maskPrefix.Length), + new string('?', 12)); + } + } + } + + // hide some of the internals of concurrent dictionary lock tables + // as the size can vary across machines + var removeLines = new string[] + { + "------[8] = 0", + "------[9] = 0", + "------[10] = 0", + "------[11] = 0", + "------[12] = 0", + "------[13] = 0", + "------[14] = 0", + "------[15] = 0", + }; + lines = lines.Where(line => !removeLines.Contains(line)).ToArray(); + + return string.Join("\r\n", lines); + } + + // PrivateDump throws an ArgumentNullException if this is null + Common.BinaryName = "MyBinary.dll"; + + // default magic number from Settings.cs + var settingsDumpObjectsLevel = 6; + + // get the expected test result from an embedded resource + var assembly = Assembly.GetExecutingAssembly(); + var resourceName = $"{typeof(LoggerTests).Namespace}.Logger.PrivateDump.expected.txt"; + using var stream = assembly.GetManifestResourceStream(resourceName) + ?? throw new InvalidOperationException(); + using var streamReader = new StreamReader(stream); + var expected = streamReader.ReadToEnd(); + + // copied from DumpObjects in Common.Log.cs + var sb = new StringBuilder(1000000); + _ = Logger.PrivateDump(sb, Logger.AllLogs, "[Program logs]\r\n===============\r\n", 0, settingsDumpObjectsLevel, false); + _ = Logger.PrivateDump(sb, new Common(), "[Other Logs]\r\n===============\r\n", 0, settingsDumpObjectsLevel, false); + sb.AppendLine("[Logger Logs]\r\n==============="); + Logger.DumpType(sb, typeof(Logger), 0, settingsDumpObjectsLevel); + var actual = sb.ToString(); + + expected = NormalizeLog(expected); + actual = NormalizeLog(actual); + + // Azure DevOps truncates debug output which makes it hard to see where + // the expected and actual differ, so we need to write a custom error message + // so we can just focus on the differences between expected and actual + var expectedLines = expected.Split("\r\n"); + var actualLines = actual.Split("\r\n"); + for (var i = 0; i < Math.Min(expectedLines.Length, actualLines.Length); i++) + { + if (actualLines[i] != expectedLines[i]) + { + var message = new StringBuilder(); + message.AppendLine(CultureInfo.InvariantCulture, $"{nameof(actual)} and {nameof(expected)} differ at line {i}:"); + + message.AppendLine(); + message.AppendLine($"{nameof(actual)}:"); + for (var j = i; j < Math.Min(i + 5, actualLines.Length); j++) + { + message.AppendLine(CultureInfo.InvariantCulture, $"[{j}]: {actualLines[j]}:"); + } + + message.AppendLine(); + message.AppendLine($"{nameof(expected)}:"); + for (var j = i; j < Math.Min(i + 5, expectedLines.Length); j++) + { + message.AppendLine(CultureInfo.InvariantCulture, $"[{j}]: {expectedLines[j]}:"); + } + + var x = new ConcurrentDictionary(-1, 16); + + Assert.Fail(message.ToString()); + } + } + + // finally, throw an exception if the two don't match + // just in case the above doesn't spot a difference + // (e.g. different number of lines in the output) + Assert.AreEqual(expected, actual); + } + } +} diff --git a/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/MouseWithoutBorders.UnitTests.csproj b/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/MouseWithoutBorders.UnitTests.csproj new file mode 100644 index 0000000000..ad95d818a1 --- /dev/null +++ b/src/modules/MouseWithoutBorders/MouseWithoutBorders.UnitTests/MouseWithoutBorders.UnitTests.csproj @@ -0,0 +1,30 @@ + + + + + + + enable + enable + Library + + + + + + + + + + + + + + + + + + + + + From 83997a560479785c762123a7dfc8cc26188f1d66 Mon Sep 17 00:00:00 2001 From: 8LWXpg <105704427+8LWXpg@users.noreply.github.com> Date: Sat, 19 Oct 2024 01:57:51 +0800 Subject: [PATCH 33/95] [PTRun][Docs] Add SSH to Third-Party plugins (#35183) Update thirdPartyRunPlugins.md --- doc/thirdPartyRunPlugins.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/thirdPartyRunPlugins.md b/doc/thirdPartyRunPlugins.md index 4f58b1bea5..ad9b417b39 100644 --- a/doc/thirdPartyRunPlugins.md +++ b/doc/thirdPartyRunPlugins.md @@ -57,3 +57,4 @@ Below are community created plugins that target a website or software. They are | [Spotify](https://github.com/waaverecords/PowerToys-Run-Spotify) | [waaverecords](https://github.com/waaverecords) | Search Spotify and control its player | | [PowerSearch for 1Password](https://github.com/KairuDeibisu/PowerToysRunPlugin1Password) | [KairuDeibisu](https://github.com/KairuDeibisu) | An unofficial plugin for searching 1Password for usernames and passwords | | [HackMD](https://github.com/8LWXpg/PowerToysRun-HackMD) | [8LWXpg](https://github.com/8LWXpg) | Open HackMD notes | +| [SSH](https://github.com/8LWXpg/PowerToysRun-SSH) | [8LWXpg](https://github.com/8LWXpg) | Connect to ssh clients | From 278424e9e9b757ac2fdfb4977b28b243ed76fc1d Mon Sep 17 00:00:00 2001 From: Clint Rutkas Date: Fri, 18 Oct 2024 11:25:24 -0700 Subject: [PATCH 34/95] Trying to correct spelling error around having leading numeric value for user name (#35447) * Update expect.txt * Update names.txt --- .github/actions/spell-check/allow/names.txt | 2 ++ .github/actions/spell-check/expect.txt | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/actions/spell-check/allow/names.txt b/.github/actions/spell-check/allow/names.txt index 97638a1684..4e28ee7a17 100644 --- a/.github/actions/spell-check/allow/names.txt +++ b/.github/actions/spell-check/allow/names.txt @@ -28,6 +28,8 @@ videoconference # USERS +# 8LWXpg is user name but user folder causes a flag +LWXpg Adoumie Advaith alekhyareddy diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index eeb735f825..fa94dd6d7e 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -2,8 +2,6 @@ ## "PackagemanagerWrapper.cs" should be "PackageManagerWrapper.cs" ## NOTICE.MD > MOZILLA PUBLIC LICENSE v1.1 -# user name but user folder causes a flag -8LWXpg aaaa abcdefghjkmnpqrstuvxyz abgr From 3a6536ce543e8a38cbc971b067986a4d5bdb2f89 Mon Sep 17 00:00:00 2001 From: Christian Gaarden Gaardmark Date: Mon, 21 Oct 2024 03:38:25 -0700 Subject: [PATCH 35/95] [New+]Fix for bug when creating folders or files rhat contain Unicode characters (#35465) wstring all the way --- .../NewShellExtensionContextMenu/template_item.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/template_item.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu/template_item.cpp index 3c863be18e..e644df5c85 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/template_item.cpp +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/template_item.cpp @@ -67,12 +67,12 @@ std::filesystem::path template_item::copy_object_to(const HWND window_handle, co { // SHFILEOPSTRUCT wants the from and to paths to be terminated with two NULLs, wchar_t double_terminated_path_from[MAX_PATH + 1] = { 0 }; - wcsncpy_s(double_terminated_path_from, this->path.c_str(), this->path.string().length()); - double_terminated_path_from[this->path.string().length() + 1] = 0; + wcsncpy_s(double_terminated_path_from, this->path.c_str(), this->path.wstring().length()); + double_terminated_path_from[this->path.wstring().length() + 1] = 0; wchar_t double_terminated_path_to[MAX_PATH + 1] = { 0 }; - wcsncpy_s(double_terminated_path_to, destination.c_str(), destination.string().length()); - double_terminated_path_to[destination.string().length() + 1] = 0; + wcsncpy_s(double_terminated_path_to, destination.c_str(), destination.wstring().length()); + double_terminated_path_to[destination.wstring().length() + 1] = 0; SHFILEOPSTRUCT file_operation_params = { 0 }; file_operation_params.wFunc = FO_COPY; From 5d2bb16e92ee8ba88660f2fdd47d479033f47cfb Mon Sep 17 00:00:00 2001 From: Christian Gaarden Gaardmark Date: Mon, 21 Oct 2024 03:51:14 -0700 Subject: [PATCH 36/95] [New+]When new folder name collides, create copy instead of creating inside existing (#35464) * Fixed target folder path when source is a folder --- .../NewShellExtensionContextMenu/new_utilities.h | 6 ++++++ .../shell_context_sub_menu_item.cpp | 10 +++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h b/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h index 55bafb8b56..7ae46635a8 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h @@ -50,6 +50,12 @@ namespace newplus::utilities return false; } + inline bool is_directory(const std::filesystem::path path) + { + const auto entry = std::filesystem::directory_entry(path); + return entry.is_directory(); + } + inline bool wstring_same_when_comparing_ignore_case(std::wstring stringA, std::wstring stringB) { transform(stringA.begin(), stringA.end(), stringA.begin(), towupper); diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu_item.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu_item.cpp index bdae885009..99378f468a 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu_item.cpp +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu_item.cpp @@ -70,9 +70,13 @@ IFACEMETHODIMP shell_context_sub_menu_item::Invoke(_In_opt_ IShellItemArray*, _I // Determine initial filename std::filesystem::path source_fullpath = template_entry->path; - std::filesystem::path target_fullpath = std::wstring(target_path_name) - + L"\\" - + this->template_entry->get_target_filename(!utilities::get_newplus_setting_hide_starting_digits()); + std::filesystem::path target_fullpath = std::wstring(target_path_name); + + // Only append name to target if source is not a directory + if (!utilities::is_directory(target_fullpath)) + { + target_fullpath.append(this->template_entry->get_target_filename(!utilities::get_newplus_setting_hide_starting_digits())); + } // Copy file and determine final filename std::filesystem::path target_final_fullpath = this->template_entry->copy_object_to(GetActiveWindow(), target_fullpath); From db3c8e621e2822aa104b988797a9aec480902d3a Mon Sep 17 00:00:00 2001 From: PesBandi <127593627+PesBandi@users.noreply.github.com> Date: Mon, 21 Oct 2024 14:42:11 +0200 Subject: [PATCH 37/95] [PTRun]Improve copying error handling on plugins (#35459) --- .../Main.cs | 11 +++++++---- .../Main.cs | 5 +++-- .../ResultHelper.cs | 6 ++++-- .../Components/ResultHelper.cs | 2 +- .../Properties/Resources.Designer.cs | 9 +++++++++ .../Properties/Resources.resx | 3 +++ 6 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs index bc4c00d7a7..727f6fbf83 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.UnitConverter/Main.cs @@ -14,6 +14,7 @@ using System.Windows.Input; using ManagedCommon; using Wox.Plugin; +using Wox.Plugin.Logger; namespace Community.PowerToys.Run.Plugin.UnitConverter { @@ -75,9 +76,10 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter Clipboard.SetText(result.ConvertedValue.ToString(UnitConversionResult.CopyFormat, CultureInfo.CurrentCulture)); ret = true; } - catch (ExternalException) + catch (ExternalException ex) { - MessageBox.Show(Properties.Resources.copy_failed); + Log.Exception("Copy failed", ex, GetType()); + MessageBox.Show(ex.Message, Properties.Resources.copy_failed); } }); thread.SetApartmentState(ApartmentState.STA); @@ -107,9 +109,10 @@ namespace Community.PowerToys.Run.Plugin.UnitConverter Clipboard.SetText(result.ConvertedValue.ToString(UnitConversionResult.CopyFormat, CultureInfo.CurrentCulture)); ret = true; } - catch (ExternalException) + catch (ExternalException ex) { - MessageBox.Show(Properties.Resources.copy_failed); + Log.Exception("Copy failed", ex, GetType()); + MessageBox.Show(ex.Message, Properties.Resources.copy_failed); } }); thread.SetApartmentState(ApartmentState.STA); diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Main.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Main.cs index 494229a456..72f390a4a5 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Main.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Main.cs @@ -184,9 +184,10 @@ namespace Community.PowerToys.Run.Plugin.ValueGenerator Clipboard.SetText(request.ResultToString()); ret = true; } - catch (ExternalException) + catch (ExternalException ex) { - MessageBox.Show(Properties.Resources.copy_failed); + Log.Exception("Copy failed", ex, GetType()); + MessageBox.Show(ex.Message, Properties.Resources.copy_failed); } }); thread.SetApartmentState(ApartmentState.STA); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/ResultHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/ResultHelper.cs index cfeabd0f92..9b32a6f1e3 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/ResultHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.Calculator/ResultHelper.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Windows; using Wox.Plugin; +using Wox.Plugin.Logger; namespace Microsoft.PowerToys.Run.Plugin.Calculator { @@ -51,9 +52,10 @@ namespace Microsoft.PowerToys.Run.Plugin.Calculator Clipboard.SetText(roundedResult?.ToString(culture)); ret = true; } - catch (ExternalException) + catch (ExternalException ex) { - MessageBox.Show(Properties.Resources.wox_plugin_calculator_copy_failed); + Log.Exception("Copy failed", ex, typeof(ResultHelper)); + MessageBox.Show(ex.Message, Properties.Resources.wox_plugin_calculator_copy_failed); } }); diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/ResultHelper.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/ResultHelper.cs index 5809fefa5d..7ea0ed798c 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/ResultHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Components/ResultHelper.cs @@ -50,13 +50,13 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Components { try { - Clipboard.Clear(); Clipboard.SetText(text); return true; } catch (Exception exception) { Log.Exception("Can't copy to clipboard", exception, typeof(ResultHelper)); + MessageBox.Show(exception.Message, Resources.Microsoft_plugin_timedate_copy_failed); return false; } } diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.Designer.cs index da32b13a6d..1ecd40721d 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.Designer.cs @@ -60,6 +60,15 @@ namespace Microsoft.PowerToys.Run.Plugin.TimeDate.Properties { } } + /// + /// Looks up a localized string similar to Copy failed. + /// + internal static string Microsoft_plugin_timedate_copy_failed { + get { + return ResourceManager.GetString("Microsoft_plugin_timedate_copy_failed", resourceCulture); + } + } + /// /// Looks up a localized string similar to Copy value (Ctrl+C). /// diff --git a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.resx b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.resx index 3cc62b0bbd..de7f2a0be5 100644 --- a/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.resx +++ b/src/modules/launcher/Plugins/Microsoft.PowerToys.Run.Plugin.TimeDate/Properties/Resources.resx @@ -121,6 +121,9 @@ Copy value (Ctrl+C) 'Ctrl+C' is a shortcut + + Copy failed + Date From dc7c7ef2b7dc73344b6064cd94e7d787f025c70e Mon Sep 17 00:00:00 2001 From: Michael Clayton Date: Mon, 21 Oct 2024 17:38:07 +0100 Subject: [PATCH 38/95] [MouseJump][CQ]Refactor "common" classes into a separate project (#34333) * [Mouse Jump] - moving common code to MouseJump.Common project - #25482 * [Mouse Jump] - fixing warnings in MouseJump.Common - #25482 * [MouseJump] - cherrypick 5653b4 - Exclude MouseJump Common tests from the checks # Conflicts: # src/modules/MouseUtils/MouseJump.Common.UnitTests/MouseJump.Common.UnitTests.csproj * [mOuSEjUMP] - cherry pick 61aab9 - Fix ci build issues # Conflicts: # src/modules/MouseUtils/MouseJump.Common.UnitTests/MouseJump.Common.UnitTests.csproj * [Mouse Jump] - remove project type guids - #25482 * [Mouse Jump] - simplify mousejump *.csproj files - #25482 * [Mouse Jump] - fixing broken tests - #25482 * [Mouse Jump] - fixing broken build - #25482 * [Mouse Jump] - editing MouseJump.Common.UnitTests.csproj - #25482 * [Mouse Jump] - editing MouseJump.Common.csproj (UseWindowsForms=true) - #25482 * [Mouse Jump] - fixing spellcheck - #25482 * [MouseJump] - enabled implicit usings - #25482 * [Mouse Jump] - re-add csproj attributes - #27511 * ci: Fix signing of Common dll --------- Co-authored-by: Clayton --- .pipelines/ESRPSigning_core.json | 1 + .pipelines/verifyDepsJsonLibraryVersions.ps1 | 2 +- Directory.Packages.props | 2 +- NOTICE.md | 2 +- PowerToys.sln | 45 ++++++++++++------ .../Helpers/DrawingHelperTests.cs | 35 +++++++------- .../Helpers/LayoutHelperTests.cs | 12 ++--- .../Helpers/MouseHelperTests.cs | 8 ++-- .../Helpers/_test-4grid-desktop.png | Bin .../Helpers/_test-4grid-expected.png | Bin .../Helpers/_test-win11-desktop.png | Bin .../Helpers/_test-win11-expected.png | Bin .../Models/Drawing/RectangleInfoTests.cs | 6 +-- .../Models/Drawing/SizeInfoTests.cs | 6 +-- .../MouseJump.Common.UnitTests.csproj | 44 +++++++++++++++++ .../Helpers/DrawingHelper.cs | 16 +++---- .../Helpers/LayoutHelper.cs | 18 +++---- .../Helpers/MouseHelper.cs | 19 ++++---- .../Helpers/ScreenHelper.cs | 22 ++++----- .../Helpers/StyleHelper.cs | 11 ++--- .../Imaging/DesktopImageRegionCopyService.cs | 13 ++--- .../Imaging/IImageRegionCopyService.cs | 8 ++-- .../Imaging/StaticImageRegionCopyService.cs | 9 ++-- .../Models/Drawing/BoxBounds.cs | 6 +-- .../Models/Drawing/PointInfo.cs | 5 +- .../Models/Drawing/RectangleInfo.cs | 9 ++-- .../Models/Drawing/ScreenInfo.cs | 10 ++-- .../Models/Drawing/SizeInfo.cs | 10 ++-- .../Models/Layout/PreviewLayout.cs | 9 ++-- .../Models/Styles/BackgroundStyle.cs | 4 +- .../Models/Styles/BorderStyle.cs | 4 +- .../Models/Styles/BoxStyle.cs | 4 +- .../Models/Styles/MarginStyle.cs | 2 +- .../Models/Styles/PaddingStyle.cs | 2 +- .../Models/Styles/PreviewStyle.cs | 6 +-- .../MouseJump.Common/MouseJump.Common.csproj | 24 ++++++++++ .../NativeMethods/Core/BOOL.cs | 2 +- .../NativeMethods/Core/CRECT.cs | 2 +- .../NativeMethods/Core/DWORD.cs | 2 +- .../NativeMethods/Core/HANDLE.cs | 4 +- .../NativeMethods/Core/HDC.cs | 4 +- .../NativeMethods/Core/HMONITOR.cs | 4 +- .../NativeMethods/Core/HWND.cs | 3 +- .../NativeMethods/Core/LONG.cs | 2 +- .../NativeMethods/Core/LPARAM.cs | 3 +- .../NativeMethods/Core/LPCRECT.cs | 3 +- .../NativeMethods/Core/LPPOINT.cs | 3 +- .../NativeMethods/Core/LPRECT.cs | 4 +- .../NativeMethods/Core/POINT.cs | 2 +- .../NativeMethods/Core/RECT.cs | 2 +- .../NativeMethods/Core/UINT.cs | 2 +- .../NativeMethods/Core/ULONG_PTR.cs | 4 +- .../NativeMethods/Core/WORD.cs | 2 +- .../Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs | 2 +- .../Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs | 2 +- .../Graphics/Gdi/Gdi32.SetStretchBltMode.cs | 4 +- .../Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs | 4 +- .../NativeMethods/Libraries.cs | 2 +- .../Gdi/User32.EnumDisplayMonitors.cs | 4 +- .../Graphics/Gdi/User32.GetMonitorInfoW.cs | 4 +- .../User32/Graphics/Gdi/User32.GetWindowDC.cs | 4 +- .../Graphics/Gdi/User32.LPMONITORINFO.cs | 3 +- .../Graphics/Gdi/User32.MONITORENUMPROC .cs | 4 +- .../User32/Graphics/Gdi/User32.MONITORINFO.cs | 4 +- .../Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs | 2 +- .../Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs | 2 +- .../Graphics/Gdi/User32.MonitorFromPoint .cs | 4 +- .../User32/Graphics/Gdi/User32.ReleaseDC.cs | 4 +- .../KeyboardAndMouse/User32.HARDWAREINPUT.cs | 4 +- .../UI/Input/KeyboardAndMouse/User32.INPUT.cs | 4 +- .../KeyboardAndMouse/User32.INPUT_TYPE.cs | 2 +- .../KeyboardAndMouse/User32.KEYBDINPUT.cs | 4 +- .../Input/KeyboardAndMouse/User32.LPINPUT.cs | 4 +- .../KeyboardAndMouse/User32.MOUSEINPUT.cs | 4 +- .../User32.MOUSE_EVENT_FLAGS.cs | 3 +- .../KeyboardAndMouse/User32.SendInput.cs | 4 +- .../User32.GetCursorPos.cs | 4 +- .../User32.GetDesktopWindow.cs | 4 +- .../User32.GetSystemMetrics.cs | 2 +- .../User32.SYSTEM_METRICS_INDEX.cs | 2 +- .../User32.SetCursorPos.cs | 4 +- .../Properties/AssemblyInfo.cs | 2 +- .../MouseJumpUI.UnitTests.csproj | 35 -------------- .../MouseJumpUI/Helpers/SettingsHelper.cs | 2 +- .../MouseJumpUI/MainForm.Designer.cs | 2 +- .../MouseUtils/MouseJumpUI/MainForm.cs | 10 ++-- .../MouseUtils/MouseJumpUI/MouseJumpUI.csproj | 22 ++++----- 87 files changed, 281 insertions(+), 308 deletions(-) rename src/modules/MouseUtils/{MouseJumpUI.UnitTests/Common => MouseJump.Common.UnitTests}/Helpers/DrawingHelperTests.cs (82%) rename src/modules/MouseUtils/{MouseJumpUI.UnitTests/Common => MouseJump.Common.UnitTests}/Helpers/LayoutHelperTests.cs (98%) rename src/modules/MouseUtils/{MouseJumpUI.UnitTests/Common => MouseJump.Common.UnitTests}/Helpers/MouseHelperTests.cs (95%) rename src/modules/MouseUtils/{MouseJumpUI.UnitTests/Common => MouseJump.Common.UnitTests}/Helpers/_test-4grid-desktop.png (100%) rename src/modules/MouseUtils/{MouseJumpUI.UnitTests/Common => MouseJump.Common.UnitTests}/Helpers/_test-4grid-expected.png (100%) rename src/modules/MouseUtils/{MouseJumpUI.UnitTests/Common => MouseJump.Common.UnitTests}/Helpers/_test-win11-desktop.png (100%) rename src/modules/MouseUtils/{MouseJumpUI.UnitTests/Common => MouseJump.Common.UnitTests}/Helpers/_test-win11-expected.png (100%) rename src/modules/MouseUtils/{MouseJumpUI.UnitTests/Common => MouseJump.Common.UnitTests}/Models/Drawing/RectangleInfoTests.cs (97%) rename src/modules/MouseUtils/{MouseJumpUI.UnitTests/Common => MouseJump.Common.UnitTests}/Models/Drawing/SizeInfoTests.cs (96%) create mode 100644 src/modules/MouseUtils/MouseJump.Common.UnitTests/MouseJump.Common.UnitTests.csproj rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Helpers/DrawingHelper.cs (96%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Helpers/LayoutHelper.cs (95%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Helpers/MouseHelper.cs (90%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Helpers/ScreenHelper.cs (87%) rename src/modules/MouseUtils/{MouseJumpUI => MouseJump.Common}/Helpers/StyleHelper.cs (93%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Imaging/DesktopImageRegionCopyService.cs (93%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Imaging/IImageRegionCopyService.cs (84%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Imaging/StaticImageRegionCopyService.cs (85%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Models/Drawing/BoxBounds.cs (96%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Models/Drawing/PointInfo.cs (96%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Models/Drawing/RectangleInfo.cs (98%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Models/Drawing/ScreenInfo.cs (76%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Models/Drawing/SizeInfo.cs (96%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Models/Layout/PreviewLayout.cs (95%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Models/Styles/BackgroundStyle.cs (92%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Models/Styles/BorderStyle.cs (96%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Models/Styles/BoxStyle.cs (97%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Models/Styles/MarginStyle.cs (96%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Models/Styles/PaddingStyle.cs (96%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/Models/Styles/PreviewStyle.cs (89%) create mode 100644 src/modules/MouseUtils/MouseJump.Common/MouseJump.Common.csproj rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/BOOL.cs (96%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/CRECT.cs (97%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/DWORD.cs (96%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/HANDLE.cs (94%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/HDC.cs (94%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/HMONITOR.cs (96%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/HWND.cs (95%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/LONG.cs (95%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/LPARAM.cs (94%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/LPCRECT.cs (95%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/LPPOINT.cs (95%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/LPRECT.cs (95%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/POINT.cs (96%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/RECT.cs (97%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/UINT.cs (96%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/ULONG_PTR.cs (95%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Core/WORD.cs (95%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs (96%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs (94%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs (90%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs (92%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/Libraries.cs (87%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs (93%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs (89%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs (93%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs (95%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs (87%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs (93%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs (95%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs (94%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs (91%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs (91%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs (92%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs (94%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs (94%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs (93%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs (95%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs (93%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs (95%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs (91%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs (89%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs (89%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs (95%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs (98%) rename src/modules/MouseUtils/{MouseJumpUI/Common => MouseJump.Common}/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs (91%) rename src/modules/MouseUtils/{MouseJumpUI => MouseJump.Common}/Properties/AssemblyInfo.cs (78%) delete mode 100644 src/modules/MouseUtils/MouseJumpUI.UnitTests/MouseJumpUI.UnitTests.csproj diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json index 57a22ffd57..b4ee89fcf0 100644 --- a/.pipelines/ESRPSigning_core.json +++ b/.pipelines/ESRPSigning_core.json @@ -166,6 +166,7 @@ "PowerToys.FindMyMouse.dll", "PowerToys.MouseHighlighter.dll", "PowerToys.MouseJump.dll", + "PowerToys.MouseJump.Common.dll", "PowerToys.MousePointerCrosshairs.dll", "PowerToys.MouseJumpUI.dll", "PowerToys.MouseJumpUI.exe", diff --git a/.pipelines/verifyDepsJsonLibraryVersions.ps1 b/.pipelines/verifyDepsJsonLibraryVersions.ps1 index 16ef379188..7d5fc02c10 100644 --- a/.pipelines/verifyDepsJsonLibraryVersions.ps1 +++ b/.pipelines/verifyDepsJsonLibraryVersions.ps1 @@ -15,7 +15,7 @@ Param( $referencedFileVersionsPerDll = @{} $totalFailures = 0 -Get-ChildItem $targetDir -Recurse -Filter *.deps.json -Exclude UITests-FancyZones* | ForEach-Object { +Get-ChildItem $targetDir -Recurse -Filter *.deps.json -Exclude UITests-FancyZones*,MouseJump.Common.UnitTests* | ForEach-Object { # Temporarily exclude FancyZones UI tests because of Appium.WebDriver dependencies $depsJsonFullFileName = $_.FullName $depsJsonFileName = $_.Name diff --git a/Directory.Packages.props b/Directory.Packages.props index e0797a6270..b46a4076b1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -69,7 +69,7 @@ - + diff --git a/NOTICE.md b/NOTICE.md index a8059397e5..515759722b 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1351,7 +1351,7 @@ EXHIBIT A -Mozilla Public License. - System.Data.OleDb 8.0.1 - System.Data.SqlClient 4.8.6 - System.Diagnostics.EventLog 8.0.1 -- System.Drawing.Common 8.0.6 +- System.Drawing.Common 8.0.7 - System.IO.Abstractions 17.2.3 - System.IO.Abstractions.TestingHelpers 17.2.3 - System.Management 8.0.0 diff --git a/PowerToys.sln b/PowerToys.sln index 1113ab5530..29744af75f 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -504,9 +504,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseWithoutBordersHelper", EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MouseJump", "src\modules\MouseUtils\MouseJump\MouseJump.vcxproj", "{8A08D663-4995-40E3-B42C-3F910625F284}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseJumpUI", "src\modules\MouseUtils\MouseJumpUI\MouseJumpUI.csproj", "{D962A009-834F-4EEC-AABB-430DF8F98E39}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseJump.Common", "src\modules\MouseUtils\MouseJump.Common\MouseJump.Common.csproj", "{923DF87C-CA99-4D1C-B1D2-959174E95BFA}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseJumpUI.UnitTests", "src\modules\MouseUtils\MouseJumpUI.UnitTests\MouseJumpUI.UnitTests.csproj", "{D9C5DE64-6849-4278-91AD-9660AECF2876}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseJump.Common.UnitTests", "src\modules\MouseUtils\MouseJump.Common.UnitTests\MouseJump.Common.UnitTests.csproj", "{D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseJumpUI", "src\modules\MouseUtils\MouseJumpUI\MouseJumpUI.csproj", "{D962A009-834F-4EEC-AABB-430DF8F98E39}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AdvancedPaste", "AdvancedPaste", "{9873BA05-4C41-4819-9283-CF45D795431B}" EndProject @@ -2252,18 +2254,6 @@ Global {D962A009-834F-4EEC-AABB-430DF8F98E39}.Release|x64.Build.0 = Release|x64 {D962A009-834F-4EEC-AABB-430DF8F98E39}.Release|x86.ActiveCfg = Release|x64 {D962A009-834F-4EEC-AABB-430DF8F98E39}.Release|x86.Build.0 = Release|x64 - {D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|ARM64.Build.0 = Debug|ARM64 - {D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|x64.ActiveCfg = Debug|x64 - {D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|x64.Build.0 = Debug|x64 - {D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|x86.ActiveCfg = Debug|x64 - {D9C5DE64-6849-4278-91AD-9660AECF2876}.Debug|x86.Build.0 = Debug|x64 - {D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|ARM64.ActiveCfg = Release|ARM64 - {D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|ARM64.Build.0 = Release|ARM64 - {D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|x64.ActiveCfg = Release|x64 - {D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|x64.Build.0 = Release|x64 - {D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|x86.ActiveCfg = Release|x64 - {D9C5DE64-6849-4278-91AD-9660AECF2876}.Release|x86.Build.0 = Release|x64 {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|ARM64.ActiveCfg = Debug|ARM64 {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|ARM64.Build.0 = Debug|ARM64 {FC373B24-3293-453C-AAF5-CF2909DCEE6A}.Debug|x64.ActiveCfg = Debug|x64 @@ -2652,6 +2642,30 @@ Global {F055103B-F80B-4D0C-BF48-057C55620033}.Release|x64.Build.0 = Release|x64 {F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.ActiveCfg = Release|x64 {F055103B-F80B-4D0C-BF48-057C55620033}.Release|x86.Build.0 = Release|x64 + {923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|ARM64.Build.0 = Debug|ARM64 + {923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.ActiveCfg = Debug|x64 + {923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x64.Build.0 = Debug|x64 + {923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.ActiveCfg = Debug|x64 + {923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Debug|x86.Build.0 = Debug|x64 + {923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.ActiveCfg = Release|ARM64 + {923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|ARM64.Build.0 = Release|ARM64 + {923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.ActiveCfg = Release|x64 + {923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x64.Build.0 = Release|x64 + {923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.ActiveCfg = Release|x64 + {923DF87C-CA99-4D1C-B1D2-959174E95BFA}.Release|x86.Build.0 = Release|x64 + {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|ARM64.Build.0 = Debug|ARM64 + {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.ActiveCfg = Debug|x64 + {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x64.Build.0 = Debug|x64 + {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.ActiveCfg = Debug|x64 + {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Debug|x86.Build.0 = Debug|x64 + {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.ActiveCfg = Release|ARM64 + {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|ARM64.Build.0 = Release|ARM64 + {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.ActiveCfg = Release|x64 + {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x64.Build.0 = Release|x64 + {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.ActiveCfg = Release|x64 + {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77}.Release|x86.Build.0 = Release|x64 {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.ActiveCfg = Debug|ARM64 {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|ARM64.Build.0 = Debug|ARM64 {B31FCC55-B5A4-4EA7-B414-2DCEAE6AF332}.Debug|x64.ActiveCfg = Debug|x64 @@ -2927,7 +2941,6 @@ Global {A663E672-B26D-4EC0-BEAB-FE2E424AC46F} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC} {8A08D663-4995-40E3-B42C-3F910625F284} = {322566EF-20DC-43A6-B9F8-616AF942579A} {D962A009-834F-4EEC-AABB-430DF8F98E39} = {322566EF-20DC-43A6-B9F8-616AF942579A} - {D9C5DE64-6849-4278-91AD-9660AECF2876} = {322566EF-20DC-43A6-B9F8-616AF942579A} {9873BA05-4C41-4819-9283-CF45D795431B} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {FC373B24-3293-453C-AAF5-CF2909DCEE6A} = {9873BA05-4C41-4819-9283-CF45D795431B} {9CE59ED5-7087-4353-88EB-788038A73CEC} = {1AFB6476-670D-4E80-A464-657E01DFF482} @@ -2966,6 +2979,8 @@ Global {8ACB33D9-C95B-47D4-8363-9731EE0930A0} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC} {CA716AE6-FE5C-40AC-BB8F-2C87912687AC} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {F055103B-F80B-4D0C-BF48-057C55620033} = {5A7818A8-109C-4E1C-850D-1A654E234B0E} + {923DF87C-CA99-4D1C-B1D2-959174E95BFA} = {322566EF-20DC-43A6-B9F8-616AF942579A} + {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77} = {322566EF-20DC-43A6-B9F8-616AF942579A} {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {BE126CBB-AE12-406A-9837-A05ACFCA57A7} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {14CB58B7-D280-4A7A-95DE-4B2DF14EA000} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/DrawingHelperTests.cs b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/DrawingHelperTests.cs similarity index 82% rename from src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/DrawingHelperTests.cs rename to src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/DrawingHelperTests.cs index 1859d085b5..5ba0a0eef8 100644 --- a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/DrawingHelperTests.cs +++ b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/DrawingHelperTests.cs @@ -2,20 +2,15 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; using System.Reflection; using Microsoft.VisualStudio.TestTools.UnitTesting; -using MouseJumpUI.Common.Helpers; -using MouseJumpUI.Common.Imaging; -using MouseJumpUI.Common.Models.Drawing; -using MouseJumpUI.Common.Models.Styles; -using MouseJumpUI.Helpers; +using MouseJump.Common.Helpers; +using MouseJump.Common.Imaging; +using MouseJump.Common.Models.Drawing; +using MouseJump.Common.Models.Styles; -namespace MouseJumpUI.UnitTests.Common.Helpers; +namespace MouseJump.Common.UnitTests.Helpers; [TestClass] public static class DrawingHelperTests @@ -60,8 +55,8 @@ public static class DrawingHelperTests new(0, 500, 500, 500), }, activatedLocation: new(x: 50, y: 50), - desktopImageFilename: "Common/Helpers/_test-4grid-desktop.png", - expectedImageFilename: "Common/Helpers/_test-4grid-expected.png"), + desktopImageFilename: "_test-4grid-desktop.png", + expectedImageFilename: "_test-4grid-expected.png"), }; /* win 11 */ yield return new object[] @@ -74,8 +69,8 @@ public static class DrawingHelperTests new(0, 0, 5120, 1440), }, activatedLocation: new(x: 50, y: 50), - desktopImageFilename: "Common/Helpers/_test-win11-desktop.png", - expectedImageFilename: "Common/Helpers/_test-win11-expected.png"), + desktopImageFilename: "_test-win11-desktop.png", + expectedImageFilename: "_test-win11-expected.png"), }; } @@ -104,13 +99,19 @@ public static class DrawingHelperTests private static Bitmap LoadImageResource(string filename) { + // assume embedded resources are in the same source folder as this + // class, and the namespace hierarchy matches the folder structure. + // that way we can build resource names from the current namespace + var resourcePrefix = typeof(DrawingHelperTests).Namespace; + var resourceName = $"{resourcePrefix}.{filename}"; + var assembly = Assembly.GetExecutingAssembly(); - var assemblyName = new AssemblyName(assembly.FullName ?? throw new InvalidOperationException()); - var resourceName = $"Microsoft.{assemblyName.Name}.{filename.Replace("/", ".")}"; var resourceNames = assembly.GetManifestResourceNames(); if (!resourceNames.Contains(resourceName)) { - throw new InvalidOperationException($"Embedded resource '{resourceName}' does not exist."); + var message = $"Embedded resource '{resourceName}' does not exist. " + + "Valid resource names are: \r\n" + string.Join("\r\n", resourceNames); + throw new InvalidOperationException(message); } var stream = assembly.GetManifestResourceStream(resourceName) diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/LayoutHelperTests.cs b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/LayoutHelperTests.cs similarity index 98% rename from src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/LayoutHelperTests.cs rename to src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/LayoutHelperTests.cs index 13141b9268..5bdd4af443 100644 --- a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/LayoutHelperTests.cs +++ b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/LayoutHelperTests.cs @@ -2,17 +2,15 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; -using System.Drawing; using System.Text.Json; using Microsoft.VisualStudio.TestTools.UnitTesting; -using MouseJumpUI.Common.Helpers; -using MouseJumpUI.Common.Models.Drawing; -using MouseJumpUI.Common.Models.Layout; -using MouseJumpUI.Common.Models.Styles; +using MouseJump.Common.Helpers; +using MouseJump.Common.Models.Drawing; +using MouseJump.Common.Models.Layout; +using MouseJump.Common.Models.Styles; -namespace MouseJumpUI.UnitTests.Common.Helpers; +namespace MouseJump.Common.UnitTests.Helpers; [TestClass] public static class LayoutHelperTests diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/MouseHelperTests.cs b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/MouseHelperTests.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/MouseHelperTests.cs rename to src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/MouseHelperTests.cs index b7e3b8a99e..52870e8cc1 100644 --- a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/MouseHelperTests.cs +++ b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/MouseHelperTests.cs @@ -2,13 +2,11 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; - using Microsoft.VisualStudio.TestTools.UnitTesting; -using MouseJumpUI.Common.Helpers; -using MouseJumpUI.Common.Models.Drawing; +using MouseJump.Common.Helpers; +using MouseJump.Common.Models.Drawing; -namespace MouseJumpUI.UnitTests.Common.Helpers; +namespace MouseJump.Common.UnitTests.Helpers; [TestClass] public static class MouseHelperTests diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/_test-4grid-desktop.png b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/_test-4grid-desktop.png similarity index 100% rename from src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/_test-4grid-desktop.png rename to src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/_test-4grid-desktop.png diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/_test-4grid-expected.png b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/_test-4grid-expected.png similarity index 100% rename from src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/_test-4grid-expected.png rename to src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/_test-4grid-expected.png diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/_test-win11-desktop.png b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/_test-win11-desktop.png similarity index 100% rename from src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/_test-win11-desktop.png rename to src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/_test-win11-desktop.png diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/_test-win11-expected.png b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/_test-win11-expected.png similarity index 100% rename from src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Helpers/_test-win11-expected.png rename to src/modules/MouseUtils/MouseJump.Common.UnitTests/Helpers/_test-win11-expected.png diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Models/Drawing/RectangleInfoTests.cs b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Models/Drawing/RectangleInfoTests.cs similarity index 97% rename from src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Models/Drawing/RectangleInfoTests.cs rename to src/modules/MouseUtils/MouseJump.Common.UnitTests/Models/Drawing/RectangleInfoTests.cs index 87d3128e3b..d2d6aba81c 100644 --- a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Models/Drawing/RectangleInfoTests.cs +++ b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Models/Drawing/RectangleInfoTests.cs @@ -2,12 +2,10 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; - using Microsoft.VisualStudio.TestTools.UnitTesting; -using MouseJumpUI.Common.Models.Drawing; +using MouseJump.Common.Models.Drawing; -namespace MouseJumpUI.UnitTests.Common.Models.Drawing; +namespace MouseJump.Common.UnitTests.Models.Drawing; [TestClass] public static class RectangleInfoTests diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Models/Drawing/SizeInfoTests.cs b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Models/Drawing/SizeInfoTests.cs similarity index 96% rename from src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Models/Drawing/SizeInfoTests.cs rename to src/modules/MouseUtils/MouseJump.Common.UnitTests/Models/Drawing/SizeInfoTests.cs index 150b15cdc1..e6c33e3d7a 100644 --- a/src/modules/MouseUtils/MouseJumpUI.UnitTests/Common/Models/Drawing/SizeInfoTests.cs +++ b/src/modules/MouseUtils/MouseJump.Common.UnitTests/Models/Drawing/SizeInfoTests.cs @@ -2,12 +2,10 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Collections.Generic; - using Microsoft.VisualStudio.TestTools.UnitTesting; -using MouseJumpUI.Common.Models.Drawing; +using MouseJump.Common.Models.Drawing; -namespace MouseJumpUI.UnitTests.Common.Models.Drawing; +namespace MouseJump.Common.UnitTests.Models.Drawing; [TestClass] public static class SizeInfoTests diff --git a/src/modules/MouseUtils/MouseJump.Common.UnitTests/MouseJump.Common.UnitTests.csproj b/src/modules/MouseUtils/MouseJump.Common.UnitTests/MouseJump.Common.UnitTests.csproj new file mode 100644 index 0000000000..c97b009bb6 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Common.UnitTests/MouseJump.Common.UnitTests.csproj @@ -0,0 +1,44 @@ + + + + + + + {D5E42C63-57C5-4EF6-AECE-1E2FCA725B77} + PowerToys.MouseJump.Common.UnitTests + PowerToys.MouseJump.Common.UnitTests + PowerToys MouseJump.Common.UnitTests + Library + ..\..\..\..\$(Platform)\$(Configuration)\tests\MouseJump.Common.UnitTests\ + false + false + + enable + true + enable + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Helpers/DrawingHelper.cs b/src/modules/MouseUtils/MouseJump.Common/Helpers/DrawingHelper.cs similarity index 96% rename from src/modules/MouseUtils/MouseJumpUI/Common/Helpers/DrawingHelper.cs rename to src/modules/MouseUtils/MouseJump.Common/Helpers/DrawingHelper.cs index 0f60adae06..278e07039f 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Helpers/DrawingHelper.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Helpers/DrawingHelper.cs @@ -2,22 +2,18 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; using System.Diagnostics; -using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; -using System.Linq; -using MouseJumpUI.Common.Imaging; -using MouseJumpUI.Common.Models.Drawing; -using MouseJumpUI.Common.Models.Layout; -using MouseJumpUI.Common.Models.Styles; +using MouseJump.Common.Imaging; +using MouseJump.Common.Models.Drawing; +using MouseJump.Common.Models.Layout; +using MouseJump.Common.Models.Styles; -namespace MouseJumpUI.Common.Helpers; +namespace MouseJump.Common.Helpers; -internal static class DrawingHelper +public static class DrawingHelper { public static Bitmap RenderPreview( PreviewLayout previewLayout, diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Helpers/LayoutHelper.cs b/src/modules/MouseUtils/MouseJump.Common/Helpers/LayoutHelper.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/Common/Helpers/LayoutHelper.cs rename to src/modules/MouseUtils/MouseJump.Common/Helpers/LayoutHelper.cs index f83540d1c0..791d1f7ffb 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Helpers/LayoutHelper.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Helpers/LayoutHelper.cs @@ -2,17 +2,13 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; -using System.Linq; +using MouseJump.Common.Models.Drawing; +using MouseJump.Common.Models.Layout; +using MouseJump.Common.Models.Styles; -using MouseJumpUI.Common.Models.Drawing; -using MouseJumpUI.Common.Models.Layout; -using MouseJumpUI.Common.Models.Styles; +namespace MouseJump.Common.Helpers; -namespace MouseJumpUI.Common.Helpers; - -internal static class LayoutHelper +public static class LayoutHelper { public static PreviewLayout GetPreviewLayout( PreviewStyle previewStyle, List screens, PointInfo activatedLocation) @@ -111,7 +107,7 @@ internal static class LayoutHelper /// A object that represents the bounds of the different areas of the box. /// Thrown when or is null. /// Thrown when any of the styles in is null. - internal static BoxBounds GetBoxBoundsFromContentBounds( + public static BoxBounds GetBoxBoundsFromContentBounds( RectangleInfo contentBounds, BoxStyle boxStyle) { @@ -139,7 +135,7 @@ internal static class LayoutHelper /// A object that represents the bounds of the different areas of the box. /// Thrown when or is null. /// Thrown when any of the styles in is null. - internal static BoxBounds GetBoxBoundsFromOuterBounds( + public static BoxBounds GetBoxBoundsFromOuterBounds( RectangleInfo outerBounds, BoxStyle boxStyle) { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Helpers/MouseHelper.cs b/src/modules/MouseUtils/MouseJump.Common/Helpers/MouseHelper.cs similarity index 90% rename from src/modules/MouseUtils/MouseJumpUI/Common/Helpers/MouseHelper.cs rename to src/modules/MouseUtils/MouseJump.Common/Helpers/MouseHelper.cs index 1c116bf2c0..9354958878 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Helpers/MouseHelper.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Helpers/MouseHelper.cs @@ -5,15 +5,14 @@ using System.ComponentModel; using System.Runtime.InteropServices; -using MouseJumpUI.Common.Models.Drawing; -using MouseJumpUI.Common.NativeMethods; +using MouseJump.Common.Models.Drawing; +using MouseJump.Common.NativeMethods; +using static MouseJump.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.User32; -using static MouseJumpUI.Common.NativeMethods.Core; -using static MouseJumpUI.Common.NativeMethods.User32; +namespace MouseJump.Common.Helpers; -namespace MouseJumpUI.Common.Helpers; - -internal static class MouseHelper +public static class MouseHelper { /// /// Calculates where to move the cursor to by projecting a point from @@ -24,7 +23,7 @@ internal static class MouseHelper /// or even negative if the primary monitor is not the at the top-left of the /// entire desktop rectangle, so results may contain negative coordinates. /// - internal static PointInfo GetJumpLocation(PointInfo previewLocation, SizeInfo previewSize, RectangleInfo desktopBounds) + public static PointInfo GetJumpLocation(PointInfo previewLocation, SizeInfo previewSize, RectangleInfo desktopBounds) { return previewLocation .Scale(previewSize.ScaleToFitRatio(desktopBounds.Size)) @@ -34,7 +33,7 @@ internal static class MouseHelper /// /// Get the current position of the cursor. /// - internal static PointInfo GetCursorPosition() + public static PointInfo GetCursorPosition() { var lpPoint = new LPPOINT(new POINT(0, 0)); var result = User32.GetCursorPos(lpPoint); @@ -57,7 +56,7 @@ internal static class MouseHelper /// /// See https://github.com/mikeclayton/FancyMouse/pull/3 /// - internal static void SetCursorPosition(PointInfo location) + public static void SetCursorPosition(PointInfo location) { // set the new cursor position *twice* - the cursor sometimes end up in // the wrong place if we try to cross the dead space between non-aligned diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Helpers/ScreenHelper.cs b/src/modules/MouseUtils/MouseJump.Common/Helpers/ScreenHelper.cs similarity index 87% rename from src/modules/MouseUtils/MouseJumpUI/Common/Helpers/ScreenHelper.cs rename to src/modules/MouseUtils/MouseJump.Common/Helpers/ScreenHelper.cs index 2cdc009867..a863b45bbe 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Helpers/ScreenHelper.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Helpers/ScreenHelper.cs @@ -2,26 +2,22 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; using System.ComponentModel; -using System.Linq; -using MouseJumpUI.Common.Models.Drawing; -using MouseJumpUI.Common.NativeMethods; +using MouseJump.Common.Models.Drawing; +using MouseJump.Common.NativeMethods; +using static MouseJump.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.User32; -using static MouseJumpUI.Common.NativeMethods.Core; -using static MouseJumpUI.Common.NativeMethods.User32; +namespace MouseJump.Common.Helpers; -namespace MouseJumpUI.Common.Helpers; - -internal static class ScreenHelper +public static class ScreenHelper { /// /// Duplicates functionality available in System.Windows.Forms.SystemInformation /// to reduce the dependency on WinForms /// - public static RectangleInfo GetVirtualScreen() + private static RectangleInfo GetVirtualScreen() { return new( User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_XVIRTUALSCREEN), @@ -30,7 +26,7 @@ internal static class ScreenHelper User32.GetSystemMetrics(SYSTEM_METRICS_INDEX.SM_CYVIRTUALSCREEN)); } - internal static IEnumerable GetAllScreens() + public static IEnumerable GetAllScreens() { // enumerate the monitors attached to the system var hMonitors = new List(); @@ -80,7 +76,7 @@ internal static class ScreenHelper } } - internal static ScreenInfo GetScreenFromPoint( + public static ScreenInfo GetScreenFromPoint( List screens, PointInfo pt) { diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/StyleHelper.cs b/src/modules/MouseUtils/MouseJump.Common/Helpers/StyleHelper.cs similarity index 93% rename from src/modules/MouseUtils/MouseJumpUI/Helpers/StyleHelper.cs rename to src/modules/MouseUtils/MouseJump.Common/Helpers/StyleHelper.cs index c6a248f906..2f02ae507d 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Helpers/StyleHelper.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Helpers/StyleHelper.cs @@ -2,15 +2,12 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Drawing; +using MouseJump.Common.Models.Drawing; +using MouseJump.Common.Models.Styles; -using MouseJumpUI.Common.Models.Drawing; -using MouseJumpUI.Common.Models.Styles; +namespace MouseJump.Common.Helpers; -namespace MouseJumpUI.Helpers; - -internal static class StyleHelper +public static class StyleHelper { /// /// Default v2 preview style diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Imaging/DesktopImageRegionCopyService.cs b/src/modules/MouseUtils/MouseJump.Common/Imaging/DesktopImageRegionCopyService.cs similarity index 93% rename from src/modules/MouseUtils/MouseJumpUI/Common/Imaging/DesktopImageRegionCopyService.cs rename to src/modules/MouseUtils/MouseJump.Common/Imaging/DesktopImageRegionCopyService.cs index 45b8251305..07ba85d09c 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Imaging/DesktopImageRegionCopyService.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Imaging/DesktopImageRegionCopyService.cs @@ -2,22 +2,19 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Diagnostics; -using System.Drawing; -using MouseJumpUI.Common.Models.Drawing; -using MouseJumpUI.Common.NativeMethods; +using MouseJump.Common.Models.Drawing; +using MouseJump.Common.NativeMethods; +using static MouseJump.Common.NativeMethods.Core; -using static MouseJumpUI.Common.NativeMethods.Core; - -namespace MouseJumpUI.Common.Imaging; +namespace MouseJump.Common.Imaging; /// /// Implements an IImageRegionCopyService that uses the current desktop window as the copy source. /// This is used during the main application runtime to generate preview images of the desktop. /// -internal sealed class DesktopImageRegionCopyService : IImageRegionCopyService +public sealed class DesktopImageRegionCopyService : IImageRegionCopyService { /// /// Copies the source region from the current desktop window diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Imaging/IImageRegionCopyService.cs b/src/modules/MouseUtils/MouseJump.Common/Imaging/IImageRegionCopyService.cs similarity index 84% rename from src/modules/MouseUtils/MouseJumpUI/Common/Imaging/IImageRegionCopyService.cs rename to src/modules/MouseUtils/MouseJump.Common/Imaging/IImageRegionCopyService.cs index 0b9e573446..c352510e6b 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Imaging/IImageRegionCopyService.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Imaging/IImageRegionCopyService.cs @@ -2,13 +2,11 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Drawing; +using MouseJump.Common.Models.Drawing; -using MouseJumpUI.Common.Models.Drawing; +namespace MouseJump.Common.Imaging; -namespace MouseJumpUI.Common.Imaging; - -internal interface IImageRegionCopyService +public interface IImageRegionCopyService { /// /// Copies the source region from the provider's source image (e.g. the interactive desktop, diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Imaging/StaticImageRegionCopyService.cs b/src/modules/MouseUtils/MouseJump.Common/Imaging/StaticImageRegionCopyService.cs similarity index 85% rename from src/modules/MouseUtils/MouseJumpUI/Common/Imaging/StaticImageRegionCopyService.cs rename to src/modules/MouseUtils/MouseJump.Common/Imaging/StaticImageRegionCopyService.cs index 313a5a0b86..6808ff09e6 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Imaging/StaticImageRegionCopyService.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Imaging/StaticImageRegionCopyService.cs @@ -2,18 +2,15 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Drawing; +using MouseJump.Common.Models.Drawing; -using MouseJumpUI.Common.Models.Drawing; - -namespace MouseJumpUI.Common.Imaging; +namespace MouseJump.Common.Imaging; /// /// Implements an IImageRegionCopyService that uses the specified image as the copy source. /// This is used for testing the DrawingHelper rather than as part of the main application. /// -internal sealed class StaticImageRegionCopyService : IImageRegionCopyService +public sealed class StaticImageRegionCopyService : IImageRegionCopyService { public StaticImageRegionCopyService(Image sourceImage) { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Drawing/BoxBounds.cs b/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/BoxBounds.cs similarity index 96% rename from src/modules/MouseUtils/MouseJumpUI/Common/Models/Drawing/BoxBounds.cs rename to src/modules/MouseUtils/MouseJump.Common/Models/Drawing/BoxBounds.cs index 8b04ab9d28..0c2d81a196 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Drawing/BoxBounds.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/BoxBounds.cs @@ -2,9 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; - -namespace MouseJumpUI.Common.Models.Drawing; +namespace MouseJump.Common.Models.Drawing; public sealed class BoxBounds { @@ -28,7 +26,7 @@ public sealed class BoxBounds */ - internal BoxBounds( + public BoxBounds( RectangleInfo outerBounds, RectangleInfo marginBounds, RectangleInfo borderBounds, diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Drawing/PointInfo.cs b/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/PointInfo.cs similarity index 96% rename from src/modules/MouseUtils/MouseJumpUI/Common/Models/Drawing/PointInfo.cs rename to src/modules/MouseUtils/MouseJump.Common/Models/Drawing/PointInfo.cs index 41ba60eec4..676fdb92c0 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Drawing/PointInfo.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/PointInfo.cs @@ -2,10 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Drawing; - -namespace MouseJumpUI.Common.Models.Drawing; +namespace MouseJump.Common.Models.Drawing; /// /// Immutable version of a System.Drawing.Point object with some extra utility methods. diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Drawing/RectangleInfo.cs b/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/RectangleInfo.cs similarity index 98% rename from src/modules/MouseUtils/MouseJumpUI/Common/Models/Drawing/RectangleInfo.cs rename to src/modules/MouseUtils/MouseJump.Common/Models/Drawing/RectangleInfo.cs index 71db6b5018..e0f8c3b7ef 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Drawing/RectangleInfo.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/RectangleInfo.cs @@ -2,15 +2,12 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Drawing; using System.Text.Json.Serialization; -using MouseJumpUI.Common.Models.Styles; +using MouseJump.Common.Models.Styles; +using BorderStyle = MouseJump.Common.Models.Styles.BorderStyle; -using BorderStyle = MouseJumpUI.Common.Models.Styles.BorderStyle; - -namespace MouseJumpUI.Common.Models.Drawing; +namespace MouseJump.Common.Models.Drawing; /// /// Immutable version of a System.Drawing.Rectangle object with some extra utility methods. diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Drawing/ScreenInfo.cs b/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/ScreenInfo.cs similarity index 76% rename from src/modules/MouseUtils/MouseJumpUI/Common/Models/Drawing/ScreenInfo.cs rename to src/modules/MouseUtils/MouseJump.Common/Models/Drawing/ScreenInfo.cs index a671be7cec..b1e9c9327a 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Drawing/ScreenInfo.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/ScreenInfo.cs @@ -2,19 +2,15 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; - -using static MouseJumpUI.Common.NativeMethods.Core; - -namespace MouseJumpUI.Common.Models.Drawing; +namespace MouseJump.Common.Models.Drawing; /// /// Immutable version of a System.Windows.Forms.Screen object so we don't need to /// take a dependency on WinForms just for screen info. /// -internal sealed class ScreenInfo +public sealed class ScreenInfo { - internal ScreenInfo(HMONITOR handle, bool primary, RectangleInfo displayArea, RectangleInfo workingArea) + public ScreenInfo(int handle, bool primary, RectangleInfo displayArea, RectangleInfo workingArea) { this.Handle = handle; this.Primary = primary; diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Drawing/SizeInfo.cs b/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/SizeInfo.cs similarity index 96% rename from src/modules/MouseUtils/MouseJumpUI/Common/Models/Drawing/SizeInfo.cs rename to src/modules/MouseUtils/MouseJump.Common/Models/Drawing/SizeInfo.cs index 3216e4d367..ffb0b4de10 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Drawing/SizeInfo.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Models/Drawing/SizeInfo.cs @@ -2,14 +2,10 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Drawing; +using MouseJump.Common.Models.Styles; +using BorderStyle = MouseJump.Common.Models.Styles.BorderStyle; -using MouseJumpUI.Common.Models.Styles; - -using BorderStyle = MouseJumpUI.Common.Models.Styles.BorderStyle; - -namespace MouseJumpUI.Common.Models.Drawing; +namespace MouseJump.Common.Models.Drawing; /// /// Immutable version of a System.Drawing.Size object with some extra utility methods. diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Layout/PreviewLayout.cs b/src/modules/MouseUtils/MouseJump.Common/Models/Layout/PreviewLayout.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/Common/Models/Layout/PreviewLayout.cs rename to src/modules/MouseUtils/MouseJump.Common/Models/Layout/PreviewLayout.cs index e09a45e79f..342389d45d 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Layout/PreviewLayout.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Models/Layout/PreviewLayout.cs @@ -2,15 +2,12 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Linq; -using MouseJumpUI.Common.Models.Drawing; -using MouseJumpUI.Common.Models.Styles; +using MouseJump.Common.Models.Drawing; +using MouseJump.Common.Models.Styles; -namespace MouseJumpUI.Common.Models.Layout; +namespace MouseJump.Common.Models.Layout; public sealed class PreviewLayout { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/BackgroundStyle.cs b/src/modules/MouseUtils/MouseJump.Common/Models/Styles/BackgroundStyle.cs similarity index 92% rename from src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/BackgroundStyle.cs rename to src/modules/MouseUtils/MouseJump.Common/Models/Styles/BackgroundStyle.cs index 332a55fcbe..1d6609a676 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/BackgroundStyle.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Models/Styles/BackgroundStyle.cs @@ -2,9 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Drawing; - -namespace MouseJumpUI.Common.Models.Styles; +namespace MouseJump.Common.Models.Styles; /// /// Represents the background fill style for a drawing object. diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/BorderStyle.cs b/src/modules/MouseUtils/MouseJump.Common/Models/Styles/BorderStyle.cs similarity index 96% rename from src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/BorderStyle.cs rename to src/modules/MouseUtils/MouseJump.Common/Models/Styles/BorderStyle.cs index 3c5b870e60..a8773b535d 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/BorderStyle.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Models/Styles/BorderStyle.cs @@ -2,9 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Drawing; - -namespace MouseJumpUI.Common.Models.Styles; +namespace MouseJump.Common.Models.Styles; /// /// Represents the border style for a drawing object. diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/BoxStyle.cs b/src/modules/MouseUtils/MouseJump.Common/Models/Styles/BoxStyle.cs similarity index 97% rename from src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/BoxStyle.cs rename to src/modules/MouseUtils/MouseJump.Common/Models/Styles/BoxStyle.cs index dac908b05c..55e27c3b9f 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/BoxStyle.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Models/Styles/BoxStyle.cs @@ -2,9 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; - -namespace MouseJumpUI.Common.Models.Styles; +namespace MouseJump.Common.Models.Styles; /// /// Represents the styles to apply to a simple box-layout based drawing object. diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/MarginStyle.cs b/src/modules/MouseUtils/MouseJump.Common/Models/Styles/MarginStyle.cs similarity index 96% rename from src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/MarginStyle.cs rename to src/modules/MouseUtils/MouseJump.Common/Models/Styles/MarginStyle.cs index 6dce63f57b..b0a369dfe7 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/MarginStyle.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Models/Styles/MarginStyle.cs @@ -2,7 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJumpUI.Common.Models.Styles; +namespace MouseJump.Common.Models.Styles; /// /// Represents the margin style for a drawing object. diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/PaddingStyle.cs b/src/modules/MouseUtils/MouseJump.Common/Models/Styles/PaddingStyle.cs similarity index 96% rename from src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/PaddingStyle.cs rename to src/modules/MouseUtils/MouseJump.Common/Models/Styles/PaddingStyle.cs index 6ac7bc6b11..d4b0c82b40 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/PaddingStyle.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Models/Styles/PaddingStyle.cs @@ -2,7 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJumpUI.Common.Models.Styles; +namespace MouseJump.Common.Models.Styles; /// /// Represents the margin style for a drawing object. diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/PreviewStyle.cs b/src/modules/MouseUtils/MouseJump.Common/Models/Styles/PreviewStyle.cs similarity index 89% rename from src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/PreviewStyle.cs rename to src/modules/MouseUtils/MouseJump.Common/Models/Styles/PreviewStyle.cs index 8b2405d59a..2ca2b1bc28 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/Models/Styles/PreviewStyle.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Models/Styles/PreviewStyle.cs @@ -2,11 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; +using MouseJump.Common.Models.Drawing; -using MouseJumpUI.Common.Models.Drawing; - -namespace MouseJumpUI.Common.Models.Styles; +namespace MouseJump.Common.Models.Styles; public sealed class PreviewStyle { diff --git a/src/modules/MouseUtils/MouseJump.Common/MouseJump.Common.csproj b/src/modules/MouseUtils/MouseJump.Common/MouseJump.Common.csproj new file mode 100644 index 0000000000..4b56443fa7 --- /dev/null +++ b/src/modules/MouseUtils/MouseJump.Common/MouseJump.Common.csproj @@ -0,0 +1,24 @@ + + + + + + + {923DF87C-CA99-4D1C-B1D2-959174E95BFA} + PowerToys.MouseJump.Common + PowerToys.MouseJump.Common + PowerToys MouseJump.Common + Library + ..\..\..\..\$(Platform)\$(Configuration) + false + false + + PerMonitorV2 + true + enable + true + enable + + + + diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/BOOL.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/BOOL.cs similarity index 96% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/BOOL.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/BOOL.cs index ec8849d952..293ae0a226 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/BOOL.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/BOOL.cs @@ -2,7 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/CRECT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/CRECT.cs similarity index 97% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/CRECT.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/CRECT.cs index 3b02b3bf5e..03e3d8efd1 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/CRECT.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/CRECT.cs @@ -5,7 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/DWORD.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/DWORD.cs similarity index 96% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/DWORD.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/DWORD.cs index 900b5bfb77..5a7ae72697 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/DWORD.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/DWORD.cs @@ -4,7 +4,7 @@ using System.Runtime.InteropServices; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/HANDLE.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HANDLE.cs similarity index 94% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/HANDLE.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HANDLE.cs index c89132e0be..d32f6f8e3c 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/HANDLE.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HANDLE.cs @@ -2,9 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; - -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/HDC.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HDC.cs similarity index 94% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/HDC.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HDC.cs index 0996030b01..038ae8df2b 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/HDC.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HDC.cs @@ -2,9 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; - -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/HMONITOR.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HMONITOR.cs similarity index 96% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/HMONITOR.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HMONITOR.cs index 5baa77bfc5..9344208770 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/HMONITOR.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HMONITOR.cs @@ -2,9 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; - -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/HWND.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HWND.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/HWND.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HWND.cs index e421a1ca91..715f922b66 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/HWND.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/HWND.cs @@ -2,10 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Diagnostics.CodeAnalysis; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/LONG.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LONG.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/LONG.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LONG.cs index 7fe4b6cb99..3ac457eb44 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/LONG.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LONG.cs @@ -2,7 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/LPARAM.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPARAM.cs similarity index 94% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/LPARAM.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPARAM.cs index f214e53a6f..f93e089b12 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/LPARAM.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPARAM.cs @@ -1,9 +1,8 @@ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/LPCRECT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPCRECT.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/LPCRECT.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPCRECT.cs index c23dbbfb85..5cc6d9f3c7 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/LPCRECT.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPCRECT.cs @@ -2,10 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Runtime.InteropServices; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/LPPOINT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPPOINT.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/LPPOINT.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPPOINT.cs index c68d8b336b..725cc154d1 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/LPPOINT.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPPOINT.cs @@ -2,10 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Runtime.InteropServices; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/LPRECT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPRECT.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/LPRECT.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPRECT.cs index 66f22da181..31ba69fd4c 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/LPRECT.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/LPRECT.cs @@ -1,10 +1,10 @@ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; + using System.Runtime.InteropServices; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/POINT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/POINT.cs similarity index 96% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/POINT.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/POINT.cs index 6077174ce6..a843059b99 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/POINT.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/POINT.cs @@ -5,7 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/RECT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/RECT.cs similarity index 97% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/RECT.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/RECT.cs index 084616cc77..04df9a98c8 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/RECT.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/RECT.cs @@ -5,7 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/UINT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/UINT.cs similarity index 96% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/UINT.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/UINT.cs index d2ce9a2e51..f178f8f866 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/UINT.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/UINT.cs @@ -2,7 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/ULONG_PTR.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/ULONG_PTR.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/ULONG_PTR.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/ULONG_PTR.cs index ed26092cb1..b8a8764c3a 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/ULONG_PTR.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/ULONG_PTR.cs @@ -2,9 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; - -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/WORD.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/WORD.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/WORD.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/WORD.cs index 987b532f29..8ef05dd17a 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Core/WORD.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Core/WORD.cs @@ -2,7 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Core { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs similarity index 96% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs index 1cb5261967..4f4a9673c4 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.ROP_CODE.cs @@ -2,7 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Gdi32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs similarity index 94% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs index f1417941d3..0b25a3d565 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.STRETCH_BLT_MODE.cs @@ -2,7 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Gdi32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs similarity index 90% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs index e2aa26b8dd..95c71137a5 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.SetStretchBltMode.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Gdi32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs similarity index 92% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs index 4c74de99a4..f6f9243815 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Gdi32/Graphics/Gdi/Gdi32.StretchBlt.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class Gdi32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Libraries.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Libraries.cs similarity index 87% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Libraries.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/Libraries.cs index e648f9b01b..970183cab0 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/Libraries.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/Libraries.cs @@ -2,7 +2,7 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static class Libraries { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs similarity index 93% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs index 2572e40881..7ebbea2dfa 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.EnumDisplayMonitors.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs similarity index 89% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs index f6680a3a27..cd328f76cc 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.GetMonitorInfoW.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs similarity index 93% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs index ce5cf1afb5..229dedcdf1 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.GetWindowDC.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs index 3896962d71..d70342fa0b 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.LPMONITORINFO.cs @@ -2,10 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Runtime.InteropServices; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs similarity index 87% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs index 169bf3e567..017f8a2d72 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORENUMPROC .cs @@ -2,9 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs similarity index 93% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs index 97af859180..06c5a07b7e 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITORINFO.cs @@ -5,9 +5,9 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs index c566193874..61f15c2944 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_FROM_FLAGS.cs @@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; [SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] internal static partial class User32 diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs similarity index 94% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs index 8bfa14eb55..c7bbe4033b 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MONITOR_INFO_FLAGS.cs @@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; [SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] internal static partial class User32 diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs similarity index 91% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs index a511631f1e..04eb36a312 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.MonitorFromPoint .cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs similarity index 91% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs index 20e1ded98f..6fa39e023d 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/Graphics/Gdi/User32.ReleaseDC.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs similarity index 92% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs index 2959b393e3..84b43227f7 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.HARDWAREINPUT.cs @@ -5,9 +5,9 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs similarity index 94% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs index 9a4b1605bd..543e7d8e18 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT.cs @@ -5,9 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; - -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs similarity index 94% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs index b065fdfc43..8fd5a8ad20 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.INPUT_TYPE.cs @@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; [SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] internal static partial class User32 diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs similarity index 93% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs index c421841e91..052836d5ef 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.KEYBDINPUT.cs @@ -5,9 +5,9 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs index e803fe3d85..5730ff9dc5 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.LPINPUT.cs @@ -2,11 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Collections.Generic; using System.Runtime.InteropServices; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs similarity index 93% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs index edc3916ba5..be243b778f 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSEINPUT.cs @@ -5,9 +5,9 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs index 6bfe6d3bbe..08b0780327 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.MOUSE_EVENT_FLAGS.cs @@ -2,10 +2,9 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; using System.Diagnostics.CodeAnalysis; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; [SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] internal static partial class User32 diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs similarity index 91% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs index b0399d8848..74eb5ac9fb 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/Input/KeyboardAndMouse/User32.SendInput.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs similarity index 89% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs index 80af7f7461..1d84e17e2a 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetCursorPos.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs similarity index 89% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs index bd2e870b87..8984e9639c 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetDesktopWindow.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs similarity index 95% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs index 0865d5af3d..dafa5daadd 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.GetSystemMetrics.cs @@ -4,7 +4,7 @@ using System.Runtime.InteropServices; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs similarity index 98% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs index 5891c53551..8a714ad052 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SYSTEM_METRICS_INDEX.cs @@ -4,7 +4,7 @@ using System.Diagnostics.CodeAnalysis; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; [SuppressMessage("SA1310", "SA1310:FieldNamesMustNotContainUnderscore", Justification = "Names match Win32 api")] internal static partial class User32 diff --git a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs similarity index 91% rename from src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs rename to src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs index 6420cd8f5b..b3bb04fef9 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs +++ b/src/modules/MouseUtils/MouseJump.Common/NativeMethods/User32/UI/WindowsAndMessaging/User32.SetCursorPos.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; -using static MouseJumpUI.Common.NativeMethods.Core; +using static MouseJump.Common.NativeMethods.Core; -namespace MouseJumpUI.Common.NativeMethods; +namespace MouseJump.Common.NativeMethods; internal static partial class User32 { diff --git a/src/modules/MouseUtils/MouseJumpUI/Properties/AssemblyInfo.cs b/src/modules/MouseUtils/MouseJump.Common/Properties/AssemblyInfo.cs similarity index 78% rename from src/modules/MouseUtils/MouseJumpUI/Properties/AssemblyInfo.cs rename to src/modules/MouseUtils/MouseJump.Common/Properties/AssemblyInfo.cs index b44c65eead..e69edb9c61 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Properties/AssemblyInfo.cs +++ b/src/modules/MouseUtils/MouseJump.Common/Properties/AssemblyInfo.cs @@ -4,4 +4,4 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("MouseJumpUI.UnitTests")] +[assembly: InternalsVisibleTo("MouseJump.Common.UnitTests")] diff --git a/src/modules/MouseUtils/MouseJumpUI.UnitTests/MouseJumpUI.UnitTests.csproj b/src/modules/MouseUtils/MouseJumpUI.UnitTests/MouseJumpUI.UnitTests.csproj deleted file mode 100644 index 67720c8bcd..0000000000 --- a/src/modules/MouseUtils/MouseJumpUI.UnitTests/MouseJumpUI.UnitTests.csproj +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - {D9C5DE64-6849-4278-91AD-9660AECF2876} - Microsoft.MouseJumpUI.UnitTests - false - enable - Library - - - - ..\..\..\..\$(Platform)\$(Configuration)\tests\MouseJumpUI.UnitTests\ - - - - - - - runtime - - - - - - - - - - - - - - diff --git a/src/modules/MouseUtils/MouseJumpUI/Helpers/SettingsHelper.cs b/src/modules/MouseUtils/MouseJumpUI/Helpers/SettingsHelper.cs index cade054f69..cfd10fbd78 100644 --- a/src/modules/MouseUtils/MouseJumpUI/Helpers/SettingsHelper.cs +++ b/src/modules/MouseUtils/MouseJumpUI/Helpers/SettingsHelper.cs @@ -13,7 +13,7 @@ using Microsoft.PowerToys.Settings.UI.Library.Utilities; namespace MouseJumpUI.Helpers; -internal class SettingsHelper +internal sealed class SettingsHelper { public SettingsHelper() { diff --git a/src/modules/MouseUtils/MouseJumpUI/MainForm.Designer.cs b/src/modules/MouseUtils/MouseJumpUI/MainForm.Designer.cs index 6300590e73..efc4310665 100644 --- a/src/modules/MouseUtils/MouseJumpUI/MainForm.Designer.cs +++ b/src/modules/MouseUtils/MouseJumpUI/MainForm.Designer.cs @@ -6,7 +6,7 @@ using System.Windows.Forms; namespace MouseJumpUI; -partial class MainForm +internal sealed partial class MainForm { /// diff --git a/src/modules/MouseUtils/MouseJumpUI/MainForm.cs b/src/modules/MouseUtils/MouseJumpUI/MainForm.cs index 1b8b311d89..88ee061c4e 100644 --- a/src/modules/MouseUtils/MouseJumpUI/MainForm.cs +++ b/src/modules/MouseUtils/MouseJumpUI/MainForm.cs @@ -9,15 +9,15 @@ using System.Linq; using System.Windows.Forms; using ManagedCommon; -using MouseJumpUI.Common.Helpers; -using MouseJumpUI.Common.Imaging; -using MouseJumpUI.Common.Models.Drawing; -using MouseJumpUI.Common.Models.Layout; +using MouseJump.Common.Helpers; +using MouseJump.Common.Imaging; +using MouseJump.Common.Models.Drawing; +using MouseJump.Common.Models.Layout; using MouseJumpUI.Helpers; namespace MouseJumpUI; -internal partial class MainForm : Form +internal sealed partial class MainForm : Form { public MainForm(SettingsHelper settingsHelper) { diff --git a/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj b/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj index 1333ead0c5..4ffd69bee6 100644 --- a/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj +++ b/src/modules/MouseUtils/MouseJumpUI/MouseJumpUI.csproj @@ -4,26 +4,24 @@ + + PowerToys.MouseJumpUI PowerToys.MouseJumpUI PowerToys MouseJumpUI + WinExe ..\..\..\..\$(Platform)\$(Configuration) false false - true - true - MouseJumpUI.Program + + PerMonitorV2 true enable - PerMonitorV2 - {D962A009-834F-4EEC-AABB-430DF8F98E39} - WinExe - MouseJumpUI - PowerToys.MouseJumpUI - {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - true + true + disable + - + PowerToys.GPOWrapper $(OutDir) @@ -36,5 +34,7 @@ + + \ No newline at end of file From e88b22937e81e08a9ac3592a5e04ed5a094ff058 Mon Sep 17 00:00:00 2001 From: Sergey Chernyaev Date: Tue, 22 Oct 2024 10:35:49 +0200 Subject: [PATCH 39/95] [QuickAccent]Add support for Serbian Cyrillic (#35500) * **Languages.cs** - Add `GetDefaultLetterKeySRCyrillic` method for Serbian Cyrillic letters. - Add `SR_CYRL` entry to `Language` enum. - Update `GetDefaultLetterKey` method to include `Language.SR_CYRL`. * **PowerAccentPage.xaml** - Add `ComboBoxItem` for Serbian Cyrillic in `SelectedLanguage` `ComboBox`. * **Resources.resw** - Add entry for Serbian Cyrillic. * **PowerAccentViewModel.cs** - Add `SR_CYRL` to the list of supported languages. --- .../poweraccent/PowerAccent.Core/Languages.cs | 16 ++++++++++++++++ .../SettingsXAML/Views/PowerAccentPage.xaml | 3 ++- .../Settings.UI/Strings/en-us/Resources.resw | 5 ++++- .../ViewModels/PowerAccentViewModel.cs | 1 + 4 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/modules/poweraccent/PowerAccent.Core/Languages.cs b/src/modules/poweraccent/PowerAccent.Core/Languages.cs index d72a7bf486..d02afd2c85 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Languages.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Languages.cs @@ -47,6 +47,7 @@ namespace PowerAccent.Core SL, SP, SR, + SR_CYRL, SV, TK, } @@ -94,6 +95,7 @@ namespace PowerAccent.Core Language.SL => GetDefaultLetterKeySL(letter), // Slovenian Language.SP => GetDefaultLetterKeySP(letter), // Spain Language.SR => GetDefaultLetterKeySR(letter), // Serbian + Language.SR_CYRL => GetDefaultLetterKeySRCyrillic(letter), // Serbian Cyrillic Language.SV => GetDefaultLetterKeySV(letter), // Swedish Language.TK => GetDefaultLetterKeyTK(letter), // Turkish _ => throw new ArgumentException("The language {0} is not known in this context", lang.ToString()), @@ -144,6 +146,7 @@ namespace PowerAccent.Core .Union(GetDefaultLetterKeySL(letter)) .Union(GetDefaultLetterKeySP(letter)) .Union(GetDefaultLetterKeySR(letter)) + .Union(GetDefaultLetterKeySRCyrillic(letter)) .Union(GetDefaultLetterKeySV(letter)) .Union(GetDefaultLetterKeyTK(letter)) .Union(GetDefaultLetterKeyAllLanguagesOnly(letter)) @@ -771,6 +774,19 @@ namespace PowerAccent.Core }; } + // Serbian Cyrillic + private static string[] GetDefaultLetterKeySRCyrillic(LetterKey letter) + { + return letter switch + { + LetterKey.VK_D => new[] { "ђ", "џ" }, + LetterKey.VK_L => new[] { "љ" }, + LetterKey.VK_N => new[] { "њ" }, + LetterKey.VK_C => new[] { "ћ" }, + _ => Array.Empty(), + }; + } + // Macedonian private static string[] GetDefaultLetterKeyMK(LetterKey letter) { diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml index 0c75c60c92..77c96b15f6 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml @@ -1,4 +1,4 @@ - + diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index ed662f87d0..781a89ae88 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -1,4 +1,4 @@ - + + @@ -57,7 +59,7 @@ - + diff --git a/NOTICE.md b/NOTICE.md index 515759722b..f90b982505 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1317,6 +1317,7 @@ EXHIBIT A -Mozilla Public License. - LazyCache 2.4.0 - Mages 2.0.2 - Markdig.Signed 0.34.0 +- MessagePack 2.5.187 - Microsoft.CodeAnalysis.NetAnalyzers 8.0.0 - Microsoft.Data.Sqlite 8.0.7 - Microsoft.Extensions.DependencyInjection 8.0.0 @@ -1342,7 +1343,7 @@ EXHIBIT A -Mozilla Public License. - ReverseMarkdown 4.1.0 - ScipBe.Common.Office.OneNote 3.0.1 - SharpCompress 0.37.2 -- StreamJsonRpc 2.14.24 +- StreamJsonRpc 2.19.27 - StyleCop.Analyzers 1.2.0-beta.556 - System.CodeDom 8.0.0 - System.CommandLine 2.0.0-beta4.22272.1 diff --git a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPaste.csproj b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPaste.csproj index ca34cf03ff..c8ea965a4d 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPaste.csproj +++ b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPaste.csproj @@ -54,12 +54,15 @@ + + + + - diff --git a/src/modules/MouseWithoutBorders/App/Helper/MouseWithoutBordersHelper.csproj b/src/modules/MouseWithoutBorders/App/Helper/MouseWithoutBordersHelper.csproj index 9a4e8acb82..705340f39f 100644 --- a/src/modules/MouseWithoutBorders/App/Helper/MouseWithoutBordersHelper.csproj +++ b/src/modules/MouseWithoutBorders/App/Helper/MouseWithoutBordersHelper.csproj @@ -57,6 +57,8 @@ + + diff --git a/src/modules/MouseWithoutBorders/App/MouseWithoutBorders.csproj b/src/modules/MouseWithoutBorders/App/MouseWithoutBorders.csproj index 5e90e1c6e8..8bb21597d5 100644 --- a/src/modules/MouseWithoutBorders/App/MouseWithoutBorders.csproj +++ b/src/modules/MouseWithoutBorders/App/MouseWithoutBorders.csproj @@ -208,6 +208,8 @@ + + diff --git a/src/modules/MouseWithoutBorders/App/Service/MouseWithoutBordersService.csproj b/src/modules/MouseWithoutBorders/App/Service/MouseWithoutBordersService.csproj index 63a0f43823..3925634618 100644 --- a/src/modules/MouseWithoutBorders/App/Service/MouseWithoutBordersService.csproj +++ b/src/modules/MouseWithoutBorders/App/Service/MouseWithoutBordersService.csproj @@ -61,6 +61,8 @@ + + diff --git a/src/settings-ui/Settings.UI/PowerToys.Settings.csproj b/src/settings-ui/Settings.UI/PowerToys.Settings.csproj index 179fd04286..d5f72b1c43 100644 --- a/src/settings-ui/Settings.UI/PowerToys.Settings.csproj +++ b/src/settings-ui/Settings.UI/PowerToys.Settings.csproj @@ -56,6 +56,8 @@ + + From 04795c6d3a4f26c0de4c5fc319fc1c34e79355e1 Mon Sep 17 00:00:00 2001 From: Jaime Bernardo Date: Wed, 23 Oct 2024 08:52:47 +0100 Subject: [PATCH 41/95] [ci]Fail when a step in multiline powershell fails (#35520) --- .pipelines/v2/templates/job-build-project.yml | 8 +++++++- .pipelines/verifyArm64Configuration.ps1 | 1 + .pipelines/versionAndSignCheck.ps1 | 3 ++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/.pipelines/v2/templates/job-build-project.yml b/.pipelines/v2/templates/job-build-project.yml index 565ba3cab7..40a0e4a866 100644 --- a/.pipelines/v2/templates/job-build-project.yml +++ b/.pipelines/v2/templates/job-build-project.yml @@ -143,13 +143,19 @@ jobs: - pwsh: |- & '.pipelines/applyXamlStyling.ps1' -Passive + displayName: Verify XAML formatting + + - pwsh: |- & '.pipelines/verifyNugetPackages.ps1' -solution '$(build.sourcesdirectory)\PowerToys.sln' + displayName: Verify Nuget package versions for PowerToys.sln + + - pwsh: |- & '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\PowerToys.sln' & '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\tools\BugReportTool\BugReportTool.sln' & '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\tools\WebcamReportTool\WebcamReportTool.sln' & '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\tools\StylesReportTool\StylesReportTool.sln' & '.pipelines/verifyArm64Configuration.ps1' -solution '$(build.sourcesdirectory)\installer\PowerToysSetup.sln' - displayName: Verify formatting, nuget, and ARM64 configurations + displayName: Verify ARM64 configurations - ${{ if eq(parameters.enablePackageCaching, true) }}: - task: Cache@2 diff --git a/.pipelines/verifyArm64Configuration.ps1 b/.pipelines/verifyArm64Configuration.ps1 index 3a6a9cfe29..a509ec374f 100644 --- a/.pipelines/verifyArm64Configuration.ps1 +++ b/.pipelines/verifyArm64Configuration.ps1 @@ -59,6 +59,7 @@ if ($errorTable.Count -gt 0) { }; Write-Host -ForegroundColor Red `r } + Write-Error "Found arm64 verification errors." exit 1; } diff --git a/.pipelines/versionAndSignCheck.ps1 b/.pipelines/versionAndSignCheck.ps1 index 1b15fe52db..1baf34570e 100644 --- a/.pipelines/versionAndSignCheck.ps1 +++ b/.pipelines/versionAndSignCheck.ps1 @@ -48,7 +48,7 @@ $totalFailure = 0; Write-Host $DirPath; if (-not (Test-Path $DirPath)) { - Write-Host "Folder does not exist!" + Write-Error "Folder does not exist!" } Write-Host "Total items: " $items.Count @@ -79,6 +79,7 @@ $items | ForEach-Object { } if ($totalFailure -gt 0) { + Write-Error "Some items had issues." exit 1 } From f33855aaaa065c2a88a2f95966dc2647ebbfbcec Mon Sep 17 00:00:00 2001 From: Laszlo Nemeth <57342539+donlaci@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:33:35 +0200 Subject: [PATCH 42/95] [Workspaces] re-implementing Monitor ID usage (#35427) --- .../WorkspacesEditor/Models/Application.cs | 33 +--------------- .../WorkspacesEditor/Models/Project.cs | 39 +++++++++++++++++++ .../WorkspacesEditor/Utils/DrawHelper.cs | 8 +++- 3 files changed, 47 insertions(+), 33 deletions(-) diff --git a/src/modules/Workspaces/WorkspacesEditor/Models/Application.cs b/src/modules/Workspaces/WorkspacesEditor/Models/Application.cs index 294d59854d..02681dd841 100644 --- a/src/modules/Workspaces/WorkspacesEditor/Models/Application.cs +++ b/src/modules/Workspaces/WorkspacesEditor/Models/Application.cs @@ -360,38 +360,7 @@ namespace WorkspacesEditor.Models { if (_monitorSetup == null) { - _monitorSetup = Parent.Monitors.Where(x => x.MonitorNumber == MonitorNumber).FirstOrDefault(); - if (_monitorSetup == null) - { - // monitors changed: try to determine monitor id based on middle point - int middleX = Position.X + (Position.Width / 2); - int middleY = Position.Y + (Position.Height / 2); - var monitorCandidate = Parent.Monitors.Where(x => - (x.MonitorDpiUnawareBounds.Left < middleX) && - (x.MonitorDpiUnawareBounds.Right > middleX) && - (x.MonitorDpiUnawareBounds.Top < middleY) && - (x.MonitorDpiUnawareBounds.Bottom > middleY)).FirstOrDefault(); - if (monitorCandidate != null) - { - _monitorSetup = monitorCandidate; - MonitorNumber = monitorCandidate.MonitorNumber; - } - else - { - // monitors and even the app's area unknown, set the main monitor (which is closer to (0,0)) as the app's monitor - monitorCandidate = Parent.Monitors.OrderBy(x => Math.Abs(x.MonitorDpiUnawareBounds.Left) + Math.Abs(x.MonitorDpiUnawareBounds.Top)).FirstOrDefault(); - if (monitorCandidate != null) - { - _monitorSetup = monitorCandidate; - MonitorNumber = monitorCandidate.MonitorNumber; - } - else - { - // no monitors defined at all. - Logger.LogError($"Wrong workspace setup. No monitors defined for the workspace: {Parent.Name}."); - } - } - } + _monitorSetup = Parent.GetMonitorForApp(this); } return _monitorSetup; diff --git a/src/modules/Workspaces/WorkspacesEditor/Models/Project.cs b/src/modules/Workspaces/WorkspacesEditor/Models/Project.cs index 07d7ad40a1..e65853f1e3 100644 --- a/src/modules/Workspaces/WorkspacesEditor/Models/Project.cs +++ b/src/modules/Workspaces/WorkspacesEditor/Models/Project.cs @@ -374,5 +374,44 @@ namespace WorkspacesEditor.Models app.IsExpanded = false; } } + + internal MonitorSetup GetMonitorForApp(Application app) + { + var monitorSetup = Monitors.Where(x => x.MonitorNumber == app.MonitorNumber).FirstOrDefault(); + if (monitorSetup == null) + { + // monitors changed: try to determine monitor id based on middle point + int middleX = app.Position.X + (app.Position.Width / 2); + int middleY = app.Position.Y + (app.Position.Height / 2); + var monitorCandidate = Monitors.Where(x => + (x.MonitorDpiUnawareBounds.Left < middleX) && + (x.MonitorDpiUnawareBounds.Right > middleX) && + (x.MonitorDpiUnawareBounds.Top < middleY) && + (x.MonitorDpiUnawareBounds.Bottom > middleY)).FirstOrDefault(); + if (monitorCandidate != null) + { + app.MonitorNumber = monitorCandidate.MonitorNumber; + return monitorCandidate; + } + else + { + // monitors and even the app's area unknown, set the main monitor (which is closer to (0,0)) as the app's monitor + monitorCandidate = Monitors.OrderBy(x => Math.Abs(x.MonitorDpiUnawareBounds.Left) + Math.Abs(x.MonitorDpiUnawareBounds.Top)).FirstOrDefault(); + if (monitorCandidate != null) + { + app.MonitorNumber = monitorCandidate.MonitorNumber; + return monitorCandidate; + } + else + { + // no monitors defined at all. + Logger.LogError($"Wrong workspace setup. No monitors defined for the workspace: {Name}."); + return null; + } + } + } + + return monitorSetup; + } } } diff --git a/src/modules/Workspaces/WorkspacesEditor/Utils/DrawHelper.cs b/src/modules/Workspaces/WorkspacesEditor/Utils/DrawHelper.cs index 217a942ba5..45da7a6c2c 100644 --- a/src/modules/Workspaces/WorkspacesEditor/Utils/DrawHelper.cs +++ b/src/modules/Workspaces/WorkspacesEditor/Utils/DrawHelper.cs @@ -54,7 +54,13 @@ namespace WorkspacesEditor.Utils if (app.Maximized) { Project project = app.Parent; - var monitor = project.Monitors.Where(x => x.MonitorNumber == app.MonitorNumber).FirstOrDefault(); + var monitor = project.GetMonitorForApp(app); + if (monitor == null) + { + // unrealistic case, there are no monitors at all in the workspace, use original rect + return new Rectangle(TransformX(app.ScaledPosition.X), TransformY(app.ScaledPosition.Y), Scaled(app.ScaledPosition.Width), Scaled(app.ScaledPosition.Height)); + } + return new Rectangle(TransformX(monitor.MonitorDpiAwareBounds.Left), TransformY(monitor.MonitorDpiAwareBounds.Top), Scaled(monitor.MonitorDpiAwareBounds.Width), Scaled(monitor.MonitorDpiAwareBounds.Height)); } else From c3fe541139a62916013f5fb0a71a7485770694e6 Mon Sep 17 00:00:00 2001 From: Laszlo Nemeth <57342539+donlaci@users.noreply.github.com> Date: Wed, 23 Oct 2024 11:34:20 +0200 Subject: [PATCH 43/95] [Workspaces] snapshot: fix coordinates for the minimized apps. (#35455) --- .../WorkspacesSnapshotTool/SnapshotUtils.cpp | 19 ++++++++++++++++--- .../WorkspacesSnapshotTool/SnapshotUtils.h | 2 +- .../WorkspacesSnapshotTool/main.cpp | 10 +++++++++- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp b/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp index 4642a5952e..519f2ccfa8 100644 --- a/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp +++ b/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.cpp @@ -168,7 +168,7 @@ namespace SnapshotUtils return false; } - std::vector GetApps(const std::function getMonitorNumberFromWindowHandle) + std::vector GetApps(const std::function getMonitorNumberFromWindowHandle, const std::function getMonitorRect) { std::vector apps{}; @@ -249,6 +249,19 @@ namespace SnapshotUtils continue; } + bool isMinimized = WindowUtils::IsMinimized(window); + unsigned int monitorNumber = getMonitorNumberFromWindowHandle(window); + + if (isMinimized) + { + // set the screen area as position, the values we get for the minimized windows are out of the screens' area + WorkspacesData::WorkspacesProject::Monitor::MonitorRect monitorRect = getMonitorRect(monitorNumber); + rect.left = monitorRect.left; + rect.top = monitorRect.top; + rect.right = monitorRect.left + monitorRect.width; + rect.bottom = monitorRect.top + monitorRect.height; + } + WorkspacesData::WorkspacesProject::Application app{ .name = data.value().name, .title = title, @@ -258,7 +271,7 @@ namespace SnapshotUtils .commandLineArgs = L"", // GetCommandLineArgs(pid, wbemHelper), .isElevated = IsProcessElevated(pid), .canLaunchElevated = data.value().canLaunchElevated, - .isMinimized = WindowUtils::IsMinimized(window), + .isMinimized = isMinimized, .isMaximized = WindowUtils::IsMaximized(window), .position = WorkspacesData::WorkspacesProject::Application::Position{ .x = rect.left, @@ -266,7 +279,7 @@ namespace SnapshotUtils .width = rect.right - rect.left, .height = rect.bottom - rect.top, }, - .monitor = getMonitorNumberFromWindowHandle(window), + .monitor = monitorNumber, }; apps.push_back(app); diff --git a/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.h b/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.h index 1009c60d3c..df95bbadf3 100644 --- a/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.h +++ b/src/modules/Workspaces/WorkspacesSnapshotTool/SnapshotUtils.h @@ -4,5 +4,5 @@ namespace SnapshotUtils { - std::vector GetApps(const std::function getMonitorNumberFromWindowHandle); + std::vector GetApps(const std::function getMonitorNumberFromWindowHandle, const std::function getMonitorRect); }; diff --git a/src/modules/Workspaces/WorkspacesSnapshotTool/main.cpp b/src/modules/Workspaces/WorkspacesSnapshotTool/main.cpp index b736ce238e..8a99ea6ef5 100644 --- a/src/modules/Workspaces/WorkspacesSnapshotTool/main.cpp +++ b/src/modules/Workspaces/WorkspacesSnapshotTool/main.cpp @@ -65,7 +65,15 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdLine, int cm } return monitorNumber; - }); + }, [&](unsigned int monitorId) -> WorkspacesData::WorkspacesProject::Monitor::MonitorRect { + for (const auto& monitor : project.monitors) + { + if (monitor.number == monitorId) + { + return monitor.monitorRectDpiUnaware; + } + } + return project.monitors[0].monitorRectDpiUnaware; }); JsonUtils::Write(WorkspacesData::TempWorkspacesFile(), project); Logger::trace(L"WorkspacesProject {}:{} created", project.name, project.id); From b0be69a1b73b5a9b78016acb1d057500c588364f Mon Sep 17 00:00:00 2001 From: Laszlo Nemeth <57342539+donlaci@users.noreply.github.com> Date: Wed, 23 Oct 2024 15:00:56 +0200 Subject: [PATCH 44/95] [Workspaces] eliminate crash threat (#35535) Co-authored-by: Seraphima --- .../Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs b/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs index 3f57532818..aae85161cb 100644 --- a/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs +++ b/src/modules/Workspaces/WorkspacesEditor/ViewModels/MainViewModel.cs @@ -398,6 +398,11 @@ namespace WorkspacesEditor.ViewModels public async void LaunchProject(Project project, bool exitAfterLaunch = false) { + if (project == null) + { + return; + } + await Task.Run(() => RunLauncher(project.Id, InvokePoint.EditorButton)); if (_workspacesEditorIO.ParseWorkspaces(this).Result == true) { From f9127b63a5d646f1c693567867db24456a59682c Mon Sep 17 00:00:00 2001 From: Davide Giacometti <25966642+davidegiacometti@users.noreply.github.com> Date: Thu, 24 Oct 2024 21:55:51 +0200 Subject: [PATCH 45/95] [RegistryPreview] Adopt Monaco Editor (#35122) --- .github/actions/spell-check/excludes.txt | 12 +- PowerToys.sln | 1 + doc/devdocs/common/FilePreviewCommon.md | 22 +-- .../PowerToysSetup/PowerToysInstaller.wixproj | 2 +- src/Monaco.props | 30 ++++ .../Monaco/customLanguages/gitignore.js | 0 .../Assets => }/Monaco/customLanguages/reg.js | 0 .../Assets => }/Monaco/customTokenColors.js | 0 .../Monaco/generateLanguagesJson.html | 0 .../Assets => }/Monaco/index.html | 0 .../browser/ui/codicons/codicon/codicon.ttf | Bin .../base/common/worker/simpleWorker.nls.de.js | 0 .../base/common/worker/simpleWorker.nls.es.js | 0 .../base/common/worker/simpleWorker.nls.fr.js | 0 .../base/common/worker/simpleWorker.nls.it.js | 0 .../base/common/worker/simpleWorker.nls.ja.js | 0 .../vs/base/common/worker/simpleWorker.nls.js | 0 .../base/common/worker/simpleWorker.nls.ko.js | 0 .../base/common/worker/simpleWorker.nls.ru.js | 0 .../common/worker/simpleWorker.nls.zh-cn.js | 0 .../common/worker/simpleWorker.nls.zh-tw.js | 0 .../min/vs/base/worker/workerMain.js | 0 .../min/vs/basic-languages/abap/abap.js | 0 .../min/vs/basic-languages/apex/apex.js | 0 .../min/vs/basic-languages/azcli/azcli.js | 0 .../min/vs/basic-languages/bat/bat.js | 0 .../min/vs/basic-languages/bicep/bicep.js | 0 .../vs/basic-languages/cameligo/cameligo.js | 0 .../min/vs/basic-languages/clojure/clojure.js | 0 .../min/vs/basic-languages/coffee/coffee.js | 0 .../min/vs/basic-languages/cpp/cpp.js | 0 .../min/vs/basic-languages/csharp/csharp.js | 0 .../min/vs/basic-languages/csp/csp.js | 0 .../min/vs/basic-languages/css/css.js | 0 .../min/vs/basic-languages/cypher/cypher.js | 0 .../min/vs/basic-languages/dart/dart.js | 0 .../basic-languages/dockerfile/dockerfile.js | 0 .../min/vs/basic-languages/ecl/ecl.js | 0 .../min/vs/basic-languages/elixir/elixir.js | 0 .../min/vs/basic-languages/flow9/flow9.js | 0 .../freemarker2/freemarker2.js | 0 .../min/vs/basic-languages/fsharp/fsharp.js | 0 .../monacoSRC/min/vs/basic-languages/go/go.js | 0 .../min/vs/basic-languages/graphql/graphql.js | 0 .../basic-languages/handlebars/handlebars.js | 0 .../min/vs/basic-languages/hcl/hcl.js | 0 .../min/vs/basic-languages/html/html.js | 0 .../min/vs/basic-languages/ini/ini.js | 0 .../min/vs/basic-languages/java/java.js | 0 .../basic-languages/javascript/javascript.js | 0 .../min/vs/basic-languages/julia/julia.js | 0 .../min/vs/basic-languages/kotlin/kotlin.js | 0 .../min/vs/basic-languages/less/less.js | 0 .../min/vs/basic-languages/lexon/lexon.js | 0 .../min/vs/basic-languages/liquid/liquid.js | 0 .../min/vs/basic-languages/lua/lua.js | 0 .../monacoSRC/min/vs/basic-languages/m3/m3.js | 0 .../vs/basic-languages/markdown/markdown.js | 0 .../min/vs/basic-languages/mdx/mdx.js | 0 .../min/vs/basic-languages/mips/mips.js | 0 .../min/vs/basic-languages/msdax/msdax.js | 0 .../min/vs/basic-languages/mysql/mysql.js | 0 .../objective-c/objective-c.js | 0 .../min/vs/basic-languages/pascal/pascal.js | 0 .../vs/basic-languages/pascaligo/pascaligo.js | 0 .../min/vs/basic-languages/perl/perl.js | 0 .../min/vs/basic-languages/pgsql/pgsql.js | 0 .../min/vs/basic-languages/php/php.js | 0 .../min/vs/basic-languages/pla/pla.js | 0 .../vs/basic-languages/postiats/postiats.js | 0 .../basic-languages/powerquery/powerquery.js | 0 .../basic-languages/powershell/powershell.js | 0 .../vs/basic-languages/protobuf/protobuf.js | 0 .../min/vs/basic-languages/pug/pug.js | 0 .../min/vs/basic-languages/python/python.js | 0 .../min/vs/basic-languages/qsharp/qsharp.js | 0 .../monacoSRC/min/vs/basic-languages/r/r.js | 0 .../min/vs/basic-languages/razor/razor.js | 0 .../min/vs/basic-languages/redis/redis.js | 0 .../vs/basic-languages/redshift/redshift.js | 0 .../restructuredtext/restructuredtext.js | 0 .../min/vs/basic-languages/ruby/ruby.js | 0 .../min/vs/basic-languages/rust/rust.js | 0 .../monacoSRC/min/vs/basic-languages/sb/sb.js | 0 .../min/vs/basic-languages/scala/scala.js | 0 .../min/vs/basic-languages/scheme/scheme.js | 0 .../min/vs/basic-languages/scss/scss.js | 0 .../min/vs/basic-languages/shell/shell.js | 0 .../vs/basic-languages/solidity/solidity.js | 0 .../min/vs/basic-languages/sophia/sophia.js | 0 .../min/vs/basic-languages/sparql/sparql.js | 0 .../min/vs/basic-languages/sql/sql.js | 0 .../monacoSRC/min/vs/basic-languages/st/st.js | 0 .../min/vs/basic-languages/swift/swift.js | 0 .../systemverilog/systemverilog.js | 0 .../min/vs/basic-languages/tcl/tcl.js | 0 .../min/vs/basic-languages/twig/twig.js | 0 .../basic-languages/typescript/typescript.js | 0 .../monacoSRC/min/vs/basic-languages/vb/vb.js | 0 .../min/vs/basic-languages/wgsl/wgsl.js | 0 .../min/vs/basic-languages/xml/xml.js | 0 .../min/vs/basic-languages/yaml/yaml.js | 0 .../monacoSRC/min/vs/editor/editor.main.css | 0 .../monacoSRC/min/vs/editor/editor.main.js | 0 .../min/vs/editor/editor.main.nls.de.js | 0 .../min/vs/editor/editor.main.nls.es.js | 0 .../min/vs/editor/editor.main.nls.fr.js | 0 .../min/vs/editor/editor.main.nls.it.js | 0 .../min/vs/editor/editor.main.nls.ja.js | 0 .../min/vs/editor/editor.main.nls.js | 0 .../min/vs/editor/editor.main.nls.ko.js | 0 .../min/vs/editor/editor.main.nls.ru.js | 0 .../min/vs/editor/editor.main.nls.zh-cn.js | 0 .../min/vs/editor/editor.main.nls.zh-tw.js | 0 .../monacoSRC/min/vs/language/css/cssMode.js | 0 .../min/vs/language/css/cssWorker.js | 0 .../min/vs/language/html/htmlMode.js | 0 .../min/vs/language/html/htmlWorker.js | 0 .../min/vs/language/json/jsonMode.js | 0 .../min/vs/language/json/jsonWorker.js | 0 .../min/vs/language/typescript/tsMode.js | 0 .../min/vs/language/typescript/tsWorker.js | 0 .../Monaco/monacoSRC/min/vs/loader.js | 0 .../Monaco/monacoSpecialLanguages.js | 0 .../Assets => }/Monaco/monaco_languages.json | 0 .../FilePreviewCommon.csproj | 25 +-- .../Assets/RegistryPreview/index.html | 64 +++++++ .../MonacoEditorControl.xaml | 35 ++++ .../MonacoEditorControl.xaml.cs | 169 ++++++++++++++++++ .../RegistryPreviewUILib/MonacoHelper.cs | 50 ++++++ .../RegistryPreviewMainPage.Events.cs | 62 +++---- .../RegistryPreviewMainPage.Utilities.cs | 78 ++++---- .../RegistryPreviewMainPage.xaml | 23 +-- .../RegistryPreviewUILib.csproj | 13 ++ .../Strings/en-US/Resources.resw | 3 - 135 files changed, 456 insertions(+), 133 deletions(-) create mode 100644 src/Monaco.props rename src/{common/FilePreviewCommon/Assets => }/Monaco/customLanguages/gitignore.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/customLanguages/reg.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/customTokenColors.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/generateLanguagesJson.html (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/index.html (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/base/browser/ui/codicons/codicon/codicon.ttf (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.de.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.es.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.fr.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.it.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.ja.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.ko.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.ru.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.zh-cn.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.zh-tw.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/base/worker/workerMain.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/abap/abap.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/apex/apex.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/azcli/azcli.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/bat/bat.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/bicep/bicep.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/cameligo/cameligo.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/clojure/clojure.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/coffee/coffee.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/cpp/cpp.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/csharp/csharp.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/csp/csp.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/css/css.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/cypher/cypher.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/dart/dart.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/dockerfile/dockerfile.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/ecl/ecl.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/elixir/elixir.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/flow9/flow9.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/freemarker2/freemarker2.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/fsharp/fsharp.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/go/go.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/graphql/graphql.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/handlebars/handlebars.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/hcl/hcl.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/html/html.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/ini/ini.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/java/java.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/javascript/javascript.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/julia/julia.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/kotlin/kotlin.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/less/less.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/lexon/lexon.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/liquid/liquid.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/lua/lua.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/m3/m3.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/markdown/markdown.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/mdx/mdx.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/mips/mips.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/msdax/msdax.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/mysql/mysql.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/objective-c/objective-c.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/pascal/pascal.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/pascaligo/pascaligo.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/perl/perl.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/pgsql/pgsql.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/php/php.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/pla/pla.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/postiats/postiats.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/powerquery/powerquery.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/powershell/powershell.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/protobuf/protobuf.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/pug/pug.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/python/python.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/qsharp/qsharp.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/r/r.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/razor/razor.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/redis/redis.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/redshift/redshift.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/restructuredtext/restructuredtext.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/ruby/ruby.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/rust/rust.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/sb/sb.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/scala/scala.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/scheme/scheme.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/scss/scss.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/shell/shell.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/solidity/solidity.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/sophia/sophia.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/sparql/sparql.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/sql/sql.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/st/st.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/swift/swift.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/systemverilog/systemverilog.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/tcl/tcl.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/twig/twig.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/typescript/typescript.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/vb/vb.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/wgsl/wgsl.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/xml/xml.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/basic-languages/yaml/yaml.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/editor/editor.main.css (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/editor/editor.main.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/editor/editor.main.nls.de.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/editor/editor.main.nls.es.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/editor/editor.main.nls.fr.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/editor/editor.main.nls.it.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/editor/editor.main.nls.ja.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/editor/editor.main.nls.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/editor/editor.main.nls.ko.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/editor/editor.main.nls.ru.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/editor/editor.main.nls.zh-cn.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/editor/editor.main.nls.zh-tw.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/language/css/cssMode.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/language/css/cssWorker.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/language/html/htmlMode.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/language/html/htmlWorker.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/language/json/jsonMode.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/language/json/jsonWorker.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/language/typescript/tsMode.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/language/typescript/tsWorker.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSRC/min/vs/loader.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monacoSpecialLanguages.js (100%) rename src/{common/FilePreviewCommon/Assets => }/Monaco/monaco_languages.json (100%) create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/Assets/RegistryPreview/index.html create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/MonacoEditorControl.xaml create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/MonacoEditorControl.xaml.cs create mode 100644 src/modules/registrypreview/RegistryPreviewUILib/MonacoHelper.cs diff --git a/.github/actions/spell-check/excludes.txt b/.github/actions/spell-check/excludes.txt index fd89d668a2..fa1d25eda9 100644 --- a/.github/actions/spell-check/excludes.txt +++ b/.github/actions/spell-check/excludes.txt @@ -2,11 +2,11 @@ (?:^|/)(?i)COPYRIGHT (?:^|/)(?i)LICEN[CS]E (?:^|/)3rdparty/ -(?:^|/)FilePreviewCommon/Assets/Monaco/customLanguages/ -(?:^|/)FilePreviewCommon/Assets/Monaco/generateLanguagesJson.html -(?:^|/)FilePreviewCommon/Assets/Monaco/index.html -(?:^|/)FilePreviewCommon/Assets/Monaco/monaco_languages.json -(?:^|/)FilePreviewCommon/Assets/Monaco/monacoSpecialLanguages.js +(?:^|/)src/Monaco/customLanguages/ +(?:^|/)src/Monaco/generateLanguagesJson.html +(?:^|/)src/Monaco/index.html +(?:^|/)src/Monaco/monaco_languages.json +(?:^|/)src/Monaco/monacoSpecialLanguages.js (?:^|/)go\.sum$ (?:^|/)monacoSRC/ (?:^|/)package(?:-lock|)\.json$ @@ -99,7 +99,7 @@ ^\Q.pipelines/ESRPSigning_core.json\E$ ^\Q.pipelines/sdl.gdnbaselines\E$ ^\Qinstaller/PowerToysSetup/Settings.wxs\E$ -^\Qsrc/common/FilePreviewCommon/Assets/Monaco/monaco_languages.json\E$ +^\Qsrc/Monaco/monaco_languages.json\E$ ^\Qsrc/common/ManagedCommon/ColorFormatHelper.cs\E$ ^\Qsrc/common/notifications/BackgroundActivatorDLL/cpp.hint\E$ ^\Qsrc/modules/colorPicker/ColorPickerUI/Assets/ColorPicker/colorPicker.cur\E$ diff --git a/PowerToys.sln b/PowerToys.sln index 29744af75f..fa6b20aacf 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -177,6 +177,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Directory.Build.props = Directory.Build.props Directory.Build.targets = Directory.Build.targets Directory.Packages.props = Directory.Packages.props + src\Monaco.props = src\Monaco.props Solution.props = Solution.props src\Version.props = src\Version.props EndProjectSection diff --git a/doc/devdocs/common/FilePreviewCommon.md b/doc/devdocs/common/FilePreviewCommon.md index aee9de2b2a..85b74ca7e7 100644 --- a/doc/devdocs/common/FilePreviewCommon.md +++ b/doc/devdocs/common/FilePreviewCommon.md @@ -12,14 +12,14 @@ This previewer is used for the File Explorer Dev File Previewer, as well as Powe 1. Download Monaco editor with [npm](https://www.npmjs.com/): Run `npm i monaco-editor` in the command prompt. 2. Delete everything except the `min` folder (the minimised code) from the downloaded files. -3. Copy the `min` folder into the `src/common/FilePreviewCommon/Assets/Monaco/monacoSRC` folder of the PowerToys project. +3. Copy the `min` folder into the `/src/Monaco/monacoSRC` folder of the PowerToys project. 4. Generate the JSON file as described in the generate [monaco_languages.json file](#monaco_languagesjson) section. ### Add a new language definition -As an example on how to add a new language definition you can look at the one for [registry files](/src/common/FilePreviewCommon/Assets/Monaco/customLanguages/reg.js). +As an example on how to add a new language definition you can look at the one for [registry files](/src/Monaco/customLanguages/reg.js). -1. Add the new language definition (written with [Monarch](https://microsoft.github.io/monaco-editor/monarch.html)) as a new file to the [folder containing Monaco custom languages](/src/common/FilePreviewCommon/Assets/Monaco/customLanguages/) (Remember the file name and the string you used for "idDefinition" as you need it later.). The file should be formatted like in the example below. (Please change `idDefinition` to the name of your language.) +1. Add the new language definition (written with [Monarch](https://microsoft.github.io/monaco-editor/monarch.html)) as a new file to the [folder containing Monaco custom languages](/src/Monaco/customLanguages/) (Remember the file name and the string you used for "idDefinition" as you need it later.). The file should be formatted like in the example below. (Please change `idDefinition` to the name of your language.) ```javascript export function idDefinition() { @@ -29,7 +29,7 @@ export function idDefinition() { } ``` -2. Add the following line to the [`monacoSpecialLanguages.js`](/src/common/FilePreviewCommon/Assets/Monaco/monacoSpecialLanguages.js) file, after the other import statements: +2. Add the following line to the [`monacoSpecialLanguages.js`](/src/Monaco/monacoSpecialLanguages.js) file, after the other import statements: ```javascript import { idDefinition } from './customLanguages/file.js'; @@ -37,7 +37,7 @@ import { idDefinition } from './customLanguages/file.js'; > Replace file.js with the name of your definition file from step 1. Please replace idDefinition with the string you used in step 1. -3. In the [`monacoSpecialLanguages.js`](/src/common/FilePreviewCommon/Assets/Monaco/monacoSpecialLanguages.js) file add the following line into the `registerAdditionalLanguages` function: +3. In the [`monacoSpecialLanguages.js`](/src/Monaco/monacoSpecialLanguages.js) file add the following line into the `registerAdditionalLanguages` function: ```javascript registerAdditionalNewLanguage("id", [".fileExtension"], idDefinition(), monaco) @@ -47,7 +47,7 @@ registerAdditionalNewLanguage("id", [".fileExtension"], idDefinition(), monaco) * The id can be anything. Recommended is one of the file extensions. For example "php" or "reg". -4. In case you wish to add a custom color for a token, you can do so by adding the following line to [`customTokenColors.js`](/src/common/FilePreviewCommon/Assets/Monaco/customTokenColors.js): +4. In case you wish to add a custom color for a token, you can do so by adding the following line to [`customTokenColors.js`](/src/Monaco/customTokenColors.js): ```javascript {token: 'token-name', foreground: 'ff0000'} ``` @@ -60,7 +60,7 @@ registerAdditionalNewLanguage("id", [".fileExtension"], idDefinition(), monaco) ### Add a new file extension to an existing language -1. In the [`monacoSpecialLanguages.js`](/src/common/FilePreviewCommon/Assets/Monaco/monacoSpecialLanguages.js) file add the following line to the `registerAdditionalLanguages` function. (`existingId` is the id of the language you want to add the extension to. You can find these id's in the [`monaco_languages.json`](/src/common/FilePreviewCommon/Assets/Monaco/monaco_languages.json) file): +1. In the [`monacoSpecialLanguages.js`](/src/Monaco/monacoSpecialLanguages.js) file add the following line to the `registerAdditionalLanguages` function. (`existingId` is the id of the language you want to add the extension to. You can find these id's in the [`monaco_languages.json`](/src/Monaco/monaco_languages.json) file): ```javascript registerAdditionalLanguage("id", [".fileExtension"], "existingId", monaco) @@ -68,17 +68,17 @@ registerAdditionalLanguage("id", [".fileExtension"], "existingId", monaco) * If for instance you want to add more extensions to the php language set the id to `phpExt` and the existingId to `php`. -2. Copy the existing language definition into the `languageDefinitions` function in the same file. You can find the existing definitions in the following folder: [`/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/`](/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/). +2. Copy the existing language definition into the `languageDefinitions` function in the same file. You can find the existing definitions in the following folder: [`/src/Monaco/monacoSRC/min/vs/basic-languages/`](/src/Monaco/monacoSRC/min/vs/basic-languages/). 3. Execute the steps described in the [monaco_languages.json](#monaco_languagesjson) section. ### monaco_languages.json -[`monaco_languages.json`](/src/common/FilePreviewCommon/Assets/Monaco/monaco_languages.json) contains all extensions and IDs for the languages supported by Monaco. The [`MonacoHelper`](/src/common/FilePreviewCommon/MonacoHelper.cs) class and the installer are using this file to register preview handlers for the defined extensions. +[`monaco_languages.json`](/src/Monaco/monaco_languages.json) contains all extensions and IDs for the languages supported by Monaco. The [`MonacoHelper`](/src/common/FilePreviewCommon/MonacoHelper.cs) class and the installer are using this file to register preview handlers for the defined extensions. -After updating Monaco Editor and/or adding a new language you should update the [`monaco_languages.json`](/src/common/FilePreviewCommon/Assets/Monaco/monaco_languages.json) file. +After updating Monaco Editor and/or adding a new language you should update the [`monaco_languages.json`](/src/Monaco/monaco_languages.json) file. -1. Run the [`generateLanguagesJson.html`](/src/common/FilePreviewCommon/Assets/Monaco/generateLanguagesJson.html) file on a local webserver (as webbrowsers will block certain needed features when running the file locally.) +1. Run the [`generateLanguagesJson.html`](/src/Monaco/generateLanguagesJson.html) file on a local webserver (as webbrowsers will block certain needed features when running the file locally.) * This can for example be achieved by using the [Preview Server](https://marketplace.visualstudio.com/items?itemName=yuichinukiyama.vscode-preview-server) extension for Visual Studio Code: Open the file in Visual Studio Code, right click in the code editor and select `vscode-preview-server: Launch on browser`. The file will be opened in a browser. 2. The browser will download the new `monaco_languages.json` file 3. Replace the old file with the newly downloaded one in the source code folder. diff --git a/installer/PowerToysSetup/PowerToysInstaller.wixproj b/installer/PowerToysSetup/PowerToysInstaller.wixproj index 2826a68493..8771920c3c 100644 --- a/installer/PowerToysSetup/PowerToysInstaller.wixproj +++ b/installer/PowerToysSetup/PowerToysInstaller.wixproj @@ -181,7 +181,7 @@ call powershell.exe -NonInteractive -executionpolicy Unrestricted -File $(MSBuil --> - + diff --git a/src/Monaco.props b/src/Monaco.props new file mode 100644 index 0000000000..0aeef121e2 --- /dev/null +++ b/src/Monaco.props @@ -0,0 +1,30 @@ + + + + + + Assets\Monaco\customTokenColors.js + Always + + + Assets\Monaco\monacoSpecialLanguages.js + Always + + + Assets\Monaco\index.html + Always + + + Assets\Monaco\monaco_languages.json + Always + + + Assets\Monaco\monacoSRC\%(RecursiveDir)%(FileName)%(Extension) + Always + + + Assets\Monaco\customLanguages\%(RecursiveDir)%(FileName)%(Extension) + Always + + + \ No newline at end of file diff --git a/src/common/FilePreviewCommon/Assets/Monaco/customLanguages/gitignore.js b/src/Monaco/customLanguages/gitignore.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/customLanguages/gitignore.js rename to src/Monaco/customLanguages/gitignore.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/customLanguages/reg.js b/src/Monaco/customLanguages/reg.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/customLanguages/reg.js rename to src/Monaco/customLanguages/reg.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/customTokenColors.js b/src/Monaco/customTokenColors.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/customTokenColors.js rename to src/Monaco/customTokenColors.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/generateLanguagesJson.html b/src/Monaco/generateLanguagesJson.html similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/generateLanguagesJson.html rename to src/Monaco/generateLanguagesJson.html diff --git a/src/common/FilePreviewCommon/Assets/Monaco/index.html b/src/Monaco/index.html similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/index.html rename to src/Monaco/index.html diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/Monaco/monacoSRC/min/vs/base/browser/ui/codicons/codicon/codicon.ttf similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/browser/ui/codicons/codicon/codicon.ttf rename to src/Monaco/monacoSRC/min/vs/base/browser/ui/codicons/codicon/codicon.ttf diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.de.js b/src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.de.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.de.js rename to src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.de.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.es.js b/src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.es.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.es.js rename to src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.es.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.fr.js b/src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.fr.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.fr.js rename to src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.fr.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.it.js b/src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.it.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.it.js rename to src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.it.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.ja.js b/src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.ja.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.ja.js rename to src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.ja.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.js b/src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.js rename to src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.ko.js b/src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.ko.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.ko.js rename to src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.ko.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.ru.js b/src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.ru.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.ru.js rename to src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.ru.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.zh-cn.js b/src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.zh-cn.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.zh-cn.js rename to src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.zh-cn.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.zh-tw.js b/src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.zh-tw.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.zh-tw.js rename to src/Monaco/monacoSRC/min/vs/base/common/worker/simpleWorker.nls.zh-tw.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/worker/workerMain.js b/src/Monaco/monacoSRC/min/vs/base/worker/workerMain.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/base/worker/workerMain.js rename to src/Monaco/monacoSRC/min/vs/base/worker/workerMain.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/abap/abap.js b/src/Monaco/monacoSRC/min/vs/basic-languages/abap/abap.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/abap/abap.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/abap/abap.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/apex/apex.js b/src/Monaco/monacoSRC/min/vs/basic-languages/apex/apex.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/apex/apex.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/apex/apex.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/azcli/azcli.js b/src/Monaco/monacoSRC/min/vs/basic-languages/azcli/azcli.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/azcli/azcli.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/azcli/azcli.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/bat/bat.js b/src/Monaco/monacoSRC/min/vs/basic-languages/bat/bat.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/bat/bat.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/bat/bat.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/bicep/bicep.js b/src/Monaco/monacoSRC/min/vs/basic-languages/bicep/bicep.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/bicep/bicep.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/bicep/bicep.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/cameligo/cameligo.js b/src/Monaco/monacoSRC/min/vs/basic-languages/cameligo/cameligo.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/cameligo/cameligo.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/cameligo/cameligo.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/clojure/clojure.js b/src/Monaco/monacoSRC/min/vs/basic-languages/clojure/clojure.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/clojure/clojure.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/clojure/clojure.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/coffee/coffee.js b/src/Monaco/monacoSRC/min/vs/basic-languages/coffee/coffee.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/coffee/coffee.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/coffee/coffee.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/cpp/cpp.js b/src/Monaco/monacoSRC/min/vs/basic-languages/cpp/cpp.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/cpp/cpp.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/cpp/cpp.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/csharp/csharp.js b/src/Monaco/monacoSRC/min/vs/basic-languages/csharp/csharp.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/csharp/csharp.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/csharp/csharp.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/csp/csp.js b/src/Monaco/monacoSRC/min/vs/basic-languages/csp/csp.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/csp/csp.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/csp/csp.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/css/css.js b/src/Monaco/monacoSRC/min/vs/basic-languages/css/css.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/css/css.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/css/css.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/cypher/cypher.js b/src/Monaco/monacoSRC/min/vs/basic-languages/cypher/cypher.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/cypher/cypher.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/cypher/cypher.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/dart/dart.js b/src/Monaco/monacoSRC/min/vs/basic-languages/dart/dart.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/dart/dart.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/dart/dart.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/dockerfile/dockerfile.js b/src/Monaco/monacoSRC/min/vs/basic-languages/dockerfile/dockerfile.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/dockerfile/dockerfile.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/dockerfile/dockerfile.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/ecl/ecl.js b/src/Monaco/monacoSRC/min/vs/basic-languages/ecl/ecl.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/ecl/ecl.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/ecl/ecl.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/elixir/elixir.js b/src/Monaco/monacoSRC/min/vs/basic-languages/elixir/elixir.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/elixir/elixir.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/elixir/elixir.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/flow9/flow9.js b/src/Monaco/monacoSRC/min/vs/basic-languages/flow9/flow9.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/flow9/flow9.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/flow9/flow9.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/freemarker2/freemarker2.js b/src/Monaco/monacoSRC/min/vs/basic-languages/freemarker2/freemarker2.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/freemarker2/freemarker2.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/freemarker2/freemarker2.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/fsharp/fsharp.js b/src/Monaco/monacoSRC/min/vs/basic-languages/fsharp/fsharp.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/fsharp/fsharp.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/fsharp/fsharp.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/go/go.js b/src/Monaco/monacoSRC/min/vs/basic-languages/go/go.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/go/go.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/go/go.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/graphql/graphql.js b/src/Monaco/monacoSRC/min/vs/basic-languages/graphql/graphql.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/graphql/graphql.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/graphql/graphql.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/handlebars/handlebars.js b/src/Monaco/monacoSRC/min/vs/basic-languages/handlebars/handlebars.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/handlebars/handlebars.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/handlebars/handlebars.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/hcl/hcl.js b/src/Monaco/monacoSRC/min/vs/basic-languages/hcl/hcl.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/hcl/hcl.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/hcl/hcl.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/html/html.js b/src/Monaco/monacoSRC/min/vs/basic-languages/html/html.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/html/html.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/html/html.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/ini/ini.js b/src/Monaco/monacoSRC/min/vs/basic-languages/ini/ini.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/ini/ini.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/ini/ini.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/java/java.js b/src/Monaco/monacoSRC/min/vs/basic-languages/java/java.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/java/java.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/java/java.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/javascript/javascript.js b/src/Monaco/monacoSRC/min/vs/basic-languages/javascript/javascript.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/javascript/javascript.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/javascript/javascript.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/julia/julia.js b/src/Monaco/monacoSRC/min/vs/basic-languages/julia/julia.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/julia/julia.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/julia/julia.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/kotlin/kotlin.js b/src/Monaco/monacoSRC/min/vs/basic-languages/kotlin/kotlin.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/kotlin/kotlin.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/kotlin/kotlin.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/less/less.js b/src/Monaco/monacoSRC/min/vs/basic-languages/less/less.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/less/less.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/less/less.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/lexon/lexon.js b/src/Monaco/monacoSRC/min/vs/basic-languages/lexon/lexon.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/lexon/lexon.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/lexon/lexon.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/liquid/liquid.js b/src/Monaco/monacoSRC/min/vs/basic-languages/liquid/liquid.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/liquid/liquid.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/liquid/liquid.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/lua/lua.js b/src/Monaco/monacoSRC/min/vs/basic-languages/lua/lua.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/lua/lua.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/lua/lua.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/m3/m3.js b/src/Monaco/monacoSRC/min/vs/basic-languages/m3/m3.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/m3/m3.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/m3/m3.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/markdown/markdown.js b/src/Monaco/monacoSRC/min/vs/basic-languages/markdown/markdown.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/markdown/markdown.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/markdown/markdown.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/mdx/mdx.js b/src/Monaco/monacoSRC/min/vs/basic-languages/mdx/mdx.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/mdx/mdx.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/mdx/mdx.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/mips/mips.js b/src/Monaco/monacoSRC/min/vs/basic-languages/mips/mips.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/mips/mips.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/mips/mips.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/msdax/msdax.js b/src/Monaco/monacoSRC/min/vs/basic-languages/msdax/msdax.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/msdax/msdax.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/msdax/msdax.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/mysql/mysql.js b/src/Monaco/monacoSRC/min/vs/basic-languages/mysql/mysql.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/mysql/mysql.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/mysql/mysql.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/objective-c/objective-c.js b/src/Monaco/monacoSRC/min/vs/basic-languages/objective-c/objective-c.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/objective-c/objective-c.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/objective-c/objective-c.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/pascal/pascal.js b/src/Monaco/monacoSRC/min/vs/basic-languages/pascal/pascal.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/pascal/pascal.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/pascal/pascal.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/pascaligo/pascaligo.js b/src/Monaco/monacoSRC/min/vs/basic-languages/pascaligo/pascaligo.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/pascaligo/pascaligo.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/pascaligo/pascaligo.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/perl/perl.js b/src/Monaco/monacoSRC/min/vs/basic-languages/perl/perl.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/perl/perl.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/perl/perl.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/pgsql/pgsql.js b/src/Monaco/monacoSRC/min/vs/basic-languages/pgsql/pgsql.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/pgsql/pgsql.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/pgsql/pgsql.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/php/php.js b/src/Monaco/monacoSRC/min/vs/basic-languages/php/php.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/php/php.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/php/php.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/pla/pla.js b/src/Monaco/monacoSRC/min/vs/basic-languages/pla/pla.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/pla/pla.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/pla/pla.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/postiats/postiats.js b/src/Monaco/monacoSRC/min/vs/basic-languages/postiats/postiats.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/postiats/postiats.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/postiats/postiats.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/powerquery/powerquery.js b/src/Monaco/monacoSRC/min/vs/basic-languages/powerquery/powerquery.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/powerquery/powerquery.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/powerquery/powerquery.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/powershell/powershell.js b/src/Monaco/monacoSRC/min/vs/basic-languages/powershell/powershell.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/powershell/powershell.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/powershell/powershell.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/protobuf/protobuf.js b/src/Monaco/monacoSRC/min/vs/basic-languages/protobuf/protobuf.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/protobuf/protobuf.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/protobuf/protobuf.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/pug/pug.js b/src/Monaco/monacoSRC/min/vs/basic-languages/pug/pug.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/pug/pug.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/pug/pug.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/python/python.js b/src/Monaco/monacoSRC/min/vs/basic-languages/python/python.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/python/python.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/python/python.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/qsharp/qsharp.js b/src/Monaco/monacoSRC/min/vs/basic-languages/qsharp/qsharp.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/qsharp/qsharp.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/qsharp/qsharp.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/r/r.js b/src/Monaco/monacoSRC/min/vs/basic-languages/r/r.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/r/r.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/r/r.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/razor/razor.js b/src/Monaco/monacoSRC/min/vs/basic-languages/razor/razor.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/razor/razor.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/razor/razor.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/redis/redis.js b/src/Monaco/monacoSRC/min/vs/basic-languages/redis/redis.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/redis/redis.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/redis/redis.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/redshift/redshift.js b/src/Monaco/monacoSRC/min/vs/basic-languages/redshift/redshift.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/redshift/redshift.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/redshift/redshift.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/restructuredtext/restructuredtext.js b/src/Monaco/monacoSRC/min/vs/basic-languages/restructuredtext/restructuredtext.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/restructuredtext/restructuredtext.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/restructuredtext/restructuredtext.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/ruby/ruby.js b/src/Monaco/monacoSRC/min/vs/basic-languages/ruby/ruby.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/ruby/ruby.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/ruby/ruby.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/rust/rust.js b/src/Monaco/monacoSRC/min/vs/basic-languages/rust/rust.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/rust/rust.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/rust/rust.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/sb/sb.js b/src/Monaco/monacoSRC/min/vs/basic-languages/sb/sb.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/sb/sb.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/sb/sb.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/scala/scala.js b/src/Monaco/monacoSRC/min/vs/basic-languages/scala/scala.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/scala/scala.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/scala/scala.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/scheme/scheme.js b/src/Monaco/monacoSRC/min/vs/basic-languages/scheme/scheme.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/scheme/scheme.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/scheme/scheme.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/scss/scss.js b/src/Monaco/monacoSRC/min/vs/basic-languages/scss/scss.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/scss/scss.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/scss/scss.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/shell/shell.js b/src/Monaco/monacoSRC/min/vs/basic-languages/shell/shell.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/shell/shell.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/shell/shell.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/solidity/solidity.js b/src/Monaco/monacoSRC/min/vs/basic-languages/solidity/solidity.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/solidity/solidity.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/solidity/solidity.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/sophia/sophia.js b/src/Monaco/monacoSRC/min/vs/basic-languages/sophia/sophia.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/sophia/sophia.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/sophia/sophia.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/sparql/sparql.js b/src/Monaco/monacoSRC/min/vs/basic-languages/sparql/sparql.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/sparql/sparql.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/sparql/sparql.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/sql/sql.js b/src/Monaco/monacoSRC/min/vs/basic-languages/sql/sql.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/sql/sql.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/sql/sql.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/st/st.js b/src/Monaco/monacoSRC/min/vs/basic-languages/st/st.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/st/st.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/st/st.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/swift/swift.js b/src/Monaco/monacoSRC/min/vs/basic-languages/swift/swift.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/swift/swift.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/swift/swift.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/systemverilog/systemverilog.js b/src/Monaco/monacoSRC/min/vs/basic-languages/systemverilog/systemverilog.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/systemverilog/systemverilog.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/systemverilog/systemverilog.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/tcl/tcl.js b/src/Monaco/monacoSRC/min/vs/basic-languages/tcl/tcl.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/tcl/tcl.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/tcl/tcl.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/twig/twig.js b/src/Monaco/monacoSRC/min/vs/basic-languages/twig/twig.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/twig/twig.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/twig/twig.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/typescript/typescript.js b/src/Monaco/monacoSRC/min/vs/basic-languages/typescript/typescript.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/typescript/typescript.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/typescript/typescript.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/vb/vb.js b/src/Monaco/monacoSRC/min/vs/basic-languages/vb/vb.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/vb/vb.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/vb/vb.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/wgsl/wgsl.js b/src/Monaco/monacoSRC/min/vs/basic-languages/wgsl/wgsl.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/wgsl/wgsl.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/wgsl/wgsl.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/xml/xml.js b/src/Monaco/monacoSRC/min/vs/basic-languages/xml/xml.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/xml/xml.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/xml/xml.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/yaml/yaml.js b/src/Monaco/monacoSRC/min/vs/basic-languages/yaml/yaml.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/basic-languages/yaml/yaml.js rename to src/Monaco/monacoSRC/min/vs/basic-languages/yaml/yaml.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.css b/src/Monaco/monacoSRC/min/vs/editor/editor.main.css similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.css rename to src/Monaco/monacoSRC/min/vs/editor/editor.main.css diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.js b/src/Monaco/monacoSRC/min/vs/editor/editor.main.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.js rename to src/Monaco/monacoSRC/min/vs/editor/editor.main.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.de.js b/src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.de.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.de.js rename to src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.de.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.es.js b/src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.es.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.es.js rename to src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.es.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.fr.js b/src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.fr.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.fr.js rename to src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.fr.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.it.js b/src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.it.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.it.js rename to src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.it.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.ja.js b/src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.ja.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.ja.js rename to src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.ja.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.js b/src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.js rename to src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.ko.js b/src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.ko.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.ko.js rename to src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.ko.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.ru.js b/src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.ru.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.ru.js rename to src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.ru.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.zh-cn.js b/src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.zh-cn.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.zh-cn.js rename to src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.zh-cn.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.zh-tw.js b/src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.zh-tw.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/editor/editor.main.nls.zh-tw.js rename to src/Monaco/monacoSRC/min/vs/editor/editor.main.nls.zh-tw.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/css/cssMode.js b/src/Monaco/monacoSRC/min/vs/language/css/cssMode.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/css/cssMode.js rename to src/Monaco/monacoSRC/min/vs/language/css/cssMode.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/css/cssWorker.js b/src/Monaco/monacoSRC/min/vs/language/css/cssWorker.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/css/cssWorker.js rename to src/Monaco/monacoSRC/min/vs/language/css/cssWorker.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/html/htmlMode.js b/src/Monaco/monacoSRC/min/vs/language/html/htmlMode.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/html/htmlMode.js rename to src/Monaco/monacoSRC/min/vs/language/html/htmlMode.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/html/htmlWorker.js b/src/Monaco/monacoSRC/min/vs/language/html/htmlWorker.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/html/htmlWorker.js rename to src/Monaco/monacoSRC/min/vs/language/html/htmlWorker.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/json/jsonMode.js b/src/Monaco/monacoSRC/min/vs/language/json/jsonMode.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/json/jsonMode.js rename to src/Monaco/monacoSRC/min/vs/language/json/jsonMode.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/json/jsonWorker.js b/src/Monaco/monacoSRC/min/vs/language/json/jsonWorker.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/json/jsonWorker.js rename to src/Monaco/monacoSRC/min/vs/language/json/jsonWorker.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/typescript/tsMode.js b/src/Monaco/monacoSRC/min/vs/language/typescript/tsMode.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/typescript/tsMode.js rename to src/Monaco/monacoSRC/min/vs/language/typescript/tsMode.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/typescript/tsWorker.js b/src/Monaco/monacoSRC/min/vs/language/typescript/tsWorker.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/language/typescript/tsWorker.js rename to src/Monaco/monacoSRC/min/vs/language/typescript/tsWorker.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/loader.js b/src/Monaco/monacoSRC/min/vs/loader.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSRC/min/vs/loader.js rename to src/Monaco/monacoSRC/min/vs/loader.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monacoSpecialLanguages.js b/src/Monaco/monacoSpecialLanguages.js similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monacoSpecialLanguages.js rename to src/Monaco/monacoSpecialLanguages.js diff --git a/src/common/FilePreviewCommon/Assets/Monaco/monaco_languages.json b/src/Monaco/monaco_languages.json similarity index 100% rename from src/common/FilePreviewCommon/Assets/Monaco/monaco_languages.json rename to src/Monaco/monaco_languages.json diff --git a/src/common/FilePreviewCommon/FilePreviewCommon.csproj b/src/common/FilePreviewCommon/FilePreviewCommon.csproj index 5849155ca0..28865605a0 100644 --- a/src/common/FilePreviewCommon/FilePreviewCommon.csproj +++ b/src/common/FilePreviewCommon/FilePreviewCommon.csproj @@ -1,6 +1,7 @@  + PowerToys FilePreviewCommon @@ -15,28 +16,4 @@ - - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - - Always - - diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Assets/RegistryPreview/index.html b/src/modules/registrypreview/RegistryPreviewUILib/Assets/RegistryPreview/index.html new file mode 100644 index 0000000000..5709e3f4f1 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/Assets/RegistryPreview/index.html @@ -0,0 +1,64 @@ + + + + + + + + + Registry Preview Editor + + + + + +
+ + + + + diff --git a/src/modules/registrypreview/RegistryPreviewUILib/MonacoEditorControl.xaml b/src/modules/registrypreview/RegistryPreviewUILib/MonacoEditorControl.xaml new file mode 100644 index 0000000000..9635708343 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/MonacoEditorControl.xaml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + diff --git a/src/modules/registrypreview/RegistryPreviewUILib/MonacoEditorControl.xaml.cs b/src/modules/registrypreview/RegistryPreviewUILib/MonacoEditorControl.xaml.cs new file mode 100644 index 0000000000..eeddfcb0f4 --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/MonacoEditorControl.xaml.cs @@ -0,0 +1,169 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Reflection; +using System.Text.Json.Nodes; +using System.Threading.Tasks; +using System.Timers; +using System.Web; +using CommunityToolkit.Mvvm.ComponentModel; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; +using Microsoft.Web.WebView2.Core; +using Windows.UI; + +namespace RegistryPreviewUILib +{ + [INotifyPropertyChanged] + public sealed partial class MonacoEditorControl : UserControl, IDisposable + { + private readonly Timer _textChangedThrottle; + private bool _textChangedThrottled; + + public string Text { get; private set; } + + [ObservableProperty] + private bool _isLoading; + + public event EventHandler TextChanged; + + public MonacoEditorControl() + { + InitializeComponent(); + Environment.SetEnvironmentVariable("WEBVIEW2_USER_DATA_FOLDER", MonacoHelper.TempFolderPath, EnvironmentVariableTarget.Process); + + _textChangedThrottle = new Timer(250); + _textChangedThrottle.Elapsed += OnTextChangedThrottleElapsed; + _textChangedThrottle.AutoReset = false; + + ActualThemeChanged += OnActualThemeChanged; + } + + public async Task SetTextAsync(string text) + { + Text = text; + + if (!IsLoading) + { + var encodedText = HttpUtility.JavaScriptStringEncode(text); + await Browser.CoreWebView2.ExecuteScriptAsync($"editor.setValue('{encodedText}')"); + } + } + + private async void OnActualThemeChanged(FrameworkElement sender, object args) + { + await SetThemeAsync(); + } + + private async void Browser_Loaded(object sender, RoutedEventArgs e) + { + IsLoading = true; + + await Browser.EnsureCoreWebView2Async(); + Browser.DefaultBackgroundColor = Color.FromArgb(0, 0, 0, 0); + Browser.CoreWebView2.NavigationCompleted += CoreWebView2_NavigationCompleted; + Browser.CoreWebView2.PermissionRequested += CoreWebView2_PermissionRequested; + Browser.CoreWebView2.Settings.AreDefaultScriptDialogsEnabled = false; + Browser.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false; + Browser.CoreWebView2.Settings.AreHostObjectsAllowed = false; + Browser.CoreWebView2.Settings.IsGeneralAutofillEnabled = false; + Browser.CoreWebView2.Settings.IsPasswordAutosaveEnabled = false; + Browser.CoreWebView2.Settings.IsScriptEnabled = true; + Browser.CoreWebView2.Settings.IsWebMessageEnabled = true; +#if DEBUG + Browser.CoreWebView2.Settings.AreDevToolsEnabled = true; +#else + Browser.CoreWebView2.Settings.AreDevToolsEnabled = false; +#endif + + Browser.CoreWebView2.SetVirtualHostNameToFolderMapping( + MonacoHelper.VirtualHostName, + MonacoHelper.MonacoDirectory, + CoreWebView2HostResourceAccessKind.Allow); + + var assemblyDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? string.Empty; + var index = Path.GetFullPath(Path.Combine(assemblyDir, "Assets", "RegistryPreview", "index.html")); + Browser.CoreWebView2.Navigate(index); + } + + private void CoreWebView2_PermissionRequested(CoreWebView2 sender, CoreWebView2PermissionRequestedEventArgs args) + { + if (args.PermissionKind == CoreWebView2PermissionKind.ClipboardRead) + { + // Hide the permission request dialog + args.State = CoreWebView2PermissionState.Allow; + args.Handled = true; + } + } + + private async void CoreWebView2_NavigationCompleted(CoreWebView2 sender, CoreWebView2NavigationCompletedEventArgs args) + { + await SetThemeAsync(); + IsLoading = false; + await SetTextAsync(Text); + + Browser.CoreWebView2.WebMessageReceived += CoreWebView2_WebMessageReceived; + Browser.Focus(FocusState.Programmatic); + } + + private void CoreWebView2_WebMessageReceived(CoreWebView2 sender, CoreWebView2WebMessageReceivedEventArgs args) + { + var json = JsonNode.Parse(args.WebMessageAsJson); + if (json == null) + { + return; + } + + var id = json["id"]; + if (id == null || !id.ToString().Equals("contentChanged", StringComparison.OrdinalIgnoreCase)) + { + return; + } + + var content = json["content"].ToString(); + if (content == null) + { + return; + } + + Text = content; + ThrottleTextChanged(); + } + + private async Task SetThemeAsync() + { + var theme = Application.Current.RequestedTheme == ApplicationTheme.Light ? "vs" : "vs-dark"; + await Browser.CoreWebView2.ExecuteScriptAsync($"monaco.editor.setTheme('{theme}')"); + } + + private void OnTextChangedThrottleElapsed(object sender, ElapsedEventArgs e) + { + if (_textChangedThrottled) + { + _textChangedThrottled = false; + TextChanged?.Invoke(this, EventArgs.Empty); + _textChangedThrottle.Start(); + } + } + + private void ThrottleTextChanged() + { + if (_textChangedThrottle.Enabled) + { + _textChangedThrottled = true; + return; + } + + TextChanged?.Invoke(this, EventArgs.Empty); + _textChangedThrottle.Start(); + } + + public void Dispose() + { + _textChangedThrottle?.Dispose(); + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/MonacoHelper.cs b/src/modules/registrypreview/RegistryPreviewUILib/MonacoHelper.cs new file mode 100644 index 0000000000..95305ff2ba --- /dev/null +++ b/src/modules/registrypreview/RegistryPreviewUILib/MonacoHelper.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using System.Reflection; + +namespace RegistryPreviewUILib +{ + public static class MonacoHelper + { + /// + /// Name of the virtual host + /// + public const string VirtualHostName = "PowerToysLocalMonaco"; + + public static string TempFolderPath { get; } = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), @"AppData\LocalLow\Microsoft\PowerToys\RegistryPreview-Temp"); + + private static string _monacoDirectory; + + public static string GetRuntimeMonacoDirectory() + { + string codeBase = Assembly.GetExecutingAssembly().Location; + string path = Path.GetFullPath(Path.Combine(Path.GetDirectoryName(codeBase) ?? string.Empty, "Assets", "Monaco")); + if (Path.Exists(path)) + { + return path; + } + else + { + // We're likely in WinUI3Apps directory and need to go back to the base directory. + return Path.GetFullPath(Path.Combine(Path.GetDirectoryName(codeBase) ?? string.Empty, "..", "Assets", "Monaco")); + } + } + + public static string MonacoDirectory + { + get + { + if (string.IsNullOrEmpty(_monacoDirectory)) + { + _monacoDirectory = GetRuntimeMonacoDirectory(); + } + + return _monacoDirectory; + } + } + } +} diff --git a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Events.cs b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Events.cs index 624b104a01..0df3a5c0f5 100644 --- a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Events.cs +++ b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Events.cs @@ -8,9 +8,9 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; -using System.Threading.Tasks; using CommunityToolkit.WinUI.UI.Controls; +using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Data; @@ -21,6 +21,8 @@ namespace RegistryPreviewUILib { public sealed partial class RegistryPreviewMainPage : Page { + private readonly DispatcherQueue _dispatcherQueue = DispatcherQueue.GetForCurrentThread(); + /// /// Event that is will prevent the app from closing if the "save file" flag is active /// @@ -40,43 +42,25 @@ namespace RegistryPreviewUILib resourceLoader.GetString("YesNoCancelDialogSecondaryButtonText"), resourceLoader.GetString("YesNoCancelDialogCloseButtonText")); } - - // Check to see if the textbox's context menu is open - if (textBox.ContextFlyout != null && textBox.ContextFlyout.IsOpen) - { - textBox.ContextFlyout.Hide(); - - // if true, the app will not close yet - args.Handled = true; - - // HACK: To fix https://github.com/microsoft/PowerToys/issues/28820, wait a bit for the close animation of the flyout to run before closing the application. - // This might be called many times if the flyout still hasn't been closed, as Window_Closed will be called again by App.Current.Exit - DispatcherQueue.TryEnqueue(async () => - { - await Task.Delay(100); - Application.Current.Exit(); - }); - return; - } } /// /// Event that gets fired after the visual tree has been fully loaded; the app opens the reg file from here so it can show a message box successfully /// - private void GridPreview_Loaded(object sender, RoutedEventArgs e) + private async void GridPreview_Loaded(object sender, RoutedEventArgs e) { // static flag to track whether the Visual Tree is ready - if the main Grid has been loaded, the tree is ready. visualTreeReady = true; // Check to see if the REG file was opened and parsed successfully - if (OpenRegistryFile(_appFileName) == false) + if (await OpenRegistryFile(_appFileName) == false) { if (File.Exists(_appFileName)) { // Allow Refresh and Edit to be enabled because a broken Reg file might be fixable UpdateToolBarAndUI(false, true, true); _updateWindowTitleFunction(resourceLoader.GetString("InvalidRegistryFileTitle")); - textBox.TextChanged += TextBox_TextChanged; + MonacoEditor.TextChanged += MonacoEditor_TextChanged; return; } else @@ -87,10 +71,10 @@ namespace RegistryPreviewUILib } else { - textBox.TextChanged += TextBox_TextChanged; + MonacoEditor.TextChanged += MonacoEditor_TextChanged; } - textBox.Focus(FocusState.Programmatic); + MonacoEditor.Focus(FocusState.Programmatic); } /// @@ -153,16 +137,15 @@ namespace RegistryPreviewUILib if (storageFile != null) { // mute the TextChanged handler to make for clean UI - textBox.TextChanged -= TextBox_TextChanged; - + MonacoEditor.TextChanged -= MonacoEditor_TextChanged; _appFileName = storageFile.Path; - UpdateToolBarAndUI(OpenRegistryFile(_appFileName)); + UpdateToolBarAndUI(await OpenRegistryFile(_appFileName)); // disable the Save button as it's a new file saveButton.IsEnabled = false; // Restore the event handler as we're loaded - textBox.TextChanged += TextBox_TextChanged; + MonacoEditor.TextChanged += MonacoEditor_TextChanged; } } @@ -177,7 +160,7 @@ namespace RegistryPreviewUILib /// /// Uses a picker to save out a copy of the current reg file /// - private void SaveAsButton_Click(object sender, RoutedEventArgs e) + private async void SaveAsButton_Click(object sender, RoutedEventArgs e) { // Save out a new REG file and then open it - we have to use the direct Win32 method because FileOpenPicker crashes when it's // called while running as admin @@ -195,24 +178,24 @@ namespace RegistryPreviewUILib _appFileName = filename; SaveFile(); - UpdateToolBarAndUI(OpenRegistryFile(_appFileName)); + UpdateToolBarAndUI(await OpenRegistryFile(_appFileName)); } /// /// Reloads the current REG file from storage /// - private void RefreshButton_Click(object sender, RoutedEventArgs e) + private async void RefreshButton_Click(object sender, RoutedEventArgs e) { // mute the TextChanged handler to make for clean UI - textBox.TextChanged -= TextBox_TextChanged; + MonacoEditor.TextChanged -= MonacoEditor_TextChanged; // reload the current Registry file and update the toolbar accordingly. - UpdateToolBarAndUI(OpenRegistryFile(_appFileName), true, true); + UpdateToolBarAndUI(await OpenRegistryFile(_appFileName), true, true); saveButton.IsEnabled = false; // restore the TextChanged handler - textBox.TextChanged += TextBox_TextChanged; + MonacoEditor.TextChanged += MonacoEditor_TextChanged; } /// @@ -364,12 +347,15 @@ namespace RegistryPreviewUILib } /// - /// When the text in textBox changes, reload treeView and possibly dataGrid and reset the save button + /// When the text in editor changes, reload treeView and possibly dataGrid and reset the save button /// - private void TextBox_TextChanged(object sender, TextChangedEventArgs e) + private void MonacoEditor_TextChanged(object sender, EventArgs e) { - RefreshRegistryFile(); - saveButton.IsEnabled = true; + _dispatcherQueue.TryEnqueue(() => + { + RefreshRegistryFile(); + saveButton.IsEnabled = true; + }); } } } diff --git a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Utilities.cs b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Utilities.cs index 6c85c08ddf..daae306006 100644 --- a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Utilities.cs +++ b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.Utilities.cs @@ -11,7 +11,8 @@ using System.IO; using System.Linq; using System.Reflection; using System.Text; - +using System.Threading; +using System.Threading.Tasks; using Microsoft.UI.Input; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; @@ -21,12 +22,14 @@ namespace RegistryPreviewUILib { public sealed partial class RegistryPreviewMainPage : Page { + private static SemaphoreSlim _dialogSemaphore = new(1); + public delegate void UpdateWindowTitleFunction(string title); /// /// Method that opens and processes the passed in file name; expected to be an absolute path and a first time open /// - private bool OpenRegistryFile(string filename) + private async Task OpenRegistryFile(string filename) { // clamp to prevent attempts to open a file larger than 10MB try @@ -46,7 +49,7 @@ namespace RegistryPreviewUILib // Disable parts of the UI that can cause trouble when loading ChangeCursor(gridPreview, true); - textBox.Text = string.Empty; + await MonacoEditor.SetTextAsync(string.Empty); // clear the treeView and dataGrid no matter what treeView.RootNodes.Clear(); @@ -55,7 +58,7 @@ namespace RegistryPreviewUILib // update the current window's title with the current filename _updateWindowTitleFunction(filename); - // Load in the whole file in one call and plop it all into textBox + // Load in the whole file in one call and plop it all into editor FileStream fileStream = null; try { @@ -68,15 +71,15 @@ namespace RegistryPreviewUILib StreamReader streamReader = new StreamReader(fileStream); string filenameText = streamReader.ReadToEnd(); - textBox.Text = filenameText; + await MonacoEditor.SetTextAsync(filenameText); streamReader.Close(); } catch { // restore TextChanged handler to make for clean UI - textBox.TextChanged += TextBox_TextChanged; + MonacoEditor.TextChanged += MonacoEditor_TextChanged; - // Reset the cursor but leave textBox disabled as no content got loaded + // Reset the cursor but leave editor disabled as no content got loaded ChangeCursor(gridPreview, false); return false; } @@ -89,8 +92,8 @@ namespace RegistryPreviewUILib } } - // now that the file is loaded and in textBox, parse the data - ParseRegistryFile(textBox.Text); + // now that the file is loaded and in editor, parse the data + ParseRegistryFile(MonacoEditor.Text); // Getting here means that the entire REG file was parsed without incident // so select the root of the tree and celebrate @@ -120,8 +123,8 @@ namespace RegistryPreviewUILib treeView.RootNodes.Clear(); ClearTable(); - // the existing text is still in textBox so parse the data again - ParseRegistryFile(textBox.Text); + // the existing text is still in editor so parse the data again + ParseRegistryFile(MonacoEditor.Text); // check to see if there was a key in treeView before the refresh happened if (currentNode != null) @@ -164,7 +167,7 @@ namespace RegistryPreviewUILib } /// - /// Parses the text that is passed in, which should be the same text that's in textBox + /// Parses the text that is passed in, which should be the same text that's in editor /// private bool ParseRegistryFile(string filenameText) { @@ -181,10 +184,10 @@ namespace RegistryPreviewUILib // As we'll be processing the text one line at a time, this string will be the current line string registryLine; - // Brute force editing: for textBox to show Cr-Lf corrected, we need to strip out the \n's + // Brute force editing: for editor to show Cr-Lf corrected, we need to strip out the \n's filenameText = filenameText.Replace("\r\n", "\r"); - // split apart all of the text in textBox, where one element in the array represents one line + // split apart all of the text in editor, where one element in the array represents one line string[] registryLines = filenameText.Split("\r"); if (registryLines.Length <= 1) { @@ -655,8 +658,8 @@ namespace RegistryPreviewUILib } /// - /// Enable command bar buttons and textBox. - /// Note that writeButton and textBox all update with the same value on purpose + /// Enable command bar buttons + /// Note that writeButton and editor all update with the same value on purpose /// private void UpdateToolBarAndUI(bool enableWrite, bool enableRefresh, bool enableEdit) { @@ -776,21 +779,34 @@ namespace RegistryPreviewUILib /// private async void ShowMessageBox(string title, string content, string closeButtonText) { - ContentDialog contentDialog = new ContentDialog() + if (_dialogSemaphore.CurrentCount == 0) { - Title = title, - Content = content, - CloseButtonText = closeButtonText, - }; - - // Use this code to associate the dialog to the appropriate AppWindow by setting - // the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow. - if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) - { - contentDialog.XamlRoot = this.Content.XamlRoot; + return; } - await contentDialog.ShowAsync(); + try + { + await _dialogSemaphore.WaitAsync(); + ContentDialog contentDialog = new ContentDialog() + { + Title = title, + Content = content, + CloseButtonText = closeButtonText, + }; + + // Use this code to associate the dialog to the appropriate AppWindow by setting + // the dialog's XamlRoot to the same XamlRoot as an element that is already present in the AppWindow. + if (ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 8)) + { + contentDialog.XamlRoot = this.Content.XamlRoot; + } + + await contentDialog.ShowAsync(); + } + finally + { + _dialogSemaphore.Release(); + } } /// @@ -894,7 +910,7 @@ namespace RegistryPreviewUILib } /// - /// Wrapper method that saves the current file in place, using the current text in textBox. + /// Wrapper method that saves the current file in place, using the current text in editor. /// private void SaveFile() { @@ -914,8 +930,8 @@ namespace RegistryPreviewUILib fileStream = new FileStream(_appFileName, fileStreamOptions); StreamWriter streamWriter = new StreamWriter(fileStream, System.Text.Encoding.Unicode); - // if we get here, the file is open and writable so dump the whole contents of textBox - string filenameText = textBox.Text; + // if we get here, the file is open and writable so dump the whole contents of editor + string filenameText = MonacoEditor.Text; streamWriter.Write(filenameText); streamWriter.Flush(); streamWriter.Close(); diff --git a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.xaml b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.xaml index f5e0c6ee1a..058663459e 100644 --- a/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.xaml +++ b/src/modules/registrypreview/RegistryPreviewUILib/RegistryPreviewMainPage.xaml @@ -3,6 +3,7 @@ xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" + xmlns:local="using:RegistryPreviewUILib" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:tk7controls="using:CommunityToolkit.WinUI.UI.Controls" xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls" @@ -133,28 +134,12 @@ - + IsTabStop="True" /> + Library @@ -39,6 +40,12 @@ + + + + + + @@ -52,4 +59,10 @@ Always + + + + MSBuild:Compile + + diff --git a/src/modules/registrypreview/RegistryPreviewUILib/Strings/en-US/Resources.resw b/src/modules/registrypreview/RegistryPreviewUILib/Strings/en-US/Resources.resw index d637b2e187..580d108d9a 100644 --- a/src/modules/registrypreview/RegistryPreviewUILib/Strings/en-US/Resources.resw +++ b/src/modules/registrypreview/RegistryPreviewUILib/Strings/en-US/Resources.resw @@ -199,9 +199,6 @@ New Registry file - - Registry file text will appear here - Registry Preview From 133aa85f2bb23d7f45b5fde9767e56cac476a025 Mon Sep 17 00:00:00 2001 From: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com> Date: Thu, 24 Oct 2024 22:04:32 +0200 Subject: [PATCH 46/95] [General]Add an option for telemetry opt-in and visualization(#34078) * Data diagnostics opt-in * [c++] Drop DROP_PII flag * Bump telemtry package to 2.0.2 * Drop DropPii from custom actions * Cleanup * Do not start manually C# EtwTrace. FZ engine exit event. * ImageResizer, PowerRename, FileLocksmith prev handlers * Revert C# handlers exe logging * Revert "Revert C# handlers exe logging" This reverts commit 4c75a3953b085aeffaefe8e9e20c39cebb899ea1. * Do not recreate EtwTrace * consume package * xaml formatting * Fix deps.json audit * Update telem package paths * Address PR comments * Fix AdvancedPaste close on PT close * Override etl file name for explorer loaded dlls Start/stop tracer when needed for explorer loaded dlls to prevent explorer overload * Fix setting desc * Fix missing events * Add infobar to restart when enable data viewing * Flush on timer every 30s * [Settings] Update View Data diagnostic description text [New+] Add tracer * Show Restart info bar for both enable/disable data viewer * Fix newplus * Fix stuck on restart and terminate AdvPaste exe on destroy() * [Installer] Add tracer * Address PR comment * Add missing tracers * Exclude etw dir from BugReport * Fix bad merge * [Hosts] Proper exit on initial dialog * [OOBE] Make Data diagnostic setting visible without scroll * [OOBE] Add hiperlynk to open general settings * Disable data view on disabling data diagnostics * Don't disable View data button * Fix disabling data viewing * Add missing dot * Revert formatting --- .github/actions/spell-check/expect.txt | 27 +- .pipelines/packages.config | 2 +- .pipelines/v2/release.yml | 4 +- Directory.Packages.props | 1 + NOTICE.md | 1 + PowerToys.sln | 16 + installer/PowerToysSetup.sln | 10 + .../CustomAction.cpp | 73 ++++- .../PowerToysSetupCustomActions.vcxproj | 3 + src/common/GPOWrapper/GPOWrapper.cpp | 4 + src/common/GPOWrapper/GPOWrapper.h | 1 + src/common/GPOWrapper/GPOWrapper.idl | 1 + .../Telemetry/DataDiagnosticsSettings.cs | 107 ++++++ .../ManagedTelemetry/Telemetry/EtwTrace.cs | 158 +++++++++ .../Telemetry/ManagedTelemetry.csproj | 6 +- .../Telemetry/PowerToysTelemetry.cs | 19 +- src/common/SettingsAPI/settings_helpers.cpp | 34 +- src/common/SettingsAPI/settings_helpers.h | 2 + src/common/Telemetry/EtwTrace/EtwTrace.cpp | 310 ++++++++++++++++++ src/common/Telemetry/EtwTrace/EtwTrace.h | 54 +++ .../Telemetry/EtwTrace/EtwTrace.vcxproj | 51 +++ .../EtwTrace/EtwTrace.vcxproj.filters | 39 +++ src/common/Telemetry/EtwTrace/packages.config | 5 + src/common/Telemetry/EtwTrace/pch.cpp | 5 + src/common/Telemetry/EtwTrace/pch.h | 23 ++ src/common/Telemetry/TraceBase.h | 63 ++++ src/common/interop/Constants.cpp | 24 ++ src/common/interop/Constants.h | 6 + src/common/interop/Constants.idl | 6 + src/common/interop/shared_constants.h | 18 + src/common/utils/EventWaiter.h | 2 + src/common/utils/gpo.h | 6 + src/gpo/assets/PowerToys.admx | 13 +- src/gpo/assets/en-US/PowerToys.adml | 10 +- .../AdvancedPasteXAML/App.xaml.cs | 15 +- .../AdvancedPasteXAML/MainWindow.xaml.cs | 2 + .../AdvancedPasteModuleInterface/dllmain.cpp | 21 +- .../AdvancedPasteModuleInterface/pch.h | 1 - .../AdvancedPasteModuleInterface/trace.cpp | 18 +- .../AdvancedPasteModuleInterface/trace.h | 6 +- .../CropAndLock/CropAndLock.vcxproj | 3 + .../CropAndLock/CropAndLock.vcxproj.filters | 1 + src/modules/CropAndLock/CropAndLock/main.cpp | 35 +- src/modules/CropAndLock/CropAndLock/pch.h | 1 - src/modules/CropAndLock/CropAndLock/trace.cpp | 27 +- src/modules/CropAndLock/CropAndLock/trace.h | 7 +- .../CropAndLockModuleInterface/dllmain.cpp | 4 +- .../CropAndLockModuleInterface/pch.h | 2 - .../EnvironmentVariablesXAML/App.xaml.cs | 2 + .../EnvironmentVariablesXAML/MainWindow.xaml | 3 +- .../MainWindow.xaml.cs | 5 + .../EnvironmentVariablesModuleInterface/pch.h | 1 - .../trace.cpp | 16 +- .../trace.h | 7 +- .../FileLocksmithContextMenu.vcxproj | 3 + .../FileLocksmithContextMenu.vcxproj.filters | 3 +- .../FileLocksmithContextMenu/dllmain.cpp | 15 + .../FileLocksmithExt/ExplorerCommand.cpp | 14 + .../FileLocksmithExt/ExplorerCommand.h | 4 + .../FileLocksmithExt/FileLocksmithExt.vcxproj | 3 + .../FileLocksmith/FileLocksmithLib/Trace.cpp | 21 +- .../FileLocksmith/FileLocksmithLib/Trace.h | 6 +- .../Hosts/Hosts/Helpers/NativeEventWaiter.cs | 30 ++ src/modules/Hosts/Hosts/HostsXAML/App.xaml.cs | 11 +- .../Hosts/Hosts/HostsXAML/MainWindow.xaml | 3 +- .../Hosts/Hosts/HostsXAML/MainWindow.xaml.cs | 5 + .../Hosts/HostsModuleInterface/dllmain.cpp | 15 + src/modules/Hosts/HostsModuleInterface/pch.h | 1 - .../Hosts/HostsModuleInterface/trace.cpp | 16 +- .../Hosts/HostsModuleInterface/trace.h | 7 +- .../Hosts/HostsUILib/HostsMainPage.xaml.cs | 2 +- .../PowerToys.MeasureToolCore.cpp | 7 + .../PowerToys.MeasureToolCore.h | 2 + .../PowerToys.MeasureToolCore.vcxproj | 3 + src/modules/MeasureTool/MeasureToolCore/pch.h | 1 - .../MeasureToolModuleInterface/pch.h | 1 - .../MeasureToolModuleInterface/trace.cpp | 18 +- .../MeasureToolModuleInterface/trace.h | 7 +- src/modules/MouseUtils/FindMyMouse/pch.h | 1 - src/modules/MouseUtils/FindMyMouse/trace.cpp | 16 +- src/modules/MouseUtils/FindMyMouse/trace.h | 7 +- src/modules/MouseUtils/MouseHighlighter/pch.h | 1 - .../MouseUtils/MouseHighlighter/trace.cpp | 16 +- .../MouseUtils/MouseHighlighter/trace.h | 7 +- src/modules/MouseUtils/MouseJump/pch.h | 1 - src/modules/MouseUtils/MouseJump/trace.cpp | 16 +- src/modules/MouseUtils/MouseJump/trace.h | 7 +- .../MouseUtils/MousePointerCrosshairs/pch.h | 1 - .../MousePointerCrosshairs/trace.cpp | 16 +- .../MouseUtils/MousePointerCrosshairs/trace.h | 7 +- .../App/Class/Common.ShutdownWithPowerToys.cs | 6 +- .../MouseWithoutBorders/App/Class/Program.cs | 6 +- .../MouseWithoutBorders/App/Helper/Program.cs | 6 + .../ModuleInterface/dllmain.cpp | 4 +- .../MouseWithoutBorders/ModuleInterface/pch.h | 2 - .../ModuleInterface/trace.cpp | 20 +- .../ModuleInterface/trace.h | 7 +- .../NewShellExtensionContextMenu.vcxproj | 3 + .../NewShellExtensionContextMenu/dll_main.cpp | 3 + .../NewShellExtensionContextMenu/dll_main.h | 5 +- .../shell_context_sub_menu.cpp | 1 + .../shell_context_sub_menu_item.cpp | 12 +- .../NewShellExtensionContextMenu/trace.cpp | 20 +- .../NewShellExtensionContextMenu/trace.h | 6 +- src/modules/PowerOCR/PowerOCR/App.xaml.cs | 12 +- .../PowerOCRModuleInterface/dllmain.cpp | 5 + .../PowerOCR/PowerOCRModuleInterface/pch.h | 1 - .../PowerOCRModuleInterface/trace.cpp | 14 +- .../PowerOCR/PowerOCRModuleInterface/trace.h | 8 +- .../ShortcutGuide/ShortcutGuide.vcxproj | 3 + .../ShortcutGuide/ShortcutGuide/main.cpp | 7 + src/modules/ShortcutGuide/ShortcutGuide/pch.h | 1 - .../ShortcutGuide/ShortcutGuide/trace.cpp | 16 +- .../ShortcutGuide/ShortcutGuide/trace.h | 6 +- .../ShortcutGuideModuleInterface/pch.h | 1 - .../Workspaces/WorkspacesEditor/App.xaml.cs | 4 + .../WorkspacesEditor/MainWindow.xaml.cs | 1 + .../WorkspacesLauncher.vcxproj | 3 + .../Workspaces/WorkspacesLauncher/main.cpp | 29 +- .../Workspaces/WorkspacesLib/trace.cpp | 14 +- src/modules/Workspaces/WorkspacesLib/trace.h | 7 +- .../WorkspacesModuleInterface/dllmain.cpp | 4 +- .../alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp | 22 +- .../alwaysontop/AlwaysOnTop/AlwaysOnTop.h | 4 +- .../AlwaysOnTop/AlwaysOnTop.vcxproj | 3 + src/modules/alwaysontop/AlwaysOnTop/main.cpp | 19 +- src/modules/alwaysontop/AlwaysOnTop/pch.h | 1 - src/modules/alwaysontop/AlwaysOnTop/trace.cpp | 18 +- src/modules/alwaysontop/AlwaysOnTop/trace.h | 7 +- .../AlwaysOnTopModuleInterface/dllmain.cpp | 12 +- .../AlwaysOnTopModuleInterface/pch.h | 2 - src/modules/awake/Awake/Program.cs | 3 + src/modules/awake/AwakeModuleInterface/pch.h | 1 - .../awake/AwakeModuleInterface/trace.cpp | 14 +- .../awake/AwakeModuleInterface/trace.h | 7 +- .../CmdNotFoundModuleInterface/pch.h | 2 - .../CmdNotFoundModuleInterface/trace.cpp | 14 +- .../CmdNotFoundModuleInterface/trace.h | 7 +- .../colorPicker/ColorPicker/dllmain.cpp | 7 + src/modules/colorPicker/ColorPicker/pch.h | 1 - src/modules/colorPicker/ColorPicker/trace.cpp | 14 +- src/modules/colorPicker/ColorPicker/trace.h | 8 +- .../colorPicker/ColorPickerUI/App.xaml.cs | 4 + .../ColorPickerUI/ColorEditorWindow.xaml.cs | 1 - .../ColorPickerUI/ViewModels/MainViewModel.cs | 6 + .../fancyzones/FancyZones/FancyZones.vcxproj | 3 + .../fancyzones/FancyZones/FancyZonesApp.cpp | 11 +- .../fancyzones/FancyZones/FancyZonesApp.h | 6 +- src/modules/fancyzones/FancyZones/main.cpp | 9 +- src/modules/fancyzones/FancyZones/pch.h | 1 - src/modules/fancyzones/FancyZonesLib/pch.h | 2 - .../fancyzones/FancyZonesLib/trace.cpp | 40 +-- src/modules/fancyzones/FancyZonesLib/trace.h | 11 +- .../FancyZonesModuleInterface/dllmain.cpp | 3 +- .../FancyZonesModuleInterface/pch.h | 2 - .../UnitTests/UnitTests.vcxproj | 4 +- .../ImageResizerContextMenu.vcxproj | 3 + .../ImageResizerContextMenu/dllmain.cpp | 6 + .../imageresizer/ImageResizerLib/pch.h | 2 - .../imageresizer/ImageResizerLib/trace.cpp | 21 +- .../imageresizer/ImageResizerLib/trace.h | 6 +- .../imageresizer/dll/ContextMenuHandler.cpp | 17 + .../imageresizer/dll/ContextMenuHandler.h | 2 + .../imageresizer/dll/ImageResizerExt.vcxproj | 9 +- src/modules/imageresizer/dll/pch.h | 1 - .../KeyboardManagerEditor.cpp | 1 - .../KeyboardManagerEditor.vcxproj | 3 + .../EditKeyboardWindow.cpp | 6 + .../EditShortcutsWindow.cpp | 6 + .../KeyboardManagerEditorLibrary/pch.h | 2 - .../KeyboardManagerEditorLibrary/trace.cpp | 20 +- .../KeyboardManagerEditorLibrary/trace.h | 7 +- .../KeyboardManagerEditorTest/pch.h | 1 - .../KeyboardManagerEngine.vcxproj | 3 + .../KeyboardManagerEngine/main.cpp | 16 +- .../KeyboardManagerEngine/pch.h | 1 - .../KeyboardManagerEngineLibrary/pch.h | 1 - .../KeyboardManagerEngineLibrary/trace.cpp | 118 +++---- .../KeyboardManagerEngineLibrary/trace.h | 7 +- .../KeyboardManagerEngineTest/pch.h | 1 - src/modules/keyboardmanager/common/pch.h | 1 - src/modules/keyboardmanager/dll/dllmain.cpp | 17 + src/modules/keyboardmanager/dll/pch.h | 1 - src/modules/keyboardmanager/dll/trace.cpp | 14 +- src/modules/keyboardmanager/dll/trace.h | 7 +- .../Microsoft.Launcher.vcxproj | 5 +- .../Microsoft.Launcher.vcxproj.filters | 2 - .../launcher/Microsoft.Launcher/dllmain.cpp | 3 - src/modules/launcher/Microsoft.Launcher/pch.h | 1 - .../launcher/Microsoft.Launcher/trace.cpp | 17 - .../launcher/Microsoft.Launcher/trace.h | 7 - .../launcher/PowerLauncher/App.xaml.cs | 5 + src/modules/peek/Peek.UI/PeekXAML/App.xaml.cs | 8 + src/modules/peek/peek/dllmain.cpp | 12 +- src/modules/peek/peek/pch.h | 1 - src/modules/peek/peek/trace.cpp | 18 +- src/modules/peek/peek/trace.h | 7 +- .../poweraccent/PowerAccent.UI/App.xaml.cs | 3 + .../PowerAccentModuleInterface/pch.h | 1 - .../PowerAccentModuleInterface/trace.cpp | 14 +- .../PowerAccentModuleInterface/trace.h | 7 +- .../PowerRenameContextMenu.vcxproj | 3 + .../PowerRenameContextMenu/dllmain.cpp | 7 + .../PowerRenameUILib/PowerRenameUI.vcxproj | 3 + .../PowerRenameUI.vcxproj.filters | 3 - .../PowerRenameXAML/MainWindow.xaml.cpp | 11 + .../PowerRenameXAML/MainWindow.xaml.h | 4 + .../powerrename/dll/PowerRenameExt.cpp | 10 + src/modules/powerrename/dll/PowerRenameExt.h | 4 + .../powerrename/dll/PowerRenameExt.vcxproj | 3 + src/modules/powerrename/lib/pch.h | 2 - src/modules/powerrename/lib/trace.cpp | 34 +- src/modules/powerrename/lib/trace.h | 6 +- .../GcodePreviewHandlerControl.cs | 17 - .../Events/GcodeFileHandlerLoaded.cs | 21 -- .../Telemetry/Events/GcodeFilePreviewError.cs | 26 -- .../Telemetry/Events/GcodeFilePreviewed.cs | 21 -- .../MarkdownPreviewHandlerControl.cs | 20 +- .../Events/MarkdownFileHandlerLoaded.cs | 21 -- .../Events/MarkdownFilePreviewError.cs | 23 -- .../Telemetry/Events/MarkdownFilePreviewed.cs | 21 -- .../PdfPreviewHandlerControl.cs | 27 +- .../Telemetry/Events/PdfFileHandlerLoaded.cs | 21 -- .../Telemetry/Events/PdfFilePreviewError.cs | 23 -- .../Telemetry/Events/PdfFilePreviewed.cs | 21 -- .../QoiPreviewHandlerControl.cs | 17 - .../Telemetry/Events/QoiFilePreviewError.cs | 26 -- .../Telemetry/Events/QoiFilePreviewed.cs | 21 -- .../SvgPreviewHandler/SvgPreviewControl.cs | 27 +- .../Telemetry/Events/SvgFileHandlerLoaded.cs | 21 -- .../Telemetry/Events/SvgFilePreviewError.cs | 26 -- .../Telemetry/Events/SvgFilePreviewed.cs | 21 -- src/modules/previewpane/powerpreview/pch.h | 1 - .../previewpane/powerpreview/powerpreview.cpp | 1 - .../previewpane/powerpreview/trace.cpp | 27 +- src/modules/previewpane/powerpreview/trace.h | 6 +- .../RegistryPreview/MainWindow.Events.cs | 1 + .../RegistryPreviewXAML/App.xaml.cs | 3 + .../RegistryPreviewExt/Trace.cpp | 16 +- .../RegistryPreviewExt/Trace.h | 7 +- .../registrypreview/RegistryPreviewExt/pch.h | 1 - .../VideoConferenceModule/pch.h | 2 - .../VideoConferenceModule/trace.cpp | 20 +- .../VideoConferenceModule/trace.h | 6 +- src/runner/general_settings.cpp | 5 +- src/runner/main.cpp | 15 + src/runner/pch.h | 1 - src/runner/runner.vcxproj | 3 + src/runner/settings_window.cpp | 4 + src/runner/trace.cpp | 16 +- src/runner/trace.h | 6 +- .../Settings.UI/Helpers/ETLConverter.cs | 102 ++++++ .../Settings.UI/Helpers/NativeEventWaiter.cs | 30 ++ .../Settings.UI/Helpers/StartProcessHelper.cs | 1 + .../Settings.UI/SettingsXAML/App.xaml.cs | 9 + .../SettingsXAML/OOBE/Views/OobeOverview.xaml | 31 ++ .../OOBE/Views/OobeOverview.xaml.cs | 49 +++ .../SettingsXAML/OOBE/Views/OobeWhatsNew.xaml | 56 +++- .../OOBE/Views/OobeWhatsNew.xaml.cs | 93 ++++++ .../SettingsXAML/Views/GeneralPage.xaml | 50 +++ .../SettingsXAML/Views/GeneralPage.xaml.cs | 29 +- .../Settings.UI/Strings/en-us/Resources.resw | 80 ++++- .../ViewModels/DashboardViewModel.cs | 40 ++- .../ViewModels/GeneralViewModel.cs | 136 ++++++++ tools/BugReportTool/BugReportTool/Main.cpp | 1 + .../BugReportTool/ReportGPOValues.cpp | 6 +- tools/project_template/ModuleTemplate/pch.h | 1 - .../project_template/ModuleTemplate/trace.cpp | 14 +- tools/project_template/ModuleTemplate/trace.h | 6 +- 269 files changed, 2622 insertions(+), 1256 deletions(-) create mode 100644 src/common/ManagedTelemetry/Telemetry/DataDiagnosticsSettings.cs create mode 100644 src/common/ManagedTelemetry/Telemetry/EtwTrace.cs create mode 100644 src/common/Telemetry/EtwTrace/EtwTrace.cpp create mode 100644 src/common/Telemetry/EtwTrace/EtwTrace.h create mode 100644 src/common/Telemetry/EtwTrace/EtwTrace.vcxproj create mode 100644 src/common/Telemetry/EtwTrace/EtwTrace.vcxproj.filters create mode 100644 src/common/Telemetry/EtwTrace/packages.config create mode 100644 src/common/Telemetry/EtwTrace/pch.cpp create mode 100644 src/common/Telemetry/EtwTrace/pch.h create mode 100644 src/common/Telemetry/TraceBase.h create mode 100644 src/modules/Hosts/Hosts/Helpers/NativeEventWaiter.cs delete mode 100644 src/modules/launcher/Microsoft.Launcher/trace.cpp delete mode 100644 src/modules/launcher/Microsoft.Launcher/trace.h delete mode 100644 src/modules/previewpane/GcodePreviewHandler/Telemetry/Events/GcodeFileHandlerLoaded.cs delete mode 100644 src/modules/previewpane/GcodePreviewHandler/Telemetry/Events/GcodeFilePreviewError.cs delete mode 100644 src/modules/previewpane/GcodePreviewHandler/Telemetry/Events/GcodeFilePreviewed.cs delete mode 100644 src/modules/previewpane/MarkdownPreviewHandler/Telemetry/Events/MarkdownFileHandlerLoaded.cs delete mode 100644 src/modules/previewpane/MarkdownPreviewHandler/Telemetry/Events/MarkdownFilePreviewError.cs delete mode 100644 src/modules/previewpane/MarkdownPreviewHandler/Telemetry/Events/MarkdownFilePreviewed.cs delete mode 100644 src/modules/previewpane/PdfPreviewHandler/Telemetry/Events/PdfFileHandlerLoaded.cs delete mode 100644 src/modules/previewpane/PdfPreviewHandler/Telemetry/Events/PdfFilePreviewError.cs delete mode 100644 src/modules/previewpane/PdfPreviewHandler/Telemetry/Events/PdfFilePreviewed.cs delete mode 100644 src/modules/previewpane/QoiPreviewHandler/Telemetry/Events/QoiFilePreviewError.cs delete mode 100644 src/modules/previewpane/QoiPreviewHandler/Telemetry/Events/QoiFilePreviewed.cs delete mode 100644 src/modules/previewpane/SvgPreviewHandler/Telemetry/Events/SvgFileHandlerLoaded.cs delete mode 100644 src/modules/previewpane/SvgPreviewHandler/Telemetry/Events/SvgFilePreviewError.cs delete mode 100644 src/modules/previewpane/SvgPreviewHandler/Telemetry/Events/SvgFilePreviewed.cs create mode 100644 src/settings-ui/Settings.UI/Helpers/ETLConverter.cs create mode 100644 src/settings-ui/Settings.UI/Helpers/NativeEventWaiter.cs diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index fa94dd6d7e..e7bcaab1b5 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -43,6 +43,7 @@ AMPROPSETID amr ANDSCANS animatedvisuals +anr ansicolor ANull AOC @@ -57,14 +58,12 @@ APPBARDATA appdata APPEXECLINK Appium -applayout Applicationcan APPLICATIONFRAMEHOST appmanifest APPNAME appref appsettings -appsfolder appwindow appwiz APSTUDIO @@ -145,7 +144,6 @@ BTNFACE bugreport BUILDARCH BUILDNUMBER -buildtask buildtransitive BVal BValue @@ -269,7 +267,6 @@ CSettings cso CSRW CStyle -cswinrt CSY CTest currentculture @@ -351,7 +348,6 @@ devpkey DEVSOURCE DIIRFLAG dimm -directshow DISABLEASACTIONKEY diskmgmt DISPLAYCHANGE @@ -367,7 +363,6 @@ dllmain DNLEN DONOTROUND DONTVALIDATEPATH -DOPUS dotnet DPICHANGED DPIs @@ -450,12 +445,13 @@ erwrite ESettings esrp etl -ETW +etw EUQ eurochange eventlog eventvwr everytime +evntrace evt EWXFORCE EWXFORCEIFHUNG @@ -527,7 +523,6 @@ FZE gacutil Gaeilge Gaidhlig -GBarm GCLP gdi gdiplus @@ -997,7 +992,6 @@ NCRENDERING ndp NEEDDISPATCH needinfo -netcore netcoreapp netcpl netframework @@ -1006,6 +1000,7 @@ netsh newcolor newdev NEWDIALOGSTYLE +NEWFILE newitem newpath newplus @@ -1074,7 +1069,6 @@ NTAPI ntdll ntfs NTSTATUS -nugets nullonfailure numberbox nwc @@ -1225,6 +1219,7 @@ proactively PROCESSENTRY PROCESSKEY processthreadsapi +PROCESSTRACE PRODEXT PRODUCTVERSION Progman @@ -1266,7 +1261,6 @@ PWSTR pwsz pwtd QDC -QDir qianlifeng qit QITAB @@ -1590,9 +1584,7 @@ SYSKEYUP SYSLIB SYSMENU SYSTEMAPPS -SYSTEMSETTINGS SYSTEMTIME -SYSTEMWOW tapp TApplication TApplied @@ -1612,7 +1604,6 @@ TCustom tdbuild TDefault TDevice -telem telephon templatenamespace testprocess @@ -1644,11 +1635,12 @@ toolkitconverters Toolset toolwindow TOPDOWNDIB -TOTALCMD TOUCHEVENTF TOUCHINPUT touchpad +TRACEHANDLE tracelogging +tracerpt trafficmanager traies transicc @@ -1706,11 +1698,9 @@ urlmon Usb USEDEFAULT USEFILEATTRIBUTES -USEPOSITION USERDATA Userenv USESHOWWINDOW -USESIZE USESTDHANDLES USRDLL UType @@ -1803,7 +1793,6 @@ wgpocpl WIC wil winapi -winappdriver wincodec Wincodecsdk wincolor @@ -1867,6 +1856,7 @@ WNDCLASSEX WNDCLASSEXW WNDCLASSW WNDPROC +wnode workarounds WORKSPACESEDITOR WORKSPACESLAUNCHER @@ -1912,7 +1902,6 @@ XNamespace Xoshiro XPels XPixel -xplorer XResource xsi XStr diff --git a/.pipelines/packages.config b/.pipelines/packages.config index a5cab593eb..43fa34c91c 100644 --- a/.pipelines/packages.config +++ b/.pipelines/packages.config @@ -1,4 +1,4 @@ - + diff --git a/.pipelines/v2/release.yml b/.pipelines/v2/release.yml index b4a6a80833..2719777aa6 100644 --- a/.pipelines/v2/release.yml +++ b/.pipelines/v2/release.yml @@ -90,8 +90,8 @@ extends: - script: | call nuget.exe restore -configFile .pipelines/release-nuget.config -PackagesDirectory . .pipelines/packages.config || exit /b 1 - move /Y "Microsoft.PowerToys.Telemetry.2.0.0\build\include\TraceLoggingDefines.h" "src\common\Telemetry\TraceLoggingDefines.h" || exit /b 1 - move /Y "Microsoft.PowerToys.Telemetry.2.0.0\build\include\TelemetryBase.cs" "src\common\Telemetry\TelemetryBase.cs" || exit /b 1 + move /Y "Microsoft.PowerToys.Telemetry.2.0.2\build\include\TraceLoggingDefines.h" "src\common\Telemetry\TraceLoggingDefines.h" || exit /b 1 + move /Y "Microsoft.PowerToys.Telemetry.2.0.2\build\include\TelemetryBase.cs" "src\common\Telemetry\TelemetryBase.cs" || exit /b 1 displayName: Emplace telemetry files - stage: Publish diff --git a/Directory.Packages.props b/Directory.Packages.props index 95bc6fa8f1..a103ca59a8 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -28,6 +28,7 @@ + diff --git a/NOTICE.md b/NOTICE.md index f90b982505..38c961a116 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1320,6 +1320,7 @@ EXHIBIT A -Mozilla Public License. - MessagePack 2.5.187 - Microsoft.CodeAnalysis.NetAnalyzers 8.0.0 - Microsoft.Data.Sqlite 8.0.7 +- Microsoft.Diagnostics.Tracing.TraceEvent 3.1.16 - Microsoft.Extensions.DependencyInjection 8.0.0 - Microsoft.Extensions.Hosting 8.0.0 - Microsoft.Extensions.Hosting.WindowsServices 8.0.0 diff --git a/PowerToys.sln b/PowerToys.sln index fa6b20aacf..80722be402 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -298,6 +298,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Telemetry", "Telemetry", "{ ProjectSection(SolutionItems) = preProject src\common\Telemetry\ProjectTelemetry.h = src\common\Telemetry\ProjectTelemetry.h src\common\Telemetry\TelemetryBase.cs = src\common\Telemetry\TelemetryBase.cs + src\common\Telemetry\TraceBase.h = src\common\Telemetry\TraceBase.h src\common\Telemetry\TraceLoggingDefines.h = src\common\Telemetry\TraceLoggingDefines.h EndProjectSection EndProject @@ -625,6 +626,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesLauncher", "src\m EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WorkspacesWindowArranger", "src\modules\Workspaces\WorkspacesWindowArranger\WorkspacesWindowArranger.vcxproj", "{37D07516-4185-43A4-924F-3C7A5D95ECF6}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EtwTrace", "src\common\Telemetry\EtwTrace\EtwTrace.vcxproj", "{8F021B46-362B-485C-BFBA-CCF83E820CBD}" +EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseWithoutBorders.UnitTests", "src\modules\MouseWithoutBorders\MouseWithoutBorders.UnitTests\MouseWithoutBorders.UnitTests.csproj", "{66614C26-314C-4B91-9071-76133422CFEF}" EndProject Global @@ -2751,6 +2754,18 @@ Global {37D07516-4185-43A4-924F-3C7A5D95ECF6}.Release|x64.Build.0 = Release|x64 {37D07516-4185-43A4-924F-3C7A5D95ECF6}.Release|x86.ActiveCfg = Release|x64 {37D07516-4185-43A4-924F-3C7A5D95ECF6}.Release|x86.Build.0 = Release|x64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Debug|ARM64.Build.0 = Debug|ARM64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Debug|x64.ActiveCfg = Debug|x64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Debug|x64.Build.0 = Debug|x64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Debug|x86.ActiveCfg = Debug|x64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Debug|x86.Build.0 = Debug|x64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|ARM64.ActiveCfg = Release|ARM64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|ARM64.Build.0 = Release|ARM64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|x64.ActiveCfg = Release|x64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|x64.Build.0 = Release|x64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|x86.ActiveCfg = Release|x64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|x86.Build.0 = Release|x64 {66614C26-314C-4B91-9071-76133422CFEF}.Debug|ARM64.ActiveCfg = Debug|ARM64 {66614C26-314C-4B91-9071-76133422CFEF}.Debug|ARM64.Build.0 = Debug|ARM64 {66614C26-314C-4B91-9071-76133422CFEF}.Debug|x64.ActiveCfg = Debug|x64 @@ -2992,6 +3007,7 @@ Global {367D7543-7DBA-4381-99F1-BF6142A996C4} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {2CAC093E-5FCF-4102-9C2C-AC7DD5D9EB96} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {37D07516-4185-43A4-924F-3C7A5D95ECF6} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} + {8F021B46-362B-485C-BFBA-CCF83E820CBD} = {8F62026A-294B-41C6-8839-87463613F216} {66614C26-314C-4B91-9071-76133422CFEF} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution diff --git a/installer/PowerToysSetup.sln b/installer/PowerToysSetup.sln index cdcef54079..540ef43d23 100644 --- a/installer/PowerToysSetup.sln +++ b/installer/PowerToysSetup.sln @@ -15,6 +15,8 @@ Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "PowerToysBootstrapper", "Po EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Version", "..\src\common\version\version.vcxproj", "{CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "EtwTrace", "..\src\common\Telemetry\EtwTrace\EtwTrace.vcxproj", "{8F021B46-362B-485C-BFBA-CCF83E820CBD}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -68,6 +70,14 @@ Global {CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Release|ARM64.Build.0 = Release|ARM64 {CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Release|x64.ActiveCfg = Release|x64 {CC6E41AC-8174-4E8A-8D22-85DD7F4851DF}.Release|x64.Build.0 = Release|x64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Debug|ARM64.Build.0 = Debug|ARM64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Debug|x64.ActiveCfg = Debug|x64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Debug|x64.Build.0 = Debug|x64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|ARM64.ActiveCfg = Release|ARM64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|ARM64.Build.0 = Release|ARM64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|x64.ActiveCfg = Release|x64 + {8F021B46-362B-485C-BFBA-CCF83E820CBD}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/installer/PowerToysSetupCustomActions/CustomAction.cpp b/installer/PowerToysSetupCustomActions/CustomAction.cpp index 8845208200..8995d2f9b6 100644 --- a/installer/PowerToysSetupCustomActions/CustomAction.cpp +++ b/installer/PowerToysSetupCustomActions/CustomAction.cpp @@ -10,6 +10,7 @@ #include "../../src/common/utils/modulesRegistry.h" #include "../../src/common/updating/installer.h" #include "../../src/common/version/version.h" +#include "../../src/common/Telemetry/EtwTrace/EtwTrace.h" #include #include @@ -27,9 +28,9 @@ HINSTANCE DLL_HANDLE = nullptr; TRACELOGGING_DEFINE_PROVIDER( g_hProvider, - "Microsoft.PowerToysInstaller", - // {e1d8165d-5cb6-5c74-3b51-bdfbfe4f7a3b} - (0xe1d8165d, 0x5cb6, 0x5c74, 0x3b, 0x51, 0xbd, 0xfb, 0xfe, 0x4f, 0x7a, 0x3b), + "Microsoft.PowerToys", + // {38e8889b-9731-53f5-e901-e8a7c1753074} + (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); const DWORD USERNAME_DOMAIN_LEN = DNLEN + UNLEN + 2; // Domain Name + '\' + User Name + '\0' @@ -38,6 +39,53 @@ const DWORD USERNAME_LEN = UNLEN + 1; // User Name + '\0' static const wchar_t* POWERTOYS_EXE_COMPONENT = L"{A2C66D91-3485-4D00-B04D-91844E6B345B}"; static const wchar_t* POWERTOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}"; +constexpr inline const wchar_t* DataDiagnosticsRegKey = L"Software\\Classes\\PowerToys"; +constexpr inline const wchar_t* DataDiagnosticsRegValueName = L"AllowDataDiagnostics"; + +#define TraceLoggingWriteWrapper(provider, eventName, ...) \ + if (isDataDiagnosticEnabled()) \ + { \ + trace.UpdateState(true); \ + TraceLoggingWrite(provider, eventName, __VA_ARGS__); \ + trace.Flush(); \ + trace.UpdateState(false); \ + } + +static Shared::Trace::ETWTrace trace{ L"PowerToys_Installer" }; + +inline bool isDataDiagnosticEnabled() +{ + HKEY key{}; + if (RegOpenKeyExW(HKEY_CURRENT_USER, + DataDiagnosticsRegKey, + 0, + KEY_READ, + &key) != ERROR_SUCCESS) + { + return false; + } + + DWORD isDataDiagnosticsEnabled = 0; + DWORD size = sizeof(isDataDiagnosticsEnabled); + + if (RegGetValueW( + HKEY_CURRENT_USER, + DataDiagnosticsRegKey, + DataDiagnosticsRegValueName, + RRF_RT_REG_DWORD, + nullptr, + &isDataDiagnosticsEnabled, + &size) != ERROR_SUCCESS) + { + RegCloseKey(key); + return false; + } + RegCloseKey(key); + + return isDataDiagnosticsEnabled == 1; +} + + HRESULT getInstallFolder(MSIHANDLE hInstall, std::wstring& installationDir) { DWORD len = 0; @@ -793,13 +841,14 @@ UINT __stdcall TelemetryLogInstallSuccessCA(MSIHANDLE hInstall) hr = WcaInitialize(hInstall, "TelemetryLogInstallSuccessCA"); ExitOnFailure(hr, "Failed to initialize"); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "Install_Success", TraceLoggingWideString(get_product_version().c_str(), "Version"), ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), - TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE) + ); LExit: er = SUCCEEDED(hr) ? ERROR_SUCCESS : ERROR_INSTALL_FAILURE; @@ -814,7 +863,7 @@ UINT __stdcall TelemetryLogInstallCancelCA(MSIHANDLE hInstall) hr = WcaInitialize(hInstall, "TelemetryLogInstallCancelCA"); ExitOnFailure(hr, "Failed to initialize"); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "Install_Cancel", TraceLoggingWideString(get_product_version().c_str(), "Version"), @@ -835,7 +884,7 @@ UINT __stdcall TelemetryLogInstallFailCA(MSIHANDLE hInstall) hr = WcaInitialize(hInstall, "TelemetryLogInstallFailCA"); ExitOnFailure(hr, "Failed to initialize"); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "Install_Fail", TraceLoggingWideString(get_product_version().c_str(), "Version"), @@ -856,7 +905,7 @@ UINT __stdcall TelemetryLogUninstallSuccessCA(MSIHANDLE hInstall) hr = WcaInitialize(hInstall, "TelemetryLogUninstallSuccessCA"); ExitOnFailure(hr, "Failed to initialize"); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "UnInstall_Success", TraceLoggingWideString(get_product_version().c_str(), "Version"), @@ -877,7 +926,7 @@ UINT __stdcall TelemetryLogUninstallCancelCA(MSIHANDLE hInstall) hr = WcaInitialize(hInstall, "TelemetryLogUninstallCancelCA"); ExitOnFailure(hr, "Failed to initialize"); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "UnInstall_Cancel", TraceLoggingWideString(get_product_version().c_str(), "Version"), @@ -898,7 +947,7 @@ UINT __stdcall TelemetryLogUninstallFailCA(MSIHANDLE hInstall) hr = WcaInitialize(hInstall, "TelemetryLogUninstallFailCA"); ExitOnFailure(hr, "Failed to initialize"); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "UnInstall_Fail", TraceLoggingWideString(get_product_version().c_str(), "Version"), @@ -919,7 +968,7 @@ UINT __stdcall TelemetryLogRepairCancelCA(MSIHANDLE hInstall) hr = WcaInitialize(hInstall, "TelemetryLogRepairCancelCA"); ExitOnFailure(hr, "Failed to initialize"); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "Repair_Cancel", TraceLoggingWideString(get_product_version().c_str(), "Version"), @@ -940,7 +989,7 @@ UINT __stdcall TelemetryLogRepairFailCA(MSIHANDLE hInstall) hr = WcaInitialize(hInstall, "TelemetryLogRepairFailCA"); ExitOnFailure(hr, "Failed to initialize"); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "Repair_Fail", TraceLoggingWideString(get_product_version().c_str(), "Version"), diff --git a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj index 959a600706..dee9f63e2b 100644 --- a/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj +++ b/installer/PowerToysSetupCustomActions/PowerToysSetupCustomActions.vcxproj @@ -161,6 +161,9 @@ {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + diff --git a/src/common/GPOWrapper/GPOWrapper.cpp b/src/common/GPOWrapper/GPOWrapper.cpp index 817db8c4de..a9ecb43818 100644 --- a/src/common/GPOWrapper/GPOWrapper.cpp +++ b/src/common/GPOWrapper/GPOWrapper.cpp @@ -224,4 +224,8 @@ namespace winrt::PowerToys::GPOWrapper::implementation { return static_cast(powertoys_gpo::getConfiguredNewPlusHideTemplateFilenameExtensionValue()); } + GpoRuleConfigured GPOWrapper::GetAllowDataDiagnosticsValue() + { + return static_cast(powertoys_gpo::getAllowDataDiagnosticsValue()); + } } diff --git a/src/common/GPOWrapper/GPOWrapper.h b/src/common/GPOWrapper/GPOWrapper.h index e1fcb5426b..34c1e3646b 100644 --- a/src/common/GPOWrapper/GPOWrapper.h +++ b/src/common/GPOWrapper/GPOWrapper.h @@ -61,6 +61,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation static GpoRuleConfigured GetConfiguredMwbDisableUserDefinedIpMappingRulesValue(); static winrt::hstring GPOWrapper::GetConfiguredMwbPolicyDefinedIpMappingRules(); static GpoRuleConfigured GetConfiguredNewPlusHideTemplateFilenameExtensionValue(); + static GpoRuleConfigured GetAllowDataDiagnosticsValue(); }; } diff --git a/src/common/GPOWrapper/GPOWrapper.idl b/src/common/GPOWrapper/GPOWrapper.idl index 1d375f1975..af58834a0c 100644 --- a/src/common/GPOWrapper/GPOWrapper.idl +++ b/src/common/GPOWrapper/GPOWrapper.idl @@ -65,6 +65,7 @@ namespace PowerToys static GpoRuleConfigured GetConfiguredMwbDisableUserDefinedIpMappingRulesValue(); static String GetConfiguredMwbPolicyDefinedIpMappingRules(); static GpoRuleConfigured GetConfiguredNewPlusHideTemplateFilenameExtensionValue(); + static GpoRuleConfigured GetAllowDataDiagnosticsValue(); } } } diff --git a/src/common/ManagedTelemetry/Telemetry/DataDiagnosticsSettings.cs b/src/common/ManagedTelemetry/Telemetry/DataDiagnosticsSettings.cs new file mode 100644 index 0000000000..0a9aa7c891 --- /dev/null +++ b/src/common/ManagedTelemetry/Telemetry/DataDiagnosticsSettings.cs @@ -0,0 +1,107 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using Microsoft.Win32; + +namespace Microsoft.PowerToys.Telemetry +{ + public static class DataDiagnosticsSettings + { + private static readonly string DataDiagnosticsRegistryKey = @"HKEY_CURRENT_USER\Software\Classes\PowerToys\"; + private static readonly string DataDiagnosticsRegistryValueName = @"AllowDataDiagnostics"; + private static readonly string DataDiagnosticsDataDiagnosticsUserActionRegistryValueName = @"DataDiagnosticsUserAction"; + private static readonly string DataDiagnosticsDataDiagnosticsViewDataRegistryValueName = @"DataDiagnosticsViewEnabled"; + + public static bool GetEnabledValue() + { + object registryValue = null; + try + { + registryValue = Registry.GetValue(DataDiagnosticsRegistryKey, DataDiagnosticsRegistryValueName, 0); + } + catch + { + } + + if (registryValue is not null) + { + return (int)registryValue == 1 ? true : false; + } + + return false; + } + + public static void SetEnabledValue(bool value) + { + try + { + Registry.SetValue(DataDiagnosticsRegistryKey, DataDiagnosticsRegistryValueName, value ? 1 : 0); + } + catch (Exception) + { + } + } + + public static bool GetUserActionValue() + { + object registryValue = null; + try + { + registryValue = Registry.GetValue(DataDiagnosticsRegistryKey, DataDiagnosticsDataDiagnosticsUserActionRegistryValueName, 0); + } + catch + { + } + + if (registryValue is not null) + { + return (int)registryValue == 1 ? true : false; + } + + return false; + } + + public static void SetUserActionValue(bool value) + { + try + { + Registry.SetValue(DataDiagnosticsRegistryKey, DataDiagnosticsDataDiagnosticsUserActionRegistryValueName, value ? 1 : 0); + } + catch (Exception) + { + } + } + + public static bool GetViewEnabledValue() + { + object registryValue = null; + try + { + registryValue = Registry.GetValue(DataDiagnosticsRegistryKey, DataDiagnosticsDataDiagnosticsViewDataRegistryValueName, 0); + } + catch + { + } + + if (registryValue is not null) + { + return (int)registryValue == 1 ? true : false; + } + + return false; + } + + public static void SetViewEnabledValue(bool value) + { + try + { + Registry.SetValue(DataDiagnosticsRegistryKey, DataDiagnosticsDataDiagnosticsViewDataRegistryValueName, value ? 1 : 0); + } + catch (Exception) + { + } + } + } +} diff --git a/src/common/ManagedTelemetry/Telemetry/EtwTrace.cs b/src/common/ManagedTelemetry/Telemetry/EtwTrace.cs new file mode 100644 index 0000000000..d1d3ec2456 --- /dev/null +++ b/src/common/ManagedTelemetry/Telemetry/EtwTrace.cs @@ -0,0 +1,158 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Diagnostics; +using System.Diagnostics.Tracing; +using System.Globalization; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Diagnostics.Tracing.Session; + +namespace Microsoft.PowerToys.Telemetry +{ + /// + /// This class is based loosely on the C++ ETWTrace class in Win32client/Framework project. + /// It is intended to record telemetry events generated by the PowerToys processes so that end users + /// can view them if they want. + /// + public class ETWTrace : IDisposable + { + internal const EventKeywords TelemetryKeyword = (EventKeywords)0x0000200000000000; + internal const EventKeywords MeasuresKeyword = (EventKeywords)0x0000400000000000; + internal const EventKeywords CriticalDataKeyword = (EventKeywords)0x0000800000000000; + + private readonly bool telemetryEnabled = DataDiagnosticsSettings.GetEnabledValue(); // This is the global telemetry setting on whether to log events + private readonly bool telemetryRecordingEnabled = DataDiagnosticsSettings.GetViewEnabledValue(); // This is the setting for recording telemetry events to disk for viewing + private readonly string etwFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Microsoft\PowerToys\", "etw"); + private bool disposedValue; + private string sessionName; + private string etwFilePath; + private bool started; +#nullable enable + private TraceEventSession? traceSession; + + internal sealed class Lister : EventListener + { + public Lister() + : base() + { + } + } + + private Lister? listener; +#nullable disable + + /// + /// Initializes a new instance of the class. + /// + public ETWTrace() + { + if (File.Exists(etwFolderPath)) + { + File.Delete(etwFolderPath); + } + + if (!Directory.Exists(etwFolderPath)) + { + Directory.CreateDirectory(etwFolderPath); + } + + if (this.telemetryEnabled && this.telemetryRecordingEnabled) + { + this.Start(); + } + + listener = new Lister(); + listener.EnableEvents(PowerToysTelemetry.Log, EventLevel.LogAlways); + } + + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + this.Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + /// Starts the trace session. + /// + public void Start() + { + lock (this) + { + if (this.started) + { + return; + } + + new Task(() => + { + while (true) + { + Thread.Sleep(30 * 1000); + + this.traceSession.Flush(); + } + }).Start(); + + string executable = Process.GetCurrentProcess().ProcessName; + string dateTimeNow = DateTime.Now.ToString("MM-d-yyyy__H_mm_ss", CultureInfo.InvariantCulture); + this.sessionName = string.Format(CultureInfo.InvariantCulture, "{0}-{1}-{2}", executable, Environment.ProcessId, dateTimeNow); + this.etwFilePath = Path.Combine(etwFolderPath, $"{this.sessionName}.etl"); + + this.traceSession = new TraceEventSession( + this.sessionName, this.etwFilePath, (TraceEventSessionOptions)(TraceEventSessionOptions.Create | TraceEventSessionOptions.PrivateLogger | TraceEventSessionOptions.PrivateInProcLogger)); + TraceEventProviderOptions args = new TraceEventProviderOptions(); + + this.traceSession.EnableProvider( + PowerToysTelemetry.Log.Guid, + matchAnyKeywords: (ulong)TelemetryKeyword | (ulong)MeasuresKeyword | (ulong)CriticalDataKeyword); + + this.started = true; + } + } + + /// + /// Stops the trace session. + /// + public void Stop() + { + lock (this) + { + if (!this.started) + { + return; + } + + if (this.traceSession != null) + { + Trace.TraceInformation("Disposing EventTraceSession"); + this.traceSession.Dispose(); + this.traceSession = null; + this.started = false; + } + } + } + + /// + /// Disposes the object. + /// + /// boolean for disposing. + protected virtual void Dispose(bool disposing) + { + if (!this.disposedValue) + { + if (disposing) + { + this.Stop(); + } + + this.disposedValue = true; + } + } + } +} diff --git a/src/common/ManagedTelemetry/Telemetry/ManagedTelemetry.csproj b/src/common/ManagedTelemetry/Telemetry/ManagedTelemetry.csproj index 504301516d..3929c60618 100644 --- a/src/common/ManagedTelemetry/Telemetry/ManagedTelemetry.csproj +++ b/src/common/ManagedTelemetry/Telemetry/ManagedTelemetry.csproj @@ -1,7 +1,7 @@  - + PowerToys Telemetry PowerToys.ManagedTelemetry @@ -11,4 +11,8 @@ + + + + diff --git a/src/common/ManagedTelemetry/Telemetry/PowerToysTelemetry.cs b/src/common/ManagedTelemetry/Telemetry/PowerToysTelemetry.cs index 9b74c682df..c1b77e67e4 100644 --- a/src/common/ManagedTelemetry/Telemetry/PowerToysTelemetry.cs +++ b/src/common/ManagedTelemetry/Telemetry/PowerToysTelemetry.cs @@ -37,14 +37,17 @@ namespace Microsoft.PowerToys.Telemetry public void WriteEvent(T telemetryEvent) where T : EventBase, IEvent { - this.Write( - telemetryEvent.EventName, - new EventSourceOptions() - { - Keywords = ProjectKeywordMeasure, - Tags = ProjectTelemetryTagProductAndServicePerformance, - }, - telemetryEvent); + if (DataDiagnosticsSettings.GetEnabledValue()) + { + this.Write( + telemetryEvent.EventName, + new EventSourceOptions() + { + Keywords = ProjectKeywordMeasure, + Tags = ProjectTelemetryTagProductAndServicePerformance, + }, + telemetryEvent); + } } } } diff --git a/src/common/SettingsAPI/settings_helpers.cpp b/src/common/SettingsAPI/settings_helpers.cpp index 974b2fff6a..25d9719a66 100644 --- a/src/common/SettingsAPI/settings_helpers.cpp +++ b/src/common/SettingsAPI/settings_helpers.cpp @@ -8,6 +8,8 @@ namespace PTSettingsHelper constexpr inline const wchar_t* last_version_run_filename = L"last_version_run.json"; constexpr inline const wchar_t* opened_at_first_launch_json_field_name = L"openedAtFirstLaunch"; constexpr inline const wchar_t* last_version_json_field_name = L"last_version"; + constexpr inline const wchar_t* DataDiagnosticsRegKey = L"Software\\Classes\\PowerToys"; + constexpr inline const wchar_t* DataDiagnosticsRegValueName = L"AllowDataDiagnostics"; std::wstring get_root_save_folder_location() { @@ -25,7 +27,7 @@ namespace PTSettingsHelper return result; } - std::wstring get_local_low_folder_location() + std::wstring get_local_low_folder_location() { PWSTR local_app_path; winrt::check_hresult(SHGetKnownFolderPath(FOLDERID_LocalAppDataLow, 0, NULL, &local_app_path)); @@ -112,7 +114,7 @@ namespace PTSettingsHelper bool opened = saved_settings->GetNamedBoolean(opened_at_first_launch_json_field_name, false); return opened; } - + return false; } @@ -124,12 +126,11 @@ namespace PTSettingsHelper json::JsonObject obj; obj.SetNamedValue(opened_at_first_launch_json_field_name, json::value(true)); - json::to_file(oobePath.c_str(), obj); + json::to_file(oobePath.c_str(), obj); } std::wstring get_last_version_run() { - std::filesystem::path lastVersionRunPath(PTSettingsHelper::get_root_save_folder_location()); lastVersionRunPath = lastVersionRunPath.append(last_version_run_filename); if (std::filesystem::exists(lastVersionRunPath)) @@ -157,4 +158,29 @@ namespace PTSettingsHelper json::to_file(lastVersionRunPath.c_str(), obj); } + void save_data_diagnostics(bool enabled) + { + HKEY key{}; + if (RegCreateKeyExW(HKEY_CURRENT_USER, + DataDiagnosticsRegKey, + 0, + nullptr, + REG_OPTION_NON_VOLATILE, + KEY_ALL_ACCESS, + nullptr, + &key, + nullptr) != ERROR_SUCCESS) + { + return; + } + + const bool value = enabled; + const size_t buf_size = sizeof(bool); + if (RegSetValueExW(key, DataDiagnosticsRegValueName, 0, REG_QWORD, reinterpret_cast(&value), buf_size) != ERROR_SUCCESS) + { + RegCloseKey(key); + return; + } + RegCloseKey(key); + } } diff --git a/src/common/SettingsAPI/settings_helpers.h b/src/common/SettingsAPI/settings_helpers.h index 9e01b3b206..14eae8ac62 100644 --- a/src/common/SettingsAPI/settings_helpers.h +++ b/src/common/SettingsAPI/settings_helpers.h @@ -24,4 +24,6 @@ namespace PTSettingsHelper void save_oobe_opened_state(); std::wstring get_last_version_run(); void save_last_version_run(const std::wstring& version); + + void save_data_diagnostics(bool enabled); } diff --git a/src/common/Telemetry/EtwTrace/EtwTrace.cpp b/src/common/Telemetry/EtwTrace/EtwTrace.cpp new file mode 100644 index 0000000000..05b6956953 --- /dev/null +++ b/src/common/Telemetry/EtwTrace/EtwTrace.cpp @@ -0,0 +1,310 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +#pragma once +#include "pch.h" + +#include "ETWTrace.h" + +#include + +#include +#include + +namespace fs = std::filesystem; + +namespace +{ + constexpr inline const wchar_t* DataDiagnosticsRegKey = L"Software\\Classes\\PowerToys"; + constexpr inline const wchar_t* DataDiagnosticsRegValueName = L"AllowDataDiagnostics"; + constexpr inline const wchar_t* ViewDataDiagnosticsRegValueName = L"DataDiagnosticsViewEnabled"; + + inline std::wstring get_root_save_folder_location() + { + PWSTR local_app_path; + winrt::check_hresult(SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, NULL, &local_app_path)); + std::wstring result{ local_app_path }; + CoTaskMemFree(local_app_path); + + result += L"\\Microsoft\\PowerToys"; + std::filesystem::path save_path(result); + if (!std::filesystem::exists(save_path)) + { + std::filesystem::create_directories(save_path); + } + return result; + } + + bool IsDataDiagnosticsEnabled() + { + HKEY key{}; + if (RegOpenKeyExW(HKEY_CURRENT_USER, + DataDiagnosticsRegKey, + 0, + KEY_READ, + &key) != ERROR_SUCCESS) + { + return false; + } + + DWORD isDataDiagnosticsEnabled = 0; + DWORD size = sizeof(isDataDiagnosticsEnabled); + + if (RegGetValueW( + HKEY_CURRENT_USER, + DataDiagnosticsRegKey, + DataDiagnosticsRegValueName, + RRF_RT_REG_DWORD, + nullptr, + &isDataDiagnosticsEnabled, + &size) != ERROR_SUCCESS) + { + RegCloseKey(key); + return false; + } + RegCloseKey(key); + + return isDataDiagnosticsEnabled; + } + + bool isViewDataDiagnosticEnabled() + { + HKEY key{}; + if (RegOpenKeyExW(HKEY_CURRENT_USER, + DataDiagnosticsRegKey, + 0, + KEY_READ, + &key) != ERROR_SUCCESS) + { + return false; + } + + DWORD isDataDiagnosticsEnabled = 0; + DWORD size = sizeof(isDataDiagnosticsEnabled); + + if (RegGetValueW( + HKEY_CURRENT_USER, + DataDiagnosticsRegKey, + ViewDataDiagnosticsRegValueName, + RRF_RT_REG_DWORD, + nullptr, + &isDataDiagnosticsEnabled, + &size) != ERROR_SUCCESS) + { + RegCloseKey(key); + return false; + } + RegCloseKey(key); + + return isDataDiagnosticsEnabled == 1; + } + +} + +namespace Shared +{ + namespace Trace + { + ETWTrace::ETWTrace() + { + GUID id; + if (SUCCEEDED(CLSIDFromString(PowerToysProviderGUID, &id))) + { + m_providerGUID = id; + } + + fs::path outputFolder = get_root_save_folder_location(); + m_etwFolder = (outputFolder / c_etwFolderName); + } + + ETWTrace::ETWTrace(const std::wstring& etlFileNameOverride) : + ETWTrace() + { + m_etlFileNameOverride = etlFileNameOverride; + } + + ETWTrace::~ETWTrace() + { + Flush(); + Stop(); + m_etwFolder.clear(); + m_providerGUID = {}; + } + + void ETWTrace::UpdateState(bool tracing) + { + if (tracing) + { + Start(); + } + else + { + Stop(); + } + } + + void ETWTrace::Flush() + { + if (m_tracing) + { + Control(EVENT_TRACE_CONTROL_FLUSH); + // Control(EVENT_TRACE_CONTROL_INCREMENT_FILE); + } + } + + void ETWTrace::CreateEtwFolderIfNeeded() + { + if (!std::filesystem::exists(m_etwFolder)) + { + std::filesystem::create_directories(m_etwFolder); + } + else if (!std::filesystem::is_directory(m_etwFolder)) + { + std::filesystem::remove(m_etwFolder); + std::filesystem::create_directory(m_etwFolder); + } + + THROW_HR_IF(E_UNEXPECTED, !std::filesystem::exists(m_etwFolder)); + } + + void ETWTrace::InitEventTraceProperties() + { + const std::filesystem::path exePath(wil::GetModuleFileNameW(nullptr)); + const auto exeName = exePath.stem().wstring(); + + auto now = std::chrono::system_clock::now(); + auto timeNow = std::chrono::system_clock::to_time_t(now); + std::wstringstream dateTime; + struct tm timeInfo + { + }; + errno_t err = localtime_s(&timeInfo, &timeNow); + if (err == 0) + { + dateTime << std::put_time(&timeInfo, L"-%m-%d-%Y__%H_%M_%S"); + } + + if (m_etlFileNameOverride.empty()) + { + m_sessionName = wil::str_printf(L"%ws-%d%ws", exeName.c_str(), GetCurrentProcessId(), dateTime.str().c_str()); + } + else + { + m_sessionName = wil::str_printf(L"%ws-%d%ws", m_etlFileNameOverride.c_str(), GetCurrentProcessId(), dateTime.str().c_str()); + } + + std::replace(m_sessionName.begin(), m_sessionName.end(), '.', '_'); + + const ULONG etwSessionNameCharCount = static_cast(m_sessionName.size() + 1); + const ULONG etwSessionNameByteSize = etwSessionNameCharCount * sizeof(m_sessionName[0]); + + auto etlFileNameFormattedCounter = m_sessionName + c_etwNewFileFormattedCounter; + std::filesystem::path etlFilePath = m_etwFolder / etlFileNameFormattedCounter; + etlFilePath.replace_extension(c_etwFileNameEnd); + THROW_HR_IF(E_UNEXPECTED, etlFilePath.empty()); + + const auto etlFilePathStr = etlFilePath.wstring(); + // std::string/wstring returns number of characters not including the null terminator, so add +1 for that. + const ULONG etwFilePathCharCount = static_cast(etlFilePathStr.size() + 1); + const ULONG etwFilePathByteSize = etwFilePathCharCount * sizeof(etlFilePathStr[0]); + + const ULONG bufferSizeInBytes = sizeof(EVENT_TRACE_PROPERTIES) + etwSessionNameByteSize + etwFilePathByteSize; + auto eventTracePropertiesBuffer = std::make_unique(bufferSizeInBytes); + ZeroMemory(eventTracePropertiesBuffer.get(), bufferSizeInBytes); + auto eventTraceProperties = reinterpret_cast(eventTracePropertiesBuffer.get()); + + eventTraceProperties->Wnode.BufferSize = bufferSizeInBytes; + eventTraceProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID; + eventTraceProperties->Wnode.ClientContext = 1; + eventTraceProperties->Wnode.Guid = m_providerGUID; + eventTraceProperties->BufferSize = 4; // 4KB, the minimum size + eventTraceProperties->LogFileMode = EVENT_TRACE_PRIVATE_LOGGER_MODE | EVENT_TRACE_PRIVATE_IN_PROC | EVENT_TRACE_FILE_MODE_NEWFILE; + eventTraceProperties->MaximumFileSize = 1; // 1 MB + + // LoggerName is placed at the end of EVENT_TRACE_PROPERTIES structure + eventTraceProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES); + wcsncpy_s(reinterpret_cast(eventTracePropertiesBuffer.get() + eventTraceProperties->LoggerNameOffset), etwSessionNameCharCount, m_sessionName.c_str(), etwSessionNameCharCount); + + // LogFileName is placed at the end of the Logger Name + eventTraceProperties->LogFileNameOffset = eventTraceProperties->LoggerNameOffset + etwSessionNameByteSize; + wcsncpy_s(reinterpret_cast(eventTracePropertiesBuffer.get() + eventTraceProperties->LogFileNameOffset), etwFilePathCharCount, etlFilePathStr.c_str(), etwFilePathCharCount); + + m_eventTracePropertiesBuffer = std::move(eventTracePropertiesBuffer); + } + + void ETWTrace::Start() + { + if (m_tracing) + { + return; + } + + if (!IsDataDiagnosticsEnabled()) + { + return; + } + + if (!isViewDataDiagnosticEnabled()) + { + return; + } + + CreateEtwFolderIfNeeded(); + InitEventTraceProperties(); + + auto eventTraceProperties = reinterpret_cast(m_eventTracePropertiesBuffer.get()); + THROW_IF_WIN32_ERROR(StartTrace(&m_traceHandle, m_sessionName.c_str(), eventTraceProperties)); + Enable(EVENT_CONTROL_CODE_ENABLE_PROVIDER); + + m_tracing = true; + + m_flushing_thread = std::thread([this] { FlushWorker(); }); + } + + void ETWTrace::Stop() + { + if (!m_tracing) + { + return; + } + + Enable(EVENT_CONTROL_CODE_DISABLE_PROVIDER); + + // ControlTrace with EVENT_TRACE_CONTROL_STOP on the trace handle, + // which is equivalent to calling CloseTrace() on the trace handle. + Control(EVENT_TRACE_CONTROL_STOP); + + m_traceHandle = INVALID_PROCESSTRACE_HANDLE; + m_eventTracePropertiesBuffer.reset(); + m_tracing = false; + m_terminate_flushing_thread.notify_one(); + m_flushing_thread.join(); + } + + void ETWTrace::Control(ULONG traceControlCode) + { + auto eventTraceProperties = reinterpret_cast(m_eventTracePropertiesBuffer.get()); + const ULONG result = ControlTrace(m_traceHandle, m_sessionName.c_str(), eventTraceProperties, traceControlCode); + THROW_IF_FAILED(HRESULT_FROM_WIN32(result)); + } + + void ETWTrace::Enable(ULONG eventControlCode) + { + // Control the main provider + THROW_IF_WIN32_ERROR(EnableTraceEx2(m_traceHandle, &m_providerGUID, eventControlCode, TRACE_LEVEL_VERBOSE, 0, 0, 0, nullptr)); + } + + void ETWTrace::FlushWorker() + { + std::unique_lock lock(m_mutex); + while (m_tracing) + { + m_terminate_flushing_thread.wait_for(lock, + std::chrono::seconds(30), + [this]() { return !m_tracing.load(); }); + Flush(); + } + } + } +} \ No newline at end of file diff --git a/src/common/Telemetry/EtwTrace/EtwTrace.h b/src/common/Telemetry/EtwTrace/EtwTrace.h new file mode 100644 index 0000000000..46b3e37c40 --- /dev/null +++ b/src/common/Telemetry/EtwTrace/EtwTrace.h @@ -0,0 +1,54 @@ +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// + +#pragma once +#include "pch.h" + +#include +#include +#include +#include + +namespace Shared +{ + namespace Trace + { + class ETWTrace + { + public: + static inline const wchar_t* PowerToysProviderGUID = L"{38e8889b-9731-53f5-e901-e8a7c1753074}"; + + ETWTrace(); + ETWTrace(const std::wstring& etlFileNameOverride); + ~ETWTrace(); + + void UpdateState(bool tracing); + void Flush(); + + private: + void CreateEtwFolderIfNeeded(); + void InitEventTraceProperties(); + void Start(); + void Stop(); + void Control(const ULONG traceControlCode); + void Enable(const ULONG eventControlCode); + void FlushWorker(); + + GUID m_providerGUID{}; + std::filesystem::path m_etwFolder; + std::wstring m_sessionName; + TRACEHANDLE m_traceHandle{ INVALID_PROCESSTRACE_HANDLE }; + std::unique_ptr m_eventTracePropertiesBuffer; + std::atomic_bool m_tracing{ false }; + std::wstring m_etlFileNameOverride{}; + std::thread m_flushing_thread; + std::condition_variable m_terminate_flushing_thread; + std::mutex m_mutex; + + static constexpr PCWSTR c_etwFolderName = L"etw"; + static constexpr PCWSTR c_etwNewFileFormattedCounter = L"-%d"; + static constexpr PCWSTR c_etwFileNameEnd = L".etl"; + }; + } +} \ No newline at end of file diff --git a/src/common/Telemetry/EtwTrace/EtwTrace.vcxproj b/src/common/Telemetry/EtwTrace/EtwTrace.vcxproj new file mode 100644 index 0000000000..17b3be7a26 --- /dev/null +++ b/src/common/Telemetry/EtwTrace/EtwTrace.vcxproj @@ -0,0 +1,51 @@ + + + + + 17.0 + Win32Proj + {8f021b46-362b-485c-bfba-ccf83e820cbd} + EtwTrace + + + + StaticLibrary + v143 + + + + + + + + + + + + + + + + + + Create + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/src/common/Telemetry/EtwTrace/EtwTrace.vcxproj.filters b/src/common/Telemetry/EtwTrace/EtwTrace.vcxproj.filters new file mode 100644 index 0000000000..9127669e77 --- /dev/null +++ b/src/common/Telemetry/EtwTrace/EtwTrace.vcxproj.filters @@ -0,0 +1,39 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + + + + + + + \ No newline at end of file diff --git a/src/common/Telemetry/EtwTrace/packages.config b/src/common/Telemetry/EtwTrace/packages.config new file mode 100644 index 0000000000..ff4b059648 --- /dev/null +++ b/src/common/Telemetry/EtwTrace/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/common/Telemetry/EtwTrace/pch.cpp b/src/common/Telemetry/EtwTrace/pch.cpp new file mode 100644 index 0000000000..64b7eef6d6 --- /dev/null +++ b/src/common/Telemetry/EtwTrace/pch.cpp @@ -0,0 +1,5 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" + +// When you are using pre-compiled headers, this source file is necessary for compilation to succeed. diff --git a/src/common/Telemetry/EtwTrace/pch.h b/src/common/Telemetry/EtwTrace/pch.h new file mode 100644 index 0000000000..8db0e0c1e8 --- /dev/null +++ b/src/common/Telemetry/EtwTrace/pch.h @@ -0,0 +1,23 @@ +// pch.h: This is a precompiled header file. +// Files listed below are compiled only once, improving build performance for future builds. +// This also affects IntelliSense performance, including code completion and many code browsing features. +// However, files listed here are ALL re-compiled if any one of them is updated between builds. +// Do not add files here that you will be updating frequently as this negates the performance advantage. + +#ifndef PCH_H +#define PCH_H + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#include + +#include +#include +#include +#include +#include +#include + +#include + +#endif //PCH_H diff --git a/src/common/Telemetry/TraceBase.h b/src/common/Telemetry/TraceBase.h new file mode 100644 index 0000000000..da722bb457 --- /dev/null +++ b/src/common/Telemetry/TraceBase.h @@ -0,0 +1,63 @@ +#pragma once + +#include "ProjectTelemetry.h" + +#define TraceLoggingWriteWrapper(provider, eventName, ...) \ + if (IsDataDiagnosticsEnabled()) \ + { \ + TraceLoggingWrite(provider, eventName, __VA_ARGS__); \ + } + +namespace telemetry +{ + +constexpr inline const wchar_t* DataDiagnosticsRegKey = L"Software\\Classes\\PowerToys"; +constexpr inline const wchar_t* DataDiagnosticsRegValueName = L"AllowDataDiagnostics"; + +class TraceBase +{ +public: + static void RegisterProvider() + { + TraceLoggingRegister(g_hProvider); + } + + static void UnregisterProvider() + { + TraceLoggingUnregister(g_hProvider); + } + + static bool IsDataDiagnosticsEnabled() + { + HKEY key{}; + if (RegOpenKeyExW(HKEY_CURRENT_USER, + DataDiagnosticsRegKey, + 0, + KEY_READ, + &key) != ERROR_SUCCESS) + { + return false; + } + + DWORD isDataDiagnosticsEnabled = 0; + DWORD size = sizeof(isDataDiagnosticsEnabled); + + if (RegGetValueW( + HKEY_CURRENT_USER, + DataDiagnosticsRegKey, + DataDiagnosticsRegValueName, + RRF_RT_REG_DWORD, + nullptr, + &isDataDiagnosticsEnabled, + &size) != ERROR_SUCCESS) + { + RegCloseKey(key); + return false; + } + RegCloseKey(key); + + return isDataDiagnosticsEnabled; + } +}; + +} // namespace telemetry \ No newline at end of file diff --git a/src/common/interop/Constants.cpp b/src/common/interop/Constants.cpp index 7a01cd225a..0a7f8c0291 100644 --- a/src/common/interop/Constants.cpp +++ b/src/common/interop/Constants.cpp @@ -51,6 +51,10 @@ namespace winrt::PowerToys::Interop::implementation { return CommonSharedConstants::SHOW_COLOR_PICKER_SHARED_EVENT; } + hstring Constants::TerminateColorPickerSharedEvent() + { + return CommonSharedConstants::TERMINATE_COLOR_PICKER_SHARED_EVENT; + } hstring Constants::AdvancedPasteShowUIMessage() { return CommonSharedConstants::ADVANCED_PASTE_SHOW_UI_MESSAGE; @@ -71,10 +75,18 @@ namespace winrt::PowerToys::Interop::implementation { return CommonSharedConstants::ADVANCED_PASTE_CUSTOM_ACTION_MESSAGE; } + hstring Constants::AdvancedPasteTerminateAppMessage() + { + return CommonSharedConstants::ADVANCED_PASTE_TERMINATE_APP_MESSAGE; + } hstring Constants::ShowPowerOCRSharedEvent() { return CommonSharedConstants::SHOW_POWEROCR_SHARED_EVENT; } + hstring Constants::TerminatePowerOCRSharedEvent() + { + return CommonSharedConstants::TERMINATE_POWEROCR_SHARED_EVENT; + } hstring Constants::MouseJumpShowPreviewEvent() { return CommonSharedConstants::MOUSE_JUMP_SHOW_PREVIEW_EVENT; @@ -87,6 +99,10 @@ namespace winrt::PowerToys::Interop::implementation { return CommonSharedConstants::SHOW_PEEK_SHARED_EVENT; } + hstring Constants::TerminatePeekEvent() + { + return CommonSharedConstants::TERMINATE_PEEK_SHARED_EVENT; + } hstring Constants::PowerAccentExitEvent() { return CommonSharedConstants::POWERACCENT_EXIT_EVENT; @@ -135,6 +151,10 @@ namespace winrt::PowerToys::Interop::implementation { return CommonSharedConstants::SHOW_HOSTS_ADMIN_EVENT; } + hstring Constants::TerminateHostsSharedEvent() + { + return CommonSharedConstants::TERMINATE_HOSTS_EVENT; + } hstring Constants::CropAndLockThumbnailEvent() { return CommonSharedConstants::CROP_AND_LOCK_THUMBNAIL_EVENT; @@ -159,4 +179,8 @@ namespace winrt::PowerToys::Interop::implementation { return CommonSharedConstants::WORKSPACES_HOTKEY_EVENT; } + hstring Constants::PowerToysRunnerTerminateSettingsEvent() + { + return CommonSharedConstants::TERMINATE_SETTINGS_SHARED_EVENT; + } } diff --git a/src/common/interop/Constants.h b/src/common/interop/Constants.h index 09a884e50b..351349959b 100644 --- a/src/common/interop/Constants.h +++ b/src/common/interop/Constants.h @@ -16,15 +16,19 @@ namespace winrt::PowerToys::Interop::implementation static hstring FZEToggleEvent(); static hstring ColorPickerSendSettingsTelemetryEvent(); static hstring ShowColorPickerSharedEvent(); + static hstring TerminateColorPickerSharedEvent(); static hstring AdvancedPasteShowUIMessage(); static hstring AdvancedPasteMarkdownMessage(); static hstring AdvancedPasteJsonMessage(); static hstring AdvancedPasteAdditionalActionMessage(); static hstring AdvancedPasteCustomActionMessage(); + static hstring AdvancedPasteTerminateAppMessage(); static hstring ShowPowerOCRSharedEvent(); + static hstring TerminatePowerOCRSharedEvent(); static hstring MouseJumpShowPreviewEvent(); static hstring AwakeExitEvent(); static hstring ShowPeekEvent(); + static hstring TerminatePeekEvent(); static hstring PowerAccentExitEvent(); static hstring ShortcutGuideTriggerEvent(); static hstring RegistryPreviewTriggerEvent(); @@ -37,12 +41,14 @@ namespace winrt::PowerToys::Interop::implementation static hstring SvgPreviewResizeEvent(); static hstring ShowHostsSharedEvent(); static hstring ShowHostsAdminSharedEvent(); + static hstring TerminateHostsSharedEvent(); static hstring CropAndLockThumbnailEvent(); static hstring CropAndLockReparentEvent(); static hstring ShowEnvironmentVariablesSharedEvent(); static hstring ShowEnvironmentVariablesAdminSharedEvent(); static hstring WorkspacesLaunchEditorEvent(); static hstring WorkspacesHotkeyEvent(); + static hstring PowerToysRunnerTerminateSettingsEvent(); }; } diff --git a/src/common/interop/Constants.idl b/src/common/interop/Constants.idl index 9ae489c62b..736f7dd49b 100644 --- a/src/common/interop/Constants.idl +++ b/src/common/interop/Constants.idl @@ -13,15 +13,19 @@ namespace PowerToys static String FZEToggleEvent(); static String ColorPickerSendSettingsTelemetryEvent(); static String ShowColorPickerSharedEvent(); + static String TerminateColorPickerSharedEvent(); static String AdvancedPasteShowUIMessage(); static String AdvancedPasteMarkdownMessage(); static String AdvancedPasteJsonMessage(); static String AdvancedPasteAdditionalActionMessage(); static String AdvancedPasteCustomActionMessage(); + static String AdvancedPasteTerminateAppMessage(); static String ShowPowerOCRSharedEvent(); + static String TerminatePowerOCRSharedEvent(); static String MouseJumpShowPreviewEvent(); static String AwakeExitEvent(); static String ShowPeekEvent(); + static String TerminatePeekEvent(); static String PowerAccentExitEvent(); static String ShortcutGuideTriggerEvent(); static String RegistryPreviewTriggerEvent(); @@ -34,12 +38,14 @@ namespace PowerToys static String SvgPreviewResizeEvent(); static String ShowHostsSharedEvent(); static String ShowHostsAdminSharedEvent(); + static String TerminateHostsSharedEvent(); static String CropAndLockThumbnailEvent(); static String CropAndLockReparentEvent(); static String ShowEnvironmentVariablesSharedEvent(); static String ShowEnvironmentVariablesAdminSharedEvent(); static String WorkspacesLaunchEditorEvent(); static String WorkspacesHotkeyEvent(); + static String PowerToysRunnerTerminateSettingsEvent(); } } } \ No newline at end of file diff --git a/src/common/interop/shared_constants.h b/src/common/interop/shared_constants.h index c98b143104..364bc6cf7f 100644 --- a/src/common/interop/shared_constants.h +++ b/src/common/interop/shared_constants.h @@ -12,6 +12,9 @@ namespace CommonSharedConstants const wchar_t APPDATA_PATH[] = L"Microsoft\\PowerToys"; + // Path to the event used by runner to terminate Settings app + const wchar_t TERMINATE_SETTINGS_SHARED_EVENT[] = L"Local\\PowerToysRunnerTerminateSettingsEvent-c34cb661-2e69-4613-a1f8-4e39c25d7ef6"; + // Path to the event used by PowerLauncher const wchar_t POWER_LAUNCHER_SHARED_EVENT[] = L"Local\\PowerToysRunInvokeEvent-30f26ad7-d36d-4c0e-ab02-68bb5ff3c4ab"; @@ -36,9 +39,13 @@ namespace CommonSharedConstants const wchar_t ADVANCED_PASTE_CUSTOM_ACTION_MESSAGE[] = L"CustomAction"; + const wchar_t ADVANCED_PASTE_TERMINATE_APP_MESSAGE[] = L"TerminateApp"; + // Path to the event used to show Color Picker const wchar_t SHOW_COLOR_PICKER_SHARED_EVENT[] = L"Local\\ShowColorPickerEvent-8c46be2a-3e05-4186-b56b-4ae986ef2525"; + const wchar_t TERMINATE_COLOR_PICKER_SHARED_EVENT[] = L"Local\\TerminateColorPickerEvent-3d676258-c4d5-424e-a87a-4be22020e813"; + const wchar_t SHORTCUT_GUIDE_TRIGGER_EVENT[] = L"Local\\ShortcutGuide-TriggerEvent-d4275ad3-2531-4d19-9252-c0becbd9b496"; const wchar_t SHORTCUT_GUIDE_EXIT_EVENT[] = L"Local\\ShortcutGuide-ExitEvent-35697cdd-a3d2-47d6-a246-34efcc73eac0"; @@ -53,18 +60,24 @@ namespace CommonSharedConstants const wchar_t SHOW_HOSTS_ADMIN_EVENT[] = L"Local\\Hosts-ShowHostsAdminEvent-60ff44e2-efd3-43bf-928a-f4d269f98bec"; + const wchar_t TERMINATE_HOSTS_EVENT[] = L"Local\\Hosts-TerminateHostsEvent-d5410d5e-45a6-4d11-bbf0-a4ec2d064888"; + // Path to the event used by Awake const wchar_t AWAKE_EXIT_EVENT[] = L"Local\\PowerToysAwakeExitEvent-c0d5e305-35fc-4fb5-83ec-f6070cfaf7fe"; // Path to the event used by AlwaysOnTop const wchar_t ALWAYS_ON_TOP_PIN_EVENT[] = L"Local\\AlwaysOnTopPinEvent-892e0aa2-cfa8-4cc4-b196-ddeb32314ce8"; + const wchar_t ALWAYS_ON_TOP_TERMINATE_EVENT[] = L"Local\\AlwaysOnTopTerminateEvent-cfdf1eae-791f-4953-8021-2f18f3837eae"; + // Path to the event used by PowerAccent const wchar_t POWERACCENT_EXIT_EVENT[] = L"Local\\PowerToysPowerAccentExitEvent-53e93389-d19a-4fbb-9b36-1981c8965e17"; // Path to the event used by PowerOCR const wchar_t SHOW_POWEROCR_SHARED_EVENT[] = L"Local\\PowerOCREvent-dc864e06-e1af-4ecc-9078-f98bee745e3a"; + const wchar_t TERMINATE_POWEROCR_SHARED_EVENT[] = L"Local\\TerminatePowerOCREvent-08e5de9d-15df-4ea8-8840-487c13435a67"; + // Path to the events used by Mouse Jump const wchar_t MOUSE_JUMP_SHOW_PREVIEW_EVENT[] = L"Local\\MouseJumpEvent-aa0be051-3396-4976-b7ba-1a9cc7d236a5"; @@ -94,6 +107,11 @@ namespace CommonSharedConstants // Path to the event used to show Peek const wchar_t SHOW_PEEK_SHARED_EVENT[] = L"Local\\ShowPeekEvent"; + // Path to the event used to terminate Peek + const wchar_t TERMINATE_PEEK_SHARED_EVENT[] = L"Local\\TerminatePeekEvent-267149fe-7ed2-427d-a3ad-9e18203c037c"; + + // Path to the event used to terminate KBM + const wchar_t TERMINATE_KBM_SHARED_EVENT[] = L"Local\\TerminateKBMSharedEvent-a787c967-55b6-47de-94d9-56f39fed839e"; // Path to the events used by CropAndLock const wchar_t CROP_AND_LOCK_REPARENT_EVENT[] = L"Local\\PowerToysCropAndLockReparentEvent-6060860a-76a1-44e8-8d0e-6355785e9c36"; diff --git a/src/common/utils/EventWaiter.h b/src/common/utils/EventWaiter.h index 304f475ade..1a2ba89f02 100644 --- a/src/common/utils/EventWaiter.h +++ b/src/common/utils/EventWaiter.h @@ -1,3 +1,5 @@ +#pragma once + #include #include #include diff --git a/src/common/utils/gpo.h b/src/common/utils/gpo.h index 0f721d93d5..04e03b4767 100644 --- a/src/common/utils/gpo.h +++ b/src/common/utils/gpo.h @@ -72,6 +72,7 @@ namespace powertoys_gpo { // The registry value names for other PowerToys policies. const std::wstring POLICY_ALLOW_EXPERIMENTATION = L"AllowExperimentation"; + const std::wstring POLICY_ALLOW_DATA_DIAGNOSTICS = L"AllowDataDiagnostics"; const std::wstring POLICY_CONFIGURE_ENABLED_POWER_LAUNCHER_ALL_PLUGINS = L"PowerLauncherAllPluginsEnabledState"; const std::wstring POLICY_ALLOW_ADVANCED_PASTE_ONLINE_AI_MODELS = L"AllowPowerToysAdvancedPasteOnlineAIModels"; const std::wstring POLICY_MWB_CLIPBOARD_SHARING_ENABLED = L"MwbClipboardSharingEnabled"; @@ -487,6 +488,11 @@ namespace powertoys_gpo { return getConfiguredValue(POLICY_ALLOW_EXPERIMENTATION); } + inline gpo_rule_configured_t getAllowDataDiagnosticsValue() + { + return getConfiguredValue(POLICY_ALLOW_DATA_DIAGNOSTICS); + } + inline gpo_rule_configured_t getRunPluginEnabledValue(std::string pluginID) { if (pluginID == "" || pluginID == " ") diff --git a/src/gpo/assets/PowerToys.admx b/src/gpo/assets/PowerToys.admx index 0459a6f601..799b1f20f3 100644 --- a/src/gpo/assets/PowerToys.admx +++ b/src/gpo/assets/PowerToys.admx @@ -5,7 +5,7 @@ - + @@ -22,6 +22,7 @@ + @@ -509,6 +510,16 @@ + + + + + + + + + + diff --git a/src/gpo/assets/en-US/PowerToys.adml b/src/gpo/assets/en-US/PowerToys.adml index 588ccf3d1d..7a8f037eee 100644 --- a/src/gpo/assets/en-US/PowerToys.adml +++ b/src/gpo/assets/en-US/PowerToys.adml @@ -1,7 +1,7 @@ - + PowerToys PowerToys @@ -28,6 +28,7 @@ PowerToys version 0.83.0 or later PowerToys version 0.84.0 or later PowerToys version 0.85.0 or later + PowerToys version 0.86.0 or later This policy configures the enabled state for all PowerToys utilities. @@ -101,6 +102,12 @@ If disabled or not configured, the user can control this in the settings of Powe If this setting is enabled or not configured, the user can control experimentation in the PowerToys settings menu. If this setting is disabled, experimentation is not allowed. + + This policy configures whether sending of PowerToys diagnostic data is allowed. With diagnostic data sending allowed the user helps inform bug fixes, performance and improvements. + +If this setting is enabled or not configured, the user can control diagnostic data sending in the PowerToys settings menu. + +If this setting is disabled, diagnostic data sending is not allowed. This policy configures the enabled state for all PowerToys Run plugins. All plugins will have the same state. @@ -258,6 +265,7 @@ If you don't configure this policy, the user takes control over the setting and Disable user defined IP Address mapping rules Predefined IP Address mapping rules Hide template filename extension + Allow sending diagnostic data diff --git a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/App.xaml.cs b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/App.xaml.cs index 77f8551803..3595276c5d 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/App.xaml.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/App.xaml.cs @@ -18,6 +18,7 @@ using AdvancedPaste.ViewModels; using ManagedCommon; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; +using Microsoft.PowerToys.Telemetry; using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; using Windows.Graphics; @@ -38,6 +39,8 @@ namespace AdvancedPaste { public IHost Host { get; private set; } + public ETWTrace EtwTrace { get; private set; } = new ETWTrace(); + private static readonly Dictionary AdditionalActionIPCKeys = typeof(PasteFormats).GetFields() .Where(field => field.IsLiteral) @@ -111,7 +114,11 @@ namespace AdvancedPaste { RunnerHelper.WaitForPowerToysRunner(powerToysRunnerPid, () => { - Environment.Exit(0); + _dispatcherQueue.TryEnqueue(() => + { + Dispose(); + Environment.Exit(0); + }); }); } } @@ -154,6 +161,11 @@ namespace AdvancedPaste { await OnAdvancedPasteCustomActionHotkey(messageParts); } + else if (messageType == PowerToys.Interop.Constants.AdvancedPasteTerminateAppMessage()) + { + Dispose(); + Environment.Exit(0); + } } private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) @@ -244,6 +256,7 @@ namespace AdvancedPaste { if (disposing) { + EtwTrace?.Dispose(); window.Dispose(); } diff --git a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/MainWindow.xaml.cs b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/MainWindow.xaml.cs index 8f675e4d4e..6536bcfed9 100644 --- a/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/MainWindow.xaml.cs +++ b/src/modules/AdvancedPaste/AdvancedPaste/AdvancedPasteXAML/MainWindow.xaml.cs @@ -12,6 +12,7 @@ using AdvancedPaste.Settings; using AdvancedPaste.ViewModels; using ManagedCommon; +using Microsoft.PowerToys.Telemetry; using Microsoft.UI.Windowing; using Microsoft.UI.Xaml; using WinUIEx; @@ -96,6 +97,7 @@ namespace AdvancedPaste if (!_disposedValue) { _msgMonitor?.Dispose(); + (Application.Current as App).EtwTrace?.Dispose(); _disposedValue = true; } diff --git a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/dllmain.cpp b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/dllmain.cpp index 4da030d6ba..8bd2e0860e 100644 --- a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/dllmain.cpp +++ b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/dllmain.cpp @@ -384,7 +384,6 @@ private: m_hProcess = sei.hProcess; } - std::optional get_pipe_name(const std::wstring& prefix) const { UUID temp_uuid; @@ -761,6 +760,8 @@ public: // Destroy the powertoy and free memory virtual void destroy() override { + Disable(false); + Logger::trace("AdvancedPaste::destroy()"); delete this; } @@ -851,15 +852,21 @@ public: launch_process_and_named_pipe(); }; - virtual void disable() + void Disable(bool traceEvent) { - Logger::trace("AdvancedPaste::disable()"); if (m_enabled) { + send_named_pipe_message(CommonSharedConstants::ADVANCED_PASTE_TERMINATE_APP_MESSAGE); + WaitForSingleObject(m_hProcess, 1500); + m_write_pipe = nullptr; TerminateProcess(m_hProcess, 1); - Trace::AdvancedPaste_Enable(false); + + if (traceEvent) + { + Trace::AdvancedPaste_Enable(false); + } CloseHandle(m_hProcess); m_hProcess = 0; @@ -868,6 +875,12 @@ public: m_enabled = false; } + virtual void disable() + { + Logger::trace("AdvancedPaste::disable()"); + Disable(true); + } + virtual bool on_hotkey(size_t hotkeyId) override { Logger::trace(L"AdvancedPaste hotkey pressed"); diff --git a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/pch.h b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/pch.h index 809b965271..e730ab2036 100644 --- a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/pch.h +++ b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/pch.h @@ -2,7 +2,6 @@ #include #include #include -#include #include #include #include \ No newline at end of file diff --git a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.cpp b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.cpp index 0ae92a7187..aa6162c465 100644 --- a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.cpp +++ b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.cpp @@ -8,20 +8,10 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() -{ - TraceLoggingUnregister(g_hProvider); -} - // Log if the user has AdvancedPaste enabled or disabled void Trace::AdvancedPaste_Enable(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "AdvancedPaste_EnableAdvancedPaste", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -32,7 +22,7 @@ void Trace::AdvancedPaste_Enable(const bool enabled) noexcept // Log if the user has invoked AdvancedPaste void Trace::AdvancedPaste_Invoked(std::wstring mode) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "AdvancedPaste_InvokeAdvancedPaste", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -43,7 +33,7 @@ void Trace::AdvancedPaste_Invoked(std::wstring mode) noexcept // Log if an error occurs in AdvancedPaste void Trace::AdvancedPaste_Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "AdvancedPaste_Error", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -83,7 +73,7 @@ void Trace::AdvancedPaste_SettingsTelemetry(const PowertoyModuleIface::Hotkey& p return it != additionalActionsHotkeys.end() ? getHotkeyCStr(it->second) : L""; }; - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "AdvancedPaste_Settings", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.h b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.h index 7c0504d58e..c7cee38877 100644 --- a/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.h +++ b/src/modules/AdvancedPaste/AdvancedPasteModuleInterface/trace.h @@ -1,13 +1,11 @@ #pragma once +#include #include #include -class Trace +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider(); - static void UnregisterProvider(); - // Log if the user has AdvancedPaste enabled or disabled static void AdvancedPaste_Enable(const bool enabled) noexcept; diff --git a/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj b/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj index 0f6c2bb1d1..89177bd56f 100644 --- a/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj +++ b/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj @@ -151,6 +151,9 @@ {6955446d-23f7-4023-9bb3-8657f904af99} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + diff --git a/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj.filters b/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj.filters index 98752f066d..bea68db119 100644 --- a/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj.filters +++ b/src/modules/CropAndLock/CropAndLock/CropAndLock.vcxproj.filters @@ -27,6 +27,7 @@ + diff --git a/src/modules/CropAndLock/CropAndLock/main.cpp b/src/modules/CropAndLock/CropAndLock/main.cpp index dc530b7769..79d26fc8c1 100644 --- a/src/modules/CropAndLock/CropAndLock/main.cpp +++ b/src/modules/CropAndLock/CropAndLock/main.cpp @@ -4,16 +4,20 @@ #include "CropAndLockWindow.h" #include "ThumbnailCropAndLockWindow.h" #include "ReparentCropAndLockWindow.h" -#include -#include -#include -#include -#include #include "ModuleConstants.h" -#include #include "trace.h" -#pragma comment(linker,"/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") +#include + +#include +#include +#include +#include +#include + +#include + +#pragma comment(linker, "/manifestdependency:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") namespace winrt { @@ -36,6 +40,11 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I // Initialize COM winrt::init_apartment(winrt::apartment_type::single_threaded); + Trace::CropAndLock::RegisterProvider(); + + Shared::Trace::ETWTrace trace; + trace.UpdateState(true); + // Initialize logger automatic logging of exceptions. LoggerHelpers::init_logger(NonLocalizable::ModuleKey, L"", LogSettings::cropAndLockLoggerName); InitUnhandledExceptionHandler(); @@ -107,8 +116,7 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I HANDLE m_exit_event_handle; std::thread m_event_triggers_thread; - std::function removeWindowCallback = [&](HWND windowHandle) - { + std::function removeWindowCallback = [&](HWND windowHandle) { if (!m_running) { // If we're not running, the reference to croppedWindows might no longer be valid and cause a crash at exit time, due to being called by destructors after wWinMain returns. @@ -122,8 +130,7 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I } }; - std::function ProcessCommand = [&](CropAndLockType mode) - { + std::function ProcessCommand = [&](CropAndLockType mode) { std::function windowCroppedCallback = [&, mode](HWND targetWindow, RECT cropRect) { auto targetInfo = util::WindowInfo(targetWindow); // TODO: Fix WindowInfo.h to not contain the null char at the end. @@ -196,7 +203,7 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I m_event_triggers_thread = std::thread([&]() { MSG msg; - HANDLE event_handles[3] = {m_reparent_event_handle, m_thumbnail_event_handle, m_exit_event_handle}; + HANDLE event_handles[3] = { m_reparent_event_handle, m_thumbnail_event_handle, m_exit_event_handle }; while (m_running) { DWORD dwEvt = MsgWaitForMultipleObjects(3, event_handles, false, INFINITE, QS_ALLINPUT); @@ -258,6 +265,10 @@ int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR lpCmdLine, _I DispatchMessageW(&msg); } + trace.Flush(); + + Trace::CropAndLock::UnregisterProvider(); + m_running = false; // Needed to unblock MsgWaitForMultipleObjects one last time SetEvent(m_reparent_event_handle); diff --git a/src/modules/CropAndLock/CropAndLock/pch.h b/src/modules/CropAndLock/CropAndLock/pch.h index 720033cbca..75bb5f4a61 100644 --- a/src/modules/CropAndLock/CropAndLock/pch.h +++ b/src/modules/CropAndLock/CropAndLock/pch.h @@ -74,7 +74,6 @@ #include "WindowRectUtil.h" // PowerToys -#include #include // Application resources diff --git a/src/modules/CropAndLock/CropAndLock/trace.cpp b/src/modules/CropAndLock/CropAndLock/trace.cpp index fb5dd802c5..42674ec624 100644 --- a/src/modules/CropAndLock/CropAndLock/trace.cpp +++ b/src/modules/CropAndLock/CropAndLock/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + // Telemetry strings should not be localized. #define LoggingProviderKey "Microsoft.PowerToys" @@ -11,19 +13,9 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - void Trace::CropAndLock::Enable(bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "CropAndLock_EnableCropAndLock", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -33,7 +25,7 @@ void Trace::CropAndLock::Enable(bool enabled) noexcept void Trace::CropAndLock::ActivateReparent() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "CropAndLock_ActivateReparent", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -42,7 +34,7 @@ void Trace::CropAndLock::ActivateReparent() noexcept void Trace::CropAndLock::ActivateThumbnail() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "CropAndLock_ActivateThumbnail", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -51,7 +43,7 @@ void Trace::CropAndLock::ActivateThumbnail() noexcept void Trace::CropAndLock::CreateReparentWindow() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "CropAndLock_CreateReparentWindow", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -60,7 +52,7 @@ void Trace::CropAndLock::CreateReparentWindow() noexcept void Trace::CropAndLock::CreateThumbnailWindow() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "CropAndLock_CreateThumbnailWindow", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -84,12 +76,11 @@ void Trace::CropAndLock::SettingsTelemetry(PowertoyModuleIface::Hotkey& reparent std::wstring(thumbnailHotkey.alt ? L"Alt + " : L"") + std::wstring(L"VK ") + std::to_wstring(thumbnailHotkey.key); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "CropAndLock_Settings", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE), TraceLoggingWideString(hotKeyStrReparent.c_str(), "ReparentHotKey"), - TraceLoggingWideString(hotKeyStrThumbnail.c_str(), "ThumbnailHotkey") - ); + TraceLoggingWideString(hotKeyStrThumbnail.c_str(), "ThumbnailHotkey")); } diff --git a/src/modules/CropAndLock/CropAndLock/trace.h b/src/modules/CropAndLock/CropAndLock/trace.h index f7e1903ee6..5a9aaa95ca 100644 --- a/src/modules/CropAndLock/CropAndLock/trace.h +++ b/src/modules/CropAndLock/CropAndLock/trace.h @@ -1,13 +1,12 @@ #pragma once + +#include #include class Trace { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; - - class CropAndLock + class CropAndLock : public telemetry::TraceBase { public: static void Enable(bool enabled) noexcept; diff --git a/src/modules/CropAndLock/CropAndLockModuleInterface/dllmain.cpp b/src/modules/CropAndLock/CropAndLockModuleInterface/dllmain.cpp index c313d63cd7..42c7c6da7e 100644 --- a/src/modules/CropAndLock/CropAndLockModuleInterface/dllmain.cpp +++ b/src/modules/CropAndLock/CropAndLockModuleInterface/dllmain.cpp @@ -40,13 +40,13 @@ BOOL APIENTRY DllMain( HMODULE /*hModule*/, switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: - Trace::RegisterProvider(); + Trace::CropAndLock::RegisterProvider(); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: - Trace::UnregisterProvider(); + Trace::CropAndLock::UnregisterProvider(); break; } return TRUE; diff --git a/src/modules/CropAndLock/CropAndLockModuleInterface/pch.h b/src/modules/CropAndLock/CropAndLockModuleInterface/pch.h index 0df2e08a6f..3914e22561 100644 --- a/src/modules/CropAndLock/CropAndLockModuleInterface/pch.h +++ b/src/modules/CropAndLock/CropAndLockModuleInterface/pch.h @@ -6,7 +6,5 @@ #include #include #include -#include -#include #include #include diff --git a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/App.xaml.cs b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/App.xaml.cs index bc6f5aa1da..e40478f222 100644 --- a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/App.xaml.cs +++ b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/App.xaml.cs @@ -26,6 +26,8 @@ namespace EnvironmentVariables { public IHost Host { get; } + public ETWTrace EtwTrace { get; } = new ETWTrace(); + public static T GetService() where T : class { diff --git a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/MainWindow.xaml b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/MainWindow.xaml index 78d46d85c7..32c536101e 100644 --- a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/MainWindow.xaml +++ b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/MainWindow.xaml @@ -1,4 +1,4 @@ - + diff --git a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/MainWindow.xaml.cs b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/MainWindow.xaml.cs index 7a67a1dfbf..891fadfe1d 100644 --- a/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/MainWindow.xaml.cs +++ b/src/modules/EnvironmentVariables/EnvironmentVariables/EnvironmentVariablesXAML/MainWindow.xaml.cs @@ -88,5 +88,10 @@ namespace EnvironmentVariables return NativeMethods.CallWindowProc(oldWndProc, hWnd, msg, wParam, lParam); } + + private void Window_Closed(object sender, WindowEventArgs args) + { + (App.Current as EnvironmentVariables.App).EtwTrace?.Dispose(); + } } } diff --git a/src/modules/EnvironmentVariables/EnvironmentVariablesModuleInterface/pch.h b/src/modules/EnvironmentVariables/EnvironmentVariablesModuleInterface/pch.h index 6f70098567..308dff2b3a 100644 --- a/src/modules/EnvironmentVariables/EnvironmentVariablesModuleInterface/pch.h +++ b/src/modules/EnvironmentVariables/EnvironmentVariablesModuleInterface/pch.h @@ -10,7 +10,6 @@ #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files #include -#include #include #include diff --git a/src/modules/EnvironmentVariables/EnvironmentVariablesModuleInterface/trace.cpp b/src/modules/EnvironmentVariables/EnvironmentVariablesModuleInterface/trace.cpp index bb458c1b6d..6cb84aed08 100644 --- a/src/modules/EnvironmentVariables/EnvironmentVariablesModuleInterface/trace.cpp +++ b/src/modules/EnvironmentVariables/EnvironmentVariablesModuleInterface/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,20 +10,10 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - // Log if the user has Environment Variables enabled or disabled void Trace::EnableEnvironmentVariables(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "EnvironmentVariables_EnableEnvironmentVariables", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -32,7 +24,7 @@ void Trace::EnableEnvironmentVariables(const bool enabled) noexcept // Log that the user tried to activate the editor void Trace::ActivateEnvironmentVariables() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "EnvironmentVariables_Activate", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/EnvironmentVariables/EnvironmentVariablesModuleInterface/trace.h b/src/modules/EnvironmentVariables/EnvironmentVariablesModuleInterface/trace.h index 0898da4602..fa37b043b5 100644 --- a/src/modules/EnvironmentVariables/EnvironmentVariablesModuleInterface/trace.h +++ b/src/modules/EnvironmentVariables/EnvironmentVariablesModuleInterface/trace.h @@ -1,11 +1,10 @@ #pragma once -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; - // Log if the user has EnvironmentVariables enabled or disabled static void EnableEnvironmentVariables(const bool enabled) noexcept; diff --git a/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj b/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj index ac0580c2a0..10478cd30c 100644 --- a/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj +++ b/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj @@ -128,6 +128,9 @@ MakeAppx.exe pack /d . /p $(OutDir)FileLocksmithContextMenuPackage.msix /nv {6955446d-23f7-4023-9bb3-8657f904af99} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + {cc6e41ac-8174-4e8a-8d22-85dd7f4851df} diff --git a/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj.filters b/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj.filters index 5ec8585829..6646b4fc10 100644 --- a/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj.filters +++ b/src/modules/FileLocksmith/FileLocksmithContextMenu/FileLocksmithContextMenu.vcxproj.filters @@ -1,5 +1,6 @@  - + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} diff --git a/src/modules/FileLocksmith/FileLocksmithContextMenu/dllmain.cpp b/src/modules/FileLocksmith/FileLocksmithContextMenu/dllmain.cpp index 1abf0e947b..a597b9ca80 100644 --- a/src/modules/FileLocksmith/FileLocksmithContextMenu/dllmain.cpp +++ b/src/modules/FileLocksmith/FileLocksmithContextMenu/dllmain.cpp @@ -1,6 +1,7 @@ // dllmain.cpp : Defines the entry point for the DLL application. #include "pch.h" +#include #include #include #include @@ -20,6 +21,7 @@ using namespace Microsoft::WRL; HINSTANCE g_hInst = 0; +Shared::Trace::ETWTrace trace(L"FileLocksmithContextMenu"); BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, @@ -91,6 +93,8 @@ public: IFACEMETHODIMP Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept { + trace.UpdateState(true); + Trace::Invoked(); ipc::Writer writer; @@ -102,6 +106,9 @@ public: if (HRESULT result = writer.start(); FAILED(result)) { Trace::InvokedRet(result); + + trace.Flush(); + trace.UpdateState(false); return result; } @@ -114,6 +121,10 @@ public: { result = E_FAIL; Trace::InvokedRet(result); + + trace.Flush(); + trace.UpdateState(false); + return result; } @@ -140,6 +151,10 @@ public: } Trace::InvokedRet(S_OK); + + trace.Flush(); + trace.UpdateState(false); + return S_OK; } diff --git a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp index bc4ab945ca..49e2e67c19 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp +++ b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.cpp @@ -152,8 +152,13 @@ IFACEMETHODIMP ExplorerCommand::QueryContextMenu(HMENU hmenu, UINT indexMenu, UI if (!InsertMenuItem(hmenu, indexMenu, TRUE, &mii)) { + m_etwTrace.UpdateState(true); + hr = HRESULT_FROM_WIN32(GetLastError()); Trace::QueryContextMenuError(hr); + + m_etwTrace.Flush(); + m_etwTrace.UpdateState(false); } else { @@ -166,6 +171,8 @@ IFACEMETHODIMP ExplorerCommand::QueryContextMenu(HMENU hmenu, UINT indexMenu, UI IFACEMETHODIMP ExplorerCommand::InvokeCommand(CMINVOKECOMMANDINFO* pici) { + m_etwTrace.UpdateState(true); + HRESULT hr = E_FAIL; if (FileLocksmithSettingsInstance().GetEnabled() && @@ -178,12 +185,16 @@ IFACEMETHODIMP ExplorerCommand::InvokeCommand(CMINVOKECOMMANDINFO* pici) if (HRESULT result = writer.start(); FAILED(result)) { Trace::InvokedRet(result); + m_etwTrace.Flush(); + m_etwTrace.UpdateState(false); return result; } if (HRESULT result = LaunchUI(pici, &writer); FAILED(result)) { Trace::InvokedRet(result); + m_etwTrace.Flush(); + m_etwTrace.UpdateState(false); return result; } @@ -217,6 +228,9 @@ IFACEMETHODIMP ExplorerCommand::InvokeCommand(CMINVOKECOMMANDINFO* pici) } Trace::InvokedRet(hr); + + m_etwTrace.Flush(); + m_etwTrace.UpdateState(false); return hr; } diff --git a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h index d59f266157..143b6afd9f 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h +++ b/src/modules/FileLocksmith/FileLocksmithExt/ExplorerCommand.h @@ -4,6 +4,8 @@ #include "FileLocksmithLib/IPC.h" +#include + #define EXPLORER_COMMAND_UUID_STR "84d68575-e186-46ad-b0cb-baeb45ee29c0" class __declspec(uuid(EXPLORER_COMMAND_UUID_STR)) ExplorerCommand : public IExplorerCommand, public IShellExtInit, public IContextMenu @@ -50,4 +52,6 @@ private: std::atomic m_ref_count = 1; IDataObject* m_data_obj = NULL; std::wstring context_menu_caption; + + Shared::Trace::ETWTrace m_etwTrace{ L"FileExplorerExt" }; }; diff --git a/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj b/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj index 9bfad7f8a8..0c285a8bfa 100644 --- a/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj +++ b/src/modules/FileLocksmith/FileLocksmithExt/FileLocksmithExt.vcxproj @@ -103,6 +103,9 @@ {6955446d-23f7-4023-9bb3-8657f904af99} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + {98537082-0fdb-40de-abd8-0dc5a4269bab} diff --git a/src/modules/FileLocksmith/FileLocksmithLib/Trace.cpp b/src/modules/FileLocksmith/FileLocksmithLib/Trace.cpp index 98b2f9985d..a3d8e9038e 100644 --- a/src/modules/FileLocksmith/FileLocksmithLib/Trace.cpp +++ b/src/modules/FileLocksmith/FileLocksmithLib/Trace.cpp @@ -1,7 +1,8 @@ #include "pch.h" #include "Trace.h" -#include "../common/Telemetry/ProjectTelemetry.h" + +#include TRACELOGGING_DEFINE_PROVIDER( g_hProvider, @@ -10,19 +11,9 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - void Trace::EnableFileLocksmith(_In_ bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "FileLocksmith_EnableFileLocksmith", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -32,7 +23,7 @@ void Trace::EnableFileLocksmith(_In_ bool enabled) noexcept void Trace::Invoked() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "FileLocksmith_Invoked", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -41,7 +32,7 @@ void Trace::Invoked() noexcept void Trace::InvokedRet(_In_ HRESULT hr) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "FileLocksmith_InvokedRet", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -51,7 +42,7 @@ void Trace::InvokedRet(_In_ HRESULT hr) noexcept void Trace::QueryContextMenuError(_In_ HRESULT hr) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "FileLocksmith_QueryContextMenuError", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/FileLocksmith/FileLocksmithLib/Trace.h b/src/modules/FileLocksmith/FileLocksmithLib/Trace.h index a9516b5d5c..98642de854 100644 --- a/src/modules/FileLocksmith/FileLocksmithLib/Trace.h +++ b/src/modules/FileLocksmith/FileLocksmithLib/Trace.h @@ -2,11 +2,11 @@ #include "pch.h" -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; static void EnableFileLocksmith(_In_ bool enabled) noexcept; static void Invoked() noexcept; static void InvokedRet(_In_ HRESULT hr) noexcept; diff --git a/src/modules/Hosts/Hosts/Helpers/NativeEventWaiter.cs b/src/modules/Hosts/Hosts/Helpers/NativeEventWaiter.cs new file mode 100644 index 0000000000..b63468d553 --- /dev/null +++ b/src/modules/Hosts/Hosts/Helpers/NativeEventWaiter.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading; + +using Microsoft.UI.Dispatching; + +namespace Hosts.Helpers +{ + public static class NativeEventWaiter + { + public static void WaitForEventLoop(string eventName, Action callback) + { + var dispatcherQueue = DispatcherQueue.GetForCurrentThread(); + new Thread(() => + { + var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName); + while (true) + { + if (eventHandle.WaitOne()) + { + dispatcherQueue.TryEnqueue(() => callback()); + } + } + }).Start(); + } + } +} diff --git a/src/modules/Hosts/Hosts/HostsXAML/App.xaml.cs b/src/modules/Hosts/Hosts/HostsXAML/App.xaml.cs index cd4d8b177f..7c4d6c389e 100644 --- a/src/modules/Hosts/Hosts/HostsXAML/App.xaml.cs +++ b/src/modules/Hosts/Hosts/HostsXAML/App.xaml.cs @@ -17,7 +17,7 @@ using Microsoft.Extensions.Hosting; using Microsoft.PowerToys.Telemetry; using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; - +using PowerToys.Interop; using static HostsUILib.Settings.IUserSettings; using Host = Hosts.Helpers.Host; @@ -93,6 +93,12 @@ namespace Hosts cleanupBackupThread.Start(); UnhandledException += App_UnhandledException; + + Hosts.Helpers.NativeEventWaiter.WaitForEventLoop(Constants.TerminateHostsSharedEvent(), () => + { + EtwTrace?.Dispose(); + Environment.Exit(0); + }); } /// @@ -112,6 +118,7 @@ namespace Hosts RunnerHelper.WaitForPowerToysRunner(powerToysRunnerPid, () => { Logger.LogInfo("PowerToys Runner exited. Exiting Hosts"); + EtwTrace?.Dispose(); dispatcher.TryEnqueue(App.Current.Exit); }); } @@ -133,5 +140,7 @@ namespace Hosts } private Window window; + + public ETWTrace EtwTrace { get; private set; } = new ETWTrace(); } } diff --git a/src/modules/Hosts/Hosts/HostsXAML/MainWindow.xaml b/src/modules/Hosts/Hosts/HostsXAML/MainWindow.xaml index c1229b2ec3..73a883f68b 100644 --- a/src/modules/Hosts/Hosts/HostsXAML/MainWindow.xaml +++ b/src/modules/Hosts/Hosts/HostsXAML/MainWindow.xaml @@ -1,4 +1,4 @@ - diff --git a/src/modules/Hosts/Hosts/HostsXAML/MainWindow.xaml.cs b/src/modules/Hosts/Hosts/HostsXAML/MainWindow.xaml.cs index 3f8c01de83..d1632c21ee 100644 --- a/src/modules/Hosts/Hosts/HostsXAML/MainWindow.xaml.cs +++ b/src/modules/Hosts/Hosts/HostsXAML/MainWindow.xaml.cs @@ -62,5 +62,10 @@ namespace Hosts MainGrid.Children.Add(MainPage); Grid.SetRow(MainPage, 1); } + + private void WindowEx_Closed(object sender, WindowEventArgs args) + { + (Application.Current as App).EtwTrace?.Dispose(); + } } } diff --git a/src/modules/Hosts/HostsModuleInterface/dllmain.cpp b/src/modules/Hosts/HostsModuleInterface/dllmain.cpp index 85077c8dbe..807c0829c3 100644 --- a/src/modules/Hosts/HostsModuleInterface/dllmain.cpp +++ b/src/modules/Hosts/HostsModuleInterface/dllmain.cpp @@ -55,6 +55,8 @@ private: HANDLE m_hShowAdminEvent{}; + HANDLE m_hTerminateEvent{}; + bool is_process_running() { return WaitForSingleObject(m_hProcess, 0) == WAIT_TIMEOUT; @@ -142,6 +144,17 @@ public: } } + m_hTerminateEvent = CreateDefaultEvent(CommonSharedConstants::TERMINATE_HOSTS_EVENT); + if (!m_hTerminateEvent) + { + Logger::error(L"Failed to create terminate hosts event"); + auto message = get_last_error_message(GetLastError()); + if (message.has_value()) + { + Logger::error(message.value()); + } + } + m_showEventWaiter = EventWaiter(CommonSharedConstants::SHOW_HOSTS_EVENT, [&](int err) { if (m_enabled && err == ERROR_SUCCESS) @@ -264,6 +277,8 @@ public: ResetEvent(m_hShowAdminEvent); } + SetEvent(m_hTerminateEvent); + WaitForSingleObject(m_hProcess, 1500); TerminateProcess(m_hProcess, 1); } diff --git a/src/modules/Hosts/HostsModuleInterface/pch.h b/src/modules/Hosts/HostsModuleInterface/pch.h index 7c6ad2a235..5cb4cbf823 100644 --- a/src/modules/Hosts/HostsModuleInterface/pch.h +++ b/src/modules/Hosts/HostsModuleInterface/pch.h @@ -2,4 +2,3 @@ #define WIN32_LEAN_AND_MEAN #include -#include \ No newline at end of file diff --git a/src/modules/Hosts/HostsModuleInterface/trace.cpp b/src/modules/Hosts/HostsModuleInterface/trace.cpp index 391917a8f2..104075a829 100644 --- a/src/modules/Hosts/HostsModuleInterface/trace.cpp +++ b/src/modules/Hosts/HostsModuleInterface/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,20 +10,10 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - // Log if the user has HostsFileEditor enabled or disabled void Trace::EnableHostsFileEditor(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "HostsFileEditor_EnableHostsFileEditor", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -32,7 +24,7 @@ void Trace::EnableHostsFileEditor(const bool enabled) noexcept // Log that the user tried to activate the editor void Trace::ActivateEditor() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "HostsFileEditor_Activate", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/Hosts/HostsModuleInterface/trace.h b/src/modules/Hosts/HostsModuleInterface/trace.h index 92132b7f54..f3dc6310b5 100644 --- a/src/modules/Hosts/HostsModuleInterface/trace.h +++ b/src/modules/Hosts/HostsModuleInterface/trace.h @@ -1,11 +1,10 @@ #pragma once -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; - // Log if the user has HostsFileEditor enabled or disabled static void EnableHostsFileEditor(const bool enabled) noexcept; diff --git a/src/modules/Hosts/HostsUILib/HostsMainPage.xaml.cs b/src/modules/Hosts/HostsUILib/HostsMainPage.xaml.cs index 8f3303c5ca..197fab4a3b 100644 --- a/src/modules/Hosts/HostsUILib/HostsMainPage.xaml.cs +++ b/src/modules/Hosts/HostsUILib/HostsMainPage.xaml.cs @@ -32,7 +32,7 @@ namespace HostsUILib.Views public ICommand UpdateAdditionalLinesCommand => new RelayCommand(UpdateAdditionalLines); - public ICommand ExitCommand => new RelayCommand(() => { Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread().TryEnqueue(Application.Current.Exit); }); + public ICommand ExitCommand => new RelayCommand(() => { Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread().TryEnqueue(() => { Environment.Exit(0); }); }); public HostsMainPage(MainViewModel viewModel) { diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.cpp b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.cpp index d4f90ba678..214d8c0818 100644 --- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.cpp +++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.cpp @@ -111,7 +111,11 @@ namespace winrt::PowerToys::MeasureToolCore::implementation #endif _overlayUIStates.push_back(std::move(overlayUI)); } + + trace.UpdateState(true); Trace::BoundsToolActivated(); + trace.Flush(); + trace.UpdateState(false); } void Core::StartMeasureTool(const bool horizontal, const bool vertical) @@ -160,7 +164,10 @@ namespace winrt::PowerToys::MeasureToolCore::implementation _screenCaptureThreads.emplace_back(std::move(thread)); } + trace.UpdateState(true); Trace::MeasureToolActivated(); + trace.Flush(); + trace.UpdateState(false); } void MeasureToolCore::implementation::Core::SetToolCompletionEvent(ToolSessionCompleted sessionCompletedTrigger) diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.h b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.h index 79728794a9..da0b3afb17 100644 --- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.h +++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.h @@ -5,6 +5,7 @@ #include "OverlayUI.h" #include "Settings.h" +#include #include #include "ScreenCapturing.h" @@ -50,6 +51,7 @@ namespace winrt::PowerToys::MeasureToolCore::implementation BoundsToolState _boundsToolState; CommonState _commonState; Settings _settings; + Shared::Trace::ETWTrace trace{}; }; } diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj index b05546d9aa..5bdad6767c 100644 --- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj +++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj @@ -125,6 +125,9 @@ {6955446d-23f7-4023-9bb3-8657f904af99} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + {98537082-0fdb-40de-abd8-0dc5a4269bab} diff --git a/src/modules/MeasureTool/MeasureToolCore/pch.h b/src/modules/MeasureTool/MeasureToolCore/pch.h index 67ca0f9d8c..16cc5a1a62 100644 --- a/src/modules/MeasureTool/MeasureToolCore/pch.h +++ b/src/modules/MeasureTool/MeasureToolCore/pch.h @@ -30,7 +30,6 @@ #include #include #include -#include // Undefine GetCurrentTime macro to prevent // conflict with Storyboard::GetCurrentTime diff --git a/src/modules/MeasureTool/MeasureToolModuleInterface/pch.h b/src/modules/MeasureTool/MeasureToolModuleInterface/pch.h index 207418a921..9e02b6c9ce 100644 --- a/src/modules/MeasureTool/MeasureToolModuleInterface/pch.h +++ b/src/modules/MeasureTool/MeasureToolModuleInterface/pch.h @@ -9,6 +9,5 @@ #include #include -#include #include #include diff --git a/src/modules/MeasureTool/MeasureToolModuleInterface/trace.cpp b/src/modules/MeasureTool/MeasureToolModuleInterface/trace.cpp index 8c1bb93605..1a56838048 100644 --- a/src/modules/MeasureTool/MeasureToolModuleInterface/trace.cpp +++ b/src/modules/MeasureTool/MeasureToolModuleInterface/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,19 +10,9 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - void Trace::EnableMeasureTool(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "MeasureTool_EnableMeasureTool", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -30,7 +22,7 @@ void Trace::EnableMeasureTool(const bool enabled) noexcept void Trace::BoundsToolActivated() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "MeasureTool_BoundsToolActivated", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -39,7 +31,7 @@ void Trace::BoundsToolActivated() noexcept void Trace::MeasureToolActivated() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "MeasureTool_MeasureToolActivated", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/MeasureTool/MeasureToolModuleInterface/trace.h b/src/modules/MeasureTool/MeasureToolModuleInterface/trace.h index e85a14bb38..fb2019c8d5 100644 --- a/src/modules/MeasureTool/MeasureToolModuleInterface/trace.h +++ b/src/modules/MeasureTool/MeasureToolModuleInterface/trace.h @@ -1,11 +1,10 @@ #pragma once -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; - static void EnableMeasureTool(const bool enabled) noexcept; static void BoundsToolActivated() noexcept; diff --git a/src/modules/MouseUtils/FindMyMouse/pch.h b/src/modules/MouseUtils/FindMyMouse/pch.h index 6dbc256004..26da2455f2 100644 --- a/src/modules/MouseUtils/FindMyMouse/pch.h +++ b/src/modules/MouseUtils/FindMyMouse/pch.h @@ -15,6 +15,5 @@ #endif #include -#include #include #include diff --git a/src/modules/MouseUtils/FindMyMouse/trace.cpp b/src/modules/MouseUtils/FindMyMouse/trace.cpp index a5cfe02417..bf79461e9a 100644 --- a/src/modules/MouseUtils/FindMyMouse/trace.cpp +++ b/src/modules/MouseUtils/FindMyMouse/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,20 +10,10 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - // Log if the user has FindMyMouse enabled or disabled void Trace::EnableFindMyMouse(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "FindMyMouse_EnableFindMyMouse", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -32,7 +24,7 @@ void Trace::EnableFindMyMouse(const bool enabled) noexcept // Log that the user activated the module by focusing the mouse pointer void Trace::MousePointerFocused() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "FindMyMouse_MousePointerFocused", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/MouseUtils/FindMyMouse/trace.h b/src/modules/MouseUtils/FindMyMouse/trace.h index 623ce60176..59d3183b5b 100644 --- a/src/modules/MouseUtils/FindMyMouse/trace.h +++ b/src/modules/MouseUtils/FindMyMouse/trace.h @@ -1,11 +1,10 @@ #pragma once -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; - // Log if the user has FindMyMouse enabled or disabled static void EnableFindMyMouse(const bool enabled) noexcept; diff --git a/src/modules/MouseUtils/MouseHighlighter/pch.h b/src/modules/MouseUtils/MouseHighlighter/pch.h index bfb4a4776a..da08f4898a 100644 --- a/src/modules/MouseUtils/MouseHighlighter/pch.h +++ b/src/modules/MouseUtils/MouseHighlighter/pch.h @@ -16,7 +16,6 @@ #include #endif -#include #include #include #include diff --git a/src/modules/MouseUtils/MouseHighlighter/trace.cpp b/src/modules/MouseUtils/MouseHighlighter/trace.cpp index feefa17745..7f8d413b5a 100644 --- a/src/modules/MouseUtils/MouseHighlighter/trace.cpp +++ b/src/modules/MouseUtils/MouseHighlighter/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,20 +10,10 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - // Log if the user has MouseHighlighter enabled or disabled void Trace::EnableMouseHighlighter(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "MouseHighlighter_EnableMouseHighlighter", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -32,7 +24,7 @@ void Trace::EnableMouseHighlighter(const bool enabled) noexcept // Log that the user activated the module by starting a highlighting session void Trace::StartHighlightingSession() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "MouseHighlighter_StartHighlightingSession", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/MouseUtils/MouseHighlighter/trace.h b/src/modules/MouseUtils/MouseHighlighter/trace.h index 01d660bbc0..12708940e9 100644 --- a/src/modules/MouseUtils/MouseHighlighter/trace.h +++ b/src/modules/MouseUtils/MouseHighlighter/trace.h @@ -1,11 +1,10 @@ #pragma once -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; - // Log if the user has MouseHighlighter enabled or disabled static void EnableMouseHighlighter(const bool enabled) noexcept; diff --git a/src/modules/MouseUtils/MouseJump/pch.h b/src/modules/MouseUtils/MouseJump/pch.h index 74abb62da1..575f5e9355 100644 --- a/src/modules/MouseUtils/MouseJump/pch.h +++ b/src/modules/MouseUtils/MouseJump/pch.h @@ -5,6 +5,5 @@ #include //#include -#include #include #include diff --git a/src/modules/MouseUtils/MouseJump/trace.cpp b/src/modules/MouseUtils/MouseJump/trace.cpp index 9f5380284d..dd2399eb68 100644 --- a/src/modules/MouseUtils/MouseJump/trace.cpp +++ b/src/modules/MouseUtils/MouseJump/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,19 +10,9 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() -{ - TraceLoggingUnregister(g_hProvider); -} - void Trace::EnableJumpTool(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "MouseJump_EnableJumpTool", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -30,7 +22,7 @@ void Trace::EnableJumpTool(const bool enabled) noexcept void Trace::InvokeJumpTool() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "MouseJump_InvokeJumpTool", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/MouseUtils/MouseJump/trace.h b/src/modules/MouseUtils/MouseJump/trace.h index aaaa336291..c70e2c746c 100644 --- a/src/modules/MouseUtils/MouseJump/trace.h +++ b/src/modules/MouseUtils/MouseJump/trace.h @@ -1,11 +1,10 @@ #pragma once -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider(); - static void UnregisterProvider(); - static void EnableJumpTool(const bool enabled) noexcept; static void InvokeJumpTool() noexcept; diff --git a/src/modules/MouseUtils/MousePointerCrosshairs/pch.h b/src/modules/MouseUtils/MousePointerCrosshairs/pch.h index 5fc459cbc9..1240987498 100644 --- a/src/modules/MouseUtils/MousePointerCrosshairs/pch.h +++ b/src/modules/MouseUtils/MousePointerCrosshairs/pch.h @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/src/modules/MouseUtils/MousePointerCrosshairs/trace.cpp b/src/modules/MouseUtils/MousePointerCrosshairs/trace.cpp index afc9844bc5..00924f030d 100644 --- a/src/modules/MouseUtils/MousePointerCrosshairs/trace.cpp +++ b/src/modules/MouseUtils/MousePointerCrosshairs/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,20 +10,10 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - // Log if the user has MousePointerCrosshairs enabled or disabled void Trace::EnableMousePointerCrosshairs(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "MousePointerCrosshairs_EnableMousePointerCrosshairs", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -32,7 +24,7 @@ void Trace::EnableMousePointerCrosshairs(const bool enabled) noexcept // Log that the user activated the module by having the crosshairs be drawn void Trace::StartDrawingCrosshairs() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "MousePointerCrosshairs_StartDrawingCrosshairs", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/MouseUtils/MousePointerCrosshairs/trace.h b/src/modules/MouseUtils/MousePointerCrosshairs/trace.h index d7373e5347..b60fc33336 100644 --- a/src/modules/MouseUtils/MousePointerCrosshairs/trace.h +++ b/src/modules/MouseUtils/MousePointerCrosshairs/trace.h @@ -1,11 +1,10 @@ #pragma once -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; - // Log if the user has MousePointerCrosshairs enabled or disabled static void EnableMousePointerCrosshairs(const bool enabled) noexcept; diff --git a/src/modules/MouseWithoutBorders/App/Class/Common.ShutdownWithPowerToys.cs b/src/modules/MouseWithoutBorders/App/Class/Common.ShutdownWithPowerToys.cs index 8a562e3b70..7c0dd4eb9b 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Common.ShutdownWithPowerToys.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Common.ShutdownWithPowerToys.cs @@ -3,9 +3,10 @@ // See the LICENSE file in the project root for more information. using System; -using System.Diagnostics; using ManagedCommon; +using Microsoft.PowerToys.Telemetry; +using MouseWithoutBorders.Class; using Logger = MouseWithoutBorders.Core.Logger; @@ -13,12 +14,13 @@ namespace MouseWithoutBorders { internal class ShutdownWithPowerToys { - public static void WaitForPowerToysRunner() + public static void WaitForPowerToysRunner(ETWTrace etwTrace) { try { RunnerHelper.WaitForPowerToysRunnerExitFallback(() => { + etwTrace?.Dispose(); Common.MainForm.Quit(true, false); }); } diff --git a/src/modules/MouseWithoutBorders/App/Class/Program.cs b/src/modules/MouseWithoutBorders/App/Class/Program.cs index 91ed60874f..ac33da9ae9 100644 --- a/src/modules/MouseWithoutBorders/App/Class/Program.cs +++ b/src/modules/MouseWithoutBorders/App/Class/Program.cs @@ -57,6 +57,8 @@ namespace MouseWithoutBorders.Class ManagedCommon.Logger.InitializeLogger("\\MouseWithoutBorders\\Logs"); Logger.Log(Application.ProductName + " Started!"); + ETWTrace etwTrace = new ETWTrace(); + if (PowerToys.GPOWrapper.GPOWrapper.GetConfiguredMouseWithoutBordersEnabledValue() == PowerToys.GPOWrapper.GpoRuleConfigured.Disabled) { Logger.Log("Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator."); @@ -117,7 +119,7 @@ namespace MouseWithoutBorders.Class } } - ShutdownWithPowerToys.WaitForPowerToysRunner(); + ShutdownWithPowerToys.WaitForPowerToysRunner(etwTrace); if (firstArg != string.Empty) { @@ -225,6 +227,8 @@ namespace MouseWithoutBorders.Class var formScreen = new FrmScreen(); Application.Run(formScreen); + + etwTrace?.Dispose(); } catch (Exception e) { diff --git a/src/modules/MouseWithoutBorders/App/Helper/Program.cs b/src/modules/MouseWithoutBorders/App/Helper/Program.cs index 7a3ae0424b..874a0ac2a4 100644 --- a/src/modules/MouseWithoutBorders/App/Helper/Program.cs +++ b/src/modules/MouseWithoutBorders/App/Helper/Program.cs @@ -8,6 +8,7 @@ using System.IO; using System.Windows.Forms; using ManagedCommon; +using Microsoft.PowerToys.Telemetry; namespace MouseWithoutBorders { @@ -38,8 +39,11 @@ namespace MouseWithoutBorders return; } + ETWTrace etwTrace = new ETWTrace(); + RunnerHelper.WaitForPowerToysRunnerExitFallback(() => { + etwTrace?.Dispose(); Application.Exit(); }); @@ -76,6 +80,8 @@ namespace MouseWithoutBorders dotForm = new FormDot(); Application.Run(FormHelper = new FormHelper()); + + etwTrace?.Dispose(); } } } diff --git a/src/modules/MouseWithoutBorders/ModuleInterface/dllmain.cpp b/src/modules/MouseWithoutBorders/ModuleInterface/dllmain.cpp index d2203e6ff5..463d527230 100644 --- a/src/modules/MouseWithoutBorders/ModuleInterface/dllmain.cpp +++ b/src/modules/MouseWithoutBorders/ModuleInterface/dllmain.cpp @@ -22,13 +22,13 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID /*lpRese { case DLL_PROCESS_ATTACH: g_hInst_MouseWithoutBorders = hModule; - Trace::RegisterProvider(); + Trace::MouseWithoutBorders::RegisterProvider(); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: - Trace::UnregisterProvider(); + Trace::MouseWithoutBorders::UnregisterProvider(); break; } return TRUE; diff --git a/src/modules/MouseWithoutBorders/ModuleInterface/pch.h b/src/modules/MouseWithoutBorders/ModuleInterface/pch.h index cb0737a69d..0270a7bc39 100644 --- a/src/modules/MouseWithoutBorders/ModuleInterface/pch.h +++ b/src/modules/MouseWithoutBorders/ModuleInterface/pch.h @@ -9,8 +9,6 @@ #include #include #include -#include -#include #include #include diff --git a/src/modules/MouseWithoutBorders/ModuleInterface/trace.cpp b/src/modules/MouseWithoutBorders/ModuleInterface/trace.cpp index fb2f631bc6..7986ceefa4 100644 --- a/src/modules/MouseWithoutBorders/ModuleInterface/trace.cpp +++ b/src/modules/MouseWithoutBorders/ModuleInterface/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + // Telemetry strings should not be localized. #define LoggingProviderKey "Microsoft.PowerToys" @@ -14,19 +16,9 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - void Trace::MouseWithoutBorders::Enable(bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventEnableMouseWithoutBordersKey, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -36,7 +28,7 @@ void Trace::MouseWithoutBorders::Enable(bool enabled) noexcept void Trace::MouseWithoutBorders::ToggleServiceRegistration(bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "MouseWithoutBorders_ToggleServiceRegistration", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -46,7 +38,7 @@ void Trace::MouseWithoutBorders::ToggleServiceRegistration(bool enabled) noexcep void Trace::MouseWithoutBorders::Activate() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "MouseWithoutBorders_Activate", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -56,7 +48,7 @@ void Trace::MouseWithoutBorders::Activate() noexcept // Log that the user tried to activate the editor void Trace::MouseWithoutBorders::AddFirewallRule() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "MouseWithoutBorders_AddFirewallRule", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/MouseWithoutBorders/ModuleInterface/trace.h b/src/modules/MouseWithoutBorders/ModuleInterface/trace.h index 1f9099eaae..ecd27cbd2d 100644 --- a/src/modules/MouseWithoutBorders/ModuleInterface/trace.h +++ b/src/modules/MouseWithoutBorders/ModuleInterface/trace.h @@ -1,12 +1,11 @@ #pragma once +#include + class Trace { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; - - class MouseWithoutBorders + class MouseWithoutBorders : public telemetry::TraceBase { public: static void Enable(bool enabled) noexcept; diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/NewShellExtensionContextMenu.vcxproj b/src/modules/NewPlus/NewShellExtensionContextMenu/NewShellExtensionContextMenu.vcxproj index 9444e849c0..8d2e617579 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/NewShellExtensionContextMenu.vcxproj +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/NewShellExtensionContextMenu.vcxproj @@ -167,6 +167,9 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv {6955446d-23f7-4023-9bb3-8657f904af99} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + {98537082-0fdb-40de-abd8-0dc5a4269bab} diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/dll_main.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu/dll_main.cpp index 5ad3537bb2..878673826b 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/dll_main.cpp +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/dll_main.cpp @@ -4,7 +4,10 @@ #include "dll_main.h" #include "trace.h" +#include + HMODULE module_instance_handle = 0; +Shared::Trace::ETWTrace trace(L"NewPlusShellExtension"); BOOL APIENTRY DllMain(HMODULE module_handle, DWORD ul_reason_for_call, LPVOID reserved) { diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/dll_main.h b/src/modules/NewPlus/NewShellExtensionContextMenu/dll_main.h index f6c4284a10..9e980caa0d 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/dll_main.h +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/dll_main.h @@ -1,3 +1,6 @@ #pragma once -extern HMODULE module_instance_handle; \ No newline at end of file +#include + +extern HMODULE module_instance_handle; +extern Shared::Trace::ETWTrace trace; \ No newline at end of file diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu.cpp index 148dffd952..47f93beac7 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu.cpp +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu.cpp @@ -7,6 +7,7 @@ using namespace Microsoft::WRL; // // Sub context menu command enumerator shell_context_sub_menu::shell_context_sub_menu(const ComPtr site_of_folder) { + trace.UpdateState(true); this->site_of_folder = site_of_folder; // Determine the New+ Template folder location diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu_item.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu_item.cpp index 99378f468a..12e5f9cf5c 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu_item.cpp +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu_item.cpp @@ -63,8 +63,11 @@ IFACEMETHODIMP shell_context_sub_menu_item::GetState(_In_opt_ IShellItemArray* s IFACEMETHODIMP shell_context_sub_menu_item::Invoke(_In_opt_ IShellItemArray*, _In_opt_ IBindCtx*) noexcept { + HRESULT hr = S_OK; try { + trace.UpdateState(true); + // Determine target path of where context menu was displayed const auto target_path_name = utilities::get_path_from_unknown_site(site_of_folder); @@ -90,16 +93,19 @@ IFACEMETHODIMP shell_context_sub_menu_item::Invoke(_In_opt_ IShellItemArray*, _I this->template_entry->enter_rename_mode(site_of_folder, target_final_fullpath); Trace::EventCopyTemplateResult(S_OK); - - return S_OK; } catch (const std::exception& ex) { Trace::EventCopyTemplateResult(S_FALSE); Logger::error(ex.what()); + + hr = S_FALSE; } - return S_FALSE; + trace.Flush(); + trace.UpdateState(false); + + return hr; } IFACEMETHODIMP shell_context_sub_menu_item::GetFlags(_Out_ EXPCMDFLAGS* returned_flags) diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/trace.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu/trace.cpp index 0def375024..3a2c8c8c91 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/trace.cpp +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/trace.cpp @@ -10,19 +10,9 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - void Trace::EventToggleOnOff(_In_ const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "NewPlus_EventToggleOnOff", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -32,7 +22,7 @@ void Trace::EventToggleOnOff(_In_ const bool enabled) noexcept void Trace::EventChangedTemplateLocation() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "NewPlus_ChangedTemplateLocation", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -41,7 +31,7 @@ void Trace::EventChangedTemplateLocation() noexcept void Trace::EventShowTemplateItems(const size_t number_of_templates) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "NewPlus_EventShowTemplateItems", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -51,7 +41,7 @@ void Trace::EventShowTemplateItems(const size_t number_of_templates) noexcept void Trace::EventCopyTemplate(_In_ const std::wstring template_file_extension) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "NewPlus_EventCopyTemplate", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -61,7 +51,7 @@ void Trace::EventCopyTemplate(_In_ const std::wstring template_file_extension) n void Trace::EventCopyTemplateResult(_In_ const HRESULT hr) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "NewPlus_EventCopyTemplateResult", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/trace.h b/src/modules/NewPlus/NewShellExtensionContextMenu/trace.h index 2d87e88588..7c21086642 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/trace.h +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/trace.h @@ -2,11 +2,11 @@ #include "pch.h" -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; static void EventToggleOnOff(_In_ const bool new_enabled_state) noexcept; static void EventChangedTemplateLocation() noexcept; static void EventShowTemplateItems(_In_ const size_t number_of_templates) noexcept; diff --git a/src/modules/PowerOCR/PowerOCR/App.xaml.cs b/src/modules/PowerOCR/PowerOCR/App.xaml.cs index eab9acd8cd..77987ab5cd 100644 --- a/src/modules/PowerOCR/PowerOCR/App.xaml.cs +++ b/src/modules/PowerOCR/PowerOCR/App.xaml.cs @@ -6,10 +6,12 @@ using System; using System.Globalization; using System.Threading; using System.Windows; - +using Common.UI; using ManagedCommon; +using Microsoft.PowerToys.Telemetry; using PowerOCR.Keyboard; using PowerOCR.Settings; +using PowerToys.Interop; namespace PowerOCR; @@ -22,6 +24,7 @@ public partial class App : Application, IDisposable private EventMonitor? eventMonitor; private Mutex? _instanceMutex; private int _powerToysRunnerPid; + private ETWTrace etwTrace = new ETWTrace(); private CancellationTokenSource NativeThreadCTS { get; set; } @@ -43,12 +46,19 @@ public partial class App : Application, IDisposable } NativeThreadCTS = new CancellationTokenSource(); + + NativeEventWaiter.WaitForEventLoop( + Constants.TerminatePowerOCRSharedEvent(), + this.Shutdown, + this.Dispatcher, + NativeThreadCTS.Token); } public void Dispose() { GC.SuppressFinalize(this); keyboardMonitor?.Dispose(); + etwTrace?.Dispose(); } private void Application_Startup(object sender, StartupEventArgs e) diff --git a/src/modules/PowerOCR/PowerOCRModuleInterface/dllmain.cpp b/src/modules/PowerOCR/PowerOCRModuleInterface/dllmain.cpp index 1eebf010ee..b9695d6d35 100644 --- a/src/modules/PowerOCR/PowerOCRModuleInterface/dllmain.cpp +++ b/src/modules/PowerOCR/PowerOCRModuleInterface/dllmain.cpp @@ -69,6 +69,8 @@ private: // Handle to event used to invoke PowerOCR HANDLE m_hInvokeEvent; + // Handle to event used to terminate PowerOCR + HANDLE m_hTerminateEvent; void parse_hotkey(PowerToysSettings::PowerToyValues& settings) { @@ -160,6 +162,7 @@ public: app_key = PowerOcrConstants::ModuleKey; LoggerHelpers::init_logger(app_key, L"ModuleInterface", "TextExtractor"); m_hInvokeEvent = CreateDefaultEvent(CommonSharedConstants::SHOW_POWEROCR_SHARED_EVENT); + m_hTerminateEvent = CreateDefaultEvent(CommonSharedConstants::TERMINATE_POWEROCR_SHARED_EVENT); init_settings(); } @@ -249,6 +252,8 @@ public: if (m_enabled) { ResetEvent(m_hInvokeEvent); + SetEvent(m_hTerminateEvent); + WaitForSingleObject(m_hProcess, 1500); TerminateProcess(m_hProcess, 1); } diff --git a/src/modules/PowerOCR/PowerOCRModuleInterface/pch.h b/src/modules/PowerOCR/PowerOCRModuleInterface/pch.h index eddac0fdc1..329705f63b 100644 --- a/src/modules/PowerOCR/PowerOCRModuleInterface/pch.h +++ b/src/modules/PowerOCR/PowerOCRModuleInterface/pch.h @@ -2,6 +2,5 @@ #include #include #include -#include #include #include \ No newline at end of file diff --git a/src/modules/PowerOCR/PowerOCRModuleInterface/trace.cpp b/src/modules/PowerOCR/PowerOCRModuleInterface/trace.cpp index 6751270726..979619b915 100644 --- a/src/modules/PowerOCR/PowerOCRModuleInterface/trace.cpp +++ b/src/modules/PowerOCR/PowerOCRModuleInterface/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,20 +10,10 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() -{ - TraceLoggingUnregister(g_hProvider); -} - // Log if the user has PowerOCR enabled or disabled void Trace::EnablePowerOCR(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "PowerOCR_EnablePowerOCR", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/PowerOCR/PowerOCRModuleInterface/trace.h b/src/modules/PowerOCR/PowerOCRModuleInterface/trace.h index a68576c302..b8e428994e 100644 --- a/src/modules/PowerOCR/PowerOCRModuleInterface/trace.h +++ b/src/modules/PowerOCR/PowerOCRModuleInterface/trace.h @@ -1,10 +1,10 @@ #pragma once -class Trace + +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider(); - static void UnregisterProvider(); - // Log if the user has PowerOCR enabled or disabled static void EnablePowerOCR(const bool enabled) noexcept; }; diff --git a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj b/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj index 68ef97516d..045be94f2b 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj +++ b/src/modules/ShortcutGuide/ShortcutGuide/ShortcutGuide.vcxproj @@ -158,6 +158,9 @@ {6955446d-23f7-4023-9bb3-8657f904af99} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + {98537082-0fdb-40de-abd8-0dc5a4269bab} diff --git a/src/modules/ShortcutGuide/ShortcutGuide/main.cpp b/src/modules/ShortcutGuide/ShortcutGuide/main.cpp index 4cd5d78a72..713446403b 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/main.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/main.cpp @@ -9,6 +9,8 @@ #include #include +#include + #include "shortcut_guide.h" #include "target_state.h" #include "ShortcutGuideConstants.h" @@ -48,6 +50,9 @@ int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, _In_opt_ HINSTANCE /*hPrevInst winrt::init_apartment(); LoggerHelpers::init_logger(ShortcutGuideConstants::ModuleKey, L"ShortcutGuide", LogSettings::shortcutGuideLoggerName); + Shared::Trace::ETWTrace trace; + trace.UpdateState(true); + if (powertoys_gpo::getConfiguredShortcutGuideEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled) { Logger::warn(L"Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator."); @@ -132,6 +137,8 @@ int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, _In_opt_ HINSTANCE /*hPrevInst window.ShowWindow(); run_message_loop(); + + trace.Flush(); Trace::UnregisterProvider(); return 0; } diff --git a/src/modules/ShortcutGuide/ShortcutGuide/pch.h b/src/modules/ShortcutGuide/ShortcutGuide/pch.h index 7cfa22c4fa..9ec2bbcb41 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/pch.h +++ b/src/modules/ShortcutGuide/ShortcutGuide/pch.h @@ -25,6 +25,5 @@ #include #include #include -#include #include #include \ No newline at end of file diff --git a/src/modules/ShortcutGuide/ShortcutGuide/trace.cpp b/src/modules/ShortcutGuide/ShortcutGuide/trace.cpp index b68e7bbcad..f2a414d6ac 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/trace.cpp +++ b/src/modules/ShortcutGuide/ShortcutGuide/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,19 +10,9 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - void Trace::SendGuideSession(const __int64 duration_ms, const wchar_t* close_type) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "ShortcutGuide_GuideSession", TraceLoggingInt64(duration_ms, "DurationInMs"), @@ -32,7 +24,7 @@ void Trace::SendGuideSession(const __int64 duration_ms, const wchar_t* close_typ void Trace::SendSettings(ShortcutGuideSettings settings) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "ShortcutGuide_Settings", TraceLoggingWideString(settings.hotkey.c_str(), "Hotkey"), diff --git a/src/modules/ShortcutGuide/ShortcutGuide/trace.h b/src/modules/ShortcutGuide/ShortcutGuide/trace.h index 97f9d75bc8..a3446a8570 100644 --- a/src/modules/ShortcutGuide/ShortcutGuide/trace.h +++ b/src/modules/ShortcutGuide/ShortcutGuide/trace.h @@ -1,11 +1,11 @@ #pragma once #include "ShortcutGuideSettings.h" -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; static void SendGuideSession(const __int64 duration_ms, const wchar_t* close_type) noexcept; static void SendSettings(ShortcutGuideSettings settings) noexcept; }; diff --git a/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/pch.h b/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/pch.h index 799a9214a1..37cb34a58a 100644 --- a/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/pch.h +++ b/src/modules/ShortcutGuide/ShortcutGuideModuleInterface/pch.h @@ -2,7 +2,6 @@ #define NOMINMAX #include #include -#include #include #include #include \ No newline at end of file diff --git a/src/modules/Workspaces/WorkspacesEditor/App.xaml.cs b/src/modules/Workspaces/WorkspacesEditor/App.xaml.cs index df86a35ed9..669230b744 100644 --- a/src/modules/Workspaces/WorkspacesEditor/App.xaml.cs +++ b/src/modules/Workspaces/WorkspacesEditor/App.xaml.cs @@ -9,6 +9,7 @@ using System.Windows; using Common.UI; using ManagedCommon; +using Microsoft.PowerToys.Telemetry; using WorkspacesEditor.Utils; using WorkspacesEditor.ViewModels; @@ -31,6 +32,8 @@ namespace WorkspacesEditor private bool _isDisposed; + private ETWTrace etwTrace = new ETWTrace(); + public App() { WorkspacesEditorIO = new WorkspacesEditorIO(); @@ -135,6 +138,7 @@ namespace WorkspacesEditor { ThemeManager?.Dispose(); _instanceMutex?.Dispose(); + etwTrace?.Dispose(); } _isDisposed = true; diff --git a/src/modules/Workspaces/WorkspacesEditor/MainWindow.xaml.cs b/src/modules/Workspaces/WorkspacesEditor/MainWindow.xaml.cs index 63bcb15016..a0a7f96a06 100644 --- a/src/modules/Workspaces/WorkspacesEditor/MainWindow.xaml.cs +++ b/src/modules/Workspaces/WorkspacesEditor/MainWindow.xaml.cs @@ -8,6 +8,7 @@ using System.Windows; using System.Windows.Interop; using ManagedCommon; +using Microsoft.PowerToys.Telemetry; using WorkspacesEditor.Utils; using WorkspacesEditor.ViewModels; diff --git a/src/modules/Workspaces/WorkspacesLauncher/WorkspacesLauncher.vcxproj b/src/modules/Workspaces/WorkspacesLauncher/WorkspacesLauncher.vcxproj index 8ff482677c..9d4fc4bcab 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/WorkspacesLauncher.vcxproj +++ b/src/modules/Workspaces/WorkspacesLauncher/WorkspacesLauncher.vcxproj @@ -155,6 +155,9 @@ {6955446d-23f7-4023-9bb3-8657f904af99} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + {b31fcc55-b5a4-4ea7-b414-2dceae6af332} diff --git a/src/modules/Workspaces/WorkspacesLauncher/main.cpp b/src/modules/Workspaces/WorkspacesLauncher/main.cpp index 3e7c8c7db1..09f63785b1 100644 --- a/src/modules/Workspaces/WorkspacesLauncher/main.cpp +++ b/src/modules/Workspaces/WorkspacesLauncher/main.cpp @@ -7,6 +7,8 @@ #include #include +#include + #include #include @@ -14,6 +16,7 @@ #include #include +#include const std::wstring moduleName = L"Workspaces\\WorkspacesLauncher"; const std::wstring internalPath = L""; @@ -22,7 +25,12 @@ const std::wstring instanceMutexName = L"Local\\PowerToys_WorkspacesLauncher_Ins int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdline, int cmdShow) { LoggerHelpers::init_logger(moduleName, internalPath, LogSettings::workspacesLauncherLoggerName); - InitUnhandledExceptionHandler(); + InitUnhandledExceptionHandler(); + + Trace::Workspaces::RegisterProvider(); + + Shared::Trace::ETWTrace trace{}; + trace.UpdateState(true); if (powertoys_gpo::getConfiguredWorkspacesEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled) { @@ -60,7 +68,7 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdline, int cm GetModuleFileNameW(nullptr, exe_path.get(), exe_path_size); const auto modulePath = get_module_folderpath(); - + std::string cmdLineStr(cmdline); std::wstring cmdLineWStr(cmdLineStr.begin(), cmdLineStr.end()); @@ -78,7 +86,7 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdline, int cm } SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2); - + Logger::trace(L"Invoke point: {}", cmdArgs.invokePoint); // read workspaces @@ -105,12 +113,12 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdline, int cm formattedMessage = fmt::format(GET_RESOURCE_STRING(IDS_INCORRECT_FILE_ERROR), file); break; } - + MessageBox(NULL, formattedMessage.c_str(), GET_RESOURCE_STRING(IDS_WORKSPACES).c_str(), MB_ICONERROR | MB_OK); return 1; } } - + if (projectToLaunch.id.empty()) { auto file = WorkspacesData::WorkspacesFile(); @@ -177,7 +185,7 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdline, int cm } } - // update the file before launching, so WorkspacesWindowArranger and WorkspacesLauncherUI could get updated app paths + // update the file before launching, so WorkspacesWindowArranger and WorkspacesLauncherUI could get updated app paths if (updatedApps || updatedIds) { for (int i = 0; i < workspaces.size(); i++) @@ -193,7 +201,14 @@ int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, LPSTR cmdline, int cm } // launch - Launcher launcher(projectToLaunch, workspaces, cmdArgs.invokePoint); + { + Launcher launcher(projectToLaunch, workspaces, cmdArgs.invokePoint); + } + + trace.Flush(); + trace.UpdateState(false); + + Trace::Workspaces::UnregisterProvider(); Logger::trace("Finished"); CoUninitialize(); diff --git a/src/modules/Workspaces/WorkspacesLib/trace.cpp b/src/modules/Workspaces/WorkspacesLib/trace.cpp index 2802175058..5b80fb2601 100644 --- a/src/modules/Workspaces/WorkspacesLib/trace.cpp +++ b/src/modules/Workspaces/WorkspacesLib/trace.cpp @@ -13,19 +13,9 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - void Trace::Workspaces::Enable(bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "Workspaces_Enable", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -77,7 +67,7 @@ void Trace::Workspaces::Launch(bool success, errorStr += exeName + L":" + errorMessage + L"; "; } - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "Workspaces_LaunchEvent", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/Workspaces/WorkspacesLib/trace.h b/src/modules/Workspaces/WorkspacesLib/trace.h index d5569ba0b0..addb4bf245 100644 --- a/src/modules/Workspaces/WorkspacesLib/trace.h +++ b/src/modules/Workspaces/WorkspacesLib/trace.h @@ -5,13 +5,12 @@ #include #include +#include + class Trace { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; - - class Workspaces + class Workspaces : public telemetry::TraceBase { public: static void Enable(bool enabled) noexcept; diff --git a/src/modules/Workspaces/WorkspacesModuleInterface/dllmain.cpp b/src/modules/Workspaces/WorkspacesModuleInterface/dllmain.cpp index 21ccd19261..90c898fc5f 100644 --- a/src/modules/Workspaces/WorkspacesModuleInterface/dllmain.cpp +++ b/src/modules/Workspaces/WorkspacesModuleInterface/dllmain.cpp @@ -38,7 +38,7 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lp switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: - Trace::RegisterProvider(); + Trace::Workspaces::RegisterProvider(); break; case DLL_THREAD_ATTACH: @@ -46,7 +46,7 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lp break; case DLL_PROCESS_DETACH: - Trace::UnregisterProvider(); + Trace::Workspaces::UnregisterProvider(); break; } return TRUE; diff --git a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp index b9ec910c62..491a0bdf42 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp +++ b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.cpp @@ -32,10 +32,11 @@ bool isExcluded(HWND window) return check_excluded_app(window, processPath, AlwaysOnTopSettings::settings().excludedApps); } -AlwaysOnTop::AlwaysOnTop(bool useLLKH) : +AlwaysOnTop::AlwaysOnTop(bool useLLKH, DWORD mainThreadId) : SettingsObserver({SettingId::FrameEnabled, SettingId::Hotkey, SettingId::ExcludeApps}), m_hinstance(reinterpret_cast(&__ImageBase)), - m_useCentralizedLLKH(useLLKH) + m_useCentralizedLLKH(useLLKH), + m_mainThreadId(mainThreadId) { s_instance = this; DPIAware::EnableDPIAwarenessForThisProcess(); @@ -282,6 +283,7 @@ void AlwaysOnTop::RegisterLLKH() } m_hPinEvent = CreateEventW(nullptr, false, false, CommonSharedConstants::ALWAYS_ON_TOP_PIN_EVENT); + m_hTerminateEvent = CreateEventW(nullptr, false, false, CommonSharedConstants::ALWAYS_ON_TOP_TERMINATE_EVENT); if (!m_hPinEvent) { @@ -289,11 +291,20 @@ void AlwaysOnTop::RegisterLLKH() return; } - m_thread = std::thread([this]() { + if (!m_hTerminateEvent) + { + Logger::warn(L"Failed to create terminateEvent. {}", get_last_error_or_default(GetLastError())); + return; + } + + HANDLE handles[2] = { m_hPinEvent, + m_hTerminateEvent }; + + m_thread = std::thread([this, handles]() { MSG msg; while (m_running) { - DWORD dwEvt = MsgWaitForMultipleObjects(1, &m_hPinEvent, false, INFINITE, QS_ALLINPUT); + DWORD dwEvt = MsgWaitForMultipleObjects(2, handles, false, INFINITE, QS_ALLINPUT); if (!m_running) { break; @@ -307,6 +318,9 @@ void AlwaysOnTop::RegisterLLKH() } break; case WAIT_OBJECT_0 + 1: + PostThreadMessage(m_mainThreadId, WM_QUIT, 0, 0); + break; + case WAIT_OBJECT_0 + 2: if (PeekMessageW(&msg, nullptr, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); diff --git a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.h b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.h index dc41a12d67..da14d508b2 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.h +++ b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.h @@ -13,7 +13,7 @@ class AlwaysOnTop : public SettingsObserver { public: - AlwaysOnTop(bool useLLKH); + AlwaysOnTop(bool useLLKH, DWORD mainThreadId); ~AlwaysOnTop(); protected: @@ -48,6 +48,8 @@ private: HINSTANCE m_hinstance; std::map> m_topmostWindows{}; HANDLE m_hPinEvent; + HANDLE m_hTerminateEvent; + DWORD m_mainThreadId; std::thread m_thread; const bool m_useCentralizedLLKH; bool m_running = true; diff --git a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj index 95e051238e..bf3e5c6851 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj +++ b/src/modules/alwaysontop/AlwaysOnTop/AlwaysOnTop.vcxproj @@ -170,6 +170,9 @@ {1d5be09d-78c0-4fd7-af00-ae7c1af7c525} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + diff --git a/src/modules/alwaysontop/AlwaysOnTop/main.cpp b/src/modules/alwaysontop/AlwaysOnTop/main.cpp index 8b7d901c7d..417525a852 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/main.cpp +++ b/src/modules/alwaysontop/AlwaysOnTop/main.cpp @@ -1,11 +1,12 @@ #include "pch.h" +#include #include #include #include #include -#include +#include #include #include @@ -17,6 +18,9 @@ const std::wstring instanceMutexName = L"Local\\PowerToys_AlwaysOnTop_InstanceMu int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ PWSTR lpCmdLine, _In_ int nCmdShow) { + Shared::Trace::ETWTrace trace; + trace.UpdateState(true); + winrt::init_apartment(); LoggerHelpers::init_logger(moduleName, internalPath, LogSettings::alwaysOnTopLoggerName); @@ -39,10 +43,11 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, return 0; } + auto mainThreadId = GetCurrentThreadId(); + std::wstring pid = std::wstring(lpCmdLine); if (!pid.empty()) { - auto mainThreadId = GetCurrentThreadId(); ProcessWaiter::OnProcessTerminate(pid, [mainThreadId](int err) { if (err != ERROR_SUCCESS) { @@ -58,13 +63,15 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, }); } - Trace::RegisterProvider(); + Trace::AlwaysOnTop::RegisterProvider(); - AlwaysOnTop app(!pid.empty()); + AlwaysOnTop app(!pid.empty(), mainThreadId); run_message_loop(); - Trace::UnregisterProvider(); - + Trace::AlwaysOnTop::UnregisterProvider(); + + trace.Flush(); + return 0; } diff --git a/src/modules/alwaysontop/AlwaysOnTop/pch.h b/src/modules/alwaysontop/AlwaysOnTop/pch.h index 848c90fea2..d3a92e228f 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/pch.h +++ b/src/modules/alwaysontop/AlwaysOnTop/pch.h @@ -4,7 +4,6 @@ #include #include #include -#include #include #include diff --git a/src/modules/alwaysontop/AlwaysOnTop/trace.cpp b/src/modules/alwaysontop/AlwaysOnTop/trace.cpp index ae32849bbf..fb3739f4df 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/trace.cpp +++ b/src/modules/alwaysontop/AlwaysOnTop/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + // Telemetry strings should not be localized. #define LoggingProviderKey "Microsoft.PowerToys" @@ -16,19 +18,9 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - void Trace::AlwaysOnTop::Enable(bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventEnableAlwaysOnTopKey, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -38,7 +30,7 @@ void Trace::AlwaysOnTop::Enable(bool enabled) noexcept void Trace::AlwaysOnTop::PinWindow() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventPinWindowKey, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -47,7 +39,7 @@ void Trace::AlwaysOnTop::PinWindow() noexcept void Trace::AlwaysOnTop::UnpinWindow() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventUnpinWindowKey, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/alwaysontop/AlwaysOnTop/trace.h b/src/modules/alwaysontop/AlwaysOnTop/trace.h index 23c88d3c01..05ac601101 100644 --- a/src/modules/alwaysontop/AlwaysOnTop/trace.h +++ b/src/modules/alwaysontop/AlwaysOnTop/trace.h @@ -1,12 +1,11 @@ #pragma once +#include + class Trace { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; - - class AlwaysOnTop + class AlwaysOnTop : public telemetry::TraceBase { public: static void Enable(bool enabled) noexcept; diff --git a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/dllmain.cpp b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/dllmain.cpp index 83158813c6..84cf9ed949 100644 --- a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/dllmain.cpp +++ b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/dllmain.cpp @@ -35,7 +35,7 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lp switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: - Trace::RegisterProvider(); + Trace::AlwaysOnTop::RegisterProvider(); break; case DLL_THREAD_ATTACH: @@ -43,7 +43,7 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lp break; case DLL_PROCESS_DETACH: - Trace::UnregisterProvider(); + Trace::AlwaysOnTop::UnregisterProvider(); break; } return TRUE; @@ -174,6 +174,7 @@ public: app_name = L"AlwaysOnTop"; //TODO: localize app_key = NonLocalizable::ModuleKey; m_hPinEvent = CreateDefaultEvent(CommonSharedConstants::ALWAYS_ON_TOP_PIN_EVENT); + m_hTerminateEvent = CreateDefaultEvent(CommonSharedConstants::ALWAYS_ON_TOP_TERMINATE_EVENT); init_settings(); } @@ -221,6 +222,12 @@ private: Trace::AlwaysOnTop::Enable(false); } + SetEvent(m_hTerminateEvent); + + // Wait for 1.5 seconds for the process to end correctly and stop etw tracer + WaitForSingleObject(m_hProcess, 1500); + + // If process is still running, terminate it if (m_hProcess) { TerminateProcess(m_hProcess, 0); @@ -294,6 +301,7 @@ private: // Handle to event used to pin/unpin windows HANDLE m_hPinEvent; + HANDLE m_hTerminateEvent; }; extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create() diff --git a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/pch.h b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/pch.h index d70de4e4d4..9dc1f70972 100644 --- a/src/modules/alwaysontop/AlwaysOnTopModuleInterface/pch.h +++ b/src/modules/alwaysontop/AlwaysOnTopModuleInterface/pch.h @@ -5,7 +5,5 @@ #include #include #include -#include -#include #include #include diff --git a/src/modules/awake/Awake/Program.cs b/src/modules/awake/Awake/Program.cs index 2dd22db874..08995479d4 100644 --- a/src/modules/awake/Awake/Program.cs +++ b/src/modules/awake/Awake/Program.cs @@ -22,6 +22,7 @@ using Awake.Core.Native; using Awake.Properties; using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Telemetry; namespace Awake { @@ -30,6 +31,7 @@ namespace Awake private static Mutex? _mutex; private static FileSystemWatcher? _watcher; private static SettingsUtils? _settingsUtils; + private static ETWTrace _etwTrace = new ETWTrace(); private static bool _startedFromPowerToys; @@ -177,6 +179,7 @@ namespace Awake private static void Exit(string message, int exitCode) { + _etwTrace?.Dispose(); Logger.LogInfo(message); Manager.CompleteExit(exitCode); } diff --git a/src/modules/awake/AwakeModuleInterface/pch.h b/src/modules/awake/AwakeModuleInterface/pch.h index eddac0fdc1..329705f63b 100644 --- a/src/modules/awake/AwakeModuleInterface/pch.h +++ b/src/modules/awake/AwakeModuleInterface/pch.h @@ -2,6 +2,5 @@ #include #include #include -#include #include #include \ No newline at end of file diff --git a/src/modules/awake/AwakeModuleInterface/trace.cpp b/src/modules/awake/AwakeModuleInterface/trace.cpp index fe90aa2658..971084e4ab 100644 --- a/src/modules/awake/AwakeModuleInterface/trace.cpp +++ b/src/modules/awake/AwakeModuleInterface/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,20 +10,10 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() -{ - TraceLoggingUnregister(g_hProvider); -} - // Log if the user has Awake enabled or disabled void Trace::EnableAwake(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "Awake_EnableAwake", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/awake/AwakeModuleInterface/trace.h b/src/modules/awake/AwakeModuleInterface/trace.h index 40a903b98a..95a9a4e46e 100644 --- a/src/modules/awake/AwakeModuleInterface/trace.h +++ b/src/modules/awake/AwakeModuleInterface/trace.h @@ -1,11 +1,10 @@ #pragma once -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider(); - static void UnregisterProvider(); - // Log if the user has Awake enabled or disabled static void EnableAwake(const bool enabled) noexcept; }; diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.h b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.h index 96a774ab2a..8706bb4cf9 100644 --- a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.h +++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/pch.h @@ -11,6 +11,4 @@ // Windows Header Files #include -#include - #endif //PCH_H diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.cpp b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.cpp index 255c46ea99..c136909f32 100644 --- a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.cpp +++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,20 +10,10 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() -{ - TraceLoggingUnregister(g_hProvider); -} - // Log if the user has CmdNotFound enabled or disabled void Trace::EnableCmdNotFoundGpo(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "CmdNotFound_EnableCmdNotFound", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.h b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.h index 4294c510a6..6fb84c5f10 100644 --- a/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.h +++ b/src/modules/cmdNotFound/CmdNotFoundModuleInterface/trace.h @@ -1,11 +1,10 @@ #pragma once -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider(); - static void UnregisterProvider(); - // Log if the user has CmdNotFound enabled or disabled static void EnableCmdNotFoundGpo(const bool enabled) noexcept; }; diff --git a/src/modules/colorPicker/ColorPicker/dllmain.cpp b/src/modules/colorPicker/ColorPicker/dllmain.cpp index ae235a6686..cf7ebec6a0 100644 --- a/src/modules/colorPicker/ColorPicker/dllmain.cpp +++ b/src/modules/colorPicker/ColorPicker/dllmain.cpp @@ -67,6 +67,8 @@ private: // Handle to event used to invoke ColorPicker HANDLE m_hInvokeEvent; + HANDLE m_hAppTerminateEvent; + void parse_hotkey(PowerToysSettings::PowerToyValues& settings) { auto settingsObject = settings.get_raw_json(); @@ -158,6 +160,7 @@ public: LoggerHelpers::init_logger(app_key, L"ModuleInterface", "ColorPicker"); send_telemetry_event = CreateDefaultEvent(CommonSharedConstants::COLOR_PICKER_SEND_SETTINGS_TELEMETRY_EVENT); m_hInvokeEvent = CreateDefaultEvent(CommonSharedConstants::SHOW_COLOR_PICKER_SHARED_EVENT); + m_hAppTerminateEvent = CreateDefaultEvent(CommonSharedConstants::TERMINATE_COLOR_PICKER_SHARED_EVENT); init_settings(); } @@ -249,6 +252,10 @@ public: { ResetEvent(send_telemetry_event); ResetEvent(m_hInvokeEvent); + + SetEvent(m_hAppTerminateEvent); + WaitForSingleObject(m_hProcess, 1500); + TerminateProcess(m_hProcess, 1); } diff --git a/src/modules/colorPicker/ColorPicker/pch.h b/src/modules/colorPicker/ColorPicker/pch.h index eddac0fdc1..329705f63b 100644 --- a/src/modules/colorPicker/ColorPicker/pch.h +++ b/src/modules/colorPicker/ColorPicker/pch.h @@ -2,6 +2,5 @@ #include #include #include -#include #include #include \ No newline at end of file diff --git a/src/modules/colorPicker/ColorPicker/trace.cpp b/src/modules/colorPicker/ColorPicker/trace.cpp index 285cea034e..61ddb9b3e1 100644 --- a/src/modules/colorPicker/ColorPicker/trace.cpp +++ b/src/modules/colorPicker/ColorPicker/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,20 +10,10 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() -{ - TraceLoggingUnregister(g_hProvider); -} - // Log if ColorPicker is enabled or disabled void Trace::EnableColorPicker(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "ColorPicker_EnableColorPicker", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/colorPicker/ColorPicker/trace.h b/src/modules/colorPicker/ColorPicker/trace.h index b133664008..594239cf34 100644 --- a/src/modules/colorPicker/ColorPicker/trace.h +++ b/src/modules/colorPicker/ColorPicker/trace.h @@ -1,10 +1,10 @@ #pragma once -class Trace + +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider(); - static void UnregisterProvider(); - // Log if ColorPicker is enabled or disabled static void EnableColorPicker(const bool enabled) noexcept; diff --git a/src/modules/colorPicker/ColorPickerUI/App.xaml.cs b/src/modules/colorPicker/ColorPickerUI/App.xaml.cs index 98646583be..0621fbc4b9 100644 --- a/src/modules/colorPicker/ColorPickerUI/App.xaml.cs +++ b/src/modules/colorPicker/ColorPickerUI/App.xaml.cs @@ -10,6 +10,7 @@ using System.Windows; using ColorPicker.Mouse; using ManagedCommon; +using Microsoft.PowerToys.Telemetry; namespace ColorPickerUI { @@ -18,6 +19,8 @@ namespace ColorPickerUI /// public partial class App : Application, IDisposable { + public ETWTrace EtwTrace { get; private set; } = new ETWTrace(); + private Mutex _instanceMutex; private static string[] _args; private int _powerToysRunnerPid; @@ -96,6 +99,7 @@ namespace ColorPickerUI if (disposing) { _instanceMutex?.Dispose(); + EtwTrace?.Dispose(); } disposedValue = true; diff --git a/src/modules/colorPicker/ColorPickerUI/ColorEditorWindow.xaml.cs b/src/modules/colorPicker/ColorPickerUI/ColorEditorWindow.xaml.cs index c135f82362..eb94d86d1e 100644 --- a/src/modules/colorPicker/ColorPickerUI/ColorEditorWindow.xaml.cs +++ b/src/modules/colorPicker/ColorPickerUI/ColorEditorWindow.xaml.cs @@ -3,7 +3,6 @@ // See the LICENSE file in the project root for more information. using System; -using System.Windows; using ColorPicker.Helpers; using Common.UI; diff --git a/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs b/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs index 63772b7963..518f253456 100644 --- a/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs +++ b/src/modules/colorPicker/ColorPickerUI/ViewModels/MainViewModel.cs @@ -57,6 +57,12 @@ namespace ColorPicker.ViewModels _userSettings = userSettings; _keyboardMonitor = keyboardMonitor; + NativeEventWaiter.WaitForEventLoop( + Constants.TerminateColorPickerSharedEvent(), + Application.Current.Shutdown, + Application.Current.Dispatcher, + exitToken); + NativeEventWaiter.WaitForEventLoop( Constants.ShowColorPickerSharedEvent(), _appStateHandler.StartUserSession, diff --git a/src/modules/fancyzones/FancyZones/FancyZones.vcxproj b/src/modules/fancyzones/FancyZones/FancyZones.vcxproj index d61eeb60fd..b54ee19e34 100644 --- a/src/modules/fancyzones/FancyZones/FancyZones.vcxproj +++ b/src/modules/fancyzones/FancyZones/FancyZones.vcxproj @@ -146,6 +146,9 @@ {6955446d-23f7-4023-9bb3-8657f904af99} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + {f9c68edf-ac74-4b77-9af1-005d9c9f6a99} diff --git a/src/modules/fancyzones/FancyZones/FancyZonesApp.cpp b/src/modules/fancyzones/FancyZones/FancyZonesApp.cpp index afa0bf610c..97faec65d5 100644 --- a/src/modules/fancyzones/FancyZones/FancyZonesApp.cpp +++ b/src/modules/fancyzones/FancyZones/FancyZonesApp.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -20,6 +21,14 @@ FancyZonesApp::FancyZonesApp(const std::wstring& appName, const std::wstring& ap InitializeWinhookEventIds(); m_app = MakeFancyZones(reinterpret_cast(&__ImageBase), std::bind(&FancyZonesApp::DisableModule, this)); + m_mainThreadId = GetCurrentThreadId(); + m_exitEventWaiter = EventWaiter(CommonSharedConstants::FZE_EXIT_EVENT, [&](int err) { + if (err == ERROR_SUCCESS) + { + DisableModule(); + } + }); + InitHooks(); s_instance = this; @@ -111,7 +120,7 @@ void FancyZonesApp::InitHooks() void FancyZonesApp::DisableModule() noexcept { - PostQuitMessage(0); + PostThreadMessage(m_mainThreadId, WM_QUIT, 0, 0); } void FancyZonesApp::HandleWinHookEvent(WinHookEvent* data) noexcept diff --git a/src/modules/fancyzones/FancyZones/FancyZonesApp.h b/src/modules/fancyzones/FancyZones/FancyZonesApp.h index b66ba5b540..44c6a306a9 100644 --- a/src/modules/fancyzones/FancyZones/FancyZonesApp.h +++ b/src/modules/fancyzones/FancyZones/FancyZonesApp.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include @@ -15,11 +16,14 @@ public: private: static inline FancyZonesApp* s_instance = nullptr; static inline HHOOK s_llKeyboardHook = nullptr; - + winrt::com_ptr m_app; HWINEVENTHOOK m_objectLocationWinEventHook = nullptr; std::vector m_staticWinEventHooks; + EventWaiter m_exitEventWaiter; + DWORD m_mainThreadId; + void DisableModule() noexcept; void InitHooks(); diff --git a/src/modules/fancyzones/FancyZones/main.cpp b/src/modules/fancyzones/FancyZones/main.cpp index 3112df7fbb..4001673ea3 100644 --- a/src/modules/fancyzones/FancyZones/main.cpp +++ b/src/modules/fancyzones/FancyZones/main.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include #include @@ -25,6 +27,9 @@ const std::wstring instanceMutexName = L"Local\\PowerToys_FancyZones_InstanceMut int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ PWSTR lpCmdLine, _In_ int nCmdShow) { + Shared::Trace::ETWTrace trace{}; + trace.UpdateState(true); + winrt::init_apartment(); LoggerHelpers::init_logger(moduleName, internalPath, LogSettings::fancyZonesLoggerName); @@ -82,6 +87,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, run_message_loop(); Trace::UnregisterProvider(); - + + trace.Flush(); + return 0; } diff --git a/src/modules/fancyzones/FancyZones/pch.h b/src/modules/fancyzones/FancyZones/pch.h index 472d051e25..291159e4e8 100644 --- a/src/modules/fancyzones/FancyZones/pch.h +++ b/src/modules/fancyzones/FancyZones/pch.h @@ -1,7 +1,6 @@ #pragma once #define WIN32_LEAN_AND_MEAN #include -#include #include #include #include diff --git a/src/modules/fancyzones/FancyZonesLib/pch.h b/src/modules/fancyzones/FancyZonesLib/pch.h index 70a402b0fa..d201dabd0c 100644 --- a/src/modules/fancyzones/FancyZonesLib/pch.h +++ b/src/modules/fancyzones/FancyZonesLib/pch.h @@ -8,11 +8,9 @@ #include #include #include -#include #include #include #include -#include #include #include #include diff --git a/src/modules/fancyzones/FancyZonesLib/trace.cpp b/src/modules/fancyzones/FancyZonesLib/trace.cpp index 4c6024fe5a..69e341197a 100644 --- a/src/modules/fancyzones/FancyZonesLib/trace.cpp +++ b/src/modules/fancyzones/FancyZonesLib/trace.cpp @@ -10,6 +10,8 @@ #include "FancyZonesLib/FancyZonesDataTypes.h" #include "FancyZonesLib/util.h" +#include + // Telemetry strings should not be localized. #define LoggingProviderKey "Microsoft.PowerToys" @@ -108,19 +110,9 @@ ZoneSetInfo GetZoneSetInfo(_In_opt_ Layout* layout, const LayoutAssignedWindows& return info; } -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - void Trace::FancyZones::EnableFancyZones(bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventEnableFancyZonesKey, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -130,7 +122,7 @@ void Trace::FancyZones::EnableFancyZones(bool enabled) noexcept void Trace::FancyZones::OnKeyDown(DWORD vkCode, bool win, bool control, bool inMoveSize) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventKeyDownKey, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -211,7 +203,7 @@ void Trace::FancyZones::DataChanged() noexcept activeZoneSetInfo += L", custom zone data was deleted"; } } - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventZoneSettingsChangedKey, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -226,7 +218,7 @@ void Trace::FancyZones::DataChanged() noexcept void Trace::FancyZones::EditorLaunched(int value) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventEditorLaunchKey, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -237,7 +229,7 @@ void Trace::FancyZones::EditorLaunched(int value) noexcept // Log if an error occurs in FZ void Trace::FancyZones::Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "FancyZones_Error", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -249,7 +241,7 @@ void Trace::FancyZones::Error(const DWORD errorCode, std::wstring errorMessage, void Trace::FancyZones::QuickLayoutSwitched(bool shortcutUsed) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventQuickLayoutSwitchKey, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -260,7 +252,7 @@ void Trace::FancyZones::QuickLayoutSwitched(bool shortcutUsed) noexcept void Trace::FancyZones::SnapNewWindowIntoZone(Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept { auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventSnapNewWindowIntoZone, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -273,7 +265,7 @@ void Trace::FancyZones::SnapNewWindowIntoZone(Layout* activeLayout, const Layout void Trace::FancyZones::KeyboardSnapWindowToZone(Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept { auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventKeyboardSnapWindowToZone, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -299,7 +291,7 @@ void Trace::SettingsTelemetry(const Settings& settings) noexcept auto nextTabHotkeyStr = HotKeyToString(settings.nextTabHotkey); auto prevTabHotkeyStr = HotKeyToString(settings.prevTabHotkey); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventSettingsKey, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -337,7 +329,7 @@ void Trace::SettingsTelemetry(const Settings& settings) noexcept void Trace::VirtualDesktopChanged() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventDesktopChangedKey, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -346,7 +338,7 @@ void Trace::VirtualDesktopChanged() noexcept void Trace::WorkArea::KeyUp(WPARAM wParam) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventWorkAreaKeyUpKey, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -357,7 +349,7 @@ void Trace::WorkArea::KeyUp(WPARAM wParam) noexcept void Trace::WorkArea::MoveOrResizeStarted(_In_opt_ Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept { auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventMoveOrResizeStartedKey, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -370,7 +362,7 @@ void Trace::WorkArea::MoveOrResizeStarted(_In_opt_ Layout* activeLayout, const L void Trace::WorkArea::MoveOrResizeEnd(_In_opt_ Layout* activeLayout, const LayoutAssignedWindows& layoutWindows) noexcept { auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventMoveOrResizeEndedKey, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -383,7 +375,7 @@ void Trace::WorkArea::MoveOrResizeEnd(_In_opt_ Layout* activeLayout, const Layou void Trace::WorkArea::CycleActiveZoneSet(_In_opt_ Layout* activeLayout, const LayoutAssignedWindows& layoutWindows, InputMode mode) noexcept { auto const zoneInfo = GetZoneSetInfo(activeLayout, layoutWindows); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, EventCycleActiveZoneSetKey, ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/fancyzones/FancyZonesLib/trace.h b/src/modules/fancyzones/FancyZonesLib/trace.h index 3bef5cc8c1..db4206fd1f 100644 --- a/src/modules/fancyzones/FancyZonesLib/trace.h +++ b/src/modules/fancyzones/FancyZonesLib/trace.h @@ -1,16 +1,15 @@ #pragma once +#include + struct Settings; class Layout; class LayoutAssignedWindows; -class Trace +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; - - class FancyZones + class FancyZones : public telemetry::TraceBase { public: static void EnableFancyZones(bool enabled) noexcept; @@ -26,7 +25,7 @@ public: static void SettingsTelemetry(const Settings& settings) noexcept; static void VirtualDesktopChanged() noexcept; - class WorkArea + class WorkArea : public telemetry::TraceBase { public: enum class InputMode diff --git a/src/modules/fancyzones/FancyZonesModuleInterface/dllmain.cpp b/src/modules/fancyzones/FancyZonesModuleInterface/dllmain.cpp index 40d0aea08f..f138c43200 100644 --- a/src/modules/fancyzones/FancyZonesModuleInterface/dllmain.cpp +++ b/src/modules/fancyzones/FancyZonesModuleInterface/dllmain.cpp @@ -205,8 +205,9 @@ private: if (m_hProcess) { - TerminateProcess(m_hProcess, 0); SendFZECloseEvent(); + WaitForSingleObject(m_hProcess, 1500); + TerminateProcess(m_hProcess, 0); m_hProcess = nullptr; } } diff --git a/src/modules/fancyzones/FancyZonesModuleInterface/pch.h b/src/modules/fancyzones/FancyZonesModuleInterface/pch.h index d70de4e4d4..9dc1f70972 100644 --- a/src/modules/fancyzones/FancyZonesModuleInterface/pch.h +++ b/src/modules/fancyzones/FancyZonesModuleInterface/pch.h @@ -5,7 +5,5 @@ #include #include #include -#include -#include #include #include diff --git a/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj b/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj index f1c2e8975e..72f540c0dc 100644 --- a/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj +++ b/src/modules/fancyzones/FancyZonesTests/UnitTests/UnitTests.vcxproj @@ -1,5 +1,6 @@ - + 16.0 @@ -83,6 +84,7 @@ + diff --git a/src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj b/src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj index 913e885fa5..e3303f759b 100644 --- a/src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj +++ b/src/modules/imageresizer/ImageResizerContextMenu/ImageResizerContextMenu.vcxproj @@ -131,6 +131,9 @@ MakeAppx.exe pack /d . /p $(OutDir)ImageResizerContextMenuPackage.msix /nv {6955446d-23f7-4023-9bb3-8657f904af99} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + {18b3db45-4ffe-4d01-97d6-5223feee1853} diff --git a/src/modules/imageresizer/ImageResizerContextMenu/dllmain.cpp b/src/modules/imageresizer/ImageResizerContextMenu/dllmain.cpp index 347475fd90..31335d59a3 100644 --- a/src/modules/imageresizer/ImageResizerContextMenu/dllmain.cpp +++ b/src/modules/imageresizer/ImageResizerContextMenu/dllmain.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -20,6 +21,7 @@ using namespace Microsoft::WRL; HINSTANCE g_hInst = 0; +Shared::Trace::ETWTrace trace(L"ImageResizerContextMenu"); #define BUFSIZE 4096 * 4 @@ -134,6 +136,7 @@ public: IFACEMETHODIMP Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept try { + trace.UpdateState(true); Trace::Invoked(); HRESULT hr = S_OK; @@ -145,6 +148,9 @@ public: Trace::InvokedRet(hr); + trace.UpdateState(false); + trace.Flush(); + return hr; } CATCH_RETURN(); diff --git a/src/modules/imageresizer/ImageResizerLib/pch.h b/src/modules/imageresizer/ImageResizerLib/pch.h index dcca9b830b..3664d7390e 100644 --- a/src/modules/imageresizer/ImageResizerLib/pch.h +++ b/src/modules/imageresizer/ImageResizerLib/pch.h @@ -13,6 +13,4 @@ #include #include -#include - #endif //PCH_H diff --git a/src/modules/imageresizer/ImageResizerLib/trace.cpp b/src/modules/imageresizer/ImageResizerLib/trace.cpp index 352af3676a..1c0caef2c3 100644 --- a/src/modules/imageresizer/ImageResizerLib/trace.cpp +++ b/src/modules/imageresizer/ImageResizerLib/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,19 +10,9 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - void Trace::EnableImageResizer(_In_ bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "ImageResizer_EnableImageResizer", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -28,10 +20,9 @@ void Trace::EnableImageResizer(_In_ bool enabled) noexcept TraceLoggingBoolean(enabled, "Enabled")); } - void Trace::Invoked() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "ImageResizer_Invoked", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -40,7 +31,7 @@ void Trace::Invoked() noexcept void Trace::InvokedRet(_In_ HRESULT hr) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "ImageResizer_InvokedRet", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -50,7 +41,7 @@ void Trace::InvokedRet(_In_ HRESULT hr) noexcept void Trace::QueryContextMenuError(_In_ HRESULT hr) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "ImageResizer_QueryContextMenuError", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/imageresizer/ImageResizerLib/trace.h b/src/modules/imageresizer/ImageResizerLib/trace.h index 9ff1e49f4e..a337e70b03 100644 --- a/src/modules/imageresizer/ImageResizerLib/trace.h +++ b/src/modules/imageresizer/ImageResizerLib/trace.h @@ -1,10 +1,10 @@ #pragma once -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; static void EnableImageResizer(_In_ bool enabled) noexcept; static void Invoked() noexcept; static void InvokedRet(_In_ HRESULT hr) noexcept; diff --git a/src/modules/imageresizer/dll/ContextMenuHandler.cpp b/src/modules/imageresizer/dll/ContextMenuHandler.cpp index dd86eed5fb..32eced1608 100644 --- a/src/modules/imageresizer/dll/ContextMenuHandler.cpp +++ b/src/modules/imageresizer/dll/ContextMenuHandler.cpp @@ -161,8 +161,13 @@ HRESULT CContextMenuHandler::QueryContextMenu(_In_ HMENU hmenu, UINT indexMenu, if (!InsertMenuItem(hmenu, indexMenu, TRUE, &mii)) { + m_etwTrace.UpdateState(true); + hr = HRESULT_FROM_WIN32(GetLastError()); Trace::QueryContextMenuError(hr); + + m_etwTrace.Flush(); + m_etwTrace.UpdateState(false); } else { @@ -193,6 +198,8 @@ HRESULT CContextMenuHandler::GetCommandString(UINT_PTR idCmd, UINT uType, _In_ U HRESULT CContextMenuHandler::InvokeCommand(_In_ CMINVOKECOMMANDINFO* pici) { + m_etwTrace.UpdateState(true); + BOOL fUnicode = FALSE; Trace::Invoked(); HRESULT hr = E_FAIL; @@ -216,6 +223,10 @@ HRESULT CContextMenuHandler::InvokeCommand(_In_ CMINVOKECOMMANDINFO* pici) hr = ResizePictures(pici, nullptr); } Trace::InvokedRet(hr); + + m_etwTrace.Flush(); + m_etwTrace.UpdateState(false); + return hr; } @@ -427,8 +438,14 @@ HRESULT __stdcall CContextMenuHandler::EnumSubCommands(IEnumExplorerCommand** pp // psiItemArray contains the list of files that have been selected when the context menu entry is invoked HRESULT __stdcall CContextMenuHandler::Invoke(IShellItemArray* psiItemArray, IBindCtx* /*pbc*/) { + m_etwTrace.UpdateState(true); + Trace::Invoked(); HRESULT hr = ResizePictures(nullptr, psiItemArray); Trace::InvokedRet(hr); + + m_etwTrace.Flush(); + m_etwTrace.UpdateState(false); + return hr; } diff --git a/src/modules/imageresizer/dll/ContextMenuHandler.h b/src/modules/imageresizer/dll/ContextMenuHandler.h index ba1465df88..abcadfa972 100644 --- a/src/modules/imageresizer/dll/ContextMenuHandler.h +++ b/src/modules/imageresizer/dll/ContextMenuHandler.h @@ -5,6 +5,7 @@ #include "pch.h" #include "Generated Files/resource.h" #include "ImageResizerExt_i.h" +#include #if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA) #error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms." @@ -53,6 +54,7 @@ private: HBITMAP m_hbmpIcon = nullptr; std::wstring context_menu_caption; std::wstring context_menu_caption_here; + Shared::Trace::ETWTrace m_etwTrace{ L"ImageResizerExt" }; }; OBJECT_ENTRY_AUTO(__uuidof(ContextMenuHandler), CContextMenuHandler) \ No newline at end of file diff --git a/src/modules/imageresizer/dll/ImageResizerExt.vcxproj b/src/modules/imageresizer/dll/ImageResizerExt.vcxproj index 90bcd8e357..df038e2c43 100644 --- a/src/modules/imageresizer/dll/ImageResizerExt.vcxproj +++ b/src/modules/imageresizer/dll/ImageResizerExt.vcxproj @@ -80,12 +80,14 @@ false - + + false - + + Create @@ -120,6 +122,9 @@ {6955446d-23f7-4023-9bb3-8657f904af99} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + {98537082-0fdb-40de-abd8-0dc5a4269bab} diff --git a/src/modules/imageresizer/dll/pch.h b/src/modules/imageresizer/dll/pch.h index 4fef14e85a..a6dd9999a9 100644 --- a/src/modules/imageresizer/dll/pch.h +++ b/src/modules/imageresizer/dll/pch.h @@ -22,4 +22,3 @@ #include #include -#include diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp index 260e4c1f5c..5b18b791f0 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.cpp @@ -80,7 +80,6 @@ int APIENTRY wWinMain(_In_ HINSTANCE hInstance, std::wstring keysForShortcutToEdit = L""; std::wstring action = L""; - // do some parsing of the cmdline arg to see if we need to behave different // like, single edit mode, or "delete" mode. // These extra args are from "OpenEditor" in the KeyboardManagerViewModel diff --git a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj index 149ec0201a..1cc89c157b 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj +++ b/src/modules/keyboardmanager/KeyboardManagerEditor/KeyboardManagerEditor.vcxproj @@ -132,6 +132,9 @@ {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + {98537082-0fdb-40de-abd8-0dc5a4269bab} diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp index 6bf0994e3f..3597107808 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditKeyboardWindow.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include #include @@ -426,11 +428,15 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMan void CreateEditKeyboardWindow(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration) { + Shared::Trace::ETWTrace trace; + trace.UpdateState(true); + // Move implementation into the separate method so resources get destroyed correctly CreateEditKeyboardWindowImpl(hInst, keyboardManagerState, mappingConfiguration); // Calling ClearXamlIslands() outside of the message loop is not enough to prevent // Microsoft.UI.XAML.dll from crashing during deinitialization, see https://github.com/microsoft/PowerToys/issues/10906 + trace.Flush(); Logger::trace("Terminating process {}", GetCurrentProcessId()); Logger::flush(); TerminateProcess(GetCurrentProcess(), 0); diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp index c85c36cde2..0a2f044765 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/EditShortcutsWindow.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include "KeyboardManagerState.h" #include "Dialog.h" #include "KeyDropDownControl.h" @@ -447,11 +449,15 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa void CreateEditShortcutsWindow(HINSTANCE hInst, KBMEditor::KeyboardManagerState& keyboardManagerState, MappingConfiguration& mappingConfiguration, std::wstring keysForShortcutToEdit, std::wstring action) { + Shared::Trace::ETWTrace trace; + trace.UpdateState(true); + // Move implementation into the separate method so resources get destroyed correctly CreateEditShortcutsWindowImpl(hInst, keyboardManagerState, mappingConfiguration, keysForShortcutToEdit, action); // Calling ClearXamlIslands() outside of the message loop is not enough to prevent // Microsoft.UI.XAML.dll from crashing during deinitialization, see https://github.com/microsoft/PowerToys/issues/10906 + trace.Flush(); Logger::trace("Terminating process {}", GetCurrentProcessId()); Logger::flush(); TerminateProcess(GetCurrentProcess(), 0); diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/pch.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/pch.h index ffc3ff39aa..316ab36e42 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/pch.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/pch.h @@ -29,8 +29,6 @@ #include #include -#include - #include //#include diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/trace.cpp b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/trace.cpp index 290b11cc41..54ec7539b1 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/trace.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,20 +10,10 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - // Log number of key remaps when the user uses Edit Keyboard and saves settings void Trace::KeyRemapCount(const DWORD keyToKeyCount, const DWORD keyToShortcutCount, const DWORD keyToTextCount) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_KeyRemapCount", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -35,7 +27,7 @@ void Trace::KeyRemapCount(const DWORD keyToKeyCount, const DWORD keyToShortcutCo // Log number of os level shortcut remaps when the user uses Edit Shortcuts and saves settings void Trace::OSLevelShortcutRemapCount(const DWORD shortcutToShortcutCount, const DWORD shortcutToKeyCount) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_OSLevelShortcutRemapCount", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -48,7 +40,7 @@ void Trace::OSLevelShortcutRemapCount(const DWORD shortcutToShortcutCount, const // Log number of app specific shortcut remaps when the user uses Edit Shortcuts and saves settings void Trace::AppSpecificShortcutRemapCount(const DWORD shortcutToShortcutCount, const DWORD shortcutToKeyCount) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_AppSpecificShortcutRemapCount", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -61,7 +53,7 @@ void Trace::AppSpecificShortcutRemapCount(const DWORD shortcutToShortcutCount, c // Log if an error occurs in KBM void Trace::Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_Error", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/trace.h b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/trace.h index b91f5b625b..9c219bf8e8 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/trace.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorLibrary/trace.h @@ -1,11 +1,10 @@ #pragma once -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; - // Log number of key remaps when the user uses Edit Keyboard and saves settings static void KeyRemapCount(const DWORD keyToKeyCount, const DWORD keyToShortcutCount, const DWORD keyToTextCount) noexcept; diff --git a/src/modules/keyboardmanager/KeyboardManagerEditorTest/pch.h b/src/modules/keyboardmanager/KeyboardManagerEditorTest/pch.h index 6364eacd48..4cf0be54c2 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEditorTest/pch.h +++ b/src/modules/keyboardmanager/KeyboardManagerEditorTest/pch.h @@ -1,7 +1,6 @@ #pragma once #define WIN32_LEAN_AND_MEAN #include -#include #include #include #include diff --git a/src/modules/keyboardmanager/KeyboardManagerEngine/KeyboardManagerEngine.vcxproj b/src/modules/keyboardmanager/KeyboardManagerEngine/KeyboardManagerEngine.vcxproj index 55d97e0b00..fd7b4a97c3 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngine/KeyboardManagerEngine.vcxproj +++ b/src/modules/keyboardmanager/KeyboardManagerEngine/KeyboardManagerEngine.vcxproj @@ -67,6 +67,9 @@ {6955446d-23f7-4023-9bb3-8657f904af99} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + {e496b7fc-1e99-4bab-849b-0e8367040b02} diff --git a/src/modules/keyboardmanager/KeyboardManagerEngine/main.cpp b/src/modules/keyboardmanager/KeyboardManagerEngine/main.cpp index 31417e9c23..5969ed6cfd 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngine/main.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEngine/main.cpp @@ -1,4 +1,6 @@ #include "pch.h" +#include +#include #include #include #include @@ -8,6 +10,7 @@ #include #include #include +#include const std::wstring instanceMutexName = L"Local\\PowerToys_KBMEngine_InstanceMutex"; @@ -19,6 +22,9 @@ int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, winrt::init_apartment(); LoggerHelpers::init_logger(KeyboardManagerConstants::ModuleName, L"Engine", LogSettings::keyboardManagerLoggerName); + Shared::Trace::ETWTrace trace; + trace.UpdateState(true); + if (powertoys_gpo::getConfiguredKeyboardManagerEnabledValue() == powertoys_gpo::gpo_rule_configured_disabled) { Logger::warn(L"Tried to start with a GPO policy setting the utility to always be disabled. Please contact your systems administrator."); @@ -42,9 +48,15 @@ int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, Trace::RegisterProvider(); std::wstring pid = std::wstring(lpCmdLine); + + auto mainThreadId = GetCurrentThreadId(); + + EventWaiter ev = EventWaiter(CommonSharedConstants::TERMINATE_KBM_SHARED_EVENT, [&](int) { + PostThreadMessage(mainThreadId, WM_QUIT, 0, 0); + }); + if (!pid.empty()) { - auto mainThreadId = GetCurrentThreadId(); ProcessWaiter::OnProcessTerminate(pid, [mainThreadId](int err) { if (err != ERROR_SUCCESS) { @@ -73,5 +85,7 @@ int WINAPI wWinMain(_In_ HINSTANCE /*hInstance*/, kbm.StopLowlevelKeyboardHook(); Trace::UnregisterProvider(); + trace.Flush(); + return 0; } diff --git a/src/modules/keyboardmanager/KeyboardManagerEngine/pch.h b/src/modules/keyboardmanager/KeyboardManagerEngine/pch.h index 472d051e25..291159e4e8 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngine/pch.h +++ b/src/modules/keyboardmanager/KeyboardManagerEngine/pch.h @@ -1,7 +1,6 @@ #pragma once #define WIN32_LEAN_AND_MEAN #include -#include #include #include #include diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/pch.h b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/pch.h index ded05a85a7..817bab9a85 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/pch.h +++ b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/pch.h @@ -1,7 +1,6 @@ #pragma once #define WIN32_LEAN_AND_MEAN #include -#include #include #include #include diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/trace.cpp b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/trace.cpp index fd11255283..ab3e49cb87 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/trace.cpp +++ b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/trace.cpp @@ -2,6 +2,8 @@ #include "trace.h" #include +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -9,74 +11,64 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - // Log if a key to key remap has been invoked today. void Trace::DailyKeyToKeyRemapInvoked() noexcept { - TraceLoggingWrite( - g_hProvider, - "KeyboardManager_DailyKeyToKeyRemapInvoked", - ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), - TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); + TraceLoggingWriteWrapper( + g_hProvider, + "KeyboardManager_DailyKeyToKeyRemapInvoked", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); } // Log if a key to shortcut remap has been invoked today. void Trace::DailyKeyToShortcutRemapInvoked() noexcept { - TraceLoggingWrite( - g_hProvider, - "KeyboardManager_DailyKeyToShortcutRemapInvoked", - ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), - TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); + TraceLoggingWriteWrapper( + g_hProvider, + "KeyboardManager_DailyKeyToShortcutRemapInvoked", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); } // Log if a shortcut to key remap has been invoked today. void Trace::DailyShortcutToKeyRemapInvoked() noexcept { - TraceLoggingWrite( - g_hProvider, - "KeyboardManager_DailyShortcutToKeyRemapInvoked", - ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), - TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); + TraceLoggingWriteWrapper( + g_hProvider, + "KeyboardManager_DailyShortcutToKeyRemapInvoked", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); } // Log if a shortcut to shortcut remap has been invoked today. void Trace::DailyShortcutToShortcutRemapInvoked() noexcept { - TraceLoggingWrite( - g_hProvider, - "KeyboardManager_DailyShortcutToShortcutRemapInvoked", - ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), - TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); + TraceLoggingWriteWrapper( + g_hProvider, + "KeyboardManager_DailyShortcutToShortcutRemapInvoked", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); } // Log if an app specific shortcut to key remap has been invoked today. void Trace::DailyAppSpecificShortcutToKeyRemapInvoked() noexcept { - TraceLoggingWrite( - g_hProvider, - "KeyboardManager_DailyAppSpecificShortcutToKeyRemapInvoked", - ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), - TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); + TraceLoggingWriteWrapper( + g_hProvider, + "KeyboardManager_DailyAppSpecificShortcutToKeyRemapInvoked", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); } // Log if an app specific shortcut to shortcut remap has been invoked today. void Trace::DailyAppSpecificShortcutToShortcutRemapInvoked() noexcept { - TraceLoggingWrite( - g_hProvider, - "KeyboardManager_DailyAppSpecificShortcutToShortcutRemapInvoked", - ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), - TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); + TraceLoggingWriteWrapper( + g_hProvider, + "KeyboardManager_DailyAppSpecificShortcutToShortcutRemapInvoked", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); } // Log if a key remap has been invoked (not being used currently, due to being garrulous) @@ -84,7 +76,7 @@ void Trace::KeyRemapInvoked(bool isKeyToKey) noexcept { if (isKeyToKey) { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_KeyToKeyRemapInvoked", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -92,7 +84,7 @@ void Trace::KeyRemapInvoked(bool isKeyToKey) noexcept } else { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_KeyToShortcutRemapInvoked", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -107,7 +99,7 @@ void Trace::ShortcutRemapInvoked(bool isShortcutToShortcut, bool isAppSpecific) { if (isShortcutToShortcut) { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_AppSpecificShortcutToShortcutRemapInvoked", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -115,7 +107,7 @@ void Trace::ShortcutRemapInvoked(bool isShortcutToShortcut, bool isAppSpecific) } else { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_AppSpecificShortcutToKeyRemapInvoked", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -126,7 +118,7 @@ void Trace::ShortcutRemapInvoked(bool isShortcutToShortcut, bool isAppSpecific) { if (isShortcutToShortcut) { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_OSLevelShortcutToShortcutRemapInvoked", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -134,7 +126,7 @@ void Trace::ShortcutRemapInvoked(bool isShortcutToShortcut, bool isAppSpecific) } else { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_OSLevelShortcutToKeyRemapInvoked", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -144,7 +136,7 @@ void Trace::ShortcutRemapInvoked(bool isShortcutToShortcut, bool isAppSpecific) } // Function to return a human readable string for the shortcut -std::wstring GetShortcutHumanReadableString(Shortcut const & shortcut, LayoutMap& keyboardMap) +std::wstring GetShortcutHumanReadableString(Shortcut const& shortcut, LayoutMap& keyboardMap) { std::wstring humanReadableShortcut = L""; if (shortcut.winKey != ModifierKey::Disabled) @@ -184,7 +176,7 @@ void Trace::SendKeyAndShortcutRemapLoadedConfiguration(State& remappings) noexce if (keyRemap.second.index() == 0) // 0 - Remapping to key { DWORD keyRemappedTo = std::get(keyRemap.second); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_KeyRemapConfigurationLoaded", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -192,13 +184,12 @@ void Trace::SendKeyAndShortcutRemapLoadedConfiguration(State& remappings) noexce TraceLoggingInt64(keyRemap.first, "KeyRemapFrom"), TraceLoggingInt64(keyRemappedTo, "KeyRemapTo"), TraceLoggingWideString(keyboardMap.GetKeyName(keyRemap.first).c_str(), "HumanRemapFrom"), - TraceLoggingWideString(keyboardMap.GetKeyName(keyRemappedTo).c_str(), "HumanRemapTo") - ); + TraceLoggingWideString(keyboardMap.GetKeyName(keyRemappedTo).c_str(), "HumanRemapTo")); } else if (keyRemap.second.index() == 1) // 1 - Remapping to shortcut { Shortcut shortcutRemappedTo = std::get(keyRemap.second); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_KeyRemapConfigurationLoaded", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -210,8 +201,7 @@ void Trace::SendKeyAndShortcutRemapLoadedConfiguration(State& remappings) noexce TraceLoggingInt8(static_cast(shortcutRemappedTo.altKey), "ModifierRemapToAlt"), TraceLoggingInt8(static_cast(shortcutRemappedTo.shiftKey), "ModifierRemapToShift"), TraceLoggingWideString(keyboardMap.GetKeyName(keyRemap.first).c_str(), "HumanRemapFrom"), - TraceLoggingWideString(GetShortcutHumanReadableString(shortcutRemappedTo, keyboardMap).c_str(), "HumanRemapTo") - ); + TraceLoggingWideString(GetShortcutHumanReadableString(shortcutRemappedTo, keyboardMap).c_str(), "HumanRemapTo")); } } @@ -221,7 +211,7 @@ void Trace::SendKeyAndShortcutRemapLoadedConfiguration(State& remappings) noexce if (shortcutRemap.second.targetShortcut.index() == 0) // 0 - Remapping to key { DWORD keyRemappedTo = std::get(shortcutRemap.second.targetShortcut); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_ShortcutRemapConfigurationLoaded", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -245,7 +235,7 @@ void Trace::SendKeyAndShortcutRemapLoadedConfiguration(State& remappings) noexce // Don't include Start app or Open URI mappings in this telemetry. continue; } - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_ShortcutRemapConfigurationLoaded", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -263,8 +253,7 @@ void Trace::SendKeyAndShortcutRemapLoadedConfiguration(State& remappings) noexce TraceLoggingInt8(static_cast(shortcutRemappedTo.altKey), "ModifierRemapToAlt"), TraceLoggingInt8(static_cast(shortcutRemappedTo.shiftKey), "ModifierRemapToShift"), TraceLoggingWideString(GetShortcutHumanReadableString(shortcutRemappedFrom, keyboardMap).c_str(), "HumanRemapFrom"), - TraceLoggingWideString(GetShortcutHumanReadableString(shortcutRemappedTo, keyboardMap).c_str(), "HumanRemapTo") - ); + TraceLoggingWideString(GetShortcutHumanReadableString(shortcutRemappedTo, keyboardMap).c_str(), "HumanRemapTo")); } } @@ -277,7 +266,7 @@ void Trace::SendKeyAndShortcutRemapLoadedConfiguration(State& remappings) noexce if (shortcutRemap.second.targetShortcut.index() == 0) // 0 - Remapping to key { DWORD keyRemappedTo = std::get(shortcutRemap.second.targetShortcut); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_AppSpecificShortcutRemapConfigurationLoaded", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -292,8 +281,7 @@ void Trace::SendKeyAndShortcutRemapLoadedConfiguration(State& remappings) noexce TraceLoggingInt64(keyRemappedTo, "KeyRemapTo"), TraceLoggingWideString(GetShortcutHumanReadableString(shortcutRemappedFrom, keyboardMap).c_str(), "HumanRemapFrom"), TraceLoggingWideString(keyboardMap.GetKeyName(keyRemappedTo).c_str(), "HumanRemapTo"), - TraceLoggingWideString(appName.c_str(), "TargetApp") - ); + TraceLoggingWideString(appName.c_str(), "TargetApp")); } else if (shortcutRemap.second.targetShortcut.index() == 1) // 1 - Remapping to shortcut { @@ -303,7 +291,7 @@ void Trace::SendKeyAndShortcutRemapLoadedConfiguration(State& remappings) noexce // Don't include Start app or Open URI mappings in this telemetry. continue; } - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_AppSpecificShortcutRemapConfigurationLoaded", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -322,8 +310,7 @@ void Trace::SendKeyAndShortcutRemapLoadedConfiguration(State& remappings) noexce TraceLoggingInt8(static_cast(shortcutRemappedTo.shiftKey), "ModifierRemapToShift"), TraceLoggingWideString(GetShortcutHumanReadableString(shortcutRemappedFrom, keyboardMap).c_str(), "HumanRemapFrom"), TraceLoggingWideString(GetShortcutHumanReadableString(shortcutRemappedTo, keyboardMap).c_str(), "HumanRemapTo"), - TraceLoggingWideString(appName.c_str(), "TargetApp") - ); + TraceLoggingWideString(appName.c_str(), "TargetApp")); } } } @@ -332,18 +319,17 @@ void Trace::SendKeyAndShortcutRemapLoadedConfiguration(State& remappings) noexce // Log an error while trying to send remappings telemetry. void Trace::ErrorSendingKeyAndShortcutRemapLoadedConfiguration() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_ErrorSendingKeyAndShortcutRemapLoadedConfiguration", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); } - // Log if an error occurs in KBM void Trace::Error(const DWORD errorCode, std::wstring errorMessage, std::wstring methodName) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_Error", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/trace.h b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/trace.h index 9408a38e0e..8a91eef9c4 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/trace.h +++ b/src/modules/keyboardmanager/KeyboardManagerEngineLibrary/trace.h @@ -2,12 +2,11 @@ #include "State.h" -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; - // Log if a key to key remap has been invoked today. static void DailyKeyToKeyRemapInvoked() noexcept; diff --git a/src/modules/keyboardmanager/KeyboardManagerEngineTest/pch.h b/src/modules/keyboardmanager/KeyboardManagerEngineTest/pch.h index 6364eacd48..4cf0be54c2 100644 --- a/src/modules/keyboardmanager/KeyboardManagerEngineTest/pch.h +++ b/src/modules/keyboardmanager/KeyboardManagerEngineTest/pch.h @@ -1,7 +1,6 @@ #pragma once #define WIN32_LEAN_AND_MEAN #include -#include #include #include #include diff --git a/src/modules/keyboardmanager/common/pch.h b/src/modules/keyboardmanager/common/pch.h index e01018e71e..48c81b6caf 100644 --- a/src/modules/keyboardmanager/common/pch.h +++ b/src/modules/keyboardmanager/common/pch.h @@ -6,4 +6,3 @@ #include #include #include -#include \ No newline at end of file diff --git a/src/modules/keyboardmanager/dll/dllmain.cpp b/src/modules/keyboardmanager/dll/dllmain.cpp index e76fd5f716..11fe776281 100644 --- a/src/modules/keyboardmanager/dll/dllmain.cpp +++ b/src/modules/keyboardmanager/dll/dllmain.cpp @@ -8,6 +8,7 @@ #include #include #include +#include BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/) { @@ -42,6 +43,8 @@ private: HANDLE m_hProcess = nullptr; + HANDLE m_hTerminateEngineEvent = nullptr; + public: // Constructor KeyboardManager() @@ -51,6 +54,17 @@ public: std::filesystem::path oldLogPath(PTSettingsHelper::get_module_save_folder_location(app_key)); oldLogPath.append("Logs"); LoggerHelpers::delete_old_log_folder(oldLogPath); + + m_hTerminateEngineEvent = CreateDefaultEvent(CommonSharedConstants::TERMINATE_KBM_SHARED_EVENT); + if (!m_hTerminateEngineEvent) + { + Logger::error(L"Failed to create terminate Engine event"); + auto message = get_last_error_message(GetLastError()); + if (message.has_value()) + { + Logger::error(message.value()); + } + } }; // Destroy the powertoy and free memory @@ -158,6 +172,9 @@ public: if (m_hProcess) { + SetEvent(m_hTerminateEngineEvent); + WaitForSingleObject(m_hProcess, 1500); + TerminateProcess(m_hProcess, 0); m_hProcess = nullptr; } diff --git a/src/modules/keyboardmanager/dll/pch.h b/src/modules/keyboardmanager/dll/pch.h index c4c30cb1ea..350550e299 100644 --- a/src/modules/keyboardmanager/dll/pch.h +++ b/src/modules/keyboardmanager/dll/pch.h @@ -1,7 +1,6 @@ #pragma once #define WIN32_LEAN_AND_MEAN #include -#include #include #include #include diff --git a/src/modules/keyboardmanager/dll/trace.cpp b/src/modules/keyboardmanager/dll/trace.cpp index 798204cae8..82057baefb 100644 --- a/src/modules/keyboardmanager/dll/trace.cpp +++ b/src/modules/keyboardmanager/dll/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,20 +10,10 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - // Log if the user has KBM enabled or disabled - Can also be used to see how often users have to restart the keyboard hook void Trace::EnableKeyboardManager(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "KeyboardManager_EnableKeyboardManager", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/keyboardmanager/dll/trace.h b/src/modules/keyboardmanager/dll/trace.h index fab670a364..55e5ff6867 100644 --- a/src/modules/keyboardmanager/dll/trace.h +++ b/src/modules/keyboardmanager/dll/trace.h @@ -1,11 +1,10 @@ #pragma once -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; - // Log if the user has KBM enabled or disabled - Can also be used to see how often users have to restart the keyboard hook static void EnableKeyboardManager(const bool enabled) noexcept; }; diff --git a/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj index fc1f8a1c85..8dc2df71bf 100644 --- a/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj +++ b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj @@ -1,5 +1,6 @@ - + @@ -46,14 +47,12 @@ - Create - diff --git a/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj.filters b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj.filters index 81c3649d1e..5e8c2b6605 100644 --- a/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj.filters +++ b/src/modules/launcher/Microsoft.Launcher/Microsoft.Launcher.vcxproj.filters @@ -3,11 +3,9 @@ - - Generated Files diff --git a/src/modules/launcher/Microsoft.Launcher/dllmain.cpp b/src/modules/launcher/Microsoft.Launcher/dllmain.cpp index e140fb728e..99c492c394 100644 --- a/src/modules/launcher/Microsoft.Launcher/dllmain.cpp +++ b/src/modules/launcher/Microsoft.Launcher/dllmain.cpp @@ -2,7 +2,6 @@ #include #include #include -#include "trace.h" #include "Generated Files/resource.h" #include #include @@ -34,13 +33,11 @@ BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lp switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: - Trace::RegisterProvider(); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: break; case DLL_PROCESS_DETACH: - Trace::UnregisterProvider(); break; } diff --git a/src/modules/launcher/Microsoft.Launcher/pch.h b/src/modules/launcher/Microsoft.Launcher/pch.h index 46fc911b44..c7e646b15e 100644 --- a/src/modules/launcher/Microsoft.Launcher/pch.h +++ b/src/modules/launcher/Microsoft.Launcher/pch.h @@ -3,6 +3,5 @@ #include #include #include -#include #include #include \ No newline at end of file diff --git a/src/modules/launcher/Microsoft.Launcher/trace.cpp b/src/modules/launcher/Microsoft.Launcher/trace.cpp deleted file mode 100644 index 3e5c2083a9..0000000000 --- a/src/modules/launcher/Microsoft.Launcher/trace.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "pch.h" -#include "trace.h" - -TRACELOGGING_DEFINE_PROVIDER( - g_hProvider, - "Microsoft.PowerToys", - // {38e8889b-9731-53f5-e901-e8a7c1753074} - (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), - TraceLoggingOptionProjectTelemetry()); - -void Trace::RegisterProvider() { - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() { - TraceLoggingUnregister(g_hProvider); -} diff --git a/src/modules/launcher/Microsoft.Launcher/trace.h b/src/modules/launcher/Microsoft.Launcher/trace.h deleted file mode 100644 index 1a260e8824..0000000000 --- a/src/modules/launcher/Microsoft.Launcher/trace.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -class Trace { -public: - static void RegisterProvider(); - static void UnregisterProvider(); -}; diff --git a/src/modules/launcher/PowerLauncher/App.xaml.cs b/src/modules/launcher/PowerLauncher/App.xaml.cs index 86255f3ad7..f110bf7142 100644 --- a/src/modules/launcher/PowerLauncher/App.xaml.cs +++ b/src/modules/launcher/PowerLauncher/App.xaml.cs @@ -46,6 +46,7 @@ namespace PowerLauncher private SettingWindowViewModel _settingsVM; private StringMatcher _stringMatcher; private SettingsReader _settingsReader; + private ETWTrace etwTrace = new ETWTrace(); // To prevent two disposals running at the same time. private static readonly object _disposingLock = new object(); @@ -101,6 +102,8 @@ namespace PowerLauncher () => { Log.Warn("RunExitEvent was signaled. Exiting PowerToys", typeof(App)); + application.etwTrace?.Dispose(); + application.etwTrace = null; ExitPowerToys(application); }, Application.Current.Dispatcher, @@ -111,6 +114,8 @@ namespace PowerLauncher RunnerHelper.WaitForPowerToysRunner(powerToysPid, () => { Log.Info($"Runner with pid={powerToysPid} exited. Exiting PowerToys Run", typeof(App)); + application.etwTrace?.Dispose(); + application.etwTrace = null; ExitPowerToys(application); }); } diff --git a/src/modules/peek/Peek.UI/PeekXAML/App.xaml.cs b/src/modules/peek/Peek.UI/PeekXAML/App.xaml.cs index 6df56d6f46..a433b00e43 100644 --- a/src/modules/peek/Peek.UI/PeekXAML/App.xaml.cs +++ b/src/modules/peek/Peek.UI/PeekXAML/App.xaml.cs @@ -26,6 +26,8 @@ namespace Peek.UI { public static int PowerToysPID { get; set; } + public ETWTrace EtwTrace { get; private set; } = new ETWTrace(); + public IHost Host { get; @@ -100,12 +102,18 @@ namespace Peek.UI { RunnerHelper.WaitForPowerToysRunner(powerToysRunnerPid, () => { + EtwTrace?.Dispose(); Environment.Exit(0); }); } } NativeEventWaiter.WaitForEventLoop(Constants.ShowPeekEvent(), OnPeekHotkey); + NativeEventWaiter.WaitForEventLoop(Constants.TerminatePeekEvent(), () => + { + EtwTrace?.Dispose(); + Environment.Exit(0); + }); } private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) diff --git a/src/modules/peek/peek/dllmain.cpp b/src/modules/peek/peek/dllmain.cpp index cef7c1cf9e..5fd4da8039 100644 --- a/src/modules/peek/peek/dllmain.cpp +++ b/src/modules/peek/peek/dllmain.cpp @@ -13,8 +13,8 @@ extern "C" IMAGE_DOS_HEADER __ImageBase; -BOOL APIENTRY DllMain(HMODULE /*hModule*/, - DWORD ul_reason_for_call, +BOOL APIENTRY DllMain(HMODULE /*hModule*/, + DWORD ul_reason_for_call, LPVOID /*lpReserved*/) { switch (ul_reason_for_call) @@ -65,6 +65,7 @@ private: DWORD m_processPid = 0; HANDLE m_hInvokeEvent; + HANDLE m_hTerminateEvent; // Load the settings file. void init_settings() @@ -163,7 +164,7 @@ private: { return false; } - if (wcsncmp(className, L"Progman", MAX_PATH) !=0 && wcsncmp(className, L"WorkerW", MAX_PATH) != 0) + if (wcsncmp(className, L"Progman", MAX_PATH) != 0 && wcsncmp(className, L"WorkerW", MAX_PATH) != 0) { return false; } @@ -239,7 +240,7 @@ private: } DWORD pid{}; - if (GetWindowThreadProcessId(foregroundWindowHandle, &pid)!=0) + if (GetWindowThreadProcessId(foregroundWindowHandle, &pid) != 0) { // If the foreground window is the Peek window, send activation signal. if (m_processPid != 0 && pid == m_processPid) @@ -321,6 +322,7 @@ public: init_settings(); m_hInvokeEvent = CreateDefaultEvent(CommonSharedConstants::SHOW_PEEK_SHARED_EVENT); + m_hTerminateEvent = CreateDefaultEvent(CommonSharedConstants::TERMINATE_PEEK_SHARED_EVENT); }; ~Peek() @@ -402,6 +404,8 @@ public: if (m_enabled) { ResetEvent(m_hInvokeEvent); + SetEvent(m_hTerminateEvent); + WaitForSingleObject(m_hProcess, 1500); auto result = TerminateProcess(m_hProcess, 1); if (result == 0) { diff --git a/src/modules/peek/peek/pch.h b/src/modules/peek/peek/pch.h index eddac0fdc1..329705f63b 100644 --- a/src/modules/peek/peek/pch.h +++ b/src/modules/peek/peek/pch.h @@ -2,6 +2,5 @@ #include #include #include -#include #include #include \ No newline at end of file diff --git a/src/modules/peek/peek/trace.cpp b/src/modules/peek/peek/trace.cpp index 1089b41ef8..529abb94f3 100644 --- a/src/modules/peek/peek/trace.cpp +++ b/src/modules/peek/peek/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,20 +10,10 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() -{ - TraceLoggingUnregister(g_hProvider); -} - // Log if the user has Peek enabled or disabled void Trace::EnablePeek(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "Peek_EnablePeek", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -32,7 +24,7 @@ void Trace::EnablePeek(const bool enabled) noexcept // Log if the user has invoked Peek void Trace::PeekInvoked() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "Peek_InvokePeek", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -49,7 +41,7 @@ void Trace::SettingsTelemetry(PowertoyModuleIface::Hotkey& hotkey) noexcept std::wstring(hotkey.alt ? L"Alt + " : L"") + std::wstring(L"VK ") + std::to_wstring(hotkey.key); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "Peek_Settings", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/peek/peek/trace.h b/src/modules/peek/peek/trace.h index 49cd45c32c..c250fc6b45 100644 --- a/src/modules/peek/peek/trace.h +++ b/src/modules/peek/peek/trace.h @@ -1,12 +1,11 @@ #pragma once #include -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider(); - static void UnregisterProvider(); - // Log if the user has Peek enabled or disabled static void EnablePeek(const bool enabled) noexcept; diff --git a/src/modules/poweraccent/PowerAccent.UI/App.xaml.cs b/src/modules/poweraccent/PowerAccent.UI/App.xaml.cs index af86d06edd..297b5f2f7b 100644 --- a/src/modules/poweraccent/PowerAccent.UI/App.xaml.cs +++ b/src/modules/poweraccent/PowerAccent.UI/App.xaml.cs @@ -7,6 +7,7 @@ using System.Threading; using System.Windows; using ManagedCommon; +using Microsoft.PowerToys.Telemetry; namespace PowerAccent.UI { @@ -17,6 +18,7 @@ namespace PowerAccent.UI { private static Mutex _mutex; private bool _disposed; + private ETWTrace _etwTrace = new ETWTrace(); protected override void OnStartup(StartupEventArgs e) { @@ -47,6 +49,7 @@ namespace PowerAccent.UI if (disposing) { _mutex?.Dispose(); + _etwTrace?.Dispose(); } _disposed = true; diff --git a/src/modules/poweraccent/PowerAccentModuleInterface/pch.h b/src/modules/poweraccent/PowerAccentModuleInterface/pch.h index eddac0fdc1..329705f63b 100644 --- a/src/modules/poweraccent/PowerAccentModuleInterface/pch.h +++ b/src/modules/poweraccent/PowerAccentModuleInterface/pch.h @@ -2,6 +2,5 @@ #include #include #include -#include #include #include \ No newline at end of file diff --git a/src/modules/poweraccent/PowerAccentModuleInterface/trace.cpp b/src/modules/poweraccent/PowerAccentModuleInterface/trace.cpp index c5f15cc216..8a063ea99f 100644 --- a/src/modules/poweraccent/PowerAccentModuleInterface/trace.cpp +++ b/src/modules/poweraccent/PowerAccentModuleInterface/trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,19 +10,9 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() -{ - TraceLoggingUnregister(g_hProvider); -} - void Trace::EnablePowerAccent(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "PowerAccent_EnablePowerAccent", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/poweraccent/PowerAccentModuleInterface/trace.h b/src/modules/poweraccent/PowerAccentModuleInterface/trace.h index 42b812f535..4d729f7683 100644 --- a/src/modules/poweraccent/PowerAccentModuleInterface/trace.h +++ b/src/modules/poweraccent/PowerAccentModuleInterface/trace.h @@ -1,11 +1,10 @@ #pragma once -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider(); - static void UnregisterProvider(); - // Log if the user has PowerAccent enabled or disabled static void EnablePowerAccent(const bool enabled) noexcept; }; diff --git a/src/modules/powerrename/PowerRenameContextMenu/PowerRenameContextMenu.vcxproj b/src/modules/powerrename/PowerRenameContextMenu/PowerRenameContextMenu.vcxproj index fb10229279..af3c71ad8e 100644 --- a/src/modules/powerrename/PowerRenameContextMenu/PowerRenameContextMenu.vcxproj +++ b/src/modules/powerrename/PowerRenameContextMenu/PowerRenameContextMenu.vcxproj @@ -132,6 +132,9 @@ MakeAppx.exe pack /d . /p $(OutDir)PowerRenameContextMenuPackage.msix /nv {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + {51920f1f-c28c-4adf-8660-4238766796c2} diff --git a/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp b/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp index d3fe2af629..ce5669fde8 100644 --- a/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp +++ b/src/modules/powerrename/PowerRenameContextMenu/dllmain.cpp @@ -18,6 +18,7 @@ #include "Generated Files/resource.h" +#include #include #include #include @@ -32,6 +33,7 @@ using namespace Microsoft::WRL; HINSTANCE g_hInst = 0; +Shared::Trace::ETWTrace trace(L"PowerRenameContextMenu"); #define BUFSIZE 4096 * 4 @@ -203,6 +205,8 @@ private: { if (CSettingsInstance().GetEnabled()) { + trace.UpdateState(true); + Trace::Invoked(); // Set the application path based on the location of the dll std::wstring path = get_module_folderpath(g_hInst); @@ -258,6 +262,9 @@ private: } Trace::InvokedRet(S_OK); + trace.Flush(); + trace.UpdateState(false); + return S_OK; } diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj index 192a1528bd..c41e461acb 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj @@ -188,6 +188,9 @@ {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + {98537082-0fdb-40de-abd8-0dc5a4269bab} diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj.filters b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj.filters index 10ef9ed4ee..bc242e4886 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj.filters +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj.filters @@ -65,7 +65,4 @@ - - - \ No newline at end of file diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.cpp b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.cpp index d1c747fc65..46463af14e 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.cpp +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include #include @@ -104,6 +106,8 @@ namespace winrt::PowerRenameUI::implementation MainWindow::MainWindow() : m_allSelected{ true }, m_managerEvents{ this } { + Trace::RegisterProvider(); + auto windowNative{ this->try_as<::IWindowNative>() }; winrt::check_bool(windowNative); windowNative->get_WindowHandle(&m_window); @@ -211,6 +215,8 @@ namespace winrt::PowerRenameUI::implementation InitializeComponent(); + m_etwTrace.UpdateState(true); + listView_ExplorerItems().ApplyTemplate(); #ifdef ENABLE_RECYCLING_VIRTUALIZATION_MODE if (auto scrollViewer = FindScrollViewer(listView_ExplorerItems()); scrollViewer) @@ -313,6 +319,11 @@ namespace winrt::PowerRenameUI::implementation { LastRunSettingsInstance().UpdateLastWindowSize(m_updatedWindowSize->first, m_updatedWindowSize->second); } + + m_etwTrace.Flush(); + m_etwTrace.UpdateState(false); + + Trace::UnregisterProvider(); } void MainWindow::InvalidateItemListViewState() diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.h b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.h index d1edf5b226..8c70194f1b 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.h +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameXAML/MainWindow.xaml.h @@ -5,6 +5,8 @@ #include "winrt/Windows.UI.Xaml.Interop.h" #include "winrt/Windows.UI.Xaml.Controls.Primitives.h" +#include + #include "MainWindow.g.h" #include "PatternSnippet.h" #include "ExplorerItem.h" @@ -143,6 +145,8 @@ namespace winrt::PowerRenameUI::implementation void SetCheckboxesFromFlags(DWORD flags); void UpdateCounts(); + Shared::Trace::ETWTrace m_etwTrace{}; + HWND m_window{}; bool m_disableCountUpdate = false; diff --git a/src/modules/powerrename/dll/PowerRenameExt.cpp b/src/modules/powerrename/dll/PowerRenameExt.cpp index 9e9f2910e7..5659f3a51c 100644 --- a/src/modules/powerrename/dll/PowerRenameExt.cpp +++ b/src/modules/powerrename/dll/PowerRenameExt.cpp @@ -133,6 +133,8 @@ HRESULT CPowerRenameMenu::InvokeCommand(_In_ LPCMINVOKECOMMANDINFO pici) HRESULT CPowerRenameMenu::RunPowerRename(CMINVOKECOMMANDINFO* pici, IShellItemArray* psiItemArray) { + m_etwTrace.UpdateState(true); + HRESULT hr = E_FAIL; if (CSettingsInstance().GetEnabled() && @@ -246,6 +248,9 @@ HRESULT CPowerRenameMenu::RunPowerRename(CMINVOKECOMMANDINFO* pici, IShellItemAr } Trace::InvokedRet(hr); + m_etwTrace.Flush(); + m_etwTrace.UpdateState(false); + return hr; } @@ -295,6 +300,8 @@ HRESULT __stdcall CPowerRenameMenu::Invoke(IShellItemArray* psiItemArray, IBindC swprintf_s(buffer, L"%d", GetCurrentProcessId()); MessageBoxW(nullptr, buffer, L"PID", MB_OK); #endif + m_etwTrace.UpdateState(true); + Trace::Invoked(); InvokeStruct* pInvokeData = new (std::nothrow) InvokeStruct; HRESULT hr = E_OUTOFMEMORY; @@ -311,6 +318,9 @@ HRESULT __stdcall CPowerRenameMenu::Invoke(IShellItemArray* psiItemArray, IBindC hr = RunPowerRename(nullptr, psiItemArray); } Trace::InvokedRet(hr); + + m_etwTrace.Flush(); + m_etwTrace.UpdateState(false); return S_OK; } diff --git a/src/modules/powerrename/dll/PowerRenameExt.h b/src/modules/powerrename/dll/PowerRenameExt.h index e2ffcda85c..3d0ca78b9f 100644 --- a/src/modules/powerrename/dll/PowerRenameExt.h +++ b/src/modules/powerrename/dll/PowerRenameExt.h @@ -1,6 +1,8 @@ #pragma once #include "pch.h" +#include + class __declspec(uuid("0440049F-D1DC-4E46-B27B-98393D79486B")) CPowerRenameMenu : public IShellExtInit, public IContextMenu, @@ -70,4 +72,6 @@ private: HBITMAP m_hbmpIcon = nullptr; CComPtr m_spdo; std::wstring context_menu_caption; + + Shared::Trace::ETWTrace m_etwTrace{ L"PowerRenameExt" }; }; diff --git a/src/modules/powerrename/dll/PowerRenameExt.vcxproj b/src/modules/powerrename/dll/PowerRenameExt.vcxproj index a994c25148..ead9518f35 100644 --- a/src/modules/powerrename/dll/PowerRenameExt.vcxproj +++ b/src/modules/powerrename/dll/PowerRenameExt.vcxproj @@ -60,6 +60,9 @@ {d9b8fc84-322a-4f9f-bbb9-20915c47ddfd} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + {98537082-0fdb-40de-abd8-0dc5a4269bab} diff --git a/src/modules/powerrename/lib/pch.h b/src/modules/powerrename/lib/pch.h index 7a12102fbe..c5a4711a03 100644 --- a/src/modules/powerrename/lib/pch.h +++ b/src/modules/powerrename/lib/pch.h @@ -29,6 +29,4 @@ #include #include -#include - #include diff --git a/src/modules/powerrename/lib/trace.cpp b/src/modules/powerrename/lib/trace.cpp index f04a085df0..9f1446c653 100644 --- a/src/modules/powerrename/lib/trace.cpp +++ b/src/modules/powerrename/lib/trace.cpp @@ -2,26 +2,18 @@ #include "trace.h" #include "Settings.h" +#include + TRACELOGGING_DEFINE_PROVIDER( - g_hProvider, - "Microsoft.PowerToys", - // {38e8889b-9731-53f5-e901-e8a7c1753074} - (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), - TraceLoggingOptionProjectTelemetry()); - -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} + g_hProvider, + "Microsoft.PowerToys", + // {38e8889b-9731-53f5-e901-e8a7c1753074} + (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), + TraceLoggingOptionProjectTelemetry()); void Trace::Invoked() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "PowerRename_Invoked", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -30,7 +22,7 @@ void Trace::Invoked() noexcept void Trace::InvokedRet(_In_ HRESULT hr) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "PowerRename_InvokedRet", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -40,7 +32,7 @@ void Trace::InvokedRet(_In_ HRESULT hr) noexcept void Trace::EnablePowerRename(_In_ bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "PowerRename_EnablePowerRename", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -50,7 +42,7 @@ void Trace::EnablePowerRename(_In_ bool enabled) noexcept void Trace::UIShownRet(_In_ HRESULT hr) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "PowerRename_UIShownRet", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -60,7 +52,7 @@ void Trace::UIShownRet(_In_ HRESULT hr) noexcept void Trace::RenameOperation(_In_ UINT totalItemCount, _In_ UINT selectedItemCount, _In_ UINT renameItemCount, _In_ DWORD flags, _In_ PCWSTR extensionList) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "PowerRename_RenameOperation", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -74,7 +66,7 @@ void Trace::RenameOperation(_In_ UINT totalItemCount, _In_ UINT selectedItemCoun void Trace::SettingsChanged() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "PowerRename_SettingsChanged", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/powerrename/lib/trace.h b/src/modules/powerrename/lib/trace.h index 9019c9c8ae..0a3a0af246 100644 --- a/src/modules/powerrename/lib/trace.h +++ b/src/modules/powerrename/lib/trace.h @@ -1,9 +1,9 @@ #pragma once -class Trace { +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; static void Invoked() noexcept; static void InvokedRet(_In_ HRESULT hr) noexcept; static void EnablePowerRename(_In_ bool enabled) noexcept; diff --git a/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandlerControl.cs b/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandlerControl.cs index 67c6d9f42e..2fd96930a2 100644 --- a/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandlerControl.cs +++ b/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandlerControl.cs @@ -4,8 +4,6 @@ using Common; using Microsoft.PowerToys.FilePreviewCommon; -using Microsoft.PowerToys.PreviewHandler.Gcode.Telemetry.Events; -using Microsoft.PowerToys.Telemetry; namespace Microsoft.PowerToys.PreviewHandler.Gcode { @@ -86,13 +84,6 @@ namespace Microsoft.PowerToys.PreviewHandler.Gcode Resize += FormResized; base.DoPreview(fs); - try - { - PowerToysTelemetry.Log.WriteEvent(new GcodeFilePreviewed()); - } - catch - { // Should not crash if sending telemetry is failing. Ignore the exception. - } } catch (Exception ex) { @@ -162,14 +153,6 @@ namespace Microsoft.PowerToys.PreviewHandler.Gcode /// Stream reference to access source file. private void PreviewError(Exception exception, T dataSource) { - try - { - PowerToysTelemetry.Log.WriteEvent(new GcodeFilePreviewError { Message = exception.Message }); - } - catch - { // Should not crash if sending telemetry is failing. Ignore the exception. - } - Controls.Clear(); _infoBarAdded = true; AddTextBoxControl(Properties.Resource.GcodeNotPreviewedError); diff --git a/src/modules/previewpane/GcodePreviewHandler/Telemetry/Events/GcodeFileHandlerLoaded.cs b/src/modules/previewpane/GcodePreviewHandler/Telemetry/Events/GcodeFileHandlerLoaded.cs deleted file mode 100644 index c1d1fba40a..0000000000 --- a/src/modules/previewpane/GcodePreviewHandler/Telemetry/Events/GcodeFileHandlerLoaded.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Tracing; - -using Microsoft.PowerToys.Telemetry; -using Microsoft.PowerToys.Telemetry.Events; - -namespace Microsoft.PowerToys.PreviewHandler.Gcode.Telemetry.Events -{ - /// - /// A telemetry event to be raised when a svg file has been viewed in the preview pane. - /// - [EventData] - public class GcodeFileHandlerLoaded : EventBase, IEvent - { - /// - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; - } -} diff --git a/src/modules/previewpane/GcodePreviewHandler/Telemetry/Events/GcodeFilePreviewError.cs b/src/modules/previewpane/GcodePreviewHandler/Telemetry/Events/GcodeFilePreviewError.cs deleted file mode 100644 index 9e9a232e04..0000000000 --- a/src/modules/previewpane/GcodePreviewHandler/Telemetry/Events/GcodeFilePreviewError.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Tracing; - -using Microsoft.PowerToys.Telemetry; -using Microsoft.PowerToys.Telemetry.Events; - -namespace Microsoft.PowerToys.PreviewHandler.Gcode.Telemetry.Events -{ - /// - /// A telemetry event to be raised when an error has occurred in the preview pane. - /// - [EventData] - public class GcodeFilePreviewError : EventBase, IEvent - { - /// - /// Gets or sets the error message to log as part of the telemetry event. - /// - public string Message { get; set; } - - /// - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServicePerformance; - } -} diff --git a/src/modules/previewpane/GcodePreviewHandler/Telemetry/Events/GcodeFilePreviewed.cs b/src/modules/previewpane/GcodePreviewHandler/Telemetry/Events/GcodeFilePreviewed.cs deleted file mode 100644 index 693e314e40..0000000000 --- a/src/modules/previewpane/GcodePreviewHandler/Telemetry/Events/GcodeFilePreviewed.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Tracing; - -using Microsoft.PowerToys.Telemetry; -using Microsoft.PowerToys.Telemetry.Events; - -namespace Microsoft.PowerToys.PreviewHandler.Gcode.Telemetry.Events -{ - /// - /// A telemetry event to be raised when a svg file has been viewed in the preview pane. - /// - [EventData] - public class GcodeFilePreviewed : EventBase, IEvent - { - /// - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; - } -} diff --git a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs index 89f480c792..bb4b1f869d 100644 --- a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs +++ b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandlerControl.cs @@ -9,8 +9,6 @@ using System.Text.RegularExpressions; using Common; using Microsoft.PowerToys.PreviewHandler.Markdown.Properties; -using Microsoft.PowerToys.PreviewHandler.Markdown.Telemetry.Events; -using Microsoft.PowerToys.Telemetry; using Microsoft.Web.WebView2.Core; using Microsoft.Web.WebView2.WinForms; using Windows.System; @@ -217,25 +215,9 @@ namespace Microsoft.PowerToys.PreviewHandler.Markdown { } }); - - try - { - PowerToysTelemetry.Log.WriteEvent(new MarkdownFilePreviewed()); - } - catch - { // Should not crash if sending telemetry is failing. Ignore the exception. - } } - catch (Exception ex) + catch (Exception) { - try - { - PowerToysTelemetry.Log.WriteEvent(new MarkdownFilePreviewError { Message = ex.Message }); - } - catch - { // Should not crash if sending telemetry is failing. Ignore the exception. - } - Controls.Clear(); _infoBarDisplayed = true; _infoBar = GetTextBoxControl(Resources.MarkdownNotPreviewedError); diff --git a/src/modules/previewpane/MarkdownPreviewHandler/Telemetry/Events/MarkdownFileHandlerLoaded.cs b/src/modules/previewpane/MarkdownPreviewHandler/Telemetry/Events/MarkdownFileHandlerLoaded.cs deleted file mode 100644 index dec9852dec..0000000000 --- a/src/modules/previewpane/MarkdownPreviewHandler/Telemetry/Events/MarkdownFileHandlerLoaded.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Tracing; - -using Microsoft.PowerToys.Telemetry; -using Microsoft.PowerToys.Telemetry.Events; - -namespace Microsoft.PowerToys.PreviewHandler.Markdown.Telemetry.Events -{ - /// - /// A telemetry event that is triggered when a markdown file is viewed in the preview pane. - /// - [EventData] - public class MarkdownFileHandlerLoaded : EventBase, IEvent - { - /// - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; - } -} diff --git a/src/modules/previewpane/MarkdownPreviewHandler/Telemetry/Events/MarkdownFilePreviewError.cs b/src/modules/previewpane/MarkdownPreviewHandler/Telemetry/Events/MarkdownFilePreviewError.cs deleted file mode 100644 index 95268b1619..0000000000 --- a/src/modules/previewpane/MarkdownPreviewHandler/Telemetry/Events/MarkdownFilePreviewError.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.PowerToys.Telemetry; -using Microsoft.PowerToys.Telemetry.Events; - -namespace Microsoft.PowerToys.PreviewHandler.Markdown.Telemetry.Events -{ - /// - /// A telemetry event that is triggered when an error occurs while attempting to view a markdown file in the preview pane. - /// - public class MarkdownFilePreviewError : EventBase, IEvent - { - /// - /// Gets or sets the error message. - /// - public string Message { get; set; } - - /// - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServicePerformance; - } -} diff --git a/src/modules/previewpane/MarkdownPreviewHandler/Telemetry/Events/MarkdownFilePreviewed.cs b/src/modules/previewpane/MarkdownPreviewHandler/Telemetry/Events/MarkdownFilePreviewed.cs deleted file mode 100644 index e38d2d38ca..0000000000 --- a/src/modules/previewpane/MarkdownPreviewHandler/Telemetry/Events/MarkdownFilePreviewed.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Tracing; - -using Microsoft.PowerToys.Telemetry; -using Microsoft.PowerToys.Telemetry.Events; - -namespace Microsoft.PowerToys.PreviewHandler.Markdown.Telemetry.Events -{ - /// - /// A telemetry event that is triggered when a markdown file is viewed in the preview pane. - /// - [EventData] - public class MarkdownFilePreviewed : EventBase, IEvent - { - /// - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; - } -} diff --git a/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandlerControl.cs b/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandlerControl.cs index 4068567985..f9b5069e88 100644 --- a/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandlerControl.cs +++ b/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandlerControl.cs @@ -1,17 +1,8 @@ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System; -using System.Drawing; -using System.IO; -using System.Runtime.InteropServices.ComTypes; -using System.Windows.Forms; - using Common; -using Common.Utilities; using Microsoft.PowerToys.PreviewHandler.Pdf.Properties; -using Microsoft.PowerToys.PreviewHandler.Pdf.Telemetry.Events; -using Microsoft.PowerToys.Telemetry; using Windows.Data.Pdf; using Windows.Storage.Streams; using Windows.UI.ViewManagement; @@ -158,25 +149,9 @@ namespace Microsoft.PowerToys.PreviewHandler.Pdf memStream.Dispose(); } } - - try - { - PowerToysTelemetry.Log.WriteEvent(new PdfFilePreviewed()); - } - catch - { // Should not crash if sending telemetry is failing. Ignore the exception. - } } - catch (Exception ex) + catch (Exception) { - try - { - PowerToysTelemetry.Log.WriteEvent(new PdfFilePreviewError { Message = ex.Message }); - } - catch - { // Should not crash if sending telemetry is failing. Ignore the exception. - } - Controls.Clear(); _infoBar = GetTextBoxControl(Resources.PdfNotPreviewedError); Controls.Add(_infoBar); diff --git a/src/modules/previewpane/PdfPreviewHandler/Telemetry/Events/PdfFileHandlerLoaded.cs b/src/modules/previewpane/PdfPreviewHandler/Telemetry/Events/PdfFileHandlerLoaded.cs deleted file mode 100644 index fc2ac2881b..0000000000 --- a/src/modules/previewpane/PdfPreviewHandler/Telemetry/Events/PdfFileHandlerLoaded.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Tracing; - -using Microsoft.PowerToys.Telemetry; -using Microsoft.PowerToys.Telemetry.Events; - -namespace Microsoft.PowerToys.PreviewHandler.Pdf.Telemetry.Events -{ - /// - /// A telemetry event that is triggered when a pdf file is viewed in the preview pane. - /// - [EventData] - public class PdfFileHandlerLoaded : EventBase, IEvent - { - /// - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; - } -} diff --git a/src/modules/previewpane/PdfPreviewHandler/Telemetry/Events/PdfFilePreviewError.cs b/src/modules/previewpane/PdfPreviewHandler/Telemetry/Events/PdfFilePreviewError.cs deleted file mode 100644 index 73dec91265..0000000000 --- a/src/modules/previewpane/PdfPreviewHandler/Telemetry/Events/PdfFilePreviewError.cs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using Microsoft.PowerToys.Telemetry; -using Microsoft.PowerToys.Telemetry.Events; - -namespace Microsoft.PowerToys.PreviewHandler.Pdf.Telemetry.Events -{ - /// - /// A telemetry event that is triggered when an error occurs while attempting to view a markdown file in the preview pane. - /// - public class PdfFilePreviewError : EventBase, IEvent - { - /// - /// Gets or sets the error message. - /// - public string Message { get; set; } - - /// - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServicePerformance; - } -} diff --git a/src/modules/previewpane/PdfPreviewHandler/Telemetry/Events/PdfFilePreviewed.cs b/src/modules/previewpane/PdfPreviewHandler/Telemetry/Events/PdfFilePreviewed.cs deleted file mode 100644 index 0a223a24b2..0000000000 --- a/src/modules/previewpane/PdfPreviewHandler/Telemetry/Events/PdfFilePreviewed.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Tracing; - -using Microsoft.PowerToys.Telemetry; -using Microsoft.PowerToys.Telemetry.Events; - -namespace Microsoft.PowerToys.PreviewHandler.Pdf.Telemetry.Events -{ - /// - /// A telemetry event that is triggered when a markdown file is viewed in the preview pane. - /// - [EventData] - public class PdfFilePreviewed : EventBase, IEvent - { - /// - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; - } -} diff --git a/src/modules/previewpane/QoiPreviewHandler/QoiPreviewHandlerControl.cs b/src/modules/previewpane/QoiPreviewHandler/QoiPreviewHandlerControl.cs index 047a3e614f..29bf8d2d7d 100644 --- a/src/modules/previewpane/QoiPreviewHandler/QoiPreviewHandlerControl.cs +++ b/src/modules/previewpane/QoiPreviewHandler/QoiPreviewHandlerControl.cs @@ -4,8 +4,6 @@ using Common; using Microsoft.PowerToys.FilePreviewCommon; -using Microsoft.PowerToys.PreviewHandler.Qoi.Telemetry.Events; -using Microsoft.PowerToys.Telemetry; namespace Microsoft.PowerToys.PreviewHandler.Qoi { @@ -73,13 +71,6 @@ namespace Microsoft.PowerToys.PreviewHandler.Qoi Resize += FormResized; base.DoPreview(fs); - try - { - PowerToysTelemetry.Log.WriteEvent(new QoiFilePreviewed()); - } - catch - { // Should not crash if sending telemetry is failing. Ignore the exception. - } } catch (Exception ex) { @@ -149,14 +140,6 @@ namespace Microsoft.PowerToys.PreviewHandler.Qoi /// Stream reference to access source file. private void PreviewError(Exception exception, T dataSource) { - try - { - PowerToysTelemetry.Log.WriteEvent(new QoiFilePreviewError { Message = exception.Message }); - } - catch - { // Should not crash if sending telemetry is failing. Ignore the exception. - } - Controls.Clear(); _infoBarAdded = true; AddTextBoxControl(Properties.Resource.QoiNotPreviewedError); diff --git a/src/modules/previewpane/QoiPreviewHandler/Telemetry/Events/QoiFilePreviewError.cs b/src/modules/previewpane/QoiPreviewHandler/Telemetry/Events/QoiFilePreviewError.cs deleted file mode 100644 index cdc4516fd9..0000000000 --- a/src/modules/previewpane/QoiPreviewHandler/Telemetry/Events/QoiFilePreviewError.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Tracing; - -using Microsoft.PowerToys.Telemetry; -using Microsoft.PowerToys.Telemetry.Events; - -namespace Microsoft.PowerToys.PreviewHandler.Qoi.Telemetry.Events -{ - /// - /// A telemetry event to be raised when an error has occurred in the preview pane. - /// - [EventData] - public class QoiFilePreviewError : EventBase, IEvent - { - /// - /// Gets or sets the error message to log as part of the telemetry event. - /// - public string Message { get; set; } - - /// - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServicePerformance; - } -} diff --git a/src/modules/previewpane/QoiPreviewHandler/Telemetry/Events/QoiFilePreviewed.cs b/src/modules/previewpane/QoiPreviewHandler/Telemetry/Events/QoiFilePreviewed.cs deleted file mode 100644 index 273ec8caf0..0000000000 --- a/src/modules/previewpane/QoiPreviewHandler/Telemetry/Events/QoiFilePreviewed.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Tracing; - -using Microsoft.PowerToys.Telemetry; -using Microsoft.PowerToys.Telemetry.Events; - -namespace Microsoft.PowerToys.PreviewHandler.Qoi.Telemetry.Events -{ - /// - /// A telemetry event to be raised when a Qoi file has been viewed in the preview pane. - /// - [EventData] - public class QoiFilePreviewed : EventBase, IEvent - { - /// - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; - } -} diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs index 095c985896..931946dc0b 100644 --- a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs +++ b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewControl.cs @@ -2,14 +2,11 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -using System.Net.Http; using System.Reflection; using System.Runtime.CompilerServices; using Common; using Common.Utilities; -using Microsoft.PowerToys.PreviewHandler.Svg.Telemetry.Events; -using Microsoft.PowerToys.Telemetry; using Microsoft.Web.WebView2.Core; using Microsoft.Web.WebView2.WinForms; using SvgPreviewHandler; @@ -142,15 +139,8 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg svgData = SvgPreviewHandlerHelper.SwapNamespaces(svgData); svgData = SvgPreviewHandlerHelper.AddStyleSVG(svgData); } - catch (Exception ex) + catch (Exception) { - try - { - PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewError { Message = ex.Message }); - } - catch - { // Should not crash if sending telemetry is failing. Ignore the exception. - } } try @@ -167,13 +157,6 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg AddWebViewControl(svgData); Resize += FormResized; base.DoPreview(dataSource); - try - { - PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewed()); - } - catch - { // Should not crash if sending telemetry is failing. Ignore the exception. - } } catch (Exception ex) { @@ -301,14 +284,6 @@ namespace Microsoft.PowerToys.PreviewHandler.Svg /// Stream reference to access source file. private void PreviewError(Exception exception, T dataSource) { - try - { - PowerToysTelemetry.Log.WriteEvent(new SvgFilePreviewError { Message = exception.Message }); - } - catch - { // Should not crash if sending telemetry is failing. Ignore the exception. - } - Controls.Clear(); _infoBarAdded = true; AddTextBoxControl(Properties.Resource.SvgNotPreviewedError); diff --git a/src/modules/previewpane/SvgPreviewHandler/Telemetry/Events/SvgFileHandlerLoaded.cs b/src/modules/previewpane/SvgPreviewHandler/Telemetry/Events/SvgFileHandlerLoaded.cs deleted file mode 100644 index aaea992ba9..0000000000 --- a/src/modules/previewpane/SvgPreviewHandler/Telemetry/Events/SvgFileHandlerLoaded.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Tracing; - -using Microsoft.PowerToys.Telemetry; -using Microsoft.PowerToys.Telemetry.Events; - -namespace Microsoft.PowerToys.PreviewHandler.Svg.Telemetry.Events -{ - /// - /// A telemetry event to be raised when a svg file has been viewed in the preview pane. - /// - [EventData] - public class SvgFileHandlerLoaded : EventBase, IEvent - { - /// - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; - } -} diff --git a/src/modules/previewpane/SvgPreviewHandler/Telemetry/Events/SvgFilePreviewError.cs b/src/modules/previewpane/SvgPreviewHandler/Telemetry/Events/SvgFilePreviewError.cs deleted file mode 100644 index daed435502..0000000000 --- a/src/modules/previewpane/SvgPreviewHandler/Telemetry/Events/SvgFilePreviewError.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Tracing; - -using Microsoft.PowerToys.Telemetry; -using Microsoft.PowerToys.Telemetry.Events; - -namespace Microsoft.PowerToys.PreviewHandler.Svg.Telemetry.Events -{ - /// - /// A telemetry event to be raised when an error has occurred in the preview pane. - /// - [EventData] - public class SvgFilePreviewError : EventBase, IEvent - { - /// - /// Gets or sets the error message to log as part of the telemetry event. - /// - public string Message { get; set; } - - /// - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServicePerformance; - } -} diff --git a/src/modules/previewpane/SvgPreviewHandler/Telemetry/Events/SvgFilePreviewed.cs b/src/modules/previewpane/SvgPreviewHandler/Telemetry/Events/SvgFilePreviewed.cs deleted file mode 100644 index 5348bcd466..0000000000 --- a/src/modules/previewpane/SvgPreviewHandler/Telemetry/Events/SvgFilePreviewed.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) Microsoft Corporation -// The Microsoft Corporation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics.Tracing; - -using Microsoft.PowerToys.Telemetry; -using Microsoft.PowerToys.Telemetry.Events; - -namespace Microsoft.PowerToys.PreviewHandler.Svg.Telemetry.Events -{ - /// - /// A telemetry event to be raised when a svg file has been viewed in the preview pane. - /// - [EventData] - public class SvgFilePreviewed : EventBase, IEvent - { - /// - public PartA_PrivTags PartA_PrivTags => PartA_PrivTags.ProductAndServiceUsage; - } -} diff --git a/src/modules/previewpane/powerpreview/pch.h b/src/modules/previewpane/powerpreview/pch.h index cace4c2c94..2f130e79fa 100644 --- a/src/modules/previewpane/powerpreview/pch.h +++ b/src/modules/previewpane/powerpreview/pch.h @@ -4,5 +4,4 @@ #include #include #include -#include diff --git a/src/modules/previewpane/powerpreview/powerpreview.cpp b/src/modules/previewpane/powerpreview/powerpreview.cpp index 1eef1f0398..84d2a590dc 100644 --- a/src/modules/previewpane/powerpreview/powerpreview.cpp +++ b/src/modules/previewpane/powerpreview/powerpreview.cpp @@ -6,7 +6,6 @@ #include #include -#include #include #include #include diff --git a/src/modules/previewpane/powerpreview/trace.cpp b/src/modules/previewpane/powerpreview/trace.cpp index 903ecff487..6bd46ed45b 100644 --- a/src/modules/previewpane/powerpreview/trace.cpp +++ b/src/modules/previewpane/powerpreview/trace.cpp @@ -2,6 +2,8 @@ #include "trace.h" #include +#include + /* * * This file captures the telemetry for the File Explorer Custom Renders project. @@ -19,19 +21,9 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() -{ - TraceLoggingUnregister(g_hProvider); -} - void Trace::EnabledPowerPreview(bool enabled) { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "PowerPreview_Enabled", TraceLoggingBoolean(enabled, "Enabled"), @@ -42,7 +34,7 @@ void Trace::EnabledPowerPreview(bool enabled) void Trace::PowerPreviewSettingsUpdated(LPCWSTR SettingsName, bool oldState, bool newState, bool globalState) { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "PowerPreview_TweakUISettings_SuccessfullyUpdatedSettings", TraceLoggingWideString(SettingsName, "Previewer_Settings_Name"), @@ -56,7 +48,7 @@ void Trace::PowerPreviewSettingsUpdated(LPCWSTR SettingsName, bool oldState, boo void Trace::PowerPreviewSettingsUpdateFailed(LPCWSTR SettingsName, bool oldState, bool newState, bool globalState) { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "PowerPreview_TweakUISettings_FailedUpdatingSettings", TraceLoggingWideString(SettingsName, "Previewer_Settings_Name"), @@ -70,7 +62,7 @@ void Trace::PowerPreviewSettingsUpdateFailed(LPCWSTR SettingsName, bool oldState void Trace::SetConfigInvalidJSON(const char* exceptionMessage) { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "PowerPreview_TweakUISettings_SetConfig__InvalidJSONGiven", TraceLoggingString(exceptionMessage, "ExceptionMessage"), @@ -81,7 +73,7 @@ void Trace::SetConfigInvalidJSON(const char* exceptionMessage) void Trace::Destroyed() { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "PowerPreview_TweakUISettings_Destroyed", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -89,9 +81,9 @@ void Trace::Destroyed() TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); } -void Trace::InitSetErrorLoadingFile(const char* exceptionMessage) +void Trace::InitSetErrorLoadingFile(const char* exceptionMessage) { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "PowerPreview_TweakUISettings_InitSet__ErrorLoadingFile", TraceLoggingString(exceptionMessage, "ExceptionMessage"), @@ -99,4 +91,3 @@ void Trace::InitSetErrorLoadingFile(const char* exceptionMessage) TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"), TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); } - diff --git a/src/modules/previewpane/powerpreview/trace.h b/src/modules/previewpane/powerpreview/trace.h index 6899bcdec1..8cd33fee52 100644 --- a/src/modules/previewpane/powerpreview/trace.h +++ b/src/modules/previewpane/powerpreview/trace.h @@ -1,10 +1,10 @@ #pragma once -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider(); - static void UnregisterProvider(); static void SetConfigInvalidJSON(const char* exceptionMessage); static void InitSetErrorLoadingFile(const char* exceptionMessage); static void EnabledPowerPreview(bool enabled); diff --git a/src/modules/registrypreview/RegistryPreview/MainWindow.Events.cs b/src/modules/registrypreview/RegistryPreview/MainWindow.Events.cs index 34de7f1752..abcbbfd09b 100644 --- a/src/modules/registrypreview/RegistryPreview/MainWindow.Events.cs +++ b/src/modules/registrypreview/RegistryPreview/MainWindow.Events.cs @@ -28,6 +28,7 @@ namespace RegistryPreview { // Save window placement SaveWindowPlacementFile(settingsFolder, windowPlacementFile); + (Application.Current as App).EtwTrace?.Dispose(); } } } diff --git a/src/modules/registrypreview/RegistryPreview/RegistryPreviewXAML/App.xaml.cs b/src/modules/registrypreview/RegistryPreview/RegistryPreviewXAML/App.xaml.cs index a6a3898f0f..6930d8bc98 100644 --- a/src/modules/registrypreview/RegistryPreview/RegistryPreviewXAML/App.xaml.cs +++ b/src/modules/registrypreview/RegistryPreview/RegistryPreviewXAML/App.xaml.cs @@ -6,6 +6,7 @@ using System; using System.Web; using ManagedCommon; +using Microsoft.PowerToys.Telemetry; using Microsoft.UI.Xaml; using Microsoft.Windows.AppLifecycle; using Windows.ApplicationModel.Activation; @@ -111,5 +112,7 @@ namespace RegistryPreview public static string AppFilename; #pragma warning restore CA2211 // Non-constant fields should not be visible #pragma warning restore SA1401 // Fields should be private + + public ETWTrace EtwTrace { get; private set; } = new ETWTrace(); } } diff --git a/src/modules/registrypreview/RegistryPreviewExt/Trace.cpp b/src/modules/registrypreview/RegistryPreviewExt/Trace.cpp index 7dda85e43e..870669606b 100644 --- a/src/modules/registrypreview/RegistryPreviewExt/Trace.cpp +++ b/src/modules/registrypreview/RegistryPreviewExt/Trace.cpp @@ -1,6 +1,8 @@ #include "pch.h" #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -8,20 +10,10 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() -{ - TraceLoggingUnregister(g_hProvider); -} - // Log if the user has enabled or disabled the app void Trace::EnableRegistryPreview(_In_ bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "RegistryPreview_EnableRegistryPreview", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -32,7 +24,7 @@ void Trace::EnableRegistryPreview(_In_ bool enabled) noexcept // Log that the user tried to activate the app void Trace::ActivateEditor() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "RegistryPreview_Activate", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), diff --git a/src/modules/registrypreview/RegistryPreviewExt/Trace.h b/src/modules/registrypreview/RegistryPreviewExt/Trace.h index d2cda345d8..7e08386e9c 100644 --- a/src/modules/registrypreview/RegistryPreviewExt/Trace.h +++ b/src/modules/registrypreview/RegistryPreviewExt/Trace.h @@ -1,11 +1,10 @@ #pragma once -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider(); - static void UnregisterProvider(); - // Log if the user has enabled or disabled the app static void EnableRegistryPreview(const bool enabled) noexcept; diff --git a/src/modules/registrypreview/RegistryPreviewExt/pch.h b/src/modules/registrypreview/RegistryPreviewExt/pch.h index be72eb015e..6b4ad38c10 100644 --- a/src/modules/registrypreview/RegistryPreviewExt/pch.h +++ b/src/modules/registrypreview/RegistryPreviewExt/pch.h @@ -9,7 +9,6 @@ #include -#include #include //#include #include diff --git a/src/modules/videoconference/VideoConferenceModule/pch.h b/src/modules/videoconference/VideoConferenceModule/pch.h index 3f4849aa44..7c614d9a5c 100644 --- a/src/modules/videoconference/VideoConferenceModule/pch.h +++ b/src/modules/videoconference/VideoConferenceModule/pch.h @@ -21,5 +21,3 @@ #include #include - -#include diff --git a/src/modules/videoconference/VideoConferenceModule/trace.cpp b/src/modules/videoconference/VideoConferenceModule/trace.cpp index 889cca2a15..043c6b30e7 100644 --- a/src/modules/videoconference/VideoConferenceModule/trace.cpp +++ b/src/modules/videoconference/VideoConferenceModule/trace.cpp @@ -2,6 +2,8 @@ #include "trace.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -9,20 +11,10 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() noexcept -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() noexcept -{ - TraceLoggingUnregister(g_hProvider); -} - // Log if the user has VCM enabled or disabled void Trace::EnableVideoConference(const bool enabled) noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "VideoConference_EnableVideoConference", ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), @@ -34,7 +26,7 @@ void Trace::SettingsChanged(const struct VideoConferenceSettings& settings) noex { bool CustomOverlayImage = (settings.imageOverlayPath.length() > 0); - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "VideoConference_SettingsChanged", TraceLoggingWideString(settings.toolbarPositionString.c_str(), "ToolbarPosition"), @@ -47,7 +39,7 @@ void Trace::SettingsChanged(const struct VideoConferenceSettings& settings) noex void Trace::MicrophoneMuted() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "VideoConference_MicrophoneMuted", TraceLoggingBoolean(true, "MicrophoneMuted"), @@ -58,7 +50,7 @@ void Trace::MicrophoneMuted() noexcept void Trace::CameraMuted() noexcept { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "VideoConference_CameraMuted", TraceLoggingBoolean(true, "CameraMuted"), diff --git a/src/modules/videoconference/VideoConferenceModule/trace.h b/src/modules/videoconference/VideoConferenceModule/trace.h index 5d42709be2..0a9f193e69 100644 --- a/src/modules/videoconference/VideoConferenceModule/trace.h +++ b/src/modules/videoconference/VideoConferenceModule/trace.h @@ -1,11 +1,11 @@ #pragma once #include "VideoConferenceModule.h" -class Trace +#include + +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider() noexcept; - static void UnregisterProvider() noexcept; static void EnableVideoConference(const bool enabled) noexcept; static void SettingsChanged(const struct VideoConferenceSettings &settings) noexcept; static void MicrophoneMuted() noexcept; diff --git a/src/runner/general_settings.cpp b/src/runner/general_settings.cpp index 9925308507..9e3b170feb 100644 --- a/src/runner/general_settings.cpp +++ b/src/runner/general_settings.cpp @@ -65,7 +65,7 @@ json::JsonObject load_general_settings() show_new_updates_toast_notification = loaded.GetNamedBoolean(L"show_new_updates_toast_notification", true); download_updates_automatically = loaded.GetNamedBoolean(L"download_updates_automatically", true) && check_user_is_admin(); show_whats_new_after_updates = loaded.GetNamedBoolean(L"show_whats_new_after_updates", true); - enable_experimentation = loaded.GetNamedBoolean(L"enable_experimentation",true); + enable_experimentation = loaded.GetNamedBoolean(L"enable_experimentation", true); enable_warnings_elevated_apps = loaded.GetNamedBoolean(L"enable_warnings_elevated_apps", true); return loaded; @@ -241,8 +241,7 @@ void start_enabled_powertoys() { std::wstring disable_module_name{ static_cast(disabled_element.Key()) }; - if (powertoys_gpo_configuration.find(disable_module_name)!=powertoys_gpo_configuration.end() - && (powertoys_gpo_configuration[disable_module_name]==powertoys_gpo::gpo_rule_configured_enabled || powertoys_gpo_configuration[disable_module_name]==powertoys_gpo::gpo_rule_configured_disabled)) + if (powertoys_gpo_configuration.find(disable_module_name) != powertoys_gpo_configuration.end() && (powertoys_gpo_configuration[disable_module_name] == powertoys_gpo::gpo_rule_configured_enabled || powertoys_gpo_configuration[disable_module_name] == powertoys_gpo::gpo_rule_configured_disabled)) { // If gpo forces the enabled setting, no need to check the setting for this PowerToy. It will be applied later on this function. continue; diff --git a/src/runner/main.cpp b/src/runner/main.cpp index dd35697e09..bd7cabdec7 100644 --- a/src/runner/main.cpp +++ b/src/runner/main.cpp @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -303,6 +304,9 @@ toast_notification_handler_result toast_notification_handler(const std::wstring_ int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR lpCmdLine, int /*nCmdShow*/) { + Shared::Trace::ETWTrace trace{}; + trace.UpdateState(true); + Gdiplus::GdiplusStartupInput gpStartupInput; ULONG_PTR gpToken; GdiplusStartup(&gpToken, &gpStartupInput, NULL); @@ -437,6 +441,13 @@ int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR l openScoobe = false; } + bool dataDiagnosticsDisabledByGpo = powertoys_gpo::getAllowDataDiagnosticsValue() == powertoys_gpo::gpo_rule_configured_disabled; + if (dataDiagnosticsDisabledByGpo) + { + Logger::info(L"Data diagnostics: Data diagnostics is disabled by GPO."); + PTSettingsHelper::save_data_diagnostics(false); + } + if (elevated && with_dont_elevate_arg && !run_elevated_setting) { Logger::info("Scheduling restart as non elevated"); @@ -473,6 +484,9 @@ int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR l result = -1; } + trace.Flush(); + trace.UpdateState(false); + // We need to release the mutexes to be able to restart the application if (msi_mutex) { @@ -481,6 +495,7 @@ int WINAPI WinMain(HINSTANCE /*hInstance*/, HINSTANCE /*hPrevInstance*/, LPSTR l if (is_restart_scheduled()) { + modules().clear(); if (!restart_if_scheduled()) { // If it's not possible to restart non-elevated due to some condition in the user's configuration, user should start PowerToys manually. diff --git a/src/runner/pch.h b/src/runner/pch.h index a01e93cc17..537bef12d6 100644 --- a/src/runner/pch.h +++ b/src/runner/pch.h @@ -26,7 +26,6 @@ #include #include #include -#include #include #include diff --git a/src/runner/runner.vcxproj b/src/runner/runner.vcxproj index 90129f37f2..a55396a71a 100644 --- a/src/runner/runner.vcxproj +++ b/src/runner/runner.vcxproj @@ -107,6 +107,9 @@ {1d5be09d-78c0-4fd7-af00-ae7c1af7c525} + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + {98537082-0fdb-40de-abd8-0dc5a4269bab} diff --git a/src/runner/settings_window.cpp b/src/runner/settings_window.cpp index 6430824bb1..7e56622443 100644 --- a/src/runner/settings_window.cpp +++ b/src/runner/settings_window.cpp @@ -6,6 +6,7 @@ #include "powertoy_module.h" #include +#include #include "tray_icon.h" #include "general_settings.h" #include "restart_elevated.h" @@ -34,6 +35,7 @@ TwoWayPipeMessageIPC* current_settings_ipc = NULL; std::mutex ipc_mutex; std::atomic_bool g_isLaunchInProgress = false; std::atomic_bool isUpdateCheckThreadRunning = false; +HANDLE g_terminateSettingsEvent = CreateEventW(nullptr, false, false, CommonSharedConstants::TERMINATE_SETTINGS_SHARED_EVENT); json::JsonObject get_power_toys_settings() { @@ -622,9 +624,11 @@ void close_settings_window() { if (g_settings_process_id != 0) { + SetEvent(g_terminateSettingsEvent); wil::unique_handle proc{ OpenProcess(PROCESS_TERMINATE, false, g_settings_process_id) }; if (proc) { + WaitForSingleObject(proc.get(), 1500); TerminateProcess(proc.get(), 0); } } diff --git a/src/runner/trace.cpp b/src/runner/trace.cpp index 8c0dd74b50..1c15092679 100644 --- a/src/runner/trace.cpp +++ b/src/runner/trace.cpp @@ -3,6 +3,8 @@ #include "general_settings.h" +#include + TRACELOGGING_DEFINE_PROVIDER( g_hProvider, "Microsoft.PowerToys", @@ -10,19 +12,9 @@ TRACELOGGING_DEFINE_PROVIDER( (0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74), TraceLoggingOptionProjectTelemetry()); -void Trace::RegisterProvider() -{ - TraceLoggingRegister(g_hProvider); -} - -void Trace::UnregisterProvider() -{ - TraceLoggingUnregister(g_hProvider); -} - void Trace::EventLaunch(const std::wstring& versionNumber, bool isProcessElevated) { - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "Runner_Launch", TraceLoggingWideString(versionNumber.c_str(), "Version"), @@ -48,7 +40,7 @@ void Trace::SettingsChanged(const GeneralSettings& settings) } } - TraceLoggingWrite( + TraceLoggingWriteWrapper( g_hProvider, "GeneralSettingsChanged", TraceLoggingBoolean(settings.isStartupEnabled, "RunAtStartup"), diff --git a/src/runner/trace.h b/src/runner/trace.h index fe337723d7..3170fa665a 100644 --- a/src/runner/trace.h +++ b/src/runner/trace.h @@ -1,12 +1,12 @@ #pragma once +#include + struct GeneralSettings; -class Trace +class Trace : public telemetry::TraceBase { public: - static void RegisterProvider(); - static void UnregisterProvider(); static void EventLaunch(const std::wstring& versionNumber, bool isProcessElevated); static void SettingsChanged(const GeneralSettings& settings); }; diff --git a/src/settings-ui/Settings.UI/Helpers/ETLConverter.cs b/src/settings-ui/Settings.UI/Helpers/ETLConverter.cs new file mode 100644 index 0000000000..a4b8b57ae8 --- /dev/null +++ b/src/settings-ui/Settings.UI/Helpers/ETLConverter.cs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using ManagedCommon; + +namespace Microsoft.PowerToys.Settings.UI.Helpers +{ + public class ETLConverter + { + private const int TracerptConversionTimeout = 60000; // 60 seconds in milliseconds + private const string ETLConversionOutputFormat = "xml"; // Assuming XML output format + + private readonly string etwDirPath; + + private readonly string tracerptPath; + + public ETLConverter(string etwDirPath, string tracerptPath) + { + this.etwDirPath = etwDirPath; + this.tracerptPath = tracerptPath; + } + + private bool ETLConversionsFailed { get; set; } + + public async Task ConvertDiagnosticsETLsAsync(CancellationToken cancellationToken = default) + { + var etlConversionTasks = new List(); + var directoryInfo = new DirectoryInfo(etwDirPath); + + foreach (var fileInfo in directoryInfo.GetFiles("*.etl", SearchOption.AllDirectories)) + { + var task = Task.Run(() => ConvertETLAsync(fileInfo.FullName, cancellationToken), cancellationToken); + etlConversionTasks.Add(task); + } + + try + { + await Task.WhenAll(etlConversionTasks); + } + catch (Exception) + { + ETLConversionsFailed = true; + } + + if (ETLConversionsFailed) + { + throw new InvalidOperationException("One or more ETL conversions failed."); + } + } + + private void ConvertETLAsync(string etlFilePathToConvert, CancellationToken cancellationToken) + { + var outputFilePath = Path.ChangeExtension(etlFilePathToConvert, $".{ETLConversionOutputFormat}"); + + if (File.Exists(outputFilePath)) + { + File.Delete(outputFilePath); + } + + var tracerPtArguments = $"\"{etlFilePathToConvert}\" -o \"{outputFilePath}\" -lr -y -of {ETLConversionOutputFormat}"; + + var startInfo = new ProcessStartInfo + { + FileName = tracerptPath + "\\tracerpt.exe", + Arguments = tracerPtArguments, + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + }; + + using (var process = Process.Start(startInfo)) + { + if (process == null) + { + Logger.LogError("Failed to start tracerpt process."); + } + + var processExited = process.WaitForExit(TracerptConversionTimeout); + + if (!processExited) + { + process.Kill(); + Logger.LogError("ETL conversion process timed out."); + } + + var exitCode = process.ExitCode; + if (exitCode != 0) + { + Logger.LogError($"ETL conversion failed with exit code {exitCode}."); + } + } + } + } +} diff --git a/src/settings-ui/Settings.UI/Helpers/NativeEventWaiter.cs b/src/settings-ui/Settings.UI/Helpers/NativeEventWaiter.cs new file mode 100644 index 0000000000..0aee5d3fd6 --- /dev/null +++ b/src/settings-ui/Settings.UI/Helpers/NativeEventWaiter.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Threading; + +using Microsoft.UI.Dispatching; + +namespace Microsoft.PowerToys.Settings.UI.Helpers +{ + public static class NativeEventWaiter + { + public static void WaitForEventLoop(string eventName, Action callback) + { + var dispatcherQueue = DispatcherQueue.GetForCurrentThread(); + new Thread(() => + { + var eventHandle = new EventWaitHandle(false, EventResetMode.AutoReset, eventName); + while (true) + { + if (eventHandle.WaitOne()) + { + dispatcherQueue.TryEnqueue(() => callback()); + } + } + }).Start(); + } + } +} diff --git a/src/settings-ui/Settings.UI/Helpers/StartProcessHelper.cs b/src/settings-ui/Settings.UI/Helpers/StartProcessHelper.cs index 816b6bcc63..70157075ac 100644 --- a/src/settings-ui/Settings.UI/Helpers/StartProcessHelper.cs +++ b/src/settings-ui/Settings.UI/Helpers/StartProcessHelper.cs @@ -11,6 +11,7 @@ namespace Microsoft.PowerToys.Settings.UI.Helpers public static class StartProcessHelper { public const string ColorsSettings = "ms-settings:colors"; + public const string DiagnosticsAndFeedback = "ms-settings:privacy-feedback"; public static string AnimationsSettings => OSVersionHelper.IsWindows11() ? "ms-settings:easeofaccess-visualeffects" diff --git a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs index 4c44dd250b..85f64f62cc 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/App.xaml.cs @@ -70,6 +70,8 @@ namespace Microsoft.PowerToys.Settings.UI public static Action IPCMessageReceivedCallback { get; set; } + public ETWTrace EtwTrace { get; private set; } = new ETWTrace(); + /// /// Initializes a new instance of the class. /// Initializes the singleton application object. This is the first line of authored code @@ -88,6 +90,13 @@ namespace Microsoft.PowerToys.Settings.UI InitializeComponent(); UnhandledException += App_UnhandledException; + + NativeEventWaiter.WaitForEventLoop( + Constants.PowerToysRunnerTerminateSettingsEvent(), () => + { + EtwTrace?.Dispose(); + Environment.Exit(0); + }); } private void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs e) diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverview.xaml b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverview.xaml index b6456d5eb0..b04c800bca 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverview.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverview.xaml @@ -5,6 +5,8 @@ xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" + xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls" + xmlns:ui="using:CommunityToolkit.WinUI" mc:Ignorable="d"> @@ -17,6 +19,35 @@ + + + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverview.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverview.xaml.cs index d43bda83bf..ba4595ea06 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverview.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeOverview.xaml.cs @@ -2,9 +2,11 @@ // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. +using global::PowerToys.GPOWrapper; using Microsoft.PowerToys.Settings.UI.OOBE.Enums; using Microsoft.PowerToys.Settings.UI.OOBE.ViewModel; using Microsoft.PowerToys.Settings.UI.Views; +using Microsoft.PowerToys.Telemetry; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Navigation; @@ -14,9 +16,46 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views { public OobePowerToysModule ViewModel { get; set; } + private bool _enableDataDiagnostics; + + public bool EnableDataDiagnostics + { + get + { + return _enableDataDiagnostics; + } + + set + { + if (_enableDataDiagnostics != value) + { + _enableDataDiagnostics = value; + + DataDiagnosticsSettings.SetEnabledValue(_enableDataDiagnostics); + + this.DispatcherQueue.TryEnqueue(Microsoft.UI.Dispatching.DispatcherQueuePriority.Normal, () => + { + ShellPage.ShellHandler?.SignalGeneralDataUpdate(); + }); + } + } + } + + public bool ShowDataDiagnosticsSetting => GetIsDataDiagnosticsInfoBarEnabled(); + + private bool GetIsDataDiagnosticsInfoBarEnabled() + { + var isDataDiagnosticsGpoDisallowed = GPOWrapper.GetAllowDataDiagnosticsValue() == GpoRuleConfigured.Disabled; + + return !isDataDiagnosticsGpoDisallowed; + } + public OobeOverview() { this.InitializeComponent(); + + _enableDataDiagnostics = DataDiagnosticsSettings.GetEnabledValue(); + ViewModel = new OobePowerToysModule(OobeShellPage.OobeShellHandler.Modules[(int)PowerToysModules.Overview]); DataContext = ViewModel; } @@ -31,6 +70,16 @@ namespace Microsoft.PowerToys.Settings.UI.OOBE.Views ViewModel.LogOpeningSettingsEvent(); } + private void GeneralSettingsLaunchButton_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e) + { + if (OobeShellPage.OpenMainWindowCallback != null) + { + OobeShellPage.OpenMainWindowCallback(typeof(GeneralPage)); + } + + ViewModel.LogOpeningSettingsEvent(); + } + protected override void OnNavigatedTo(NavigationEventArgs e) { ViewModel.LogOpeningModuleEvent(); diff --git a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeWhatsNew.xaml b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeWhatsNew.xaml index 06c3b29067..febd14186a 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeWhatsNew.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/OOBE/Views/OobeWhatsNew.xaml @@ -6,18 +6,66 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:tk7controls="using:CommunityToolkit.WinUI.UI.Controls" + xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls" + xmlns:ui="using:CommunityToolkit.WinUI" Loaded="Page_Loaded" mc:Ignorable="d"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + Launch Registry Preview "Registry Preview" is the name of the utility + + Mouse Jump + Refers to the utility name + Quickly move the mouse pointer long distances. "Mouse Jump" is the name of the utility. Mouse is the hardware mouse. - - Mouse Jump - Refers to the utility name + + Enable Mouse Jump + "Mouse Jump" is the name of the utility. Customize the shortcut to turn on or off this mode @@ -4138,9 +4142,124 @@ Activate by holding the key for the character you want to add an accent to, then Activation shortcut - - Enable Mouse Jump - "Mouse Jump" is the name of the utility. + + Thumbnail Size + + + Constrain thumbnail image size to a maximum of + + + pixels + + + Maximum height (px) + px = pixels + + + Maximum width (px) + px = pixels + + + Appearance + + + Preview style + + + Select a predefined style, or apply a custom one + + + Compact + + + Bezelled + + + Custom + + + Copy to Custom preview style + + + Background color 1 + + + The start color for the background gradient fill on the preview image + + + Background color 2 + + + The end color for the background gradient fill on the preview image + + + Border thickness + + + The thickness (in pixels) of the border that surrounds the preview image + + + Border color + + + The color of the border that surrounds the preview image + + + Border 3D depth + + + The width (in pixels) of the 3d effect on the border that surrounds the preview image + + + Border padding + + + The amount of padding to draw between the border that surrounds the main preview image and the screen images + + + Bezel thickness + + + The thickness (in pixels) of the border that surrounds the individual screen images + + + Bezel color + + + The color of the border that surrounds the individual screen images + + + Bezel 3D depth + + + The width (in pixels) of the 3d effect on the border that surrounds individual screen images + + + Screen spacing + + + The width (in pixels) of the margin drawn between individual screen images + + + Screen color 1 + + + The start color for the background gradient fill on individual screen images + + + Screen color 2 + + + The end color for the background gradient fill on individual screen images + + + Copy to Custom preview style + + + This will replace the current settings in the Custom preview style. + + + Copy 127.0.0.1, ::1, ... @@ -4168,23 +4287,6 @@ Activate by holding the key for the character you want to add an accent to, then Using this shortcut may prevent non-text paste actions (e.g. images, files) or built-in paste plain text actions in other applications from functioning. - - Thumbnail Size - - - Constrain thumbnail image size to a maximum of - - - pixels - - - Maximum height (px) - px = pixels - - - Maximum width (px) - px = pixels - A lightning fast file preview feature for Windows. {Locked="Windows"} diff --git a/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs index 5533a6f04f..e307b40606 100644 --- a/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs +++ b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel.cs @@ -14,7 +14,7 @@ using Microsoft.PowerToys.Settings.Utilities; namespace Microsoft.PowerToys.Settings.UI.ViewModels { - public class MouseUtilsViewModel : Observable + public partial class MouseUtilsViewModel : Observable { private ISettingsUtils SettingsUtils { get; set; } @@ -24,8 +24,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private MouseHighlighterSettings MouseHighlighterSettingsConfig { get; set; } - private MouseJumpSettings MouseJumpSettingsConfig { get; set; } - private MousePointerCrosshairsSettings MousePointerCrosshairsSettingsConfig { get; set; } public MouseUtilsViewModel(ISettingsUtils settingsUtils, ISettingsRepository settingsRepository, ISettingsRepository findMyMouseSettingsRepository, ISettingsRepository mouseHighlighterSettingsRepository, ISettingsRepository mouseJumpSettingsRepository, ISettingsRepository mousePointerCrosshairsSettingsRepository, Func ipcMSGCallBackFunc) @@ -80,10 +78,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels _highlightFadeDurationMs = MouseHighlighterSettingsConfig.Properties.HighlightFadeDurationMs.Value; _highlighterAutoActivate = MouseHighlighterSettingsConfig.Properties.AutoActivate.Value; - ArgumentNullException.ThrowIfNull(mouseJumpSettingsRepository); - - MouseJumpSettingsConfig = mouseJumpSettingsRepository.SettingsConfig; - MouseJumpSettingsConfig.Properties.ThumbnailSize.PropertyChanged += MouseJumpThumbnailSizePropertyChanged; + this.InitializeMouseJumpSettings(mouseJumpSettingsRepository); ArgumentNullException.ThrowIfNull(mousePointerCrosshairsSettingsRepository); @@ -138,17 +133,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels _isMouseHighlighterEnabled = GeneralSettingsConfig.Enabled.MouseHighlighter; } - _jumpEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredMouseJumpEnabledValue(); - if (_jumpEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _jumpEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) - { - // Get the enabled state from GPO. - _jumpEnabledStateIsGPOConfigured = true; - _isMouseJumpEnabled = _jumpEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; - } - else - { - _isMouseJumpEnabled = GeneralSettingsConfig.Enabled.MouseJump; - } + this.InitializeMouseJumpEnabledValues(); _mousePointerCrosshairsEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredMousePointerCrosshairsEnabledValue(); if (_mousePointerCrosshairsEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _mousePointerCrosshairsEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) @@ -657,87 +642,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels SettingsUtils.SaveSettings(MouseHighlighterSettingsConfig.ToJsonString(), MouseHighlighterSettings.ModuleName); } - public bool IsMouseJumpEnabled - { - get => _isMouseJumpEnabled; - set - { - if (_jumpEnabledStateIsGPOConfigured) - { - // If it's GPO configured, shouldn't be able to change this state. - return; - } - - if (_isMouseJumpEnabled != value) - { - _isMouseJumpEnabled = value; - - GeneralSettingsConfig.Enabled.MouseJump = value; - OnPropertyChanged(nameof(_isMouseJumpEnabled)); - - OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig); - SendConfigMSG(outgoing.ToString()); - - NotifyMouseJumpPropertyChanged(); - } - } - } - - public bool IsJumpEnabledGpoConfigured - { - get => _jumpEnabledStateIsGPOConfigured; - } - - public HotkeySettings MouseJumpActivationShortcut - { - get - { - return MouseJumpSettingsConfig.Properties.ActivationShortcut; - } - - set - { - if (MouseJumpSettingsConfig.Properties.ActivationShortcut != value) - { - MouseJumpSettingsConfig.Properties.ActivationShortcut = value ?? MouseJumpSettingsConfig.Properties.DefaultActivationShortcut; - NotifyMouseJumpPropertyChanged(); - } - } - } - - public MouseJumpThumbnailSize MouseJumpThumbnailSize - { - get - { - return MouseJumpSettingsConfig.Properties.ThumbnailSize; - } - - set - { - if ((MouseJumpSettingsConfig.Properties.ThumbnailSize.Width != value?.Width) - && (MouseJumpSettingsConfig.Properties.ThumbnailSize.Height != value?.Height)) - { - MouseJumpSettingsConfig.Properties.ThumbnailSize = value; - NotifyMouseJumpPropertyChanged(); - } - } - } - - public void MouseJumpThumbnailSizePropertyChanged(object sender, PropertyChangedEventArgs e) - { - NotifyMouseJumpPropertyChanged(nameof(MouseJumpThumbnailSize)); - } - - public void NotifyMouseJumpPropertyChanged([CallerMemberName] string propertyName = null) - { - OnPropertyChanged(propertyName); - - SndMouseJumpSettings outsettings = new SndMouseJumpSettings(MouseJumpSettingsConfig); - SndModuleSettings ipcMessage = new SndModuleSettings(outsettings); - SendConfigMSG(ipcMessage.ToJsonString()); - SettingsUtils.SaveSettings(MouseJumpSettingsConfig.ToJsonString(), MouseJumpSettings.ModuleName); - } - public bool IsMousePointerCrosshairsEnabled { get => _isMousePointerCrosshairsEnabled; @@ -1017,10 +921,6 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels private int _highlightFadeDurationMs; private bool _highlighterAutoActivate; - private GpoRuleConfigured _jumpEnabledGpoRuleConfiguration; - private bool _jumpEnabledStateIsGPOConfigured; - private bool _isMouseJumpEnabled; - private GpoRuleConfigured _mousePointerCrosshairsEnabledGpoRuleConfiguration; private bool _mousePointerCrosshairsEnabledStateIsGPOConfigured; private bool _isMousePointerCrosshairsEnabled; diff --git a/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel_MouseJump.cs b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel_MouseJump.cs new file mode 100644 index 0000000000..2818c34a99 --- /dev/null +++ b/src/settings-ui/Settings.UI/ViewModels/MouseUtilsViewModel_MouseJump.cs @@ -0,0 +1,513 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Drawing.Imaging; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using global::PowerToys.GPOWrapper; +using Microsoft.PowerToys.Settings.UI.Library; +using Microsoft.PowerToys.Settings.UI.Library.Helpers; +using Microsoft.PowerToys.Settings.UI.Library.Interfaces; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Media.Imaging; +using MouseJump.Common.Helpers; +using MouseJump.Common.Imaging; +using MouseJump.Common.Models.Drawing; +using MouseJump.Common.Models.Settings; +using MouseJump.Common.Models.Styles; + +namespace Microsoft.PowerToys.Settings.UI.ViewModels +{ + public partial class MouseUtilsViewModel : Observable + { + private GpoRuleConfigured _jumpEnabledGpoRuleConfiguration; + private bool _jumpEnabledStateIsGPOConfigured; + private bool _isMouseJumpEnabled; + + internal MouseJumpSettings MouseJumpSettingsConfig { get; set; } + + private void InitializeMouseJumpSettings(ISettingsRepository mouseJumpSettingsRepository) + { + ArgumentNullException.ThrowIfNull(mouseJumpSettingsRepository); + this.MouseJumpSettingsConfig = mouseJumpSettingsRepository.SettingsConfig; + this.MouseJumpSettingsConfig.Properties.ThumbnailSize.PropertyChanged += this.MouseJumpThumbnailSizePropertyChanged; + } + + private void InitializeMouseJumpEnabledValues() + { + _jumpEnabledGpoRuleConfiguration = GPOWrapper.GetConfiguredMouseJumpEnabledValue(); + if (_jumpEnabledGpoRuleConfiguration == GpoRuleConfigured.Disabled || _jumpEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled) + { + // Get the enabled state from GPO. + _jumpEnabledStateIsGPOConfigured = true; + _isMouseJumpEnabled = _jumpEnabledGpoRuleConfiguration == GpoRuleConfigured.Enabled; + } + else + { + _isMouseJumpEnabled = GeneralSettingsConfig.Enabled.MouseJump; + } + } + + public bool IsMouseJumpEnabled + { + get => _isMouseJumpEnabled; + set + { + if (_jumpEnabledStateIsGPOConfigured) + { + // If it's GPO configured, shouldn't be able to change this state. + return; + } + + if (_isMouseJumpEnabled != value) + { + _isMouseJumpEnabled = value; + + GeneralSettingsConfig.Enabled.MouseJump = value; + OnPropertyChanged(nameof(_isMouseJumpEnabled)); + + OutGoingGeneralSettings outgoing = new OutGoingGeneralSettings(GeneralSettingsConfig); + SendConfigMSG(outgoing.ToString()); + + NotifyMouseJumpPropertyChanged(); + } + } + } + + public bool IsJumpEnabledGpoConfigured + { + get => _jumpEnabledStateIsGPOConfigured; + } + + public HotkeySettings MouseJumpActivationShortcut + { + get + { + return MouseJumpSettingsConfig.Properties.ActivationShortcut; + } + + set + { + if (MouseJumpSettingsConfig.Properties.ActivationShortcut != value) + { + MouseJumpSettingsConfig.Properties.ActivationShortcut = value ?? MouseJumpSettingsConfig.Properties.DefaultActivationShortcut; + NotifyMouseJumpPropertyChanged(); + } + } + } + + public MouseJumpThumbnailSize MouseJumpThumbnailSize + { + get + { + return MouseJumpSettingsConfig.Properties.ThumbnailSize; + } + + set + { + if ((MouseJumpSettingsConfig.Properties.ThumbnailSize.Width != value?.Width) + && (MouseJumpSettingsConfig.Properties.ThumbnailSize.Height != value?.Height)) + { + MouseJumpSettingsConfig.Properties.ThumbnailSize = value; + NotifyMouseJumpPropertyChanged(); + } + } + } + + private static Bitmap LoadImageResource(string filename) + { + var assembly = Assembly.GetExecutingAssembly(); + var assemblyName = new AssemblyName(assembly.FullName ?? throw new InvalidOperationException()); + var resourceName = $"Microsoft.{assemblyName.Name}.{filename.Replace("/", ".")}"; + var resourceNames = assembly.GetManifestResourceNames(); + if (!resourceNames.Contains(resourceName)) + { + throw new InvalidOperationException($"Embedded resource '{resourceName}' does not exist."); + } + + var stream = assembly.GetManifestResourceStream(resourceName) + ?? throw new InvalidOperationException(); + var image = (Bitmap)Image.FromStream(stream); + return image; + } + + private static Lazy MouseJumpDesktopImage => new( + () => MouseUtilsViewModel.LoadImageResource("UI/Images/MouseJump-Desktop.png") + ); + + public ImageSource MouseJumpPreviewImage + { + get + { + // keep these in sync with the layout of "Images\MouseJump-Desktop.png" + var screens = new List() + { + /* + these magic numbers are the pixel dimensions of the individual screens on the + fake desktop image - "Images\MouseJump-Desktop.png" - used to generate the + preview image in the Settings UI properties page for Mouse Jump. if you update + the fake desktop image be sure to update these values as well. + */ + new(635, 172, 272, 168), + new(0, 0, 635, 339), + }; + var desktopSize = LayoutHelper.GetCombinedScreenBounds(screens).Size; + /* + magic number 283 is the content height left in the settings card after removing the top and bottom chrome: + + 300px settings card height - 1px top border - 7px top margin - 8px bottom margin - 1px bottom border = 283px image height + + this ensures we get a preview image scaled at 100% so borders etc are shown at exact pixel sizes in the preview + */ + var canvasSize = new SizeInfo(desktopSize.Width, 283).Clamp(desktopSize); + + var previewType = Enum.TryParse(this.MouseJumpPreviewType, true, out var previewTypeResult) + ? previewTypeResult + : PreviewType.Bezelled; + var previewStyle = previewType switch + { + PreviewType.Compact => StyleHelper.CompactPreviewStyle.WithCanvasSize(desktopSize), + PreviewType.Bezelled => StyleHelper.BezelledPreviewStyle.WithCanvasSize(desktopSize), + PreviewType.Custom => new PreviewStyle( + canvasSize: canvasSize, + canvasStyle: new( + marginStyle: new(0), + borderStyle: new( + color: ConfigHelper.DeserializeFromConfigColorString( + this.MouseJumpBorderColor), + all: this.MouseJumpBorderThickness, + depth: this.MouseJumpBorder3dDepth + ), + paddingStyle: new( + all: this.MouseJumpBorderPadding + ), + backgroundStyle: new( + color1: ConfigHelper.DeserializeFromConfigColorString( + this.MouseJumpBackgroundColor1), + color2: ConfigHelper.DeserializeFromConfigColorString( + this.MouseJumpBackgroundColor2) + ) + ), + screenStyle: new( + marginStyle: new( + all: this.MouseJumpScreenMargin + ), + borderStyle: new( + color: ConfigHelper.DeserializeFromConfigColorString( + this.MouseJumpBezelColor), + all: this.MouseJumpBezelThickness, + depth: this.MouseJumpBezel3dDepth + ), + paddingStyle: new(0), + backgroundStyle: new( + color1: ConfigHelper.DeserializeFromConfigColorString( + this.MouseJumpScreenColor1), + color2: ConfigHelper.DeserializeFromConfigColorString( + this.MouseJumpScreenColor2) + ) + )), + _ => throw new InvalidOperationException( + $"Unhandled {nameof(MouseJumpPreviewType)} '{previewType}'"), + }; + + var previewLayout = LayoutHelper.GetPreviewLayout( + previewStyle: previewStyle, + screens: screens, + activatedLocation: new(0, 0)); + + var desktopImage = MouseUtilsViewModel.MouseJumpDesktopImage.Value; + var imageCopyService = new StaticImageRegionCopyService(desktopImage); + using var previewImage = DrawingHelper.RenderPreview( + previewLayout, + imageCopyService); + + // save the image to a memory stream + using var stream = new MemoryStream(); + previewImage.Save(stream, ImageFormat.Png); + stream.Position = 0; + + // load the memory stream into a bitmap image + var bitmap = new BitmapImage(); + var rnd = stream.AsRandomAccessStream(); + bitmap.DecodePixelWidth = previewImage.Width; + bitmap.DecodePixelHeight = previewImage.Height; + bitmap.SetSource(rnd); + return bitmap; + } + } + + public string MouseJumpPreviewType + { + get + { + return MouseJumpSettingsConfig.Properties.PreviewType; + } + + set + { + if (value != MouseJumpSettingsConfig.Properties.PreviewType) + { + MouseJumpSettingsConfig.Properties.PreviewType = value; + NotifyMouseJumpPropertyChanged(); + NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage)); + } + } + } + + public string MouseJumpBackgroundColor1 + { + get + { + var value = MouseJumpSettingsConfig.Properties.BackgroundColor1; + value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000"; + return value; + } + + set + { + value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000"; + if (!value.Equals(MouseJumpSettingsConfig.Properties.BackgroundColor1, StringComparison.OrdinalIgnoreCase)) + { + MouseJumpSettingsConfig.Properties.BackgroundColor1 = value; + NotifyMouseJumpPropertyChanged(); + NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage)); + } + } + } + + public string MouseJumpBackgroundColor2 + { + get + { + var value = MouseJumpSettingsConfig.Properties.BackgroundColor2; + value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000"; + return value; + } + + set + { + value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000"; + if (!value.Equals(MouseJumpSettingsConfig.Properties.BackgroundColor2, StringComparison.OrdinalIgnoreCase)) + { + MouseJumpSettingsConfig.Properties.BackgroundColor2 = value; + NotifyMouseJumpPropertyChanged(); + NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage)); + } + } + } + + public int MouseJumpBorderThickness + { + get + { + return MouseJumpSettingsConfig.Properties.BorderThickness; + } + + set + { + if (value != MouseJumpSettingsConfig.Properties.BorderThickness) + { + MouseJumpSettingsConfig.Properties.BorderThickness = value; + NotifyMouseJumpPropertyChanged(); + NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage)); + } + } + } + + public string MouseJumpBorderColor + { + get + { + var value = MouseJumpSettingsConfig.Properties.BorderColor; + value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000"; + return value; + } + + set + { + value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000"; + if (!value.Equals(MouseJumpSettingsConfig.Properties.BorderColor, StringComparison.OrdinalIgnoreCase)) + { + MouseJumpSettingsConfig.Properties.BorderColor = value; + NotifyMouseJumpPropertyChanged(); + NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage)); + } + } + } + + public int MouseJumpBorder3dDepth + { + get + { + return MouseJumpSettingsConfig.Properties.Border3dDepth; + } + + set + { + if (value != MouseJumpSettingsConfig.Properties.Border3dDepth) + { + MouseJumpSettingsConfig.Properties.Border3dDepth = value; + NotifyMouseJumpPropertyChanged(); + NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage)); + } + } + } + + public int MouseJumpBorderPadding + { + get + { + return MouseJumpSettingsConfig.Properties.BorderPadding; + } + + set + { + if (value != MouseJumpSettingsConfig.Properties.BorderPadding) + { + MouseJumpSettingsConfig.Properties.BorderPadding = value; + NotifyMouseJumpPropertyChanged(); + NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage)); + } + } + } + + public int MouseJumpBezelThickness + { + get + { + return MouseJumpSettingsConfig.Properties.BezelThickness; + } + + set + { + if (value != MouseJumpSettingsConfig.Properties.BezelThickness) + { + MouseJumpSettingsConfig.Properties.BezelThickness = value; + NotifyMouseJumpPropertyChanged(); + NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage)); + } + } + } + + public string MouseJumpBezelColor + { + get + { + var value = MouseJumpSettingsConfig.Properties.BezelColor; + value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000"; + return value; + } + + set + { + value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000"; + if (!value.Equals(MouseJumpSettingsConfig.Properties.BezelColor, StringComparison.OrdinalIgnoreCase)) + { + MouseJumpSettingsConfig.Properties.BezelColor = value; + NotifyMouseJumpPropertyChanged(); + NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage)); + } + } + } + + public int MouseJumpBezel3dDepth + { + get + { + return MouseJumpSettingsConfig.Properties.Bezel3dDepth; + } + + set + { + if (value != MouseJumpSettingsConfig.Properties.Bezel3dDepth) + { + MouseJumpSettingsConfig.Properties.Bezel3dDepth = value; + NotifyMouseJumpPropertyChanged(); + NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage)); + } + } + } + + public int MouseJumpScreenMargin + { + get + { + return MouseJumpSettingsConfig.Properties.ScreenMargin; + } + + set + { + if (value != MouseJumpSettingsConfig.Properties.ScreenMargin) + { + MouseJumpSettingsConfig.Properties.ScreenMargin = value; + NotifyMouseJumpPropertyChanged(); + NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage)); + } + } + } + + public string MouseJumpScreenColor1 + { + get + { + var value = MouseJumpSettingsConfig.Properties.ScreenColor1; + value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000"; + return value; + } + + set + { + value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000"; + if (!value.Equals(MouseJumpSettingsConfig.Properties.ScreenColor1, StringComparison.OrdinalIgnoreCase)) + { + MouseJumpSettingsConfig.Properties.ScreenColor1 = value; + NotifyMouseJumpPropertyChanged(); + NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage)); + } + } + } + + public string MouseJumpScreenColor2 + { + get + { + var value = MouseJumpSettingsConfig.Properties.ScreenColor2; + value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000"; + return value; + } + + set + { + value = (value != null) ? SettingsUtilities.ToRGBHex(value) : "#000000"; + if (!value.Equals(MouseJumpSettingsConfig.Properties.ScreenColor2, StringComparison.OrdinalIgnoreCase)) + { + MouseJumpSettingsConfig.Properties.ScreenColor2 = value; + NotifyMouseJumpPropertyChanged(); + NotifyMouseJumpPropertyChanged(nameof(this.MouseJumpPreviewImage)); + } + } + } + + public void MouseJumpThumbnailSizePropertyChanged(object sender, PropertyChangedEventArgs e) + { + NotifyMouseJumpPropertyChanged(nameof(MouseJumpThumbnailSize)); + } + + public void NotifyMouseJumpPropertyChanged([CallerMemberName] string propertyName = null) + { + OnPropertyChanged(propertyName); + + SndMouseJumpSettings outsettings = new SndMouseJumpSettings(MouseJumpSettingsConfig); + SndModuleSettings ipcMessage = new SndModuleSettings(outsettings); + SendConfigMSG(ipcMessage.ToJsonString()); + SettingsUtils.SaveSettings(MouseJumpSettingsConfig.ToJsonString(), MouseJumpSettings.ModuleName); + } + } +} From 08fe8089b84468b66628af221bffddd1750e02f0 Mon Sep 17 00:00:00 2001 From: Ani <115020168+drawbyperpetual@users.noreply.github.com> Date: Tue, 26 Nov 2024 21:38:41 +0100 Subject: [PATCH 80/95] [Logs]Fixed name of logging method in managed logging (#35952) * Fixed name of method in managed logging * Added hardening --- src/common/ManagedCommon/Logger.cs | 52 +++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/src/common/ManagedCommon/Logger.cs b/src/common/ManagedCommon/Logger.cs index 367480d293..78b27afefc 100644 --- a/src/common/ManagedCommon/Logger.cs +++ b/src/common/ManagedCommon/Logger.cs @@ -7,6 +7,7 @@ using System.Diagnostics; using System.Globalization; using System.IO; using System.Reflection; +using System.Runtime.CompilerServices; using PowerToys.Interop; @@ -52,16 +53,18 @@ namespace ManagedCommon Trace.AutoFlush = true; } + [MethodImpl(MethodImplOptions.NoInlining)] public static void LogError(string message) { Log(message, Error); } + [MethodImpl(MethodImplOptions.NoInlining)] public static void LogError(string message, Exception ex) { if (ex == null) { - LogError(message); + Log(message, Error); } else { @@ -84,26 +87,31 @@ namespace ManagedCommon } } + [MethodImpl(MethodImplOptions.NoInlining)] public static void LogWarning(string message) { Log(message, Warning); } + [MethodImpl(MethodImplOptions.NoInlining)] public static void LogInfo(string message) { Log(message, Info); } + [MethodImpl(MethodImplOptions.NoInlining)] public static void LogDebug(string message) { Log(message, Debug); } + [MethodImpl(MethodImplOptions.NoInlining)] public static void LogTrace() { Log(string.Empty, TraceFlag); } + [MethodImpl(MethodImplOptions.NoInlining)] private static void Log(string message, string type) { Trace.WriteLine("[" + DateTime.Now.TimeOfDay + "] [" + type + "] " + GetCallerInfo()); @@ -116,13 +124,49 @@ namespace ManagedCommon Trace.Unindent(); } + [MethodImpl(MethodImplOptions.NoInlining)] private static string GetCallerInfo() { StackTrace stackTrace = new(); - var methodName = stackTrace.GetFrame(3)?.GetMethod(); - var className = methodName?.DeclaringType.Name; - return className + "::" + methodName?.Name; + var callerMethod = GetCallerMethod(stackTrace); + + return $"{callerMethod?.DeclaringType?.Name}::{callerMethod.Name}"; + } + + private static MethodBase GetCallerMethod(StackTrace stackTrace) + { + const int topFrame = 3; + + var topMethod = stackTrace.GetFrame(topFrame)?.GetMethod(); + + try + { + if (topMethod?.Name == nameof(IAsyncStateMachine.MoveNext) && typeof(IAsyncStateMachine).IsAssignableFrom(topMethod?.DeclaringType)) + { + // Async method; return actual method as determined by heuristic: + // "Nearest method on stack to async state-machine's MoveNext() in same namespace but in a different type". + // There are tighter ways of determining the actual method, but this is good enough and probably faster. + for (int deepFrame = topFrame + 1; deepFrame < stackTrace.FrameCount; deepFrame++) + { + var deepMethod = stackTrace.GetFrame(deepFrame)?.GetMethod(); + + if (deepMethod?.DeclaringType != topMethod?.DeclaringType && deepMethod?.DeclaringType?.Namespace == topMethod?.DeclaringType?.Namespace) + { + return deepMethod; + } + } + } + } + catch (Exception) + { + // Ignore exceptions in Release. The code above won't throw, but if it does, we don't want to crash the app. +#if DEBUG + throw; +#endif + } + + return topMethod; } } } From 084402a2bd1986c775762abc917889124b45afc3 Mon Sep 17 00:00:00 2001 From: Christian Gaarden Gaardmark Date: Wed, 27 Nov 2024 06:22:05 -0800 Subject: [PATCH 81/95] [New+]Windows 10 support (#35832) --- .github/actions/spell-check/expect.txt | 2 + .pipelines/ESRPSigning_core.json | 1 + PowerToys.sln | 15 + installer/PowerToysSetup/NewPlus.wxs | 14 + .../NewPlus.ShellExtension.win10.vcxproj | 152 ++++++++++ ...wPlus.ShellExtension.win10.vcxproj.filters | 116 ++++++++ .../dll.def | 6 + .../dll_main.cpp | 44 +++ .../dll_main.h | 6 + .../new.base.rc | 54 ++++ .../packages.config | 5 + .../pch.cpp | 3 + .../NewShellExtensionContextMenu.win10/pch.h | 39 +++ .../resource.base.h | 12 + .../resources.resx | 132 +++++++++ .../shell_context_menu_win10.cpp | 270 ++++++++++++++++++ .../shell_context_menu_win10.h | 45 +++ .../NewShellExtensionContextMenu.vcxproj | 3 +- ...wShellExtensionContextMenu.vcxproj.filters | 18 +- .../new_utilities.cpp | 13 + .../new_utilities.h | 256 +++++++++++++++++ .../shell_context_menu.h | 1 - .../shell_context_sub_menu.cpp | 7 +- .../shell_context_sub_menu_item.cpp | 50 +--- .../template_folder.cpp | 5 + .../template_folder.h | 2 + .../template_item.cpp | 56 ++-- .../template_item.h | 8 +- .../NewShellExtensionContextMenu/trace.cpp | 9 + .../NewShellExtensionContextMenu/trace.h | 1 + .../SettingsXAML/Views/NewPlusPage.xaml | 7 - .../Settings.UI/Strings/en-us/Resources.resw | 3 - 32 files changed, 1246 insertions(+), 109 deletions(-) create mode 100644 src/modules/NewPlus/NewShellExtensionContextMenu.win10/NewPlus.ShellExtension.win10.vcxproj create mode 100644 src/modules/NewPlus/NewShellExtensionContextMenu.win10/NewPlus.ShellExtension.win10.vcxproj.filters create mode 100644 src/modules/NewPlus/NewShellExtensionContextMenu.win10/dll.def create mode 100644 src/modules/NewPlus/NewShellExtensionContextMenu.win10/dll_main.cpp create mode 100644 src/modules/NewPlus/NewShellExtensionContextMenu.win10/dll_main.h create mode 100644 src/modules/NewPlus/NewShellExtensionContextMenu.win10/new.base.rc create mode 100644 src/modules/NewPlus/NewShellExtensionContextMenu.win10/packages.config create mode 100644 src/modules/NewPlus/NewShellExtensionContextMenu.win10/pch.cpp create mode 100644 src/modules/NewPlus/NewShellExtensionContextMenu.win10/pch.h create mode 100644 src/modules/NewPlus/NewShellExtensionContextMenu.win10/resource.base.h create mode 100644 src/modules/NewPlus/NewShellExtensionContextMenu.win10/resources.resx create mode 100644 src/modules/NewPlus/NewShellExtensionContextMenu.win10/shell_context_menu_win10.cpp create mode 100644 src/modules/NewPlus/NewShellExtensionContextMenu.win10/shell_context_menu_win10.h create mode 100644 src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.cpp diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index d657c65ff3..dc7eefe607 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -997,6 +997,7 @@ newitem newpath newplus NEWPLUSCONTEXTMENU +NEWPLUSSHELLEXTENSIONWIN newrow newsgroups NIF @@ -1168,6 +1169,7 @@ pnid Pnp Popups POPUPWINDOW +POSITIONITEM POWERRENAMECONTEXTMENU powerrenameinput POWERRENAMETEST diff --git a/.pipelines/ESRPSigning_core.json b/.pipelines/ESRPSigning_core.json index e1f0101fa2..878716c7d7 100644 --- a/.pipelines/ESRPSigning_core.json +++ b/.pipelines/ESRPSigning_core.json @@ -181,6 +181,7 @@ "WinUI3Apps\\PowerToys.NewPlus.ShellExtension.dll", "WinUI3Apps\\NewPlusPackage.msix", + "WinUI3Apps\\PowerToys.NewPlus.ShellExtension.win10.dll", "PowerAccent.Core.dll", "PowerToys.PowerAccent.dll", diff --git a/PowerToys.sln b/PowerToys.sln index 053bfd0c73..fc40f9d7d5 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -632,6 +632,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseWithoutBorders.UnitTes EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesCsharpLibrary", "src\modules\Workspaces\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj", "{89D0E199-B17A-418C-B2F8-7375B6708357}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NewPlus.ShellExtension.win10", "src\modules\NewPlus\NewShellExtensionContextMenu.win10\NewPlus.ShellExtension.win10.vcxproj", "{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM64 = Debug|ARM64 @@ -2792,6 +2794,18 @@ Global {89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x64.Build.0 = Release|x64 {89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.ActiveCfg = Release|x64 {89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.Build.0 = Release|x64 + {0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|ARM64.Build.0 = Debug|ARM64 + {0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x64.ActiveCfg = Debug|x64 + {0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x64.Build.0 = Debug|x64 + {0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x86.ActiveCfg = Debug|x64 + {0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x86.Build.0 = Debug|x64 + {0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|ARM64.ActiveCfg = Release|ARM64 + {0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|ARM64.Build.0 = Release|ARM64 + {0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.ActiveCfg = Release|x64 + {0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.Build.0 = Release|x64 + {0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x86.ActiveCfg = Release|x64 + {0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x86.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -3024,6 +3038,7 @@ Global {8F021B46-362B-485C-BFBA-CCF83E820CBD} = {8F62026A-294B-41C6-8839-87463613F216} {66614C26-314C-4B91-9071-76133422CFEF} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC} {89D0E199-B17A-418C-B2F8-7375B6708357} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} + {0DB0F63A-D2F8-4DA3-A650-2D0B8724218E} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} diff --git a/installer/PowerToysSetup/NewPlus.wxs b/installer/PowerToysSetup/NewPlus.wxs index 80fd5a94f4..4dd1c67701 100644 --- a/installer/PowerToysSetup/NewPlus.wxs +++ b/installer/PowerToysSetup/NewPlus.wxs @@ -18,6 +18,19 @@ + + + + + + + + + + + + + @@ -27,6 +40,7 @@ + diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu.win10/NewPlus.ShellExtension.win10.vcxproj b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/NewPlus.ShellExtension.win10.vcxproj new file mode 100644 index 0000000000..6d975b3326 --- /dev/null +++ b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/NewPlus.ShellExtension.win10.vcxproj @@ -0,0 +1,152 @@ + + + + + + + + 17.0 + Win32Proj + {0db0f63a-d2f8-4da3-a650-2d0b8724218e} + NewPlusShellExtensionWin10 + 10.0.22621.0 + + + + DynamicLibrary + v143 + Unicode + + + true + + + false + true + + + + + + + + + + + + ..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\ + PowerToys.NewPlus.ShellExtension.win10 + + + + + + Level3 + true + _DEBUG;NEWPLUSSHELLEXTENSIONWIN10_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories);..\NewShellExtensionContextMenu + false + stdcpplatest + + + Windows + true + false + runtimeobject.lib;$(CoreLibraryDependencies) + dll.def + + + + + Level3 + true + true + true + NDEBUG;NEWPLUSSHELLEXTENSIONWIN10_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + true + Use + ..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories);..\NewShellExtensionContextMenu + false + stdcpplatest + + + Windows + true + true + true + false + runtimeobject.lib;$(CoreLibraryDependencies) + dll.def + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Create + + + + + + + + + + Designer + + + + + {6955446d-23f7-4023-9bb3-8657f904af99} + + + {8f021b46-362b-485c-bfba-ccf83e820cbd} + + + {98537082-0fdb-40de-abd8-0dc5a4269bab} + + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu.win10/NewPlus.ShellExtension.win10.vcxproj.filters b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/NewPlus.ShellExtension.win10.vcxproj.filters new file mode 100644 index 0000000000..25399a81dc --- /dev/null +++ b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/NewPlus.ShellExtension.win10.vcxproj.filters @@ -0,0 +1,116 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {4cea4fff-ccef-4b62-9e46-f33da2b9a0cc} + + + + + Header Files + + + Header Files + + + Header Files + + + Generated Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Generated Files + + + + + + Source Files + + + Resource Files + + + Resource Files + + + + + + \ No newline at end of file diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu.win10/dll.def b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/dll.def new file mode 100644 index 0000000000..74de7c3181 --- /dev/null +++ b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/dll.def @@ -0,0 +1,6 @@ +LIBRARY + +EXPORTS + DllGetClassObject PRIVATE + DllCanUnloadNow PRIVATE + DllGetActivationFactory PRIVATE diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu.win10/dll_main.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/dll_main.cpp new file mode 100644 index 0000000000..b036eb2ed0 --- /dev/null +++ b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/dll_main.cpp @@ -0,0 +1,44 @@ +#include "pch.h" + +#include "shell_context_menu_win10.h" +#include "dll_main.h" +#include "trace.h" + +#include + +HMODULE module_instance_handle = 0; +Shared::Trace::ETWTrace trace(L"NewPlusShellExtension_Win10"); + +BOOL APIENTRY DllMain(HMODULE module_handle, DWORD ul_reason_for_call, LPVOID reserved) +{ + switch (ul_reason_for_call) + { + case DLL_PROCESS_ATTACH: + module_instance_handle = module_handle; + Trace::RegisterProvider(); + newplus::utilities::init_logger(); + break; + + case DLL_PROCESS_DETACH: + Trace::UnregisterProvider(); + break; + } + return TRUE; +} + +STDAPI DllGetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ IActivationFactory** factory) +{ + return Module::GetModule().GetActivationFactory(activatableClassId, factory); +} + +STDAPI DllCanUnloadNow() +{ + return Module::GetModule().GetObjectCount() == 0 ? S_OK : S_FALSE; +} + +STDAPI DllGetClassObject(_In_ REFCLSID ref_class_id, _In_ REFIID ref_interface_id, _Outptr_ LPVOID FAR* object) +{ + return Module::GetModule().GetClassObject(ref_class_id, ref_interface_id, object); +} + +CoCreatableClass(shell_context_menu_win10) diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu.win10/dll_main.h b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/dll_main.h new file mode 100644 index 0000000000..c2d69eebfd --- /dev/null +++ b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/dll_main.h @@ -0,0 +1,6 @@ +#pragma once + +#include + +extern HMODULE module_instance_handle; +extern Shared::Trace::ETWTrace trace; diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu.win10/new.base.rc b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/new.base.rc new file mode 100644 index 0000000000..5a35892751 --- /dev/null +++ b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/new.base.rc @@ -0,0 +1,54 @@ +#include +#include "Generated Files/resource.h" +#include "../../../../common/version/version.h" + +#define APSTUDIO_READONLY_SYMBOLS +#include "winres.h" +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION FILE_VERSION + PRODUCTVERSION PRODUCT_VERSION + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "CompanyName", COMPANY_NAME + VALUE "FileDescription", FILE_DESCRIPTION + VALUE "FileVersion", FILE_VERSION_STRING + VALUE "InternalName", INTERNAL_NAME + VALUE "LegalCopyright", COPYRIGHT_NOTE + VALUE "OriginalFilename", ORIGINAL_FILENAME + VALUE "ProductName", PRODUCT_NAME + VALUE "ProductVersion", PRODUCT_VERSION_STRING + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. + diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu.win10/packages.config b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/packages.config new file mode 100644 index 0000000000..ff4b059648 --- /dev/null +++ b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu.win10/pch.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/pch.cpp new file mode 100644 index 0000000000..1a6a81e238 --- /dev/null +++ b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/pch.cpp @@ -0,0 +1,3 @@ +// pch.cpp: source file corresponding to the pre-compiled header + +#include "pch.h" diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu.win10/pch.h b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/pch.h new file mode 100644 index 0000000000..3ddfa219b2 --- /dev/null +++ b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/pch.h @@ -0,0 +1,39 @@ +// Precompiled header file. + +#pragma once + +#define WIN32_LEAN_AND_MEAN +#define NOMCX +#define NOHELP +#define NOCOMM + +// Windows and STL +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +using namespace Microsoft::WRL; + +// PowerToys project common +#include +#include +#include +#include +#include +#include + +// New project specific +#include "dll_main.h" +#include "template_folder.h" +#include "settings.h" +#include "new_utilities.h" diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu.win10/resource.base.h b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/resource.base.h new file mode 100644 index 0000000000..25dc1f71b0 --- /dev/null +++ b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/resource.base.h @@ -0,0 +1,12 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. + +////////////////////////////// +// Non-localizable + +#define FILE_DESCRIPTION "PowerToys.New+" +#define INTERNAL_NAME "PowerToys.New+" +#define ORIGINAL_FILENAME "PowerToys.NewPlus.ShellExtension.win10.dll" + +// Non-localizable +////////////////////////////// diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu.win10/resources.resx b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/resources.resx new file mode 100644 index 0000000000..7db1823f95 --- /dev/null +++ b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/resources.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + New+ + The main context menu item that users click on. This should be localized to match New in Windows. e.g. Danish it would become Ny+ + + + Open templates + The menu item in the context menu that enables user to open the folder that contains their templates. + + + Templates + Default subfolder name where templates are stored. + + \ No newline at end of file diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu.win10/shell_context_menu_win10.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/shell_context_menu_win10.cpp new file mode 100644 index 0000000000..c54631df09 --- /dev/null +++ b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/shell_context_menu_win10.cpp @@ -0,0 +1,270 @@ +#include "pch.h" + +#include "shell_context_menu_win10.h" +#include "shell_context_sub_menu.h" +#include "shell_context_sub_menu_item.h" +#include "new_utilities.h" +#include "settings.h" +#include "trace.h" +#include "Generated Files/resource.h" +#include +#include + +using namespace Microsoft::WRL; +using namespace newplus; + +shell_context_menu_win10::~shell_context_menu_win10() +{ + for (const auto& handle : bitmap_handles) + { + DeleteObject(handle); + } +} + +#pragma region IShellExtInit +IFACEMETHODIMP shell_context_menu_win10::Initialize(PCIDLIST_ABSOLUTE, IDataObject*, HKEY) +{ + return S_OK; +} +#pragma endregion + +#pragma region IContextMenu +IFACEMETHODIMP shell_context_menu_win10::QueryContextMenu(HMENU menu_handle, UINT menu_index, UINT menu_first_cmd_id, UINT, UINT menu_flags) +{ + if (!NewSettingsInstance().GetEnabled() + || package::IsWin11OrGreater() + ) + { + return E_FAIL; + } + + if (menu_flags & CMF_DEFAULTONLY) + { + return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0); + } + + try + { + // Create the initial context popup menu containing the list of templates and open templates action + int menu_id = menu_first_cmd_id; + MENUITEMINFO newplus_main_context_menu_item; + HMENU sub_menu_of_templates = CreatePopupMenu(); + int sub_menu_index = 0; + + // Determine the New+ Template folder location + const std::filesystem::path template_folder_root = utilities::get_new_template_folder_location(); + + // Create the New+ Template folder location if it doesn't exist (very rare scenario) + utilities::create_folder_if_not_exist(template_folder_root); + + // Scan the folder for any files and folders (the templates) + templates = new template_folder(template_folder_root); + templates->rescan_template_folder(); + const auto number_of_templates = templates->list_of_templates.size(); + + // Create the New+ menu item and point to the initial context popup menu + static const std::wstring localized_context_menu_item = + GET_RESOURCE_STRING_FALLBACK(IDS_CONTEXT_MENU_ITEM_NEW, L"New+"); + wchar_t newplus_menu_name[20] = { 0 }; + wcscpy_s(newplus_menu_name, ARRAYSIZE(newplus_menu_name), localized_context_menu_item.c_str()); + newplus_main_context_menu_item.cbSize = sizeof(MENUITEMINFOW); + newplus_main_context_menu_item.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_SUBMENU; + newplus_main_context_menu_item.wID = menu_id; + newplus_main_context_menu_item.fType = MFT_STRING; + newplus_main_context_menu_item.dwTypeData = (PWSTR)newplus_menu_name; + newplus_main_context_menu_item.hSubMenu = sub_menu_of_templates; + const auto newplus_icon_index = 0; + + if (bitmap_handles.size() == 0) + { + const std::wstring icon_file = utilities::get_new_icon_resource_filepath( + module_instance_handle, ThemeHelpers::GetAppTheme()) + .c_str(); + HICON local_icon_handle = static_cast( + LoadImage(NULL, icon_file.c_str(), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_LOADFROMFILE)); + + if (local_icon_handle) + { + bitmap_handles.push_back(CreateBitmapFromIcon(local_icon_handle)); + DestroyIcon(local_icon_handle); + } + } + if (bitmap_handles.size() > newplus_icon_index && bitmap_handles[newplus_icon_index]) + { + newplus_main_context_menu_item.fMask |= MIIM_BITMAP; + newplus_main_context_menu_item.hbmpItem = bitmap_handles[newplus_icon_index]; + } + + menu_id++; + + // Add template items to context menu + int index = 0; + for (; index < number_of_templates; index++) + { + const auto template_item = templates->get_template_item(index); + add_template_item_to_context_menu(sub_menu_of_templates, sub_menu_index, template_item, menu_id, index); + menu_id++; + sub_menu_index++; + } + + // Add separator to context menu + add_separator_to_context_menu(sub_menu_of_templates, sub_menu_index); + sub_menu_index++; + + // Add "Open templates" item to context menu + add_open_templates_to_context_menu(sub_menu_of_templates, sub_menu_index, template_folder_root, menu_id, index); + menu_id++; + + if (!InsertMenuItem(menu_handle, menu_index, TRUE, &newplus_main_context_menu_item)) + { + Logger::error(L"QueryContextMenu() failed. {}", get_last_error_or_default(GetLastError())); + return HRESULT_FROM_WIN32(GetLastError()); + } + else + { + // Return the amount if entries inserted + const auto number_of_items_inserted = menu_id - menu_first_cmd_id; + return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, number_of_items_inserted); + } + } + catch (const std::exception& ex) + { + Logger::error(ex.what()); + } + + return E_FAIL; +} + +void shell_context_menu_win10::add_open_templates_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, const std::filesystem::path& template_folder_root, int menu_id, int index) +{ + static const std::wstring localized_context_menu_item_open_templates = + GET_RESOURCE_STRING_FALLBACK(IDS_CONTEXT_MENU_ITEM_OPEN_TEMPLATES, L"Open templates"); + wchar_t menu_name_open[256] = { 0 }; + wcscpy_s(menu_name_open, ARRAYSIZE(menu_name_open), localized_context_menu_item_open_templates.c_str()); + const auto open_folder_item = Make(template_folder_root); + MENUITEMINFO newplus_menu_item_open_templates; + newplus_menu_item_open_templates.cbSize = sizeof(MENUITEMINFO); + newplus_menu_item_open_templates.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID; + newplus_menu_item_open_templates.wID = menu_id; + newplus_menu_item_open_templates.fType = MFT_STRING; + newplus_menu_item_open_templates.dwTypeData = (PWSTR)menu_name_open; + + const auto open_templates_icon_index = index + 1; + if (bitmap_handles.size() <= open_templates_icon_index) + { + const std::wstring icon_file = utilities::get_open_templates_icon_resource_filepath( + module_instance_handle, ThemeHelpers::GetAppTheme()) + .c_str(); + HICON open_template_icon_handle = static_cast( + LoadImage(NULL, icon_file.c_str(), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_LOADFROMFILE)); + if (open_template_icon_handle) + { + bitmap_handles.push_back(CreateBitmapFromIcon(open_template_icon_handle)); + DestroyIcon(open_template_icon_handle); + } + } + if (bitmap_handles.size() > open_templates_icon_index && bitmap_handles[open_templates_icon_index]) + { + newplus_menu_item_open_templates.fMask |= MIIM_BITMAP; + newplus_menu_item_open_templates.hbmpItem = bitmap_handles[open_templates_icon_index]; + } + + InsertMenuItem(sub_menu_of_templates, sub_menu_index, TRUE, &newplus_menu_item_open_templates); +} + +void shell_context_menu_win10::add_separator_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index) +{ + MENUITEMINFO menu_item_separator; + menu_item_separator.cbSize = sizeof(MENUITEMINFO); + menu_item_separator.fMask = MIIM_FTYPE; + menu_item_separator.fType = MFT_SEPARATOR; + InsertMenuItem(sub_menu_of_templates, sub_menu_index, TRUE, &menu_item_separator); +} + +void shell_context_menu_win10::add_template_item_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, newplus::template_item* const template_item, int menu_id, int index) +{ + wchar_t menu_name[256] = { 0 }; + wcscpy_s(menu_name, ARRAYSIZE(menu_name), template_item->get_menu_title(!utilities::get_newplus_setting_hide_extension(), !utilities::get_newplus_setting_hide_starting_digits()).c_str()); + MENUITEMINFO newplus_menu_item_template; + newplus_menu_item_template.cbSize = sizeof(MENUITEMINFO); + newplus_menu_item_template.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_DATA; + newplus_menu_item_template.wID = menu_id; + newplus_menu_item_template.fType = MFT_STRING; + newplus_menu_item_template.dwTypeData = (PWSTR)menu_name; + const auto current_template_icon_index = index + 1; + if (bitmap_handles.size() <= current_template_icon_index) + { + HICON template_icon_handle = template_item->get_explorer_icon_handle(); + if (template_icon_handle) + { + bitmap_handles.push_back(CreateBitmapFromIcon(template_icon_handle)); + DestroyIcon(template_icon_handle); + } + } + if (bitmap_handles.size() > current_template_icon_index && bitmap_handles[current_template_icon_index]) + { + newplus_menu_item_template.fMask |= MIIM_BITMAP; + newplus_menu_item_template.hbmpItem = bitmap_handles[current_template_icon_index]; + } + + InsertMenuItem(sub_menu_of_templates, sub_menu_index, TRUE, &newplus_menu_item_template); +} + +IFACEMETHODIMP shell_context_menu_win10::InvokeCommand(CMINVOKECOMMANDINFO* params) +{ + if (!params) + { + return E_FAIL; + } + + // Get selected menu item (a template or the "Open templates" item) + const auto selected_menu_item_index = LOWORD(params->lpVerb) - 1; + if (selected_menu_item_index < 0) + { + return E_FAIL; + } + + const auto number_of_templates = templates->list_of_templates.size(); + const bool is_template_item = selected_menu_item_index < number_of_templates; + + // Save how many item templates we have so it can be sent later when we do something with New+. + // It will be sent when the user does something, similar to Windows 11 context menu. + newplus::utilities::set_saved_number_of_templates(static_cast(number_of_templates)); + + if (is_template_item) + { + // It's a template menu item + const auto template_entry = templates->get_template_item(selected_menu_item_index); + + return newplus::utilities::copy_template(template_entry, site_of_folder); + } + else + { + // It's the "Open templates" menu item + const std::filesystem::path template_folder_root = utilities::get_new_template_folder_location(); + + return newplus::utilities::open_template_folder(template_folder_root); + } + + return E_FAIL; +} + +IFACEMETHODIMP shell_context_menu_win10::GetCommandString(UINT_PTR, UINT, UINT*, CHAR*, UINT) +{ + return E_NOTIMPL; +} +#pragma endregion + +#pragma region IObjectWithSite +IFACEMETHODIMP shell_context_menu_win10::SetSite(_In_ IUnknown* site) noexcept +{ + this->site_of_folder = site; + + return S_OK; +} +IFACEMETHODIMP shell_context_menu_win10::GetSite(_In_ REFIID riid, _COM_Outptr_ void** returned_site) noexcept +{ + return this->site_of_folder.CopyTo(riid, returned_site); +} +#pragma endregion + diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu.win10/shell_context_menu_win10.h b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/shell_context_menu_win10.h new file mode 100644 index 0000000000..f69355cccb --- /dev/null +++ b/src/modules/NewPlus/NewShellExtensionContextMenu.win10/shell_context_menu_win10.h @@ -0,0 +1,45 @@ +#pragma once + +#include "pch.h" +#include + +using namespace Microsoft::WRL; + +#define NEW_SHELL_EXTENSION_EXPLORER_COMMAND_WIN10_UUID "FF90D477-E32A-4BE8-8CC5-A502A97F5401" + +// File Explorer context menu "New+" for Windows 10 +class __declspec(uuid(NEW_SHELL_EXTENSION_EXPLORER_COMMAND_WIN10_UUID)) shell_context_menu_win10 : + public RuntimeClass< + RuntimeClassFlags, + IShellExtInit, + IContextMenu, + IObjectWithSite> +{ +public: + ~shell_context_menu_win10(); + +#pragma region IShellExtInit + IFACEMETHODIMP Initialize(_In_opt_ PCIDLIST_ABSOLUTE, _In_ IDataObject*, HKEY); +#pragma endregion + +#pragma region IContextMenu + IFACEMETHODIMP QueryContextMenu(HMENU menu_handle, UINT menu_index, UINT menu_first_cmd_id, UINT, UINT menu_flags); + IFACEMETHODIMP InvokeCommand(CMINVOKECOMMANDINFO* pici); + IFACEMETHODIMP GetCommandString(UINT_PTR, UINT, UINT*, CHAR*, UINT); +#pragma endregion + +#pragma region IObjectWithSite + IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept; + IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept; +#pragma endregion + +protected: + void add_open_templates_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, const std::filesystem::path& template_folder_root, int menu_id, int index); + void add_separator_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index); + void add_template_item_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, newplus::template_item* const template_item, int menu_id, int index); + + HINSTANCE instance_handle = 0; + ComPtr site_of_folder; + newplus::template_folder* templates = nullptr; + std::vector bitmap_handles; +}; diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/NewShellExtensionContextMenu.vcxproj b/src/modules/NewPlus/NewShellExtensionContextMenu/NewShellExtensionContextMenu.vcxproj index 8d2e617579..68b74d6ae1 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/NewShellExtensionContextMenu.vcxproj +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/NewShellExtensionContextMenu.vcxproj @@ -128,6 +128,7 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv + @@ -227,7 +228,7 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv - + diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/NewShellExtensionContextMenu.vcxproj.filters b/src/modules/NewPlus/NewShellExtensionContextMenu/NewShellExtensionContextMenu.vcxproj.filters index 2e8323a6f9..de0cea2017 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/NewShellExtensionContextMenu.vcxproj.filters +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/NewShellExtensionContextMenu.vcxproj.filters @@ -31,6 +31,9 @@ Source Files + + Source Files + @@ -123,9 +126,6 @@ Source Files - - Asset Files - Asset Files @@ -190,14 +190,8 @@ - - Template Examples\Example folder - - - Template Examples\Example folder - - - Template Examples - + + + \ No newline at end of file diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.cpp new file mode 100644 index 0000000000..d7c324e5af --- /dev/null +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.cpp @@ -0,0 +1,13 @@ +#include "pch.h" +#include "new_utilities.h" + +// HACK: Store number of templates when generating the menu entries to send later. +size_t saved_number_of_templates = -1; +size_t newplus::utilities::get_saved_number_of_templates() +{ + return saved_number_of_templates; +} +void newplus::utilities::set_saved_number_of_templates(size_t templates) +{ + saved_number_of_templates = templates; +} diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h b/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h index 7ae46635a8..adf186a509 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h @@ -7,11 +7,17 @@ #include "constants.h" #include "settings.h" +#include "template_item.h" +#include "trace.h" #pragma comment(lib, "Shlwapi.lib") +using namespace newplus; + namespace newplus::utilities { + size_t get_saved_number_of_templates(); + void set_saved_number_of_templates(size_t templates); inline std::wstring get_explorer_icon(std::filesystem::path path) { @@ -39,6 +45,33 @@ namespace newplus::utilities return icon_resource; } + inline HICON get_explorer_icon_handle(std::filesystem::path path) + { + SHFILEINFO shell_file_info = { 0 }; + const std::wstring filepath = path.wstring(); + DWORD_PTR result = SHGetFileInfo(filepath.c_str(), 0, &shell_file_info, sizeof(shell_file_info), SHGFI_ICON); + if (shell_file_info.hIcon) + { + return shell_file_info.hIcon; + } + + WCHAR icon_resource_specifier[MAX_PATH] = { 0 }; + DWORD buffer_length = MAX_PATH; + const std::wstring extension = path.extension().wstring(); + const HRESULT hr = AssocQueryString(ASSOCF_INIT_IGNOREUNKNOWN, + ASSOCSTR_DEFAULTICON, + extension.c_str(), + NULL, + icon_resource_specifier, + &buffer_length); + const std::wstring icon_resource = icon_resource_specifier; + + const auto icon_x = GetSystemMetrics(SM_CXSMICON); + const auto icon_y = GetSystemMetrics(SM_CYSMICON); + HICON hIcon = static_cast(LoadImage(NULL, icon_resource.c_str(), IMAGE_ICON, icon_x, icon_y, LR_LOADFROMFILE)); + return hIcon; + } + inline bool is_hidden(const std::filesystem::path path) { const std::filesystem::path::string_type name = path.filename(); @@ -180,4 +213,227 @@ namespace newplus::utilities return path; } + inline bool is_desktop_folder(const std::filesystem::path target_fullpath) + { + TCHAR desktopPath[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath))) + { + return StrCmpIW(target_fullpath.c_str(), desktopPath) == 0; + } + return false; + } + + inline void explorer_enter_rename_mode(const std::filesystem::path target_fullpath_of_new_instance) + { + const std::filesystem::path path_without_new_file_or_dir = target_fullpath_of_new_instance.parent_path(); + const std::filesystem::path new_file_or_dir_without_path = target_fullpath_of_new_instance.filename(); + + ComPtr shell_windows; + + HRESULT hr; + if (FAILED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL, IID_PPV_ARGS(&shell_windows)))) + { + return; + } + + long window_handle; + ComPtr shell_window; + const bool object_created_on_desktop = is_desktop_folder(path_without_new_file_or_dir.c_str()); + if (object_created_on_desktop) + { + // Special handling for desktop folder + VARIANT empty_yet_needed_incl_init; + VariantInit(&empty_yet_needed_incl_init); + + if (FAILED(shell_windows->FindWindowSW(&empty_yet_needed_incl_init, &empty_yet_needed_incl_init, SWC_DESKTOP, &window_handle, SWFO_NEEDDISPATCH, &shell_window))) + { + return; + } + } + else + { + long count_of_shell_windows = 0; + shell_windows->get_Count(&count_of_shell_windows); + + for (long i = 0; i < count_of_shell_windows; ++i) + { + ComPtr web_browser_app; + VARIANT v; + V_VT(&v) = VT_I4; + V_I4(&v) = i; + hr = shell_windows->Item(v, &shell_window); + if (SUCCEEDED(hr) && shell_window) + { + hr = shell_window.As(&web_browser_app); + if (SUCCEEDED(hr)) + { + BSTR folder_view_location; + hr = web_browser_app->get_LocationURL(&folder_view_location); + if (SUCCEEDED(hr) && folder_view_location) + { + wchar_t path[MAX_PATH]; + DWORD pathLength = ARRAYSIZE(path); + hr = PathCreateFromUrl(folder_view_location, path, &pathLength, 0); + SysFreeString(folder_view_location); + if (SUCCEEDED(hr) && StrCmpIW(path_without_new_file_or_dir.c_str(), path) == 0) + { + break; + } + } + } + } + shell_window = nullptr; + } + } + + if (!shell_window) + { + return; + } + + ComPtr service_provider; + shell_window.As(&service_provider); + ComPtr shell_browser; + service_provider->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&shell_browser)); + ComPtr shell_view; + shell_browser->QueryActiveShellView(&shell_view); + ComPtr folder_view; + shell_view.As(&folder_view); + + // Find the newly created object (file or folder) + // And put object into edit mode (SVSI_EDIT) and if desktop also reposition + int number_of_objects_in_view = 0; + bool done = false; + folder_view->ItemCount(SVGIO_ALLVIEW, &number_of_objects_in_view); + for (int i = 0; i < number_of_objects_in_view && !done; ++i) + { + std::wstring path_of_item(MAX_PATH, 0); + LPITEMIDLIST shell_item_ids; + + folder_view->Item(i, &shell_item_ids); + SHGetPathFromIDList(shell_item_ids, &path_of_item[0]); + + const std::wstring current_filename = std::filesystem::path(path_of_item.c_str()).filename(); + + if (utilities::wstring_same_when_comparing_ignore_case(new_file_or_dir_without_path, current_filename)) + { + const DWORD common_select_flags = SVSI_EDIT | SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED; + + if (object_created_on_desktop) + { + // Newly created object is on the desktop -- reposition under mouse and enter rename mode + LPCITEMIDLIST shell_item_to_select_and_position[] = { shell_item_ids }; + POINT mouse_position; + GetCursorPos(&mouse_position); + mouse_position.x -= GetSystemMetrics(SM_CXMENUSIZE); + mouse_position.x = max(mouse_position.x, 20); + mouse_position.y -= GetSystemMetrics(SM_CXMENUSIZE)/2; + mouse_position.y = max(mouse_position.y, 20); + POINT position[] = { mouse_position }; + folder_view->SelectAndPositionItems(1, shell_item_to_select_and_position, position, common_select_flags | SVSI_POSITIONITEM); + } + else + { + // Enter rename mode + folder_view->SelectItem(i, common_select_flags); + } + done = true; + } + CoTaskMemFree(shell_item_ids); + } + } + + inline HRESULT copy_template(const template_item* template_entry, const ComPtr site_of_folder) + { + HRESULT hr = S_OK; + + try + { + Logger::info(L"Copying template"); + + if (newplus::utilities::get_saved_number_of_templates() >= 0) + { + // Log that context menu was shown and with how many items + trace.UpdateState(true); + Trace::EventShowTemplateItems(newplus::utilities::get_saved_number_of_templates()); + trace.Flush(); + trace.UpdateState(false); + } + + // Determine target path of where context menu was displayed + const auto target_path_name = utilities::get_path_from_unknown_site(site_of_folder); + + // Determine initial filename + std::filesystem::path source_fullpath = template_entry->path; + std::filesystem::path target_fullpath = std::wstring(target_path_name); + + // Only append name to target if source is not a directory + if (!utilities::is_directory(source_fullpath)) + { + target_fullpath.append(template_entry->get_target_filename(!utilities::get_newplus_setting_hide_starting_digits())); + } + + // Copy file and determine final filename + std::filesystem::path target_final_fullpath = template_entry->copy_object_to(GetActiveWindow(), target_fullpath); + + trace.UpdateState(true); + Trace::EventCopyTemplate(target_final_fullpath.extension().c_str()); + trace.Flush(); + trace.UpdateState(false); + + // Refresh folder items + template_entry->refresh_target(target_final_fullpath); + + // Enter rename mode + template_entry->enter_rename_mode(target_final_fullpath); + } + catch (const std::exception& ex) + { + Logger::error(ex.what()); + + hr = S_FALSE; + } + + trace.UpdateState(true); + Trace::EventCopyTemplateResult(hr); + trace.Flush(); + trace.UpdateState(false); + + return hr; + } + + inline HRESULT open_template_folder(const std::filesystem::path template_folder) + { + HRESULT hr = S_OK; + + try + { + Logger::info(L"Open templates folder"); + + if (newplus::utilities::get_saved_number_of_templates() >= 0) + { + // Log that context menu was shown and with how many items + trace.UpdateState(true); + Trace::EventShowTemplateItems(newplus::utilities::get_saved_number_of_templates()); + trace.Flush(); + trace.UpdateState(false); + } + + const std::wstring verb_hardcoded_do_not_change = L"open"; + ShellExecute(nullptr, verb_hardcoded_do_not_change.c_str(), template_folder.c_str(), NULL, NULL, SW_SHOWNORMAL); + + trace.UpdateState(true); + Trace::EventOpenTemplates(); + trace.Flush(); + trace.UpdateState(false); + } + catch (const std::exception& ex) + { + Logger::error(ex.what()); + + hr = S_FALSE; + } + + return hr; + } } diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_menu.h b/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_menu.h index 564069254d..6e74d9730d 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_menu.h +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_menu.h @@ -27,7 +27,6 @@ public: #pragma region IObjectWithSite IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept; - IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept; #pragma endregion diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu.cpp index 47f93beac7..921130baa4 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu.cpp +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu.cpp @@ -1,13 +1,13 @@ #include "pch.h" #include "shell_context_sub_menu.h" #include "trace.h" +#include "new_utilities.h" using namespace Microsoft::WRL; // // Sub context menu command enumerator shell_context_sub_menu::shell_context_sub_menu(const ComPtr site_of_folder) { - trace.UpdateState(true); this->site_of_folder = site_of_folder; // Determine the New+ Template folder location @@ -36,8 +36,9 @@ shell_context_sub_menu::shell_context_sub_menu(const ComPtr site_of_fo current_command = explorer_menu_item_commands.cbegin(); - // Log that context menu was shown and with how many items - Trace::EventShowTemplateItems(number_of_templates); + // Save how many item templates we have so it can be sent later when we do something with New+. + // We don't send it here or it would send an event every time we open a context menu. + newplus::utilities::set_saved_number_of_templates(static_cast(number_of_templates)); } // IEnumExplorerCommand diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu_item.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu_item.cpp index 12e5f9cf5c..2b1b940d65 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu_item.cpp +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/shell_context_sub_menu_item.cpp @@ -63,49 +63,7 @@ IFACEMETHODIMP shell_context_sub_menu_item::GetState(_In_opt_ IShellItemArray* s IFACEMETHODIMP shell_context_sub_menu_item::Invoke(_In_opt_ IShellItemArray*, _In_opt_ IBindCtx*) noexcept { - HRESULT hr = S_OK; - try - { - trace.UpdateState(true); - - // Determine target path of where context menu was displayed - const auto target_path_name = utilities::get_path_from_unknown_site(site_of_folder); - - // Determine initial filename - std::filesystem::path source_fullpath = template_entry->path; - std::filesystem::path target_fullpath = std::wstring(target_path_name); - - // Only append name to target if source is not a directory - if (!utilities::is_directory(target_fullpath)) - { - target_fullpath.append(this->template_entry->get_target_filename(!utilities::get_newplus_setting_hide_starting_digits())); - } - - // Copy file and determine final filename - std::filesystem::path target_final_fullpath = this->template_entry->copy_object_to(GetActiveWindow(), target_fullpath); - - Trace::EventCopyTemplate(target_final_fullpath.extension().c_str()); - - // Refresh folder items - SHChangeNotify(SHCNE_CREATE, SHCNF_PATH | SHCNF_FLUSH, target_final_fullpath.wstring().c_str(), NULL); - - // Enter rename mode - this->template_entry->enter_rename_mode(site_of_folder, target_final_fullpath); - - Trace::EventCopyTemplateResult(S_OK); - } - catch (const std::exception& ex) - { - Trace::EventCopyTemplateResult(S_FALSE); - Logger::error(ex.what()); - - hr = S_FALSE; - } - - trace.Flush(); - trace.UpdateState(false); - - return hr; + return newplus::utilities::copy_template(template_entry, site_of_folder); } IFACEMETHODIMP shell_context_sub_menu_item::GetFlags(_Out_ EXPCMDFLAGS* returned_flags) @@ -162,9 +120,5 @@ IFACEMETHODIMP template_folder_context_menu_item::GetIcon(_In_opt_ IShellItemArr IFACEMETHODIMP template_folder_context_menu_item::Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept { - Logger::info(L"Open templates folder"); - const std::wstring verb_hardcoded_do_not_change = L"open"; - ShellExecute(nullptr, verb_hardcoded_do_not_change.c_str(), shell_template_folder.c_str(), NULL, NULL, SW_SHOWNORMAL); - - return S_OK; + return newplus::utilities::open_template_folder(shell_template_folder); } diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/template_folder.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu/template_folder.cpp index b4bfdf63f5..e52a871e80 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/template_folder.cpp +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/template_folder.cpp @@ -11,6 +11,11 @@ template_folder::template_folder(const std::filesystem::path newplus_template_fo this->template_folder_path = newplus_template_folder; } +template_folder::~template_folder() +{ + list_of_templates.clear(); +} + void template_folder::init() { rescan_template_folder(); diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/template_folder.h b/src/modules/NewPlus/NewShellExtensionContextMenu/template_folder.h index fe644be2e9..4ff9499819 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/template_folder.h +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/template_folder.h @@ -13,6 +13,8 @@ namespace newplus { public: template_folder(const std::filesystem::path newplus_template_folder); + ~template_folder(); + void rescan_template_folder(); std::filesystem::path template_folder_path; diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/template_item.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu/template_item.cpp index e644df5c85..5de461bece 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/template_item.cpp +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/template_item.cpp @@ -63,6 +63,11 @@ std::wstring template_item::get_explorer_icon() const return utilities::get_explorer_icon(path); } +HICON template_item::get_explorer_icon_handle() const +{ + return utilities::get_explorer_icon_handle(path); +} + std::filesystem::path template_item::copy_object_to(const HWND window_handle, const std::filesystem::path destination) const { // SHFILEOPSTRUCT wants the from and to paths to be terminated with two NULLs, @@ -86,6 +91,14 @@ std::filesystem::path template_item::copy_object_to(const HWND window_handle, co if (!file_operation_params.hNameMappings) { // No file name collision on copy + if (utilities::is_directory(this->path)) + { + // Append dir for consistency on directory naming inclusion for with and without collision + std::filesystem::path with_dir = destination; + with_dir /= this->path.filename(); + return with_dir; + } + return destination; } @@ -104,44 +117,23 @@ std::filesystem::path template_item::copy_object_to(const HWND window_handle, co return final_path; } -void template_item::enter_rename_mode(const ComPtr site, const std::filesystem::path target_fullpath) const +void template_item::refresh_target(const std::filesystem::path target_final_fullpath) const { - std::thread thread_for_renaming_workaround(rename_on_other_thread_workaround, site, target_fullpath); + SHChangeNotify(SHCNE_CREATE, SHCNF_PATH | SHCNF_FLUSH, target_final_fullpath.wstring().c_str(), NULL); +} + +void template_item::enter_rename_mode(const std::filesystem::path target_fullpath) const +{ + std::thread thread_for_renaming_workaround(rename_on_other_thread_workaround, target_fullpath); thread_for_renaming_workaround.detach(); } -void template_item::rename_on_other_thread_workaround(const ComPtr site, const std::filesystem::path target_fullpath) +void template_item::rename_on_other_thread_workaround(const std::filesystem::path target_fullpath) { // Have been unable to have Windows Explorer Shell enter rename mode from the main thread - // Sleep for a bit to only enter rename mode when icon has been drawn. Not strictly needed. - const std::chrono::milliseconds approx_wait_for_icon_redraw_not_needed{ 350 }; + // Sleep for a bit to only enter rename mode when icon has been drawn. + const std::chrono::milliseconds approx_wait_for_icon_redraw_not_needed{ 50 }; std::this_thread::sleep_for(std::chrono::milliseconds(approx_wait_for_icon_redraw_not_needed)); - const std::wstring filename = target_fullpath.filename(); - - ComPtr service_provider; - site->QueryInterface(IID_PPV_ARGS(&service_provider)); - ComPtr folder_view; - service_provider->QueryService(__uuidof(IFolderView), IID_PPV_ARGS(&folder_view)); - - int count = 0; - folder_view->ItemCount(SVGIO_ALLVIEW, &count); - - for (int i = 0; i < count; ++i) - { - std::wstring path_of_item(MAX_PATH, 0); - LPITEMIDLIST pidl; - - folder_view->Item(i, &pidl); - SHGetPathFromIDList(pidl, &path_of_item[0]); - CoTaskMemFree(pidl); - - std::wstring current_filename = std::filesystem::path(path_of_item.c_str()).filename(); - - if (utilities::wstring_same_when_comparing_ignore_case(filename, current_filename)) - { - folder_view->SelectItem(i, SVSI_EDIT | SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED); - break; - } - } + newplus::utilities::explorer_enter_rename_mode(target_fullpath); } diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/template_item.h b/src/modules/NewPlus/NewShellExtensionContextMenu/template_item.h index ffb1e8839f..7b8ba78aee 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/template_item.h +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/template_item.h @@ -20,15 +20,19 @@ namespace newplus std::wstring get_target_filename(const bool include_starting_digits) const; std::wstring get_explorer_icon() const; + + HICON get_explorer_icon_handle() const; std::filesystem::path copy_object_to(const HWND window_handle, const std::filesystem::path destination) const; - void enter_rename_mode(const ComPtr site, const std::filesystem::path target_folder) const; + void refresh_target(const std::filesystem::path target_final_fullpath) const; + + void enter_rename_mode(const std::filesystem::path target_fullpath) const; std::filesystem::path path; private: - static void rename_on_other_thread_workaround(const ComPtr site, const std::filesystem::path target_fullpath); + static void rename_on_other_thread_workaround(const std::filesystem::path target_fullpath); std::wstring remove_starting_digits_from_filename(std::wstring filename) const; }; diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/trace.cpp b/src/modules/NewPlus/NewShellExtensionContextMenu/trace.cpp index 3a2c8c8c91..d126c6dd02 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/trace.cpp +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/trace.cpp @@ -58,3 +58,12 @@ void Trace::EventCopyTemplateResult(_In_ const HRESULT hr) noexcept TraceLoggingHResult(hr), TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); } + +void Trace::EventOpenTemplates() noexcept +{ + TraceLoggingWriteWrapper( + g_hProvider, + "NewPlus_EventOpenTemplates", + ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance), + TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); +} diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/trace.h b/src/modules/NewPlus/NewShellExtensionContextMenu/trace.h index 7c21086642..1d0c256285 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/trace.h +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/trace.h @@ -12,4 +12,5 @@ public: static void EventShowTemplateItems(_In_ const size_t number_of_templates) noexcept; static void EventCopyTemplate(_In_ const std::wstring template_file_extension) noexcept; static void EventCopyTemplateResult(_In_ const HRESULT hr) noexcept; + static void EventOpenTemplates() noexcept; }; diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml index ef8b9c68e8..7f4e427d65 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/NewPlusPage.xaml @@ -23,13 +23,6 @@ - - Enable New+ Localize product name in accordance with Windows New - - New+ is not supported in Windows 10 and is not expected to work. - PowerToys "Backup and Restore" feature doesn't take templates into account at this moment. If you use that feature, templates will have to be copied manually. From a90b646e72ccdcc81cd532e412d0927711871205 Mon Sep 17 00:00:00 2001 From: Andrea Bartolucci Date: Wed, 27 Nov 2024 18:30:25 +0100 Subject: [PATCH 82/95] [PTRun][WindowWalker]Added score to the results (#35691) Added Score to the results returned by WindowWalker Co-authored-by: Andrea Bartolucci <8514491+andbartol@users.noreply.github.com> --- .../Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs index 7ca5423d32..ee277fd268 100644 --- a/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs +++ b/src/modules/launcher/Plugins/Microsoft.Plugin.WindowWalker/Components/ResultHelper.cs @@ -68,6 +68,7 @@ namespace Microsoft.Plugin.WindowWalker.Components searchResult.Result.SwitchToWindow(); return true; }, + Score = searchResult.Score, // For debugging you can set the second parameter to true to see more information. ToolTipData = GetToolTip(searchResult.Result, false), From 271d64b8dc8330f59dbcb47fe84272b435360b50 Mon Sep 17 00:00:00 2001 From: grzhan Date: Thu, 28 Nov 2024 03:44:56 +0800 Subject: [PATCH 83/95] [PTRun][Docs] Add HttpStatusCodes to Third-Party plugins (#36055) * [PTRun][Docs] Add HttpStatusCodes to Third-Party plugins * adding grzhan to names.txt --------- Co-authored-by: Clint Rutkas --- .github/actions/spell-check/allow/names.txt | 1 + doc/thirdPartyRunPlugins.md | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/actions/spell-check/allow/names.txt b/.github/actions/spell-check/allow/names.txt index 4c038bdf1c..f23f204e3a 100644 --- a/.github/actions/spell-check/allow/names.txt +++ b/.github/actions/spell-check/allow/names.txt @@ -71,6 +71,7 @@ Garside Gershaft Giordani Gokce +grzhan Guo hanselman Harmath diff --git a/doc/thirdPartyRunPlugins.md b/doc/thirdPartyRunPlugins.md index a8136ec623..7e59e3acd4 100644 --- a/doc/thirdPartyRunPlugins.md +++ b/doc/thirdPartyRunPlugins.md @@ -41,6 +41,7 @@ Contact the developers of a plugin directly for assistance with a specific plugi | [ChatGPT](https://github.com/ferraridavide/ChatGPTPowerToys) | [ferraridavide](https://github.com/ferraridavide) | Ask a question to ChatGPT | | [CanIUse](https://github.com/skttl/ptrun-caniuse) | [skttl](https://github.com/skttl) | Look up browser feature support with caniuse.com | | [TailwindCSS](https://github.com/skttl/ptrun-tailwindcss) | [skttl](https://github.com/skttl) | Search the documentation of TailwindCSS | +| [HttpStatusCodes](https://github.com/grzhan/HttpStatusCodePowerToys) | [grzhan](https://github.com/grzhan) | Search for http status codes | ## Extending software plugins From 3dc491339af5ab3ce83d4f94e646f99f115504a9 Mon Sep 17 00:00:00 2001 From: Shuai Yuan <128874481+shuaiyuanxx@users.noreply.github.com> Date: Thu, 28 Nov 2024 05:57:20 +0800 Subject: [PATCH 84/95] Upgrade Windows App SDK to the latest version (#36093) Signed-off-by: Shawn Yuan --- Directory.Packages.props | 2 +- NOTICE.md | 2 +- .../MeasureToolCore/PowerToys.MeasureToolCore.vcxproj | 8 ++++---- src/modules/MeasureTool/MeasureToolCore/packages.config | 2 +- .../powerrename/PowerRenameUILib/PowerRenameUI.vcxproj | 8 ++++---- .../PowerRenameUILib/PowerRenameUI.vcxproj.filters | 3 +++ src/modules/powerrename/PowerRenameUILib/packages.config | 2 +- 7 files changed, 15 insertions(+), 12 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index a0d29eb311..1af9962224 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -47,7 +47,7 @@ --> - + diff --git a/NOTICE.md b/NOTICE.md index e22705e254..9573caebb6 100644 --- a/NOTICE.md +++ b/NOTICE.md @@ -1333,7 +1333,7 @@ EXHIBIT A -Mozilla Public License. - Microsoft.Windows.CsWin32 0.2.46-beta - Microsoft.Windows.CsWinRT 2.1.5 - Microsoft.Windows.SDK.BuildTools 10.0.22621.2428 -- Microsoft.WindowsAppSDK 1.6.240923002 +- Microsoft.WindowsAppSDK 1.6.241114003 - Microsoft.Xaml.Behaviors.WinUI.Managed 2.0.9 - Microsoft.Xaml.Behaviors.Wpf 1.1.39 - ModernWpfUI 0.9.4 diff --git a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj index 5bdad6767c..0c3cde328a 100644 --- a/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj +++ b/src/modules/MeasureTool/MeasureToolCore/PowerToys.MeasureToolCore.vcxproj @@ -1,6 +1,6 @@  - + @@ -141,7 +141,7 @@ - + @@ -153,7 +153,7 @@ - - + + \ No newline at end of file diff --git a/src/modules/MeasureTool/MeasureToolCore/packages.config b/src/modules/MeasureTool/MeasureToolCore/packages.config index ed165ac47c..56a9b5b463 100644 --- a/src/modules/MeasureTool/MeasureToolCore/packages.config +++ b/src/modules/MeasureTool/MeasureToolCore/packages.config @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj index c41e461acb..302e3baf17 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj @@ -1,6 +1,6 @@  - + @@ -209,7 +209,7 @@ - + @@ -223,8 +223,8 @@ - - + + diff --git a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj.filters b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj.filters index bc242e4886..10ef9ed4ee 100644 --- a/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj.filters +++ b/src/modules/powerrename/PowerRenameUILib/PowerRenameUI.vcxproj.filters @@ -65,4 +65,7 @@ + + + \ No newline at end of file diff --git a/src/modules/powerrename/PowerRenameUILib/packages.config b/src/modules/powerrename/PowerRenameUILib/packages.config index 1ff9b46877..9bbe299db9 100644 --- a/src/modules/powerrename/PowerRenameUILib/packages.config +++ b/src/modules/powerrename/PowerRenameUILib/packages.config @@ -6,5 +6,5 @@ - + \ No newline at end of file From fc29fc7426122f6f1b03ec2a6810c80fe7bee3d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frederik=20H=C3=B6ft?= <43813142+frederik-hoeft@users.noreply.github.com> Date: Thu, 28 Nov 2024 16:52:16 +0100 Subject: [PATCH 85/95] [PTRun][ValueGenerator]Add support for UUIDv7 (#35757) * add Run support for UUIDv7 generation * simplify comments and maybe satisfy spell check * fix endianess * prefer stack allocation for temporary fixed-size buffer * perhaps the async test caused the pipeline to hang * switch to .NET 9 BCL implementation of UUIDv7 * add UUIDv7 to input query suggestions + update exception messages to include v7 * simplify Guid description switch + update devdocs --- .../plugins/community.valuegenerator.md | 7 +- .../GUIDGeneratorTests.cs | 36 +++++++++- .../InputParserTests.cs | 21 +++--- .../Generators/GUID/GUIDGenerator.cs | 5 ++ .../Generators/GUID/GUIDRequest.cs | 69 +++++++------------ .../Helper/QueryHelper.cs | 8 ++- .../InputParser.cs | 2 +- .../Properties/Resources.Designer.cs | 9 +++ .../Properties/Resources.resx | 3 + 9 files changed, 98 insertions(+), 62 deletions(-) diff --git a/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md b/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md index 4a580f090d..9b94ae78f2 100644 --- a/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md +++ b/doc/devdocs/modules/launcher/plugins/community.valuegenerator.md @@ -1,6 +1,6 @@ # Value Generator Plugin -The Value Generator plugin is used to generate hashes for strings, to calculate base64 encodings, escape and encode URLs/URIs and to generate GUIDs versions 1, 3, 4 and 5. +The Value Generator plugin is used to generate hashes for strings, to calculate base64 encodings, escape and encode URLs/URIs and to generate GUIDs of version 1, 3, 4, 5, and 7. ![Image of Value Generator plugin](/doc/images/launcher/plugin/community.valuegenerator.png) @@ -34,7 +34,10 @@ The Value Generator plugin is used to generate hashes for strings, to calculate ### [`GUIDGenerator`](/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDGenerator.cs) - Utility class for generating or calculating GUIDs -- Generating GUID versions 1 and 4 is done using builtin APIs. [`UuidCreateSequential`](https://learn.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-uuidcreatesequential) for version 1 and `System.Guid.NewGuid()` for version 4 +- Generating GUID versions 1, 4, and 7 is done using builtin APIs: + - [`UuidCreateSequential`](https://learn.microsoft.com/en-us/windows/win32/api/rpcdce/nf-rpcdce-uuidcreatesequential) for version 1 + - `System.Guid.NewGuid()` for version 4 + - `System.Guid.CreateVersion7()` for version 7 - Versions 3 and 5 take two parameters, a namespace and a name - The namespace must be a valid GUID or one of the [predefined ones](https://datatracker.ietf.org/doc/html/rfc4122#appendix-C) - The `PredefinedNamespaces` dictionary contains aliases for the predefined namespaces diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/GUIDGeneratorTests.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/GUIDGeneratorTests.cs index 7e3bffb7e5..c7f976c134 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/GUIDGeneratorTests.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/GUIDGeneratorTests.cs @@ -3,8 +3,9 @@ // See the LICENSE file in the project root for more information. using System; +using System.Buffers.Binary; using System.Linq; - +using System.Threading; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests @@ -56,6 +57,39 @@ namespace Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests Assert.AreEqual(0x5000, GetGUIDVersion(guid)); } + [TestMethod] + public void GUIDv7Generator() + { + var guidRequest = new GUID.GUIDRequest(7); + guidRequest.Compute(); + var guid = guidRequest.Result; + + Assert.IsNotNull(guid); + Assert.AreEqual(0x7000, GetGUIDVersion(guid)); + } + + [TestMethod] + public void GUIDv7GeneratorTimeOrdered() + { + const int numberOfSamplesToCheck = 10; + ulong previousTimestampWithTrailingRandomData = 0uL; + for (int i = 0; i < numberOfSamplesToCheck; i++) + { + var guidRequest = new GUID.GUIDRequest(7); + guidRequest.Compute(); + var guid = guidRequest.Result; + + // can't hurt to assert invariants again + Assert.IsNotNull(guid); + Assert.AreEqual(0x7000, GetGUIDVersion(guid)); + ulong timestampWithTrailingRandomData = BinaryPrimitives.ReadUInt64BigEndian(guid.AsSpan()); + Assert.IsTrue(timestampWithTrailingRandomData > previousTimestampWithTrailingRandomData, "UUIDv7 wasn't time-ordered"); + + // ensure at least one millisecond passes for consistent time-ordering. we wait 10 ms just to be sure. + Thread.Sleep(10); + } + } + [DataTestMethod] [DataRow(3, "ns:DNS", "abc", "5bd670ce-29c8-3369-a8a1-10ce44c7259e")] [DataRow(3, "ns:URL", "abc", "874a8cb4-4e91-3055-a476-3d3e2ffe375f")] diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/InputParserTests.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/InputParserTests.cs index 970cedfc1f..65b630adcd 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/InputParserTests.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests/InputParserTests.cs @@ -12,7 +12,7 @@ using Wox.Plugin; namespace Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests { [TestClass] - public class InputParserTests + public partial class InputParserTests { [DataTestMethod] [DataRow("md5 abc", typeof(Hashing.HashRequest))] @@ -27,6 +27,8 @@ namespace Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests [DataRow("uUiD5 ns:URL abc", typeof(GUID.GUIDRequest))] [DataRow("Guidvv ns:DNS abc", null)] [DataRow("guidv4", typeof(GUID.GUIDRequest))] + [DataRow("guidv7", typeof(GUID.GUIDRequest))] + [DataRow("GUIDv7", typeof(GUID.GUIDRequest))] [DataRow("base64 abc", typeof(Base64.Base64Request))] [DataRow("base99 abc", null)] [DataRow("base64s abc", null)] @@ -90,25 +92,22 @@ namespace Community.PowerToys.Run.Plugin.ValueGenerator.UnitTests private static bool CommandIsKnown(string command) { - string[] hashes = new string[] { "md5", "sha1", "sha256", "sha384", "sha512", "base64", "base64d" }; + string[] hashes = ["md5", "sha1", "sha256", "sha384", "sha512", "base64", "base64d"]; if (hashes.Contains(command.ToLowerInvariant())) { return true; } - Regex regexGuiUUID = new Regex("^(guid|uuid)([1345]{0,1}|v[1345]{1})$", RegexOptions.IgnoreCase | RegexOptions.Compiled); - if (regexGuiUUID.IsMatch(command)) + if (GetKnownUuidImplementations().IsMatch(command)) { return true; } - string[] uriCommands = new string[] { "url", "urld", "esc:hex", "uesc:hex", "esc:data", "uesc:data" }; - if (uriCommands.Contains(command.ToLowerInvariant())) - { - return true; - } - - return false; + string[] uriCommands = ["url", "urld", "esc:hex", "uesc:hex", "esc:data", "uesc:data"]; + return uriCommands.Contains(command.ToLowerInvariant()); } + + [GeneratedRegex("^(guid|uuid)([13457]{0,1}|v[13457]{1})$", RegexOptions.IgnoreCase | RegexOptions.Compiled, "en-US")] + private static partial Regex GetKnownUuidImplementations(); } } diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDGenerator.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDGenerator.cs index e83649e327..8039877665 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDGenerator.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDGenerator.cs @@ -52,6 +52,11 @@ namespace Community.PowerToys.Run.Plugin.ValueGenerator.GUID return V3AndV5(uuidNamespace, uuidName, 5); } + public static Guid V7() + { + return Guid.CreateVersion7(); + } + private static Guid V3AndV5(Guid uuidNamespace, string uuidName, short version) { byte[] namespaceBytes = uuidNamespace.ToByteArray(); diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDRequest.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDRequest.cs index dc4392a744..718b4b709a 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDRequest.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Generators/GUID/GUIDRequest.cs @@ -4,7 +4,6 @@ using System; using System.Security.Cryptography; - using Wox.Plugin.Logger; namespace Community.PowerToys.Run.Plugin.ValueGenerator.GUID @@ -19,34 +18,15 @@ namespace Community.PowerToys.Run.Plugin.ValueGenerator.GUID private int Version { get; set; } - public string Description + public string Description => Version switch { - get - { - switch (Version) - { - case 1: - return "Version 1: Time base GUID"; - case 3: - case 5: - string hashAlgorithm; - if (Version == 3) - { - hashAlgorithm = HashAlgorithmName.MD5.ToString(); - } - else - { - hashAlgorithm = HashAlgorithmName.SHA1.ToString(); - } - - return $"Version {Version} ({hashAlgorithm}): Namespace and name based GUID."; - case 4: - return "Version 4: Randomly generated GUID"; - default: - return string.Empty; - } - } - } + 1 => "Version 1: Time base GUID", + 3 => $"Version 3 ({HashAlgorithmName.MD5}): Namespace and name based GUID.", + 4 => "Version 4: Randomly generated GUID", + 5 => $"Version 5 ({HashAlgorithmName.SHA1}): Namespace and name based GUID.", + 7 => "Version 7: Time-ordered randomly generated GUID", + _ => string.Empty, + }; private Guid? GuidNamespace { get; set; } @@ -60,20 +40,19 @@ namespace Community.PowerToys.Run.Plugin.ValueGenerator.GUID { Version = version; - if (Version < 1 || Version > 5 || Version == 2) + if (Version is < 1 or > 7 or 2 or 6) { - throw new ArgumentException("Unsupported GUID version. Supported versions are 1, 3, 4 and 5"); + throw new ArgumentException("Unsupported GUID version. Supported versions are 1, 3, 4, 5, and 7"); } - if (version == 3 || version == 5) + if (version is 3 or 5) { if (guidNamespace == null) { throw new ArgumentNullException(null, NullNamespaceError); } - Guid guid; - if (GUIDGenerator.PredefinedNamespaces.TryGetValue(guidNamespace.ToLowerInvariant(), out guid)) + if (GUIDGenerator.PredefinedNamespaces.TryGetValue(guidNamespace.ToLowerInvariant(), out Guid guid)) { GuidNamespace = guid; } @@ -108,20 +87,18 @@ namespace Community.PowerToys.Run.Plugin.ValueGenerator.GUID IsSuccessful = true; try { - switch (Version) + Guid guid = Version switch { - case 1: - GuidResult = GUIDGenerator.V1(); - break; - case 3: - GuidResult = GUIDGenerator.V3(GuidNamespace.Value, GuidName); - break; - case 4: - GuidResult = GUIDGenerator.V4(); - break; - case 5: - GuidResult = GUIDGenerator.V5(GuidNamespace.Value, GuidName); - break; + 1 => GUIDGenerator.V1(), + 3 => GUIDGenerator.V3(GuidNamespace.Value, GuidName), + 4 => GUIDGenerator.V4(), + 5 => GUIDGenerator.V5(GuidNamespace.Value, GuidName), + 7 => GUIDGenerator.V7(), + _ => default, + }; + if (guid != default) + { + GuidResult = guid; } Result = GuidResult.ToByteArray(); diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Helper/QueryHelper.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Helper/QueryHelper.cs index d984ebe006..6fb73f5268 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Helper/QueryHelper.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Helper/QueryHelper.cs @@ -4,7 +4,6 @@ using System.Collections.Generic; using System.Globalization; -using System.Text; using Community.PowerToys.Run.Plugin.ValueGenerator.Properties; @@ -23,6 +22,7 @@ namespace Community.PowerToys.Run.Plugin.ValueGenerator.Helper private static readonly string GeneratorDescriptionUuidv3 = Resources.generator_description_uuidv3; private static readonly string GeneratorDescriptionUuidv4 = Resources.generator_description_uuidv4; private static readonly string GeneratorDescriptionUuidv5 = Resources.generator_description_uuidv5; + private static readonly string GeneratorDescriptionUuidv7 = Resources.generator_description_uuidv7; private static readonly string GeneratorDescriptionHash = Resources.generator_description_hash; private static readonly string GeneratorDescriptionBase64 = Resources.generator_description_base64; private static readonly string GeneratorDescriptionBase64d = Resources.generator_description_base64d; @@ -92,6 +92,12 @@ namespace Community.PowerToys.Run.Plugin.ValueGenerator.Helper Example = $"uuidv5 ns: <{GetStringFormat(GeneratorDescriptionYourInput)}>", }, new() + { + Keyword = "uuidv7", + Description = GetStringFormat(GeneratorDescriptionUuidv7), + Example = $"uuidv7 {GetStringFormat(Or)} uuid7", + }, + new() { Keyword = "md5", Description = GetStringFormat(GeneratorDescriptionHash, "MD5"), diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs index cda6020ea6..e8bf4a577a 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/InputParser.cs @@ -94,7 +94,7 @@ namespace Community.PowerToys.Run.Plugin.ValueGenerator if (!int.TryParse(versionQuery, null, out version)) { - throw new FormatException("Could not determine requested GUID version. Supported versions are 1, 3, 4 and 5"); + throw new FormatException("Could not determine requested GUID version. Supported versions are 1, 3, 4, 5, and 7"); } } diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Properties/Resources.Designer.cs b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Properties/Resources.Designer.cs index 90c3adeff5..86c46aa615 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Properties/Resources.Designer.cs +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Properties/Resources.Designer.cs @@ -213,6 +213,15 @@ namespace Community.PowerToys.Run.Plugin.ValueGenerator.Properties { } } + /// + /// Looks up a localized string similar to Generate a version 7: Time-ordered randomly generated UUID. + /// + public static string generator_description_uuidv7 { + get { + return ResourceManager.GetString("generator_description_uuidv7", resourceCulture); + } + } + /// /// Looks up a localized string similar to your input. /// diff --git a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Properties/Resources.resx b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Properties/Resources.resx index 1daf99ab71..a3f8a9aa8f 100644 --- a/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Properties/Resources.resx +++ b/src/modules/launcher/Plugins/Community.PowerToys.Run.Plugin.ValueGenerator/Properties/Resources.resx @@ -168,6 +168,9 @@ Generate a version 5 (SHA1): Namespace and name based UUID + + Generate a version 7: Time-ordered randomly generated UUID + your input Usage example: "md5 <your input>" From 28304838afc60ef3622eae35a4b35a380155d1b7 Mon Sep 17 00:00:00 2001 From: octastylos-pseudodipteros <128853928+octastylos-pseudodipteros@users.noreply.github.com> Date: Thu, 28 Nov 2024 18:11:28 +0100 Subject: [PATCH 86/95] [FilePreview]Add support for descript.ion files (#35599) [FilePreview] Add support for descript.ion file --- src/Monaco/monacoSpecialLanguages.js | 2 +- src/Monaco/monaco_languages.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Monaco/monacoSpecialLanguages.js b/src/Monaco/monacoSpecialLanguages.js index e237c404f0..816b843a28 100644 --- a/src/Monaco/monacoSpecialLanguages.js +++ b/src/Monaco/monacoSpecialLanguages.js @@ -7,7 +7,7 @@ export async function registerAdditionalLanguages(monaco){ await languageDefinitions(); registerAdditionalLanguage("cppExt", [".ino", ".pde"], "cpp", monaco) registerAdditionalLanguage("xmlExt", [".wsdl", ".csproj", ".vcxproj", ".vbproj", ".fsproj"], "xml", monaco) - registerAdditionalLanguage("txtExt", [".sln", ".log", ".vsconfig", ".env", ".srt", ".ahk"], "txt", monaco) + registerAdditionalLanguage("txtExt", [".sln", ".log", ".vsconfig", ".env", ".srt", ".ahk", ".ion"], "txt", monaco) registerAdditionalLanguage("razorExt", [".razor"], "razor", monaco) registerAdditionalLanguage("vbExt", [".vbs"], "vb", monaco) registerAdditionalLanguage("iniExt", [".inf", ".gitconfig", ".gitattributes", ".editorconfig"], "ini", monaco) diff --git a/src/Monaco/monaco_languages.json b/src/Monaco/monaco_languages.json index 7cdb5ae252..d5ee760617 100644 --- a/src/Monaco/monaco_languages.json +++ b/src/Monaco/monaco_languages.json @@ -1 +1 @@ -{"list":[{"id":"plaintext","extensions":[".txt"],"aliases":["Plain Text","text"],"mimetypes":["text/plain"]},{"id":"abap","extensions":[".abap"],"aliases":["abap","ABAP"]},{"id":"apex","extensions":[".cls"],"aliases":["Apex","apex"],"mimetypes":["text/x-apex-source","text/x-apex"]},{"id":"azcli","extensions":[".azcli"],"aliases":["Azure CLI","azcli"]},{"id":"bat","extensions":[".bat",".cmd"],"aliases":["Batch","bat"]},{"id":"bicep","extensions":[".bicep"],"aliases":["Bicep"]},{"id":"cameligo","extensions":[".mligo"],"aliases":["Cameligo"]},{"id":"clojure","extensions":[".clj",".cljs",".cljc",".edn"],"aliases":["clojure","Clojure"]},{"id":"coffeescript","extensions":[".coffee"],"aliases":["CoffeeScript","coffeescript","coffee"],"mimetypes":["text/x-coffeescript","text/coffeescript"]},{"id":"c","extensions":[".c",".h"],"aliases":["C","c"]},{"id":"cpp","extensions":[".cpp",".cc",".cxx",".hpp",".hh",".hxx"],"aliases":["C++","Cpp","cpp"]},{"id":"csharp","extensions":[".cs",".csx",".cake"],"aliases":["C#","csharp"]},{"id":"csp","extensions":[],"aliases":["CSP","csp"]},{"id":"css","extensions":[".css"],"aliases":["CSS","css"],"mimetypes":["text/css"]},{"id":"cypher","extensions":[".cypher",".cyp"],"aliases":["Cypher","OpenCypher"]},{"id":"dart","extensions":[".dart"],"aliases":["Dart","dart"],"mimetypes":["text/x-dart-source","text/x-dart"]},{"id":"dockerfile","extensions":[".dockerfile"],"filenames":["Dockerfile"],"aliases":["Dockerfile"]},{"id":"ecl","extensions":[".ecl"],"aliases":["ECL","Ecl","ecl"]},{"id":"elixir","extensions":[".ex",".exs"],"aliases":["Elixir","elixir","ex"]},{"id":"flow9","extensions":[".flow"],"aliases":["Flow9","Flow","flow9","flow"]},{"id":"fsharp","extensions":[".fs",".fsi",".ml",".mli",".fsx",".fsscript"],"aliases":["F#","FSharp","fsharp"]},{"id":"freemarker2","extensions":[".ftl",".ftlh",".ftlx"],"aliases":["FreeMarker2","Apache FreeMarker2"]},{"id":"freemarker2.tag-angle.interpolation-dollar","aliases":["FreeMarker2 (Angle/Dollar)","Apache FreeMarker2 (Angle/Dollar)"]},{"id":"freemarker2.tag-bracket.interpolation-dollar","aliases":["FreeMarker2 (Bracket/Dollar)","Apache FreeMarker2 (Bracket/Dollar)"]},{"id":"freemarker2.tag-angle.interpolation-bracket","aliases":["FreeMarker2 (Angle/Bracket)","Apache FreeMarker2 (Angle/Bracket)"]},{"id":"freemarker2.tag-bracket.interpolation-bracket","aliases":["FreeMarker2 (Bracket/Bracket)","Apache FreeMarker2 (Bracket/Bracket)"]},{"id":"freemarker2.tag-auto.interpolation-dollar","aliases":["FreeMarker2 (Auto/Dollar)","Apache FreeMarker2 (Auto/Dollar)"]},{"id":"freemarker2.tag-auto.interpolation-bracket","aliases":["FreeMarker2 (Auto/Bracket)","Apache FreeMarker2 (Auto/Bracket)"]},{"id":"go","extensions":[".go"],"aliases":["Go"]},{"id":"graphql","extensions":[".graphql",".gql"],"aliases":["GraphQL","graphql","gql"],"mimetypes":["application/graphql"]},{"id":"handlebars","extensions":[".handlebars",".hbs"],"aliases":["Handlebars","handlebars","hbs"],"mimetypes":["text/x-handlebars-template"]},{"id":"hcl","extensions":[".tf",".tfvars",".hcl"],"aliases":["Terraform","tf","HCL","hcl"]},{"id":"html","extensions":[".html",".htm",".shtml",".xhtml",".mdoc",".jsp",".asp",".aspx",".jshtm"],"aliases":["HTML","htm","html","xhtml"],"mimetypes":["text/html","text/x-jshtm","text/template","text/ng-template"]},{"id":"ini","extensions":[".ini",".properties",".gitconfig"],"filenames":["config",".gitattributes",".gitconfig",".editorconfig"],"aliases":["Ini","ini"]},{"id":"java","extensions":[".java",".jav"],"aliases":["Java","java"],"mimetypes":["text/x-java-source","text/x-java"]},{"id":"javascript","extensions":[".js",".es6",".jsx",".mjs",".cjs"],"firstLine":"^#!.*\\bnode","filenames":["jakefile"],"aliases":["JavaScript","javascript","js"],"mimetypes":["text/javascript"]},{"id":"julia","extensions":[".jl"],"aliases":["julia","Julia"]},{"id":"kotlin","extensions":[".kt",".kts"],"aliases":["Kotlin","kotlin"],"mimetypes":["text/x-kotlin-source","text/x-kotlin"]},{"id":"less","extensions":[".less"],"aliases":["Less","less"],"mimetypes":["text/x-less","text/less"]},{"id":"lexon","extensions":[".lex"],"aliases":["Lexon"]},{"id":"lua","extensions":[".lua"],"aliases":["Lua","lua"]},{"id":"liquid","extensions":[".liquid",".html.liquid"],"aliases":["Liquid","liquid"],"mimetypes":["application/liquid"]},{"id":"m3","extensions":[".m3",".i3",".mg",".ig"],"aliases":["Modula-3","Modula3","modula3","m3"]},{"id":"markdown","extensions":[".md",".markdown",".mdown",".mkdn",".mkd",".mdwn",".mdtxt",".mdtext"],"aliases":["Markdown","markdown"]},{"id":"mdx","extensions":[".mdx"],"aliases":["MDX","mdx"]},{"id":"mips","extensions":[".s"],"aliases":["MIPS","MIPS-V"],"mimetypes":["text/x-mips","text/mips","text/plaintext"]},{"id":"msdax","extensions":[".dax",".msdax"],"aliases":["DAX","MSDAX"]},{"id":"mysql","extensions":[],"aliases":["MySQL","mysql"]},{"id":"objective-c","extensions":[".m"],"aliases":["Objective-C"]},{"id":"pascal","extensions":[".pas",".p",".pp"],"aliases":["Pascal","pas"],"mimetypes":["text/x-pascal-source","text/x-pascal"]},{"id":"pascaligo","extensions":[".ligo"],"aliases":["Pascaligo","ligo"]},{"id":"perl","extensions":[".pl",".pm"],"aliases":["Perl","pl"]},{"id":"pgsql","extensions":[],"aliases":["PostgreSQL","postgres","pg","postgre"]},{"id":"php","extensions":[".php",".php4",".php5",".phtml",".ctp"],"aliases":["PHP","php"],"mimetypes":["application/x-php"]},{"id":"pla","extensions":[".pla"]},{"id":"postiats","extensions":[".dats",".sats",".hats"],"aliases":["ATS","ATS/Postiats"]},{"id":"powerquery","extensions":[".pq",".pqm"],"aliases":["PQ","M","Power Query","Power Query M"]},{"id":"powershell","extensions":[".ps1",".psm1",".psd1"],"aliases":["PowerShell","powershell","ps","ps1"]},{"id":"proto","extensions":[".proto"],"aliases":["protobuf","Protocol Buffers"]},{"id":"pug","extensions":[".jade",".pug"],"aliases":["Pug","Jade","jade"]},{"id":"python","extensions":[".py",".rpy",".pyw",".cpy",".gyp",".gypi"],"aliases":["Python","py"],"firstLine":"^#!/.*\\bpython[0-9.-]*\\b"},{"id":"qsharp","extensions":[".qs"],"aliases":["Q#","qsharp"]},{"id":"r","extensions":[".r",".rhistory",".rmd",".rprofile",".rt"],"aliases":["R","r"]},{"id":"razor","extensions":[".cshtml"],"aliases":["Razor","razor"],"mimetypes":["text/x-cshtml"]},{"id":"redis","extensions":[".redis"],"aliases":["redis"]},{"id":"redshift","extensions":[],"aliases":["Redshift","redshift"]},{"id":"restructuredtext","extensions":[".rst"],"aliases":["reStructuredText","restructuredtext"]},{"id":"ruby","extensions":[".rb",".rbx",".rjs",".gemspec",".pp"],"filenames":["rakefile","Gemfile"],"aliases":["Ruby","rb"]},{"id":"rust","extensions":[".rs",".rlib"],"aliases":["Rust","rust"]},{"id":"sb","extensions":[".sb"],"aliases":["Small Basic","sb"]},{"id":"scala","extensions":[".scala",".sc",".sbt"],"aliases":["Scala","scala","SBT","Sbt","sbt","Dotty","dotty"],"mimetypes":["text/x-scala-source","text/x-scala","text/x-sbt","text/x-dotty"]},{"id":"scheme","extensions":[".scm",".ss",".sch",".rkt"],"aliases":["scheme","Scheme"]},{"id":"scss","extensions":[".scss"],"aliases":["Sass","sass","scss"],"mimetypes":["text/x-scss","text/scss"]},{"id":"shell","extensions":[".sh",".bash"],"aliases":["Shell","sh"]},{"id":"sol","extensions":[".sol"],"aliases":["sol","solidity","Solidity"]},{"id":"aes","extensions":[".aes"],"aliases":["aes","sophia","Sophia"]},{"id":"sparql","extensions":[".rq"],"aliases":["sparql","SPARQL"]},{"id":"sql","extensions":[".sql"],"aliases":["SQL"]},{"id":"st","extensions":[".st",".iecst",".iecplc",".lc3lib",".TcPOU",".TcDUT",".TcGVL",".TcIO"],"aliases":["StructuredText","scl","stl"]},{"id":"swift","aliases":["Swift","swift"],"extensions":[".swift"],"mimetypes":["text/swift"]},{"id":"systemverilog","extensions":[".sv",".svh"],"aliases":["SV","sv","SystemVerilog","systemverilog"]},{"id":"verilog","extensions":[".v",".vh"],"aliases":["V","v","Verilog","verilog"]},{"id":"tcl","extensions":[".tcl"],"aliases":["tcl","Tcl","tcltk","TclTk","tcl/tk","Tcl/Tk"]},{"id":"twig","extensions":[".twig"],"aliases":["Twig","twig"],"mimetypes":["text/x-twig"]},{"id":"typescript","extensions":[".ts",".tsx",".cts",".mts"],"aliases":["TypeScript","ts","typescript"],"mimetypes":["text/typescript"]},{"id":"vb","extensions":[".vb"],"aliases":["Visual Basic","vb"]},{"id":"wgsl","extensions":[".wgsl"],"aliases":["WebGPU Shading Language","WGSL","wgsl"]},{"id":"xml","extensions":[".xml",".xsd",".dtd",".ascx",".csproj",".config",".props",".targets",".wxi",".wxl",".wxs",".xaml",".svg",".svgz",".opf",".xslt",".xsl"],"firstLine":"(\\<\\?xml.*)|(\\ Date: Thu, 28 Nov 2024 17:36:32 +0000 Subject: [PATCH 87/95] [Peek]UserSettings load logging fix and refactor (#36111) * UserSettings refactor to fix logging bug, properly initialise defaults etc. * Apply lock only to code which changes shared data. --- .../peek/Peek.UI/Services/UserSettings.cs | 96 ++++++++++--------- .../Settings.UI.Library/SettingsUtils.cs | 2 +- 2 files changed, 51 insertions(+), 47 deletions(-) diff --git a/src/modules/peek/Peek.UI/Services/UserSettings.cs b/src/modules/peek/Peek.UI/Services/UserSettings.cs index 894df5b8c4..e4a73353c1 100644 --- a/src/modules/peek/Peek.UI/Services/UserSettings.cs +++ b/src/modules/peek/Peek.UI/Services/UserSettings.cs @@ -3,10 +3,8 @@ // See the LICENSE file in the project root for more information. using System; -using System.IO; using System.IO.Abstractions; using System.Threading; - using ManagedCommon; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.Library.Utilities; @@ -16,71 +14,77 @@ namespace Peek.UI public class UserSettings : IUserSettings { private const string PeekModuleName = "Peek"; - private const int MaxNumberOfRetry = 5; + private const int MaxAttempts = 4; private readonly SettingsUtils _settingsUtils; - private readonly IFileSystemWatcher _watcher; - private readonly Lock _loadingSettingsLock = new(); + private readonly Lock _settingsLock = new(); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Defined in helper called in constructor.")] + private readonly IFileSystemWatcher _watcher; + + /// + /// Gets a value indicating whether Peek closes automatically when the window loses focus. + /// public bool CloseAfterLosingFocus { get; private set; } public UserSettings() { _settingsUtils = new SettingsUtils(); - CloseAfterLosingFocus = false; LoadSettingsFromJson(); - _watcher = Helper.GetFileWatcher(PeekModuleName, "settings.json", () => LoadSettingsFromJson()); + _watcher = Helper.GetFileWatcher(PeekModuleName, SettingsUtils.DefaultFileName, LoadSettingsFromJson); + } + + private void ApplySettings(PeekSettings settings) + { + lock (_settingsLock) + { + CloseAfterLosingFocus = settings.Properties.CloseAfterLosingFocus.Value; + } + } + + private void ApplyDefaultSettings() + { + ApplySettings(new PeekSettings()); } private void LoadSettingsFromJson() { - lock (_loadingSettingsLock) + for (int attempt = 1; attempt <= MaxAttempts; attempt++) { - var retry = true; - var retryCount = 0; - - while (retry) + try { - try + ApplySettings(_settingsUtils.GetSettingsOrDefault(PeekModuleName)); + return; + } + catch (System.IO.IOException ex) + { + Logger.LogError($"Peek settings load attempt {attempt} failed: {ex.Message}", ex); + if (attempt == MaxAttempts) { - retryCount++; - - if (!_settingsUtils.SettingsExists(PeekModuleName)) - { - Logger.LogInfo("Peek settings.json was missing, creating a new one"); - var defaultSettings = new PeekSettings(); - defaultSettings.Save(_settingsUtils); - } - - var settings = _settingsUtils.GetSettingsOrDefault(PeekModuleName); - if (settings != null) - { - CloseAfterLosingFocus = settings.Properties.CloseAfterLosingFocus.Value; - } - - retry = false; - } - catch (IOException e) - { - if (retryCount > MaxNumberOfRetry) - { - retry = false; - Logger.LogError($"Failed to Deserialize PowerToys settings, Retrying {e.Message}", e); - } - else - { - Thread.Sleep(500); - } - } - catch (Exception ex) - { - retry = false; - Logger.LogError("Failed to read changed settings", ex); + Logger.LogError($"Failed to load Peek settings after {MaxAttempts} attempts. Continuing with default settings."); + ApplyDefaultSettings(); + return; } + + // Exponential back-off then retry. + Thread.Sleep(CalculateRetryDelay(attempt)); + } + catch (Exception ex) + { + // Anything other than an IO exception is an immediate failure. + Logger.LogError($"Peek settings load failed, continuing with defaults: {ex.Message}", ex); + ApplyDefaultSettings(); + return; } } } + + private static int CalculateRetryDelay(int attempt) + { + return (int)Math.Pow(2, attempt) * 100; + } } } diff --git a/src/settings-ui/Settings.UI.Library/SettingsUtils.cs b/src/settings-ui/Settings.UI.Library/SettingsUtils.cs index 13065b1a71..2c41850201 100644 --- a/src/settings-ui/Settings.UI.Library/SettingsUtils.cs +++ b/src/settings-ui/Settings.UI.Library/SettingsUtils.cs @@ -14,7 +14,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library { public class SettingsUtils : ISettingsUtils { - private const string DefaultFileName = "settings.json"; + public const string DefaultFileName = "settings.json"; private const string DefaultModuleName = ""; private readonly IFile _file; private readonly ISettingsPath _settingsPath; From f565874fc842006f01d34f4f2c8086b029660c20 Mon Sep 17 00:00:00 2001 From: moooyo <42196638+moooyo@users.noreply.github.com> Date: Fri, 29 Nov 2024 12:07:13 +0800 Subject: [PATCH 88/95] [AOT Compatible] Add word "AOT" to spelling check allow list. (#36138) * init * Add new empty line in the end * Add Aot to allow list --------- Co-authored-by: Yu Leng (from Dev Box) --- .github/actions/spell-check/allow/code.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/actions/spell-check/allow/code.txt b/.github/actions/spell-check/allow/code.txt index 2737b7327c..752c1864bd 100644 --- a/.github/actions/spell-check/allow/code.txt +++ b/.github/actions/spell-check/allow/code.txt @@ -226,3 +226,8 @@ arcosh dbus anypass gpg + +# .NET + +AOT +Aot From 0ee1befe7b6207602fa14bec2cdd0a4695ad2bc4 Mon Sep 17 00:00:00 2001 From: Kayla Cinnamon Date: Sun, 1 Dec 2024 16:26:33 -0500 Subject: [PATCH 89/95] Update README with advent calendar (#36172) * update readme * fix spellcheck --- .github/actions/spell-check/allow/names.txt | 2 ++ .github/actions/spell-check/expect.txt | 2 ++ README.md | 8 +++++++- doc/images/overview/PT_holiday_hero_image.png | Bin 0 -> 514891 bytes 4 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 doc/images/overview/PT_holiday_hero_image.png diff --git a/.github/actions/spell-check/allow/names.txt b/.github/actions/spell-check/allow/names.txt index f23f204e3a..c141bd5980 100644 --- a/.github/actions/spell-check/allow/names.txt +++ b/.github/actions/spell-check/allow/names.txt @@ -44,6 +44,7 @@ Bartosz betadele betsegaw bricelam +bsky CCcat Chinh chrdavis @@ -90,6 +91,7 @@ Kairu Kamra Kantarci Karthick +kaylacinnamon kevinguo Krigun Lambson diff --git a/.github/actions/spell-check/expect.txt b/.github/actions/spell-check/expect.txt index dc7eefe607..de32d2a324 100644 --- a/.github/actions/spell-check/expect.txt +++ b/.github/actions/spell-check/expect.txt @@ -667,6 +667,7 @@ Iindex IJson Ijwhost IKs +iljxck ILogon IMAGEHLP IMAGERESIZERCONTEXTMENU @@ -770,6 +771,7 @@ lastcodeanalysissucceeded Lastdevice LASTEXITCODE LAYOUTRTL +lcb LCIDTo lcl Lclean diff --git a/README.md b/README.md index 2fd9e6e755..3369b8386a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Microsoft PowerToys -![Hero image for Microsoft PowerToys](doc/images/overview/PT_hero_image.png) +![Hero image for Microsoft PowerToys](doc/images/overview/PT_holiday_hero_image.png) [How to use PowerToys][usingPowerToys-docs-link] | [Downloads & Release notes][github-release-link] | [Contributing to PowerToys](#contributing) | [What's Happening](#whats-happening) | [Roadmap](#powertoys-roadmap) @@ -20,6 +20,12 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline | [Registry Preview](https://aka.ms/PowerToysOverview_RegistryPreview) | [Screen Ruler](https://aka.ms/PowerToysOverview_ScreenRuler) | [Shortcut Guide](https://aka.ms/PowerToysOverview_ShortcutGuide) | | [Text Extractor](https://aka.ms/PowerToysOverview_TextExtractor) | [Video Conference Mute](https://aka.ms/PowerToysOverview_VideoConference) | [Workspaces](https://aka.ms/PowerToysOverview_Workspaces) | +## 🎁⭐ PowerToys Advent calendar ⭐🎁 + +We will be highlighting a cool utility each day for 24 days in December! To follow along, check out these threads: +- https://bsky.app/profile/kaylacinnamon.bsky.social/post/3lcb7iljxck2o +- https://x.com/cinnamon_msft/status/1863284610773246257 + ## Installing and running Microsoft PowerToys ### Requirements diff --git a/doc/images/overview/PT_holiday_hero_image.png b/doc/images/overview/PT_holiday_hero_image.png new file mode 100644 index 0000000000000000000000000000000000000000..77351927fdb3454fae292f283eba4f5c2630dae2 GIT binary patch literal 514891 zcmV)EK)}C=P)Dt-(;ykii&uA}2Ls^SnVrSXqIc%rXTmewce{V~+izxp0S3HD@k$bz85zXG ziJ9rjIU;Iq?%^Kc!7{U|L#sT)iJ6+3nld?h0OrP zHm`Nld0&)Y`aOiO@i{kj?RCeXEY~f=WzK)sb$(mtUgjFJStW)_vSqu~MEN(8J#J@tl3I=jXH8Rpa_XoOjyB`LE8f z7pHQN-IE`)&-GK~FFj{k$#APH4~B*O_;w;yt>$>LHkmtEnRh53gfFWDMp$zr)TE~o{=*WZndpd^9c-~0YlhesWCex|p zS>9YLWFP9Na=nzQGA|0Pvr69Vzmn1DM2fu7`_ClPSD|%SYJH;q@EwkSdvv1DRG(O8 z^ILn)Juk%-pThDJK5txGl|ziny}&<2@rSpRQScj&{x>XK?@V4iE_m~?zv)QM?|v}9 z5ACw0FYpd^l<^(&!G96?X*;d$Xw>zWpMNTU{_lP*i?^}5v0rw6J@5GR46iz8x7z^T z_wpdyoyB^;xP>fTdxIvG+D^2ZJvZ;xs!;2i|H`X3$MVDT11YjXx@hzIYp(Z!@e2K+ zo=qgLOT8bxmKLwl?+WcJ9etsnbUGagMb7bK(C@KNV5mDDO=YalwbSqOii2)X%F`3+ zcRSMS4R{=$eOJ$q=v&&y@Hx|brsETQly1@GxwT#&HB+&TWgOFLj%ils8E+<9nKh3> z^BkX^N=J{~8usN>$20a_{oE;XzMo8{?AN(Ir%E&K_BuQlZ82gb);w{EuHI+fD|9%o zbPN#wt+gic!nn$@w$e`_vWy(&1)0?BD=70nJ$oUqj!$Hu<+@9E=_XU~e=uy7+8(EB z3*f`{)<7QLzb79(zAsPpg?5Z_Nn0i7Hf^+0mZi4)9_K!eAJN8SW|;*WueCu3?F;yv zVJ)HOOwhMFU0u!ujt5AbvM_$uHUCyx4!kg5>fj>5*CD}6o$1;F$4#dbb{h0?^qm6p zt&SCuV{y%O49Z*Uk2oLsV_ec4B-6gkc?9D}ZSx1FvYPi1IOjz4TYYif#&ys)1MWx1 zkXfa9WE>ALkF(6$mys{Z$TgI{%Ch2^!*xJeN~$F5{~7Bv@(#!ALhjOCx<>j? zK)?4@j+?mdZIpdE&rKGl?cZ;*OjiZ|E_41(ocFHp`8LkIOv2>!>dk?@6!^nur0bgL zD-FpT$nVM*4;vicPFDoQmM?vya7Vo1{KC@T4LcHe)h2+t9`MXE0|Ww=xDo$0t1y*E^*$H9vht`6?@to= zXNED1b8HS^F*=>j(^Yw!?>y4aOmav99;2v7=}hy7x^PKgmmR^)zer`D=N!@SH4}i z_8Q1$O??xmi|u}5Mveh|aSp=C+#nNK9Mj@L2Mvoqw9ajQXn--l-a^?TjkI(EjhD*H z6QY;T+7H_{k-n6dgE#m-pWo9r-~Aw4gMobU*{AbTtXcnTx-Lq`op=25F_#4c+vm}+ z)mt5vQHI4FqC4|on;{{d{chcLr2S0eneP|VBIwutH_y8=*7ChNIFdr!a8LU?eyrVq zN9yQv>FE38@Khi~m{^uGws~&GxX}+IyPKP_595C}Ev2t{;C^Q1 zRBFw4a&$uDe{Xvz<7t#qtS`n6fWgSWT>_Yu9G^~5yBs5Cld)u)@1H+?D1WErIMVwb z@Sf!^-KA@#G!B>AW&!$f(?Nlyk00HWPo6xICy(}IXJ_cZmjY1QA2Fw)&*6(gkHsb+ zz%qbZ=<~J-RyX4_!8`)p+Tu%Wk^zXSI7dcZ@t}WY4(#EaOhK>&ni-�parb5A$p> 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

H&CK(-ob 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 PM**LSO_O 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 =vPo!@p&00@i`dYi%H38 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

QrwKwM9q$%(C)R@USj|dY(IcspMLr&=Z8%+ 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 JPDHLkV1jPll%@ax literal 0 HcmV?d00001 From db23992aacc3bc2399aed5ea9bb5ea7499d7be98 Mon Sep 17 00:00:00 2001 From: Christian Gaarden Gaardmark Date: Mon, 2 Dec 2024 07:13:54 -0800 Subject: [PATCH 90/95] [New+]Fix for renaming when creating file (#36143) --- .../new_utilities.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h b/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h index adf186a509..66ac6deccc 100644 --- a/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h +++ b/src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h @@ -215,10 +215,10 @@ namespace newplus::utilities inline bool is_desktop_folder(const std::filesystem::path target_fullpath) { - TCHAR desktopPath[MAX_PATH]; - if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath))) + TCHAR desktop_path[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktop_path))) { - return StrCmpIW(target_fullpath.c_str(), desktopPath) == 0; + return StrCmpIW(target_fullpath.c_str(), desktop_path) == 0; } return false; } @@ -376,8 +376,10 @@ namespace newplus::utilities // Copy file and determine final filename std::filesystem::path target_final_fullpath = template_entry->copy_object_to(GetActiveWindow(), target_fullpath); + // Consider copy completed. If we do tracing after enter_rename_mode, then rename mode won't consistently work trace.UpdateState(true); Trace::EventCopyTemplate(target_final_fullpath.extension().c_str()); + Trace::EventCopyTemplateResult(hr); trace.Flush(); trace.UpdateState(false); @@ -392,13 +394,12 @@ namespace newplus::utilities Logger::error(ex.what()); hr = S_FALSE; + trace.UpdateState(true); + Trace::EventCopyTemplateResult(hr); + trace.Flush(); + trace.UpdateState(false); } - trace.UpdateState(true); - Trace::EventCopyTemplateResult(hr); - trace.Flush(); - trace.UpdateState(false); - return hr; } From 54aab5d10954c53d78143eddb1b65c114f93e472 Mon Sep 17 00:00:00 2001 From: Sergey Chernyaev Date: Mon, 2 Dec 2024 16:21:56 +0100 Subject: [PATCH 91/95] [QuickAccent]Multi-language selection (#35539) * PowerAccent with multi-language selection - Updated Language enum, method signatures, and settings to support multiple language selections. - Remove ALL language and added special characters language instead. - Modified UI to use ListView with checkboxes for language selection, including a "Select All" option. - Adjusted ViewModel and code-behind to handle multi-selection logic. - Updated Resources.resw and PowerAccentViewModel.cs to reflect these changes. * Changes Language selection UI - Groups languages into two groups - Sort them by localized language name - Remove unneeded looping when no languages selected --- .../poweraccent/PowerAccent.Core/Languages.cs | 110 +++++++------ .../Services/SettingsService.cs | 9 +- .../PowerAccentLanguageGroupModel.cs | 19 +++ .../PowerAccentLanguageModel.cs | 11 ++ .../SettingsXAML/Views/PowerAccentPage.xaml | 150 ++++++++++++------ .../Views/PowerAccentPage.xaml.cs | 58 ++++++- .../Settings.UI/Strings/en-us/Resources.resw | 89 ++++++----- .../ViewModels/PowerAccentViewModel.cs | 149 ++++++++++------- 8 files changed, 398 insertions(+), 197 deletions(-) create mode 100644 src/settings-ui/Settings.UI.Library/PowerAccentLanguageGroupModel.cs create mode 100644 src/settings-ui/Settings.UI.Library/PowerAccentLanguageModel.cs diff --git a/src/modules/poweraccent/PowerAccent.Core/Languages.cs b/src/modules/poweraccent/PowerAccent.Core/Languages.cs index d02afd2c85..2838fe9588 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Languages.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Languages.cs @@ -10,7 +10,7 @@ namespace PowerAccent.Core { public enum Language { - ALL, + SPECIAL, BG, CA, CRH, @@ -54,52 +54,68 @@ namespace PowerAccent.Core internal sealed class Languages { - public static string[] GetDefaultLetterKey(LetterKey letter, Language lang) + public static string[] GetDefaultLetterKey(LetterKey letter, Language[] langs) { - return lang switch + if (langs.Length == Enum.GetValues(typeof(Language)).Length) { - Language.ALL => GetDefaultLetterKeyALL(letter), // All - Language.BG => GetDefaultLetterKeyBG(letter), // Bulgarian - Language.CA => GetDefaultLetterKeyCA(letter), // Catalan - Language.CRH => GetDefaultLetterKeyCRH(letter), // Crimean Tatar - Language.CUR => GetDefaultLetterKeyCUR(letter), // Currency - Language.CY => GetDefaultLetterKeyCY(letter), // Welsh - Language.CZ => GetDefaultLetterKeyCZ(letter), // Czech - Language.DK => GetDefaultLetterKeyDK(letter), // Danish - Language.GA => GetDefaultLetterKeyGA(letter), // Gaeilge (Irish) - Language.GD => GetDefaultLetterKeyGD(letter), // Gàidhlig (Scottish Gaelic) - Language.DE => GetDefaultLetterKeyDE(letter), // German - Language.EL => GetDefaultLetterKeyEL(letter), // Greek - Language.EST => GetDefaultLetterKeyEST(letter), // Estonian - Language.EPO => GetDefaultLetterKeyEPO(letter), // Esperanto - Language.FI => GetDefaultLetterKeyFI(letter), // Finnish - Language.FR => GetDefaultLetterKeyFR(letter), // French - Language.HR => GetDefaultLetterKeyHR(letter), // Croatian - Language.HE => GetDefaultLetterKeyHE(letter), // Hebrew - Language.HU => GetDefaultLetterKeyHU(letter), // Hungarian - Language.IS => GetDefaultLetterKeyIS(letter), // Iceland - Language.IPA => GetDefaultLetterKeyIPA(letter), // IPA (International phonetic alphabet) - Language.IT => GetDefaultLetterKeyIT(letter), // Italian - Language.KU => GetDefaultLetterKeyKU(letter), // Kurdish - Language.LT => GetDefaultLetterKeyLT(letter), // Lithuanian - Language.MK => GetDefaultLetterKeyMK(letter), // Macedonian - Language.MI => GetDefaultLetterKeyMI(letter), // Maori - Language.NL => GetDefaultLetterKeyNL(letter), // Dutch - Language.NO => GetDefaultLetterKeyNO(letter), // Norwegian - Language.PI => GetDefaultLetterKeyPI(letter), // Pinyin - Language.PL => GetDefaultLetterKeyPL(letter), // Polish - Language.PT => GetDefaultLetterKeyPT(letter), // Portuguese - Language.RO => GetDefaultLetterKeyRO(letter), // Romanian - Language.ROM => GetDefaultLetterKeyROM(letter), // Middle Eastern Romanization - Language.SK => GetDefaultLetterKeySK(letter), // Slovak - Language.SL => GetDefaultLetterKeySL(letter), // Slovenian - Language.SP => GetDefaultLetterKeySP(letter), // Spain - Language.SR => GetDefaultLetterKeySR(letter), // Serbian - Language.SR_CYRL => GetDefaultLetterKeySRCyrillic(letter), // Serbian Cyrillic - Language.SV => GetDefaultLetterKeySV(letter), // Swedish - Language.TK => GetDefaultLetterKeyTK(letter), // Turkish - _ => throw new ArgumentException("The language {0} is not known in this context", lang.ToString()), - }; + return GetDefaultLetterKeyALL(letter); + } + + if (langs.Length == 0) + { + return Array.Empty(); + } + + var characters = new List(); + foreach (var lang in langs) + { + characters.AddRange(lang switch + { + Language.SPECIAL => GetDefaultLetterKeySPECIAL(letter), // Special Characters + Language.BG => GetDefaultLetterKeyBG(letter), // Bulgarian + Language.CA => GetDefaultLetterKeyCA(letter), // Catalan + Language.CRH => GetDefaultLetterKeyCRH(letter), // Crimean Tatar + Language.CUR => GetDefaultLetterKeyCUR(letter), // Currency + Language.CY => GetDefaultLetterKeyCY(letter), // Welsh + Language.CZ => GetDefaultLetterKeyCZ(letter), // Czech + Language.DK => GetDefaultLetterKeyDK(letter), // Danish + Language.GA => GetDefaultLetterKeyGA(letter), // Gaeilge (Irish) + Language.GD => GetDefaultLetterKeyGD(letter), // Gàidhlig (Scottish Gaelic) + Language.DE => GetDefaultLetterKeyDE(letter), // German + Language.EL => GetDefaultLetterKeyEL(letter), // Greek + Language.EST => GetDefaultLetterKeyEST(letter), // Estonian + Language.EPO => GetDefaultLetterKeyEPO(letter), // Esperanto + Language.FI => GetDefaultLetterKeyFI(letter), // Finnish + Language.FR => GetDefaultLetterKeyFR(letter), // French + Language.HR => GetDefaultLetterKeyHR(letter), // Croatian + Language.HE => GetDefaultLetterKeyHE(letter), // Hebrew + Language.HU => GetDefaultLetterKeyHU(letter), // Hungarian + Language.IS => GetDefaultLetterKeyIS(letter), // Iceland + Language.IPA => GetDefaultLetterKeyIPA(letter), // IPA (International phonetic alphabet) + Language.IT => GetDefaultLetterKeyIT(letter), // Italian + Language.KU => GetDefaultLetterKeyKU(letter), // Kurdish + Language.LT => GetDefaultLetterKeyLT(letter), // Lithuanian + Language.MK => GetDefaultLetterKeyMK(letter), // Macedonian + Language.MI => GetDefaultLetterKeyMI(letter), // Maori + Language.NL => GetDefaultLetterKeyNL(letter), // Dutch + Language.NO => GetDefaultLetterKeyNO(letter), // Norwegian + Language.PI => GetDefaultLetterKeyPI(letter), // Pinyin + Language.PL => GetDefaultLetterKeyPL(letter), // Polish + Language.PT => GetDefaultLetterKeyPT(letter), // Portuguese + Language.RO => GetDefaultLetterKeyRO(letter), // Romanian + Language.ROM => GetDefaultLetterKeyROM(letter), // Middle Eastern Romanization + Language.SK => GetDefaultLetterKeySK(letter), // Slovak + Language.SL => GetDefaultLetterKeySL(letter), // Slovenian + Language.SP => GetDefaultLetterKeySP(letter), // Spain + Language.SR => GetDefaultLetterKeySR(letter), // Serbian + Language.SR_CYRL => GetDefaultLetterKeySRCyrillic(letter), // Serbian Cyrillic + Language.SV => GetDefaultLetterKeySV(letter), // Swedish + Language.TK => GetDefaultLetterKeyTK(letter), // Turkish + _ => throw new ArgumentException("The language {0} is not known in this context", lang.ToString()), + }); + } + + return characters.Distinct().ToArray(); } // Store the computed letters for each key, so that subsequent calls don't take as long. @@ -149,7 +165,7 @@ namespace PowerAccent.Core .Union(GetDefaultLetterKeySRCyrillic(letter)) .Union(GetDefaultLetterKeySV(letter)) .Union(GetDefaultLetterKeyTK(letter)) - .Union(GetDefaultLetterKeyAllLanguagesOnly(letter)) + .Union(GetDefaultLetterKeySPECIAL(letter)) .ToArray(); _allLanguagesCache[letter] = cachedValue; @@ -160,7 +176,7 @@ namespace PowerAccent.Core // Contains all characters that should be shown in all languages but currently don't belong to any of the single languages available for that letter. // These characters can be removed from this list after they've been added to one of the other languages for that specific letter. - private static string[] GetDefaultLetterKeyAllLanguagesOnly(LetterKey letter) + private static string[] GetDefaultLetterKeySPECIAL(LetterKey letter) { return letter switch { diff --git a/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs b/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs index b234fcfde4..9647fb13c3 100644 --- a/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs +++ b/src/modules/poweraccent/PowerAccent.Core/Services/SettingsService.cs @@ -66,7 +66,10 @@ public class SettingsService ExcludedApps = settings.Properties.ExcludedApps.Value; _keyboardListener.UpdateExcludedApps(ExcludedApps); - SelectedLang = Enum.TryParse(settings.Properties.SelectedLang.Value, out Language selectedLangValue) ? selectedLangValue : Language.ALL; + SelectedLang = settings.Properties.SelectedLang.Value + .Split(',', StringSplitOptions.RemoveEmptyEntries) + .Select(lang => Enum.TryParse(lang, out Language selectedLangValue) ? selectedLangValue : Language.SPECIAL) + .ToArray(); switch (settings.Properties.ToolbarPosition.Value) { @@ -187,9 +190,9 @@ public class SettingsService } } - private Language _selectedLang; + private Language[] _selectedLang; - public Language SelectedLang + public Language[] SelectedLang { get { diff --git a/src/settings-ui/Settings.UI.Library/PowerAccentLanguageGroupModel.cs b/src/settings-ui/Settings.UI.Library/PowerAccentLanguageGroupModel.cs new file mode 100644 index 0000000000..8033782bd7 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/PowerAccentLanguageGroupModel.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections.Generic; + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public class PowerAccentLanguageGroupModel : List + { + public PowerAccentLanguageGroupModel(List languages, string group) + : base(languages) + { + this.Group = group; + } + + public string Group { get; init; } + } +} diff --git a/src/settings-ui/Settings.UI.Library/PowerAccentLanguageModel.cs b/src/settings-ui/Settings.UI.Library/PowerAccentLanguageModel.cs new file mode 100644 index 0000000000..13de2ac653 --- /dev/null +++ b/src/settings-ui/Settings.UI.Library/PowerAccentLanguageModel.cs @@ -0,0 +1,11 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.PowerToys.Settings.UI.Library +{ + public record PowerAccentLanguageModel(string LanguageCode, string LanguageResourceID, string GroupResourceID) + { + public string Language { get; set; } + } +} diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml index 77c96b15f6..790d8182b3 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml @@ -1,7 +1,8 @@ - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml.cs b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml.cs index 91e57b8105..c0e78d3e87 100644 --- a/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml.cs +++ b/src/settings-ui/Settings.UI/SettingsXAML/Views/PowerAccentPage.xaml.cs @@ -1,10 +1,12 @@ // Copyright (c) Microsoft Corporation // The Microsoft Corporation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - +using System.Linq; +using CommunityToolkit.WinUI; using Microsoft.PowerToys.Settings.UI.Helpers; using Microsoft.PowerToys.Settings.UI.Library; using Microsoft.PowerToys.Settings.UI.ViewModels; +using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; namespace Microsoft.PowerToys.Settings.UI.Views @@ -19,11 +21,65 @@ namespace Microsoft.PowerToys.Settings.UI.Views ViewModel = new PowerAccentViewModel(settingsUtils, SettingsRepository.GetInstance(settingsUtils), ShellPage.SendDefaultIPCMessage); DataContext = ViewModel; this.InitializeComponent(); + this.InitializeControlsStates(); } public void RefreshEnabledState() { ViewModel.RefreshEnabledState(); } + + private void InitializeControlsStates() + { + SetCheckBoxStatus(); + } + + private void SetCheckBoxStatus() + { + if (ViewModel.SelectedLanguageOptions.Length == 0) + { + this.QuickAccent_SelectedLanguage_All.IsChecked = false; + this.QuickAccent_SelectedLanguage_All.IsThreeState = false; + } + else if (ViewModel.AllSelected) + { + this.QuickAccent_SelectedLanguage_All.IsChecked = true; + this.QuickAccent_SelectedLanguage_All.IsThreeState = false; + } + else + { + this.QuickAccent_SelectedLanguage_All.IsThreeState = true; + this.QuickAccent_SelectedLanguage_All.IsChecked = null; + } + } + + private void QuickAccent_SelectedLanguage_SelectAll(object sender, RoutedEventArgs e) + { + this.QuickAccent_Language_Select.SelectAllSafe(); + } + + private void QuickAccent_SelectedLanguage_UnselectAll(object sender, RoutedEventArgs e) + { + this.QuickAccent_Language_Select.DeselectAll(); + } + + private void QuickAccent_SelectedLanguage_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + var listView = sender as ListView; + + ViewModel.SelectedLanguageOptions = listView.SelectedItems + .Select(item => item as PowerAccentLanguageModel) + .ToArray(); + + SetCheckBoxStatus(); + } + + private void QuickAccent_Language_Select_Loaded(object sender, RoutedEventArgs e) + { + foreach (var languageOption in ViewModel.SelectedLanguageOptions) + { + this.QuickAccent_Language_Select.SelectedItems.Add(languageOption); + } + } } } diff --git a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw index 66a332433c..57f6468b0a 100644 --- a/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw +++ b/src/settings-ui/Settings.UI/Strings/en-us/Resources.resw @@ -1,4 +1,4 @@ - + + + + true + true + 2 + + From 866518e1198adf519d6015ed936d188d7046b659 Mon Sep 17 00:00:00 2001 From: KITAGAWA Yasutaka Date: Wed, 4 Dec 2024 01:06:43 +0900 Subject: [PATCH 93/95] [KeyboardManager]Add IME On, IME Off (#34697) --- src/common/interop/keyboard_layout.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/common/interop/keyboard_layout.cpp b/src/common/interop/keyboard_layout.cpp index 5254f1ee78..12253c55eb 100644 --- a/src/common/interop/keyboard_layout.cpp +++ b/src/common/interop/keyboard_layout.cpp @@ -241,10 +241,12 @@ void LayoutMap::LayoutMapImpl::UpdateLayout() keyboardLayoutMap[VK_KANA] = L"IME Kana"; keyboardLayoutMap[VK_HANGEUL] = L"IME Hangeul"; keyboardLayoutMap[VK_HANGUL] = L"IME Hangul"; + keyboardLayoutMap[VK_IME_ON] = L"IME On"; keyboardLayoutMap[VK_JUNJA] = L"IME Junja"; keyboardLayoutMap[VK_FINAL] = L"IME Final"; keyboardLayoutMap[VK_HANJA] = L"IME Hanja"; keyboardLayoutMap[VK_KANJI] = L"IME Kanji"; + keyboardLayoutMap[VK_IME_OFF] = L"IME Off"; keyboardLayoutMap[VK_CONVERT] = L"IME Convert"; keyboardLayoutMap[VK_NONCONVERT] = L"IME Non-Convert"; keyboardLayoutMap[VK_ACCEPT] = L"IME Kana"; From 50b13422348719cf9cd387c88daa2d4bb8cbbb06 Mon Sep 17 00:00:00 2001 From: Davide Giacometti <25966642+davidegiacometti@users.noreply.github.com> Date: Tue, 3 Dec 2024 18:24:43 +0100 Subject: [PATCH 94/95] [PreviewPane]Use PerMonitorV2 DPI mode (#36174) use PerMonitorV2 DPI mode --- .../previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj | 1 + .../MarkdownPreviewHandler/MarkdownPreviewHandler.csproj | 1 + .../previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj | 1 + .../previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj | 1 + .../previewpane/QoiPreviewHandler/QoiPreviewHandler.csproj | 1 + .../previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj | 1 + 6 files changed, 6 insertions(+) diff --git a/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj b/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj index 4f809cb757..51bf6f1f7e 100644 --- a/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj +++ b/src/modules/previewpane/GcodePreviewHandler/GcodePreviewHandler.csproj @@ -18,6 +18,7 @@ PowerToys.GcodePreviewHandler {805306FF-A562-4415-8DEF-E493BDC45918} Microsoft.PowerToys.PreviewHandler.Gcode + PerMonitorV2 diff --git a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj index c8b832717b..074b94ec5c 100644 --- a/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj +++ b/src/modules/previewpane/MarkdownPreviewHandler/MarkdownPreviewHandler.csproj @@ -15,6 +15,7 @@ false true true + PerMonitorV2 diff --git a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj index a3e2e497bc..5753ad910d 100644 --- a/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj +++ b/src/modules/previewpane/MonacoPreviewHandler/MonacoPreviewHandler.csproj @@ -17,6 +17,7 @@ Microsoft.PowerToys.PreviewHandler.Monaco PowerToys.MonacoPreviewHandler WinExe + PerMonitorV2 diff --git a/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj b/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj index ffb2a07763..069ce76d8f 100644 --- a/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj +++ b/src/modules/previewpane/PdfPreviewHandler/PdfPreviewHandler.csproj @@ -18,6 +18,7 @@ {69E1EE8D-143A-4060-9129-4658ACF14AAF} Microsoft.PowerToys.PreviewHandler.Pdf PowerToys.PdfPreviewHandler + PerMonitorV2 diff --git a/src/modules/previewpane/QoiPreviewHandler/QoiPreviewHandler.csproj b/src/modules/previewpane/QoiPreviewHandler/QoiPreviewHandler.csproj index 40bb260f73..eaa6744203 100644 --- a/src/modules/previewpane/QoiPreviewHandler/QoiPreviewHandler.csproj +++ b/src/modules/previewpane/QoiPreviewHandler/QoiPreviewHandler.csproj @@ -18,6 +18,7 @@ PowerToys.QoiPreviewHandler {6B04803D-B418-4833-A67E-B0FC966636A5} Microsoft.PowerToys.PreviewHandler.Qoi + PerMonitorV2 diff --git a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj index 975c3c4f92..32f8bc0e30 100644 --- a/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj +++ b/src/modules/previewpane/SvgPreviewHandler/SvgPreviewHandler.csproj @@ -16,6 +16,7 @@ false true true + PerMonitorV2 From 35b9bcacdb34371f4f66360c4be74d5c4f859d81 Mon Sep 17 00:00:00 2001 From: PesBandi <127593627+PesBandi@users.noreply.github.com> Date: Tue, 3 Dec 2024 18:25:35 +0100 Subject: [PATCH 95/95] [Monaco] Use syntax highlighting for `.srt` (#35651) * [FilePreview] Use syntax highlighting for .srt * Change customTokenColors to customTokenThemeRules * Ignore text on the same line as a timestamp * Update tokenization rules --- doc/devdocs/common/FilePreviewCommon.md | 2 +- .../generateAllFileComponents.ps1 | 2 +- src/Monaco.props | 4 +-- src/Monaco/customLanguages/gitignore.js | 2 +- src/Monaco/customLanguages/srt.js | 29 +++++++++++++++++++ src/Monaco/customTokenColors.js | 3 -- src/Monaco/customTokenThemeRules.js | 3 ++ src/Monaco/index.html | 4 +-- src/Monaco/monacoSpecialLanguages.js | 20 +++++++------ src/Monaco/monaco_languages.json | 2 +- .../Assets/RegistryPreview/index.html | 2 +- 11 files changed, 52 insertions(+), 21 deletions(-) create mode 100644 src/Monaco/customLanguages/srt.js delete mode 100644 src/Monaco/customTokenColors.js create mode 100644 src/Monaco/customTokenThemeRules.js diff --git a/doc/devdocs/common/FilePreviewCommon.md b/doc/devdocs/common/FilePreviewCommon.md index 85b74ca7e7..33c11aad28 100644 --- a/doc/devdocs/common/FilePreviewCommon.md +++ b/doc/devdocs/common/FilePreviewCommon.md @@ -47,7 +47,7 @@ registerAdditionalNewLanguage("id", [".fileExtension"], idDefinition(), monaco) * The id can be anything. Recommended is one of the file extensions. For example "php" or "reg". -4. In case you wish to add a custom color for a token, you can do so by adding the following line to [`customTokenColors.js`](/src/Monaco/customTokenColors.js): +4. In case you wish to add a custom color for a token, you can do so by adding the following line to [`customTokenThemeRules.js`](/src/Monaco/customTokenThemeRules.js): ```javascript {token: 'token-name', foreground: 'ff0000'} ``` diff --git a/installer/PowerToysSetup/generateAllFileComponents.ps1 b/installer/PowerToysSetup/generateAllFileComponents.ps1 index 50ce0f2dd6..3592b14362 100644 --- a/installer/PowerToysSetup/generateAllFileComponents.ps1 +++ b/installer/PowerToysSetup/generateAllFileComponents.ps1 @@ -30,7 +30,7 @@ Function Generate-FileList() { $fileExclusionList = @("*.pdb", "*.lastcodeanalysissucceeded", "createdump.exe", "powertoys.exe") - $fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "monacoSpecialLanguages.js", "customTokenColors.js", "*.pri") + $fileInclusionList = @("*.dll", "*.exe", "*.json", "*.msix", "*.png", "*.gif", "*.ico", "*.cur", "*.svg", "index.html", "reg.js", "gitignore.js", "srt.js", "monacoSpecialLanguages.js", "customTokenThemeRules.js", "*.pri") $dllsToIgnore = @("System.CodeDom.dll", "WindowsBase.dll") diff --git a/src/Monaco.props b/src/Monaco.props index 0aeef121e2..471772823d 100644 --- a/src/Monaco.props +++ b/src/Monaco.props @@ -2,8 +2,8 @@ - - Assets\Monaco\customTokenColors.js + + Assets\Monaco\customTokenThemeRules.js Always diff --git a/src/Monaco/customLanguages/gitignore.js b/src/Monaco/customLanguages/gitignore.js index e4539dd50a..8b5d393993 100644 --- a/src/Monaco/customLanguages/gitignore.js +++ b/src/Monaco/customLanguages/gitignore.js @@ -6,7 +6,7 @@ root: [ [/^#.*$/, 'comment'], [/.*((? \d{2}:\d{2}:\d{2},\d{3}/, { + cases: { + '@eos': {token: 'type.identifier', next: '@subtitle'}, + '@default': {token: 'type.identifier', next: '@ignore'} + } + }], + [/^$/, 'string', '@pop'] + ], + + ignore: [ + [/.+$/, '', '@subtitle'] + ], + + subtitle: [ + [/^$/, 'string', '@popall'], + [/<\/?(?:[ibu]|font(?:\s+color="[^"]+"\s*)?)>/, 'tag'], + [/./, 'string'] + ] + } + }; +} \ No newline at end of file diff --git a/src/Monaco/customTokenColors.js b/src/Monaco/customTokenColors.js deleted file mode 100644 index aa20d58d42..0000000000 --- a/src/Monaco/customTokenColors.js +++ /dev/null @@ -1,3 +0,0 @@ -export const customTokenColors = [ - {token: 'custom-gitignore.negation', foreground: 'c00ce0'} -]; \ No newline at end of file diff --git a/src/Monaco/customTokenThemeRules.js b/src/Monaco/customTokenThemeRules.js new file mode 100644 index 0000000000..cbba1b1f39 --- /dev/null +++ b/src/Monaco/customTokenThemeRules.js @@ -0,0 +1,3 @@ +export const customTokenThemeRules = [ + {token: 'custom-negation.gitignore', foreground: 'c00ce0'} +]; \ No newline at end of file diff --git a/src/Monaco/index.html b/src/Monaco/index.html index 1888cdac39..020c4c3c33 100644 --- a/src/Monaco/index.html +++ b/src/Monaco/index.html @@ -79,7 +79,7 @@