mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 03:37:59 +01:00
Refactor LightSwitch integration: decouple event handling
Removed LightSwitchListener and ThemeChangedEventArgs. Added LightSwitchService to centralize theme/profile logic. Updated event registration and MainViewModel to handle LightSwitch theme changes via service, decoupling event listening from profile application. Event listening now handled externally.
This commit is contained in:
@@ -1,239 +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;
|
|
||||||
using System.IO;
|
|
||||||
using System.Text.Json;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using ManagedCommon;
|
|
||||||
|
|
||||||
namespace PowerDisplay.Common.Services
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Listens for LightSwitch theme change events and notifies subscribers.
|
|
||||||
/// Encapsulates all LightSwitch integration logic including:
|
|
||||||
/// - Background thread management for event listening (light/dark theme events)
|
|
||||||
/// - LightSwitch settings file parsing
|
|
||||||
/// Theme is determined directly from which event was signaled (not from registry).
|
|
||||||
/// </summary>
|
|
||||||
public sealed partial class LightSwitchListener : IDisposable
|
|
||||||
{
|
|
||||||
private const string LogPrefix = "[LightSwitch Listener]";
|
|
||||||
|
|
||||||
private Thread? _listenerThread;
|
|
||||||
private CancellationTokenSource? _cancellationTokenSource;
|
|
||||||
private bool _disposed;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Fired when LightSwitch signals a theme change and a profile should be applied
|
|
||||||
/// </summary>
|
|
||||||
public event EventHandler<ThemeChangedEventArgs>? ThemeChanged;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Starts the background thread to listen for LightSwitch theme change events
|
|
||||||
/// </summary>
|
|
||||||
public void Start()
|
|
||||||
{
|
|
||||||
if (_listenerThread != null && _listenerThread.IsAlive)
|
|
||||||
{
|
|
||||||
Logger.LogWarning($"{LogPrefix} Listener already running");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cancellationTokenSource = new CancellationTokenSource();
|
|
||||||
var token = _cancellationTokenSource.Token;
|
|
||||||
|
|
||||||
_listenerThread = new Thread(() => ListenerThreadProc(token))
|
|
||||||
{
|
|
||||||
IsBackground = true,
|
|
||||||
Name = "LightSwitchEventListener",
|
|
||||||
};
|
|
||||||
|
|
||||||
_listenerThread.Start();
|
|
||||||
Logger.LogInfo($"{LogPrefix} Listener started");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stops the background listener thread
|
|
||||||
/// </summary>
|
|
||||||
public void Stop()
|
|
||||||
{
|
|
||||||
if (_cancellationTokenSource == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_cancellationTokenSource.Cancel();
|
|
||||||
|
|
||||||
if (_listenerThread != null && _listenerThread.IsAlive)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!_listenerThread.Join(TimeSpan.FromSeconds(2)))
|
|
||||||
{
|
|
||||||
Logger.LogWarning($"{LogPrefix} Listener thread did not stop in time");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogDebug($"{LogPrefix} Error joining listener thread: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_listenerThread = null;
|
|
||||||
_cancellationTokenSource.Dispose();
|
|
||||||
_cancellationTokenSource = null;
|
|
||||||
|
|
||||||
Logger.LogInfo($"{LogPrefix} Listener stopped");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ListenerThreadProc(CancellationToken cancellationToken)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Logger.LogInfo($"{LogPrefix} Event listener thread started");
|
|
||||||
|
|
||||||
// Use separate events for light and dark themes to avoid race conditions
|
|
||||||
// where we might read the registry before LightSwitch has updated it
|
|
||||||
using var lightThemeEvent = new EventWaitHandle(false, EventResetMode.AutoReset, PathConstants.LightSwitchLightThemeEventName);
|
|
||||||
using var darkThemeEvent = new EventWaitHandle(false, EventResetMode.AutoReset, PathConstants.LightSwitchDarkThemeEventName);
|
|
||||||
|
|
||||||
var waitHandles = new WaitHandle[] { lightThemeEvent, darkThemeEvent };
|
|
||||||
|
|
||||||
while (!cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
// Wait for either light or dark theme event (with timeout to allow cancellation check)
|
|
||||||
int index = WaitHandle.WaitAny(waitHandles, TimeSpan.FromSeconds(1));
|
|
||||||
|
|
||||||
if (index == WaitHandle.WaitTimeout)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine theme from which event was signaled
|
|
||||||
bool isLightMode = index == 0; // 0 = lightThemeEvent, 1 = darkThemeEvent
|
|
||||||
Logger.LogInfo($"{LogPrefix} Theme event received: {(isLightMode ? "Light" : "Dark")}");
|
|
||||||
|
|
||||||
// Process the theme change with the known theme
|
|
||||||
_ = Task.Run(() => ProcessThemeChange(isLightMode), CancellationToken.None);
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.LogInfo($"{LogPrefix} Event listener thread stopping");
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError($"{LogPrefix} Event listener thread failed: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProcessThemeChange(bool isLightMode)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Logger.LogInfo($"{LogPrefix} Processing theme change to {(isLightMode ? "light" : "dark")} mode");
|
|
||||||
|
|
||||||
var profileToApply = ReadProfileFromLightSwitchSettings(isLightMode);
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(profileToApply) || profileToApply == "(None)")
|
|
||||||
{
|
|
||||||
Logger.LogInfo($"{LogPrefix} No profile configured for {(isLightMode ? "light" : "dark")} mode");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.LogInfo($"{LogPrefix} Requesting profile application: {profileToApply}");
|
|
||||||
|
|
||||||
// Notify subscribers
|
|
||||||
ThemeChanged?.Invoke(this, new ThemeChangedEventArgs(isLightMode, profileToApply));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError($"{LogPrefix} Failed to process theme change: {ex.Message}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Reads LightSwitch settings and returns the profile name to apply for the given theme.
|
|
||||||
/// The theme is determined by which event was signaled (light or dark), not by reading the registry.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="isLightMode">Whether the theme is light mode (determined from the signaled event)</param>
|
|
||||||
/// <returns>The profile name to apply, or null if not configured</returns>
|
|
||||||
private static string? ReadProfileFromLightSwitchSettings(bool isLightMode)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var settingsPath = PathConstants.LightSwitchSettingsFilePath;
|
|
||||||
|
|
||||||
if (!File.Exists(settingsPath))
|
|
||||||
{
|
|
||||||
Logger.LogWarning($"{LogPrefix} LightSwitch settings file not found");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var json = File.ReadAllText(settingsPath);
|
|
||||||
var settings = JsonDocument.Parse(json);
|
|
||||||
var root = settings.RootElement;
|
|
||||||
|
|
||||||
if (!root.TryGetProperty("properties", out var properties))
|
|
||||||
{
|
|
||||||
Logger.LogWarning($"{LogPrefix} LightSwitch settings has no properties");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if monitor settings integration is enabled
|
|
||||||
if (!properties.TryGetProperty("apply_monitor_settings", out var applyMonitorSettingsElement) ||
|
|
||||||
!applyMonitorSettingsElement.TryGetProperty("value", out var applyValue) ||
|
|
||||||
!applyValue.GetBoolean())
|
|
||||||
{
|
|
||||||
Logger.LogInfo($"{LogPrefix} Monitor settings integration is disabled");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the appropriate profile name based on the theme from the event
|
|
||||||
if (isLightMode)
|
|
||||||
{
|
|
||||||
return GetProfileFromSettings(properties, "enable_light_mode_profile", "light_mode_profile");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return GetProfileFromSettings(properties, "enable_dark_mode_profile", "dark_mode_profile");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Logger.LogError($"{LogPrefix} Failed to read LightSwitch settings: {ex.Message}");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string? GetProfileFromSettings(
|
|
||||||
JsonElement properties,
|
|
||||||
string enableKey,
|
|
||||||
string profileKey)
|
|
||||||
{
|
|
||||||
if (properties.TryGetProperty(enableKey, out var enableElement) &&
|
|
||||||
enableElement.TryGetProperty("value", out var enableValue) &&
|
|
||||||
enableValue.GetBoolean() &&
|
|
||||||
properties.TryGetProperty(profileKey, out var profileElement) &&
|
|
||||||
profileElement.TryGetProperty("value", out var profileValue))
|
|
||||||
{
|
|
||||||
return profileValue.GetString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stop();
|
|
||||||
_disposed = true;
|
|
||||||
GC.SuppressFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
// 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 ManagedCommon;
|
||||||
|
|
||||||
|
namespace PowerDisplay.Common.Services
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Service for handling LightSwitch theme change events.
|
||||||
|
/// Provides methods to process theme changes and read LightSwitch settings.
|
||||||
|
/// Event listening is handled externally via NativeEventWaiter.
|
||||||
|
/// </summary>
|
||||||
|
public static class LightSwitchService
|
||||||
|
{
|
||||||
|
private const string LogPrefix = "[LightSwitch]";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Process a theme change event and return the profile name to apply.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isLightMode">Whether the theme changed to light mode.</param>
|
||||||
|
/// <returns>The profile name to apply, or null if no profile is configured.</returns>
|
||||||
|
public static string? GetProfileForTheme(bool isLightMode)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Logger.LogInfo($"{LogPrefix} Processing theme change to {(isLightMode ? "light" : "dark")} mode");
|
||||||
|
|
||||||
|
var profileToApply = ReadProfileFromLightSwitchSettings(isLightMode);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(profileToApply) || profileToApply == "(None)")
|
||||||
|
{
|
||||||
|
Logger.LogInfo($"{LogPrefix} No profile configured for {(isLightMode ? "light" : "dark")} mode");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger.LogInfo($"{LogPrefix} Profile to apply: {profileToApply}");
|
||||||
|
return profileToApply;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Logger.LogError($"{LogPrefix} Failed to process theme change: {ex.Message}");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Reads LightSwitch settings and returns the profile name to apply for the given theme.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="isLightMode">Whether the theme is light mode.</param>
|
||||||
|
/// <returns>The profile name to apply, or null if not configured.</returns>
|
||||||
|
private static string? ReadProfileFromLightSwitchSettings(bool isLightMode)
|
||||||
|
{
|
||||||
|
var settingsPath = PathConstants.LightSwitchSettingsFilePath;
|
||||||
|
|
||||||
|
if (!File.Exists(settingsPath))
|
||||||
|
{
|
||||||
|
Logger.LogWarning($"{LogPrefix} LightSwitch settings file not found");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var json = File.ReadAllText(settingsPath);
|
||||||
|
var settings = JsonDocument.Parse(json);
|
||||||
|
var root = settings.RootElement;
|
||||||
|
|
||||||
|
if (!root.TryGetProperty("properties", out var properties))
|
||||||
|
{
|
||||||
|
Logger.LogWarning($"{LogPrefix} LightSwitch settings has no properties");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if monitor settings integration is enabled
|
||||||
|
if (!properties.TryGetProperty("apply_monitor_settings", out var applyMonitorSettingsElement) ||
|
||||||
|
!applyMonitorSettingsElement.TryGetProperty("value", out var applyValue) ||
|
||||||
|
!applyValue.GetBoolean())
|
||||||
|
{
|
||||||
|
Logger.LogInfo($"{LogPrefix} Monitor settings integration is disabled");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the appropriate profile name based on the theme
|
||||||
|
if (isLightMode)
|
||||||
|
{
|
||||||
|
return GetProfileFromSettings(properties, "enable_light_mode_profile", "light_mode_profile");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetProfileFromSettings(properties, "enable_dark_mode_profile", "dark_mode_profile");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? GetProfileFromSettings(
|
||||||
|
JsonElement properties,
|
||||||
|
string enableKey,
|
||||||
|
string profileKey)
|
||||||
|
{
|
||||||
|
if (properties.TryGetProperty(enableKey, out var enableElement) &&
|
||||||
|
enableElement.TryGetProperty("value", out var enableValue) &&
|
||||||
|
enableValue.GetBoolean() &&
|
||||||
|
properties.TryGetProperty(profileKey, out var profileElement) &&
|
||||||
|
profileElement.TryGetProperty("value", out var profileValue))
|
||||||
|
{
|
||||||
|
return profileValue.GetString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,30 +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;
|
|
||||||
|
|
||||||
namespace PowerDisplay.Common.Services
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Event arguments for theme change notifications from LightSwitch
|
|
||||||
/// </summary>
|
|
||||||
public class ThemeChangedEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets a value indicating whether the system is currently in light mode
|
|
||||||
/// </summary>
|
|
||||||
public bool IsLightMode { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets profile name to apply (null if no profile configured for current theme)
|
|
||||||
/// </summary>
|
|
||||||
public string? ProfileToApply { get; }
|
|
||||||
|
|
||||||
public ThemeChangedEventArgs(bool isLightMode, string? profileToApply)
|
|
||||||
{
|
|
||||||
IsLightMode = isLightMode;
|
|
||||||
ProfileToApply = profileToApply;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -11,6 +11,7 @@ using Microsoft.PowerToys.Telemetry;
|
|||||||
using Microsoft.UI.Dispatching;
|
using Microsoft.UI.Dispatching;
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.Windows.AppLifecycle;
|
using Microsoft.Windows.AppLifecycle;
|
||||||
|
using PowerDisplay.Common;
|
||||||
using PowerDisplay.Helpers;
|
using PowerDisplay.Helpers;
|
||||||
using PowerDisplay.Serialization;
|
using PowerDisplay.Serialization;
|
||||||
using PowerToys.Interop;
|
using PowerToys.Interop;
|
||||||
@@ -106,6 +107,10 @@ namespace PowerDisplay
|
|||||||
RegisterViewModelEvent(Constants.ApplyColorTemperaturePowerDisplayEvent(), vm => vm.ApplyColorTemperatureFromSettings(), "ApplyColorTemperature");
|
RegisterViewModelEvent(Constants.ApplyColorTemperaturePowerDisplayEvent(), vm => vm.ApplyColorTemperatureFromSettings(), "ApplyColorTemperature");
|
||||||
RegisterViewModelEvent(Constants.ApplyProfilePowerDisplayEvent(), vm => vm.ApplyProfileFromSettings(), "ApplyProfile");
|
RegisterViewModelEvent(Constants.ApplyProfilePowerDisplayEvent(), vm => vm.ApplyProfileFromSettings(), "ApplyProfile");
|
||||||
|
|
||||||
|
// LightSwitch integration - apply profiles when theme changes
|
||||||
|
RegisterViewModelEvent(PathConstants.LightSwitchLightThemeEventName, vm => vm.ApplyLightSwitchProfile(isLightMode: true), "LightSwitch-Light");
|
||||||
|
RegisterViewModelEvent(PathConstants.LightSwitchDarkThemeEventName, vm => vm.ApplyLightSwitchProfile(isLightMode: false), "LightSwitch-Dark");
|
||||||
|
|
||||||
// Monitor Runner process (backup exit mechanism)
|
// Monitor Runner process (backup exit mechanism)
|
||||||
if (_powerToysRunnerPid > 0)
|
if (_powerToysRunnerPid > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -209,11 +209,15 @@ public partial class MainViewModel
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Handle theme change notification from LightSwitch
|
/// Handle theme change from LightSwitch by applying the appropriate profile.
|
||||||
|
/// Called from App.xaml.cs when LightSwitch theme events are received.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void OnLightSwitchThemeChanged(object? sender, ThemeChangedEventArgs e)
|
/// <param name="isLightMode">Whether the theme changed to light mode.</param>
|
||||||
|
public void ApplyLightSwitchProfile(bool isLightMode)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(e.ProfileToApply))
|
var profileName = LightSwitchService.GetProfileForTheme(isLightMode);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(profileName))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -222,21 +226,21 @@ public partial class MainViewModel
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Logger.LogInfo($"[LightSwitch Integration] Applying profile: {e.ProfileToApply}");
|
Logger.LogInfo($"[LightSwitch Integration] Applying profile: {profileName}");
|
||||||
|
|
||||||
// Load and apply the profile
|
// Load and apply the profile
|
||||||
var profilesData = ProfileService.LoadProfiles();
|
var profilesData = ProfileService.LoadProfiles();
|
||||||
var profile = profilesData.GetProfile(e.ProfileToApply);
|
var profile = profilesData.GetProfile(profileName);
|
||||||
|
|
||||||
if (profile == null || !profile.IsValid())
|
if (profile == null || !profile.IsValid())
|
||||||
{
|
{
|
||||||
Logger.LogWarning($"[LightSwitch Integration] Profile '{e.ProfileToApply}' not found or invalid");
|
Logger.LogWarning($"[LightSwitch Integration] Profile '{profileName}' not found or invalid");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply the profile
|
// Apply the profile
|
||||||
await ApplyProfileAsync(profile.MonitorSettings);
|
await ApplyProfileAsync(profile.MonitorSettings);
|
||||||
Logger.LogInfo($"[LightSwitch Integration] Successfully applied profile '{e.ProfileToApply}'");
|
Logger.LogInfo($"[LightSwitch Integration] Successfully applied profile '{profileName}'");
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ public partial class MainViewModel : INotifyPropertyChanged, IDisposable
|
|||||||
private readonly CancellationTokenSource _cancellationTokenSource;
|
private readonly CancellationTokenSource _cancellationTokenSource;
|
||||||
private readonly ISettingsUtils _settingsUtils;
|
private readonly ISettingsUtils _settingsUtils;
|
||||||
private readonly MonitorStateManager _stateManager;
|
private readonly MonitorStateManager _stateManager;
|
||||||
private readonly LightSwitchListener _lightSwitchListener;
|
|
||||||
private readonly DisplayChangeWatcher _displayChangeWatcher;
|
private readonly DisplayChangeWatcher _displayChangeWatcher;
|
||||||
|
|
||||||
private ObservableCollection<MonitorViewModel> _monitors;
|
private ObservableCollection<MonitorViewModel> _monitors;
|
||||||
@@ -75,11 +74,6 @@ public partial class MainViewModel : INotifyPropertyChanged, IDisposable
|
|||||||
// Initialize the monitor manager
|
// Initialize the monitor manager
|
||||||
_monitorManager = new MonitorManager();
|
_monitorManager = new MonitorManager();
|
||||||
|
|
||||||
// Initialize and start LightSwitch integration listener
|
|
||||||
_lightSwitchListener = new LightSwitchListener();
|
|
||||||
_lightSwitchListener.ThemeChanged += OnLightSwitchThemeChanged;
|
|
||||||
_lightSwitchListener.Start();
|
|
||||||
|
|
||||||
// Load profiles for quick apply feature
|
// Load profiles for quick apply feature
|
||||||
LoadProfiles();
|
LoadProfiles();
|
||||||
|
|
||||||
@@ -256,7 +250,6 @@ public partial class MainViewModel : INotifyPropertyChanged, IDisposable
|
|||||||
|
|
||||||
// Dispose all resources safely (don't throw from Dispose)
|
// Dispose all resources safely (don't throw from Dispose)
|
||||||
SafeDispose(_displayChangeWatcher, "DisplayChangeWatcher");
|
SafeDispose(_displayChangeWatcher, "DisplayChangeWatcher");
|
||||||
SafeDispose(_lightSwitchListener, "LightSwitchListener");
|
|
||||||
|
|
||||||
// Dispose monitor view models
|
// Dispose monitor view models
|
||||||
foreach (var vm in Monitors)
|
foreach (var vm in Monitors)
|
||||||
|
|||||||
Reference in New Issue
Block a user