mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-23 19:49:43 +01:00
- Add TOGGLE_POWER_DISPLAY_EVENT and listener thread to PowerDisplay module for Quick Access integration - Implement TogglePowerDisplay() to launch or toggle window as needed - Update QuickAccessLauncher to signal new event for PowerDisplay - Ensure process starts on message send in PowerDisplayProcessManager - Change OnActivated to toggle window on redirect activation - Only show monitor controls if both enabled and hardware-supported - Add proper cleanup for new event and thread resources
553 lines
22 KiB
C#
553 lines
22 KiB
C#
// 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.Linq;
|
|
using System.Threading.Tasks;
|
|
using ManagedCommon;
|
|
using Microsoft.PowerToys.Settings.UI.Library;
|
|
using Microsoft.PowerToys.Telemetry;
|
|
using PowerDisplay.Common.Models;
|
|
using PowerDisplay.Common.Services;
|
|
using PowerDisplay.Common.Utils;
|
|
using PowerDisplay.Serialization;
|
|
using PowerDisplay.Services;
|
|
using PowerDisplay.Telemetry.Events;
|
|
using PowerToys.Interop;
|
|
|
|
namespace PowerDisplay.ViewModels;
|
|
|
|
/// <summary>
|
|
/// MainViewModel - Settings UI synchronization and Profile management methods
|
|
/// </summary>
|
|
public partial class MainViewModel
|
|
{
|
|
/// <summary>
|
|
/// Check if a value is within the valid range (inclusive).
|
|
/// </summary>
|
|
private static bool IsValueInRange(int value, int min, int max) => value >= min && value <= max;
|
|
|
|
/// <summary>
|
|
/// Apply settings changes from Settings UI (IPC event handler entry point)
|
|
/// Only applies UI configuration changes. Hardware parameter changes (e.g., color temperature)
|
|
/// should be triggered via custom actions to avoid unwanted side effects when non-hardware
|
|
/// settings (like RestoreSettingsOnStartup) are changed.
|
|
/// </summary>
|
|
public void ApplySettingsFromUI()
|
|
{
|
|
try
|
|
{
|
|
// Rebuild monitor list with updated hidden monitor settings
|
|
// UpdateMonitorList already handles filtering hidden monitors
|
|
UpdateMonitorList(_monitorManager.Monitors, isInitialLoad: false);
|
|
|
|
// Apply UI configuration changes only (feature visibility toggles, etc.)
|
|
// Hardware parameters (brightness, color temperature) are applied via custom actions
|
|
var settings = _settingsUtils.GetSettingsOrDefault<PowerDisplaySettings>("PowerDisplay");
|
|
ApplyUIConfiguration(settings);
|
|
|
|
// Reload profiles in case they were added/updated/deleted in Settings UI
|
|
LoadProfiles();
|
|
|
|
// Reload UI display settings (profile switcher, identify button, color temp switcher)
|
|
LoadUIDisplaySettings();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError($"[Settings] Failed to apply settings from UI: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Apply UI-only configuration changes (feature visibility toggles)
|
|
/// Synchronous, lightweight operation
|
|
/// </summary>
|
|
private void ApplyUIConfiguration(PowerDisplaySettings settings)
|
|
{
|
|
try
|
|
{
|
|
foreach (var monitorVm in Monitors)
|
|
{
|
|
ApplyFeatureVisibility(monitorVm, settings);
|
|
}
|
|
|
|
// Trigger UI refresh
|
|
UIRefreshRequested?.Invoke(this, EventArgs.Empty);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError($"[Settings] Failed to apply UI configuration: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Apply profile by name (called via Named Pipe from Settings UI)
|
|
/// This is the new direct method that receives the profile name via IPC.
|
|
/// </summary>
|
|
/// <param name="profileName">The name of the profile to apply.</param>
|
|
public async Task ApplyProfileByNameAsync(string profileName)
|
|
{
|
|
try
|
|
{
|
|
Logger.LogInfo($"[Profile] Applying profile by name: {profileName}");
|
|
|
|
// Load profiles and find the requested one
|
|
var profilesData = ProfileService.LoadProfiles();
|
|
var profile = profilesData.GetProfile(profileName);
|
|
|
|
if (profile == null || !profile.IsValid())
|
|
{
|
|
Logger.LogWarning($"[Profile] Profile '{profileName}' not found or invalid");
|
|
return;
|
|
}
|
|
|
|
// Apply the profile settings to monitors
|
|
await ApplyProfileAsync(profile.MonitorSettings);
|
|
Logger.LogInfo($"[Profile] Successfully applied profile: {profileName}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError($"[Profile] Failed to apply profile '{profileName}': {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Handle theme change from LightSwitch by applying the appropriate profile.
|
|
/// Called from App.xaml.cs when LightSwitch theme events are received.
|
|
/// </summary>
|
|
/// <param name="isLightMode">Whether the theme changed to light mode.</param>
|
|
public void ApplyLightSwitchProfile(bool isLightMode)
|
|
{
|
|
var profileName = LightSwitchService.GetProfileForTheme(isLightMode);
|
|
|
|
if (string.IsNullOrEmpty(profileName))
|
|
{
|
|
return;
|
|
}
|
|
|
|
_ = Task.Run(async () =>
|
|
{
|
|
try
|
|
{
|
|
Logger.LogInfo($"[LightSwitch Integration] Applying profile: {profileName}");
|
|
|
|
// Load and apply the profile
|
|
var profilesData = ProfileService.LoadProfiles();
|
|
var profile = profilesData.GetProfile(profileName);
|
|
|
|
if (profile == null || !profile.IsValid())
|
|
{
|
|
Logger.LogWarning($"[LightSwitch Integration] Profile '{profileName}' not found or invalid");
|
|
return;
|
|
}
|
|
|
|
// Apply the profile - need to dispatch to UI thread since MonitorViewModels are UI-bound
|
|
var tcs = new TaskCompletionSource<bool>();
|
|
var enqueued = _dispatcherQueue.TryEnqueue(() =>
|
|
{
|
|
// Start the async operation and handle completion
|
|
_ = ApplyProfileAndCompleteAsync(profile.MonitorSettings, tcs);
|
|
});
|
|
|
|
if (!enqueued)
|
|
{
|
|
Logger.LogError($"[LightSwitch Integration] Failed to enqueue profile application to UI thread");
|
|
return;
|
|
}
|
|
|
|
await tcs.Task;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError($"[LightSwitch Integration] Failed to apply profile: {ex.GetType().Name}: {ex.Message}");
|
|
if (ex.InnerException != null)
|
|
{
|
|
Logger.LogError($"[LightSwitch Integration] Inner exception: {ex.InnerException.GetType().Name}: {ex.InnerException.Message}");
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// Helper method to apply profile and signal completion.
|
|
/// </summary>
|
|
private async Task ApplyProfileAndCompleteAsync(List<ProfileMonitorSetting> monitorSettings, TaskCompletionSource<bool> tcs)
|
|
{
|
|
try
|
|
{
|
|
await ApplyProfileAsync(monitorSettings);
|
|
tcs.SetResult(true);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
tcs.SetException(ex);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Apply profile settings to monitors
|
|
/// </summary>
|
|
private async Task ApplyProfileAsync(List<ProfileMonitorSetting> monitorSettings)
|
|
{
|
|
var updateTasks = new List<Task>();
|
|
|
|
foreach (var setting in monitorSettings)
|
|
{
|
|
// Find monitor by Id (unique identifier)
|
|
var monitorVm = Monitors.FirstOrDefault(m => m.Id == setting.MonitorId);
|
|
|
|
if (monitorVm == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Apply brightness if included in profile
|
|
if (setting.Brightness.HasValue &&
|
|
IsValueInRange(setting.Brightness.Value, monitorVm.MinBrightness, monitorVm.MaxBrightness))
|
|
{
|
|
updateTasks.Add(monitorVm.SetBrightnessAsync(setting.Brightness.Value));
|
|
}
|
|
|
|
// Apply contrast if supported and value provided
|
|
if (setting.Contrast.HasValue && monitorVm.ShowContrast &&
|
|
IsValueInRange(setting.Contrast.Value, monitorVm.MinContrast, monitorVm.MaxContrast))
|
|
{
|
|
updateTasks.Add(monitorVm.SetContrastAsync(setting.Contrast.Value));
|
|
}
|
|
|
|
// Apply volume if supported and value provided
|
|
if (setting.Volume.HasValue && monitorVm.ShowVolume &&
|
|
IsValueInRange(setting.Volume.Value, monitorVm.MinVolume, monitorVm.MaxVolume))
|
|
{
|
|
updateTasks.Add(monitorVm.SetVolumeAsync(setting.Volume.Value));
|
|
}
|
|
|
|
// Apply color temperature if included in profile
|
|
if (setting.ColorTemperatureVcp.HasValue && setting.ColorTemperatureVcp.Value > 0)
|
|
{
|
|
updateTasks.Add(monitorVm.SetColorTemperatureAsync(setting.ColorTemperatureVcp.Value));
|
|
}
|
|
}
|
|
|
|
// Wait for all updates to complete
|
|
if (updateTasks.Count > 0)
|
|
{
|
|
await Task.WhenAll(updateTasks);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Restore monitor settings from state file - ONLY called at startup when RestoreSettingsOnStartup is enabled.
|
|
/// Compares saved values with current hardware values and only writes when different.
|
|
/// </summary>
|
|
public async Task RestoreMonitorSettingsAsync()
|
|
{
|
|
try
|
|
{
|
|
IsLoading = true;
|
|
var updateTasks = new List<Task>();
|
|
|
|
foreach (var monitorVm in Monitors)
|
|
{
|
|
var savedState = _stateManager.GetMonitorParameters(monitorVm.Id);
|
|
if (!savedState.HasValue)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// Restore brightness if different from current
|
|
if (IsValueInRange(savedState.Value.Brightness, monitorVm.MinBrightness, monitorVm.MaxBrightness) &&
|
|
savedState.Value.Brightness != monitorVm.Brightness)
|
|
{
|
|
updateTasks.Add(monitorVm.SetBrightnessAsync(savedState.Value.Brightness));
|
|
}
|
|
|
|
// Restore color temperature if different from current
|
|
if (savedState.Value.ColorTemperatureVcp > 0 &&
|
|
savedState.Value.ColorTemperatureVcp != monitorVm.ColorTemperature)
|
|
{
|
|
updateTasks.Add(monitorVm.SetColorTemperatureAsync(savedState.Value.ColorTemperatureVcp));
|
|
}
|
|
|
|
// Restore contrast if different from current
|
|
if (monitorVm.ShowContrast &&
|
|
IsValueInRange(savedState.Value.Contrast, monitorVm.MinContrast, monitorVm.MaxContrast) &&
|
|
savedState.Value.Contrast != monitorVm.Contrast)
|
|
{
|
|
updateTasks.Add(monitorVm.SetContrastAsync(savedState.Value.Contrast));
|
|
}
|
|
|
|
// Restore volume if different from current
|
|
if (monitorVm.ShowVolume &&
|
|
IsValueInRange(savedState.Value.Volume, monitorVm.MinVolume, monitorVm.MaxVolume) &&
|
|
savedState.Value.Volume != monitorVm.Volume)
|
|
{
|
|
updateTasks.Add(monitorVm.SetVolumeAsync(savedState.Value.Volume));
|
|
}
|
|
}
|
|
|
|
if (updateTasks.Count > 0)
|
|
{
|
|
await Task.WhenAll(updateTasks);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError($"[RestoreMonitorSettings] Failed: {ex.Message}");
|
|
}
|
|
finally
|
|
{
|
|
IsLoading = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Apply feature visibility settings to a monitor ViewModel.
|
|
/// Only shows features that are both enabled by user AND supported by hardware.
|
|
/// </summary>
|
|
private void ApplyFeatureVisibility(MonitorViewModel monitorVm, PowerDisplaySettings settings)
|
|
{
|
|
var monitorSettings = settings.Properties.Monitors.FirstOrDefault(m =>
|
|
m.Id == monitorVm.Id);
|
|
|
|
if (monitorSettings != null)
|
|
{
|
|
// Only show features that are both enabled by user AND supported by hardware
|
|
monitorVm.ShowContrast = monitorSettings.EnableContrast && monitorVm.SupportsContrast;
|
|
monitorVm.ShowVolume = monitorSettings.EnableVolume && monitorVm.SupportsVolume;
|
|
monitorVm.ShowInputSource = monitorSettings.EnableInputSource && monitorVm.SupportsInputSource;
|
|
monitorVm.ShowRotation = monitorSettings.EnableRotation;
|
|
monitorVm.ShowColorTemperature = monitorSettings.EnableColorTemperature && monitorVm.SupportsColorTemperature;
|
|
monitorVm.ShowPowerState = monitorSettings.EnablePowerState && monitorVm.SupportsPowerState;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Thread-safe save method that can be called from background threads.
|
|
/// Does not access UI collections or update UI properties.
|
|
/// </summary>
|
|
public void SaveMonitorSettingDirect(string monitorId, string property, int value)
|
|
{
|
|
try
|
|
{
|
|
// This is thread-safe - _stateManager has internal locking
|
|
// No UI thread operations, no ObservableCollection access
|
|
_stateManager.UpdateMonitorParameter(monitorId, property, value);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Only log, don't update UI from background thread
|
|
Logger.LogError($"Failed to queue setting save for monitorId '{monitorId}': {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Save monitor information to settings.json for Settings UI to read
|
|
/// </summary>
|
|
private void SaveMonitorsToSettings()
|
|
{
|
|
try
|
|
{
|
|
// Load current settings to preserve user preferences (including IsHidden)
|
|
var settings = _settingsUtils.GetSettingsOrDefault<PowerDisplaySettings>(PowerDisplaySettings.ModuleName);
|
|
|
|
// Create lookup of existing monitors by Id to preserve settings
|
|
// Filter out monitors with empty IDs to avoid dictionary key collision errors
|
|
var existingMonitorSettings = settings.Properties.Monitors
|
|
.Where(m => !string.IsNullOrEmpty(m.Id))
|
|
.GroupBy(m => m.Id)
|
|
.ToDictionary(g => g.Key, g => g.First());
|
|
|
|
// Build monitor list using Settings UI's MonitorInfo model
|
|
// Only include monitors with valid (non-empty) IDs to auto-fix corrupted settings
|
|
var monitors = new List<Microsoft.PowerToys.Settings.UI.Library.MonitorInfo>();
|
|
|
|
foreach (var vm in Monitors)
|
|
{
|
|
// Skip monitors with empty IDs - they are invalid and would cause issues
|
|
if (string.IsNullOrEmpty(vm.Id))
|
|
{
|
|
Logger.LogWarning($"[SaveMonitors] Skipping monitor '{vm.Name}' with empty Id");
|
|
continue;
|
|
}
|
|
|
|
var monitorInfo = CreateMonitorInfo(vm);
|
|
ApplyPreservedUserSettings(monitorInfo, existingMonitorSettings);
|
|
monitors.Add(monitorInfo);
|
|
}
|
|
|
|
// Also add hidden monitors from existing settings (monitors that are hidden but still connected)
|
|
// Only include those with valid IDs
|
|
foreach (var existingMonitor in settings.Properties.Monitors.Where(m => m.IsHidden && !string.IsNullOrEmpty(m.Id)))
|
|
{
|
|
// Only add if not already in the list (to avoid duplicates)
|
|
if (!monitors.Any(m => m.Id == existingMonitor.Id))
|
|
{
|
|
monitors.Add(existingMonitor);
|
|
}
|
|
}
|
|
|
|
// Update monitors list
|
|
settings.Properties.Monitors = monitors;
|
|
|
|
// Save back to settings.json using source-generated context for AOT
|
|
_settingsUtils.SaveSettings(
|
|
System.Text.Json.JsonSerializer.Serialize(settings, AppJsonContext.Default.PowerDisplaySettings),
|
|
PowerDisplaySettings.ModuleName);
|
|
|
|
// Signal Settings UI that monitor list has been updated
|
|
SignalMonitorsRefreshEvent();
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError($"Failed to save monitors to settings.json: {ex.Message}");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create MonitorInfo object from MonitorViewModel
|
|
/// </summary>
|
|
private Microsoft.PowerToys.Settings.UI.Library.MonitorInfo CreateMonitorInfo(MonitorViewModel vm)
|
|
{
|
|
// Validate monitor Id - this should never be empty for properly discovered monitors
|
|
if (string.IsNullOrEmpty(vm.Id))
|
|
{
|
|
Logger.LogWarning($"[CreateMonitorInfo] Monitor '{vm.Name}' has empty Id - this may cause issues with Settings UI");
|
|
}
|
|
|
|
var monitorInfo = new Microsoft.PowerToys.Settings.UI.Library.MonitorInfo
|
|
{
|
|
Name = vm.Name,
|
|
Id = vm.Id,
|
|
CommunicationMethod = vm.CommunicationMethod,
|
|
CurrentBrightness = vm.Brightness,
|
|
ColorTemperatureVcp = vm.ColorTemperature,
|
|
CapabilitiesRaw = vm.CapabilitiesRaw,
|
|
VcpCodesFormatted = vm.VcpCapabilitiesInfo?.GetSortedVcpCodes()
|
|
.Select(info => FormatVcpCodeForDisplay(info.Code, info))
|
|
.ToList() ?? new List<Microsoft.PowerToys.Settings.UI.Library.VcpCodeDisplayInfo>(),
|
|
|
|
// Infer support flags from VCP capabilities
|
|
// VCP 0x12 (18) = Contrast, 0x14 (20) = Color Temperature, 0x60 (96) = Input Source, 0x62 (98) = Volume, 0xD6 (214) = Power Mode
|
|
SupportsContrast = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x12) ?? false,
|
|
SupportsColorTemperature = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x14) ?? false,
|
|
SupportsInputSource = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x60) ?? false,
|
|
SupportsVolume = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x62) ?? false,
|
|
SupportsPowerState = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0xD6) ?? false,
|
|
|
|
// Default Enable* to match Supports* for new monitors (first-time setup)
|
|
// ApplyPreservedUserSettings will override these with saved user preferences if they exist
|
|
EnableContrast = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x12) ?? false,
|
|
EnableVolume = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x62) ?? false,
|
|
EnableInputSource = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x60) ?? false,
|
|
EnableColorTemperature = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0x14) ?? false,
|
|
EnablePowerState = vm.VcpCapabilitiesInfo?.SupportedVcpCodes.ContainsKey(0xD6) ?? false,
|
|
|
|
// Monitor number for display name formatting
|
|
MonitorNumber = vm.MonitorNumber,
|
|
};
|
|
|
|
return monitorInfo;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Apply preserved user settings from existing monitor settings
|
|
/// </summary>
|
|
private void ApplyPreservedUserSettings(
|
|
Microsoft.PowerToys.Settings.UI.Library.MonitorInfo monitorInfo,
|
|
Dictionary<string, Microsoft.PowerToys.Settings.UI.Library.MonitorInfo> existingSettings)
|
|
{
|
|
if (existingSettings.TryGetValue(monitorInfo.Id, out var existingMonitor))
|
|
{
|
|
monitorInfo.IsHidden = existingMonitor.IsHidden;
|
|
monitorInfo.EnableContrast = existingMonitor.EnableContrast;
|
|
monitorInfo.EnableVolume = existingMonitor.EnableVolume;
|
|
monitorInfo.EnableInputSource = existingMonitor.EnableInputSource;
|
|
monitorInfo.EnableRotation = existingMonitor.EnableRotation;
|
|
monitorInfo.EnableColorTemperature = existingMonitor.EnableColorTemperature;
|
|
monitorInfo.EnablePowerState = existingMonitor.EnablePowerState;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Signal Settings UI that the monitor list has been refreshed
|
|
/// </summary>
|
|
private void SignalMonitorsRefreshEvent()
|
|
{
|
|
EventHelper.SignalEvent(Constants.RefreshPowerDisplayMonitorsEvent());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Format VCP code information for display in Settings UI
|
|
/// </summary>
|
|
private Microsoft.PowerToys.Settings.UI.Library.VcpCodeDisplayInfo FormatVcpCodeForDisplay(byte code, VcpCodeInfo info)
|
|
{
|
|
var result = new Microsoft.PowerToys.Settings.UI.Library.VcpCodeDisplayInfo
|
|
{
|
|
Code = info.FormattedCode,
|
|
Title = info.FormattedTitle,
|
|
};
|
|
|
|
if (info.IsContinuous)
|
|
{
|
|
result.Values = "Continuous range";
|
|
result.HasValues = true;
|
|
}
|
|
else if (info.HasDiscreteValues)
|
|
{
|
|
var formattedValues = info.SupportedValues
|
|
.Select(v => Common.Utils.VcpNames.GetFormattedValueName(code, v))
|
|
.ToList();
|
|
result.Values = $"Values: {string.Join(", ", formattedValues)}";
|
|
result.HasValues = true;
|
|
|
|
// Populate value list for Settings UI ComboBox
|
|
// Store raw name (without formatting) so Settings UI can format it consistently
|
|
result.ValueList = info.SupportedValues
|
|
.Select(v => new Microsoft.PowerToys.Settings.UI.Library.VcpValueInfo
|
|
{
|
|
Value = $"0x{v:X2}",
|
|
Name = Common.Utils.VcpNames.GetValueName(code, v),
|
|
})
|
|
.ToList();
|
|
}
|
|
else
|
|
{
|
|
result.HasValues = false;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Send settings telemetry event (triggered by Runner via send_settings_telemetry())
|
|
/// </summary>
|
|
public void SendSettingsTelemetry()
|
|
{
|
|
try
|
|
{
|
|
// Load current settings to get hotkey and tray icon status
|
|
var settings = _settingsUtils.GetSettingsOrDefault<PowerDisplaySettings>(PowerDisplaySettings.ModuleName);
|
|
|
|
// Load profiles to get count
|
|
var profilesData = ProfileService.LoadProfiles();
|
|
|
|
var telemetryEvent = new PowerDisplaySettingsTelemetryEvent
|
|
{
|
|
HotkeyEnabled = settings.Properties.ActivationShortcut?.IsValid() ?? false,
|
|
TrayIconEnabled = settings.Properties.ShowSystemTrayIcon,
|
|
MonitorCount = Monitors.Count,
|
|
ProfileCount = profilesData?.Profiles?.Count ?? 0,
|
|
};
|
|
|
|
PowerToysTelemetry.Log.WriteEvent(telemetryEvent);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Logger.LogError($"[Telemetry] Failed to send settings telemetry: {ex.Message}");
|
|
}
|
|
}
|
|
}
|