diff --git a/AutoDarkModeApp/Pages/PageSwitchModes.xaml b/AutoDarkModeApp/Pages/PageSwitchModes.xaml
index e0de10772..8ca1e662d 100644
--- a/AutoDarkModeApp/Pages/PageSwitchModes.xaml
+++ b/AutoDarkModeApp/Pages/PageSwitchModes.xaml
@@ -88,7 +88,7 @@
Background="{DynamicResource Win11Border}" />
+ Margin="0,5,0,10">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/AutoDarkModeApp/Pages/PageSwitchModes.xaml.cs b/AutoDarkModeApp/Pages/PageSwitchModes.xaml.cs
index f4f53519e..f3d594333 100644
--- a/AutoDarkModeApp/Pages/PageSwitchModes.xaml.cs
+++ b/AutoDarkModeApp/Pages/PageSwitchModes.xaml.cs
@@ -16,6 +16,7 @@
#endregion
using AutoDarkModeLib;
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
@@ -105,6 +106,12 @@ public PageSwitchModes()
ToggleHotkeys.IsOn = builder.Config.Hotkeys.Enabled;
TextBlockHotkeyEditHint.Visibility = ToggleHotkeys.IsOn ? Visibility.Visible : Visibility.Hidden;
+ SimpleStackPanelProcessBlockList.Visibility =
+ builder.Config.ProcessBlockList.Enabled ? Visibility.Visible : Visibility.Collapsed;
+ CheckBoxBlockList.IsChecked = builder.Config.ProcessBlockList.Enabled;
+ ItemsControlProcessBlockList.ItemsSource = builder.Config.ProcessBlockList.ProcessNames;
+ ComboBoxProcessBlockList.DropDownOpened += (sender, args) => RefreshProcessComboBox();
+
init = false;
}
@@ -490,5 +497,71 @@ private void HotkeyCheckboxTogglePostpone_Click(object sender, RoutedEventArgs e
ShowErrorMessage(ex, "HotkeyCheckboxToggleAutomaticThemeSwitchNotification_Click");
}
}
+
+ private void SwitchModesAddSelectedProcess_OnClick(object sender, RoutedEventArgs e)
+ {
+ if (ComboBoxProcessBlockList.SelectedItem is not string processName ||
+ builder.Config.ProcessBlockList.ProcessNames.Contains(processName)) return;
+
+ builder.Config.ProcessBlockList.ProcessNames.Add(processName);
+ try
+ {
+ builder.Save();
+ ItemsControlProcessBlockList.Items.Refresh();
+ ComboBoxProcessBlockList.SelectedIndex = -1;
+ }
+ catch (Exception ex)
+ {
+ ShowErrorMessage(ex, "SwitchModesAddSelectedProcess_OnClick");
+ }
+ }
+
+ private void RefreshProcessComboBox()
+ {
+ var processes = Process.GetProcesses();
+ var filteredProcesses = new SortedSet();
+ foreach (var process in processes)
+ {
+ // MainWindowHandle can throw exceptions, hence the try catch
+ try
+ {
+ // A process without a main window handle probably isn't interactive and thus irrelevant to theme changes
+ if (process.MainWindowHandle == -0) continue;
+ // No point in showing a process' name in the dropdown if it's already being excluded out
+ if (!builder.Config.ProcessBlockList.ProcessNames.Contains(process.ProcessName))
+ {
+ filteredProcesses.Add(process.ProcessName);
+ }
+ }
+ catch (Exception ex)
+ {
+ ShowErrorMessage(ex, "RefreshProcessComboBox");
+ }
+ }
+
+ ComboBoxProcessBlockList.ItemsSource = filteredProcesses;
+ }
+
+ private void SwitchModesRemoveProcess_OnClick(object sender, RoutedEventArgs e)
+ {
+ if (sender is Button {Tag: string entry})
+ {
+ var result = builder.Config.ProcessBlockList.ProcessNames.Remove(entry);
+ if (result)
+ {
+ ItemsControlProcessBlockList.Items.Refresh();
+ builder.Save();
+ }
+ }
+ }
+
+ private void CheckBoxProcessBlockList_Click(object sender, RoutedEventArgs e)
+ {
+ builder.Config.ProcessBlockList.Enabled = !builder.Config.ProcessBlockList.Enabled;
+ CheckBoxBlockList.IsChecked = builder.Config.ProcessBlockList.Enabled;
+ SimpleStackPanelProcessBlockList.Visibility =
+ builder.Config.ProcessBlockList.Enabled ? Visibility.Visible : Visibility.Collapsed;
+ builder.Save();
+ }
}
}
diff --git a/AutoDarkModeLib/Configs/AdmConfig.cs b/AutoDarkModeLib/Configs/AdmConfig.cs
index 4268012d0..4f0b368b8 100644
--- a/AutoDarkModeLib/Configs/AdmConfig.cs
+++ b/AutoDarkModeLib/Configs/AdmConfig.cs
@@ -31,6 +31,7 @@ public AdmConfig()
Location = new();
Tunable = new();
GPUMonitoring = new();
+ ProcessBlockList = new();
Events = new();
WindowsThemeMode = new();
Updater = new();
@@ -65,6 +66,7 @@ public AdmConfig()
public Location Location { get; set; }
public Tunable Tunable { get; set; }
public GPUMonitoring GPUMonitoring { get; set; }
+ public ProcessBlockList ProcessBlockList { get; set; }
public Events Events { get; set; }
public Notifications Notifications { get; set; }
public AutoSwitchNotify AutoSwitchNotify { get; set; }
@@ -205,4 +207,14 @@ public int Samples
}
}
}
+
+ ///
+ /// Configures the , used for postponing theme switches while
+ /// some processes are running
+ ///
+ public class ProcessBlockList
+ {
+ public SortedSet ProcessNames { get; set; } = new();
+ public bool Enabled { get; set; }
+ }
}
diff --git a/AutoDarkModeLib/Properties/Resources.Designer.cs b/AutoDarkModeLib/Properties/Resources.Designer.cs
index 163af5306..50e370439 100644
--- a/AutoDarkModeLib/Properties/Resources.Designer.cs
+++ b/AutoDarkModeLib/Properties/Resources.Designer.cs
@@ -1,7 +1,6 @@
//------------------------------------------------------------------------------
//
// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
@@ -1918,6 +1917,24 @@ public static string StartupServiceUnresponsive {
}
}
+ ///
+ /// Looks up a localized string similar to Add.
+ ///
+ public static string SwitchModesButtonAddProcess {
+ get {
+ return ResourceManager.GetString("SwitchModesButtonAddProcess", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Remove.
+ ///
+ public static string SwitchModesButtonRemoveProcess {
+ get {
+ return ResourceManager.GetString("SwitchModesButtonRemoveProcess", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Notify before automatically switching themes.
///
@@ -1945,6 +1962,15 @@ public static string SwitchModesCheckBoxIdleTimer {
}
}
+ ///
+ /// Looks up a localized string similar to Don't switch while certain processes are running.
+ ///
+ public static string SwitchModesCheckBoxProcessBlockList {
+ get {
+ return ResourceManager.GetString("SwitchModesCheckBoxProcessBlockList", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Fast (1 sample).
///
@@ -1972,6 +1998,15 @@ public static string SwitchModesComboBoxItemSamplesSlow {
}
}
+ ///
+ /// Looks up a localized string similar to Blocking Processes.
+ ///
+ public static string SwitchModesExpanderHeaderBlockedProcesses {
+ get {
+ return ResourceManager.GetString("SwitchModesExpanderHeaderBlockedProcesses", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to hotkey.
///
diff --git a/AutoDarkModeLib/Properties/Resources.de.resx b/AutoDarkModeLib/Properties/Resources.de.resx
index aa473a287..e12514ac8 100644
--- a/AutoDarkModeLib/Properties/Resources.de.resx
+++ b/AutoDarkModeLib/Properties/Resources.de.resx
@@ -1235,4 +1235,16 @@ In einigen Fällen kann ein Piepston auftauchen, wenn die Maus während der DWM
Manual
+
+ Hinzufügen
+
+
+ Entfernen
+
+
+ Anwendungen
+
+
+ Wechsle nicht das Design während manche Anwendungen ausgeführt werden
+
\ No newline at end of file
diff --git a/AutoDarkModeLib/Properties/Resources.resx b/AutoDarkModeLib/Properties/Resources.resx
index 3e4d480aa..7919f4098 100644
--- a/AutoDarkModeLib/Properties/Resources.resx
+++ b/AutoDarkModeLib/Properties/Resources.resx
@@ -1235,4 +1235,16 @@ On rare occasions, Windows may emit a beeping sound when the mouse is moved duri
Manual
+
+ Add
+
+
+ Remove
+
+
+ Blocking Processes
+
+
+ Don't switch while certain processes are running
+
\ No newline at end of file
diff --git a/AutoDarkModeSvc/Modules/ProcessBlockListModule.cs b/AutoDarkModeSvc/Modules/ProcessBlockListModule.cs
new file mode 100644
index 000000000..0968c4a43
--- /dev/null
+++ b/AutoDarkModeSvc/Modules/ProcessBlockListModule.cs
@@ -0,0 +1,125 @@
+using System;
+using System.Diagnostics;
+using System.Linq;
+using System.Threading.Tasks;
+using AutoDarkModeLib;
+using AutoDarkModeSvc.Core;
+using AutoDarkModeSvc.Timers;
+
+namespace AutoDarkModeSvc.Modules;
+
+///
+/// This Module postpones theme switches if processes with a certain name are running.
+/// The process names are configured in
+///
+///
+///
+///
+public class ProcessBlockListModule : AutoDarkModeModule
+{
+ private static readonly NLog.Logger Logger = NLog.LogManager.GetCurrentClassLogger();
+ private GlobalState State { get; }
+ private bool IsPostponing => State.PostponeManager.Get(Name) != null;
+ private AdmConfigBuilder ConfigBuilder { get; }
+
+ public ProcessBlockListModule(string name, bool fireOnRegistration) : base(name, fireOnRegistration)
+ {
+ State = GlobalState.Instance();
+ ConfigBuilder = AdmConfigBuilder.Instance();
+ }
+
+ public override string TimerAffinity { get; } = TimerName.Main;
+
+ public override void Fire()
+ {
+ if (!ConfigBuilder.Config.ProcessBlockList.Enabled)
+ {
+ RemovePostpone();
+ Logger.Debug("No processes are excluded, skipping checks");
+ return;
+ }
+
+ // While postponing, continue checking
+ if (!IsPostponing && !State.ThemeSwitchApproaching)
+ {
+ Logger.Debug("It's still a while until a time based switch would happen, skip checking processes");
+ return;
+ }
+
+ Task.Run(() =>
+ {
+ if (TestRunningProcesses())
+ {
+ Postpone();
+ }
+ else
+ {
+ RemovePostpone();
+ }
+ });
+ }
+
+ ///
+ /// Tests if any of the blocked processes are currently running
+ ///
+ /// True if any of the processes are running
+ private bool TestRunningProcesses()
+ {
+ var activeProcesses = Process
+ .GetProcesses()
+ .Select(p =>
+ {
+ try
+ {
+ if (p.MainWindowHandle != 0)
+ {
+ return p.ProcessName;
+ }
+ }
+ catch (Exception e)
+ {
+ Logger.Debug(e, "");
+ }
+
+ return null;
+ })
+ .Where(name => name != null)
+ .ToHashSet();
+
+ return ConfigBuilder.Config.ProcessBlockList.ProcessNames.Any(p => activeProcesses.Contains(p));
+ }
+
+ public override void EnableHook()
+ {
+ State.AddSwitchApproachDependency(GetType().Name);
+ base.EnableHook();
+ }
+
+ public override void DisableHook()
+ {
+ Logger.Info("Removing any leftover process block list postones");
+ RemovePostpone();
+ State.RemoveSwitchApproachDependency(GetType().Name);
+ base.DisableHook();
+ }
+
+ ///
+ /// Add this module's postpone
+ ///
+ /// True if a new postpone was added
+ private bool Postpone()
+ {
+ Logger.Debug("Adding postpone from process exclusion");
+ return State.PostponeManager.Add(new PostponeItem(Name));
+ }
+
+ ///
+ /// Remove this module's postpone
+ ///
+ /// True if a postpone was removed
+ private bool RemovePostpone()
+ {
+ Logger.Info("Clearing postpone from process exclusion");
+ return State.PostponeManager.Remove(Name);
+ }
+}
\ No newline at end of file
diff --git a/AutoDarkModeSvc/Modules/WardenModule.cs b/AutoDarkModeSvc/Modules/WardenModule.cs
index f8092ace9..c6b08140b 100644
--- a/AutoDarkModeSvc/Modules/WardenModule.cs
+++ b/AutoDarkModeSvc/Modules/WardenModule.cs
@@ -60,6 +60,7 @@ public override void Fire()
AutoManageModule(typeof(SystemIdleCheckModule), true, config.IdleChecker.Enabled);
//AutoManageModule(typeof(ThemeUpdateModule), true, config.WindowsThemeMode.Enabled && config.WindowsThemeMode.MonitorActiveTheme);
AutoManageModule(typeof(GPUMonitorModule), true, config.GPUMonitoring.Enabled);
+ AutoManageModule(typeof(ProcessBlockListModule), true, config.ProcessBlockList.Enabled);
AutoManageModule(typeof(UpdaterModule), true, config.Updater.Enabled);
governorModule.AutoManageGovernors(config.Governor);
}