diff --git a/DellMonitorControl/ConfigWindow.xaml b/DellMonitorControl/ConfigWindow.xaml
index b71d5b4..d7b13c4 100644
--- a/DellMonitorControl/ConfigWindow.xaml
+++ b/DellMonitorControl/ConfigWindow.xaml
@@ -98,12 +98,20 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
+
diff --git a/DellMonitorControl/ConfigWindow.xaml.cs b/DellMonitorControl/ConfigWindow.xaml.cs
index ed667ee..42c8e03 100644
--- a/DellMonitorControl/ConfigWindow.xaml.cs
+++ b/DellMonitorControl/ConfigWindow.xaml.cs
@@ -1,7 +1,10 @@
using CMM.Library.Config;
+using CMM.Library.Method;
using CMM.Library.ViewModel;
+using System;
using System.Collections.Generic;
using System.Linq;
+using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
@@ -180,6 +183,107 @@ public partial class ConfigWindow : Window
Close();
}
+ private void ResetButton_Click(object sender, RoutedEventArgs e)
+ {
+ // Reset all port rows to default values
+ foreach (var row in _portRows)
+ {
+ row.CustomLabel = "";
+ row.IsHidden = false;
+ row.ShowInQuickSwitch = false;
+ }
+
+ // Reload the UI to reflect changes
+ LoadPortConfiguration();
+
+ // Clear any discovered ports from config
+ var config = MonitorConfigManager.GetMonitorConfig(_serialNumber);
+ config.Ports.Clear();
+ MonitorConfigManager.SaveMonitorConfig(config);
+ MonitorConfigManager.ClearCache();
+ }
+
+ private async void DetectButton_Click(object sender, RoutedEventArgs e)
+ {
+ btnDetect.IsEnabled = false;
+ btnDetect.Content = "...";
+
+ try
+ {
+ // Common VCP 60 values: 15=DP1, 16=DP2, 17=HDMI1, 18=HDMI2, 3=DVI1, 4=DVI2, 1=VGA1, 2=VGA2
+ var commonPorts = new[] { 15, 16, 17, 18, 3, 4, 1, 2 };
+ var detectedPorts = new List();
+
+ // Get current input so we can restore it
+ var currentInput = await CMMCommand.GetInputSource(_serialNumber);
+
+ foreach (var vcpValue in commonPorts)
+ {
+ // Skip ports we already know about
+ if (_availablePorts.Any(p => p.Value == vcpValue))
+ continue;
+
+ try
+ {
+ // Try to set the input - if it succeeds, the port exists
+ await CMMCommand.SetInputSource(_serialNumber, vcpValue);
+ await Task.Delay(500); // Give monitor time to respond
+
+ // Check if the input actually changed
+ var newInput = await CMMCommand.GetInputSource(_serialNumber);
+ if (newInput == vcpValue)
+ {
+ detectedPorts.Add(vcpValue);
+ }
+ }
+ catch
+ {
+ // Port doesn't exist or isn't supported
+ }
+ }
+
+ // Restore original input if we have one
+ if (currentInput.HasValue)
+ {
+ await CMMCommand.SetInputSource(_serialNumber, currentInput.Value);
+ }
+
+ if (detectedPorts.Count > 0)
+ {
+ // Add detected ports to the available list and config
+ foreach (var vcpValue in detectedPorts)
+ {
+ var name = CMMCommand.GetInputSourceName(vcpValue);
+ _availablePorts.Add(new InputSourceOption(vcpValue, name));
+ MonitorConfigManager.AddDiscoveredPort(_serialNumber, _monitorName, vcpValue, name);
+ }
+
+ // Reload to show new ports
+ MonitorConfigManager.ClearCache();
+ LoadPortConfiguration();
+
+ MessageBox.Show($"Detected {detectedPorts.Count} new port(s):\n" +
+ string.Join("\n", detectedPorts.Select(v => $" • {CMMCommand.GetInputSourceName(v)}")),
+ "Detection Complete", MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ else
+ {
+ MessageBox.Show("No additional ports were detected.", "Detection Complete",
+ MessageBoxButton.OK, MessageBoxImage.Information);
+ }
+ }
+ catch (Exception ex)
+ {
+ MessageBox.Show($"Detection failed: {ex.Message}", "Error",
+ MessageBoxButton.OK, MessageBoxImage.Error);
+ }
+ finally
+ {
+ btnDetect.Content = "Detect";
+ btnDetect.IsEnabled = true;
+ }
+ }
+
private class PortConfigRow
{
public int VcpValue { get; set; }
diff --git a/DellMonitorControl/DellMonitorControl.csproj b/DellMonitorControl/DellMonitorControl.csproj
index 5233e95..74dd39a 100644
--- a/DellMonitorControl/DellMonitorControl.csproj
+++ b/DellMonitorControl/DellMonitorControl.csproj
@@ -15,9 +15,9 @@
- 1.1.1
- 1.1.1.0
- 1.1.1.0
+ 1.1.2
+ 1.1.2.0
+ 1.1.2.0
diff --git a/DellMonitorControl/MainWindow.xaml.cs b/DellMonitorControl/MainWindow.xaml.cs
index 6aa7383..271882c 100644
--- a/DellMonitorControl/MainWindow.xaml.cs
+++ b/DellMonitorControl/MainWindow.xaml.cs
@@ -181,13 +181,25 @@ public partial class MainWindow : Window
var inputOptions = await CMMCommand.GetInputSourceOptions(m.SerialNumber);
DebugLogger.Log($" Input options count: {inputOptions.Count}");
+ // Add any previously discovered ports from config
+ var discoveredPorts = MonitorConfigManager.GetDiscoveredPorts(m.SerialNumber, inputOptions);
+ foreach (var port in discoveredPorts)
+ {
+ inputOptions.Insert(0, port);
+ DebugLogger.Log($" Added discovered port from config: {port.Value} ({port.Name})");
+ }
+
// Some monitors don't report current input in their possible values list
- // Add it if missing so user can see what's currently selected
+ // Add it if missing and save to config for future
if (inputSource.HasValue && !inputOptions.Any(o => o.Value == inputSource.Value))
{
var currentInputName = CMMCommand.GetInputSourceName(inputSource.Value);
inputOptions.Insert(0, new InputSourceOption(inputSource.Value, currentInputName));
DebugLogger.Log($" Added missing current input: {inputSource.Value} ({currentInputName})");
+
+ // Save this discovered port so it appears in future even when not current
+ MonitorConfigManager.AddDiscoveredPort(m.SerialNumber, m.MonitorName, inputSource.Value, currentInputName);
+ DebugLogger.Log($" Saved discovered port to config");
}
DebugLogger.Log($" Getting power status...");
diff --git a/DellMonitorControl/MonitorControl.iss b/DellMonitorControl/MonitorControl.iss
index a415f7e..e43e889 100644
--- a/DellMonitorControl/MonitorControl.iss
+++ b/DellMonitorControl/MonitorControl.iss
@@ -1,5 +1,5 @@
#define MyAppName "Monitor Control"
-#define MyAppVersion "1.1.0"
+#define MyAppVersion "1.1.2"
#define MyAppPublisher "MarketAlly"
#define MyAppExeName "DellMonitorControl.exe"
#define MyAppIcon "MonitorIcon.ico"
diff --git a/Library/Config/MonitorConfigManager.cs b/Library/Config/MonitorConfigManager.cs
index 811a4fa..3a22736 100644
--- a/Library/Config/MonitorConfigManager.cs
+++ b/Library/Config/MonitorConfigManager.cs
@@ -126,4 +126,57 @@ public static class MonitorConfigManager
{
_cachedConfig = null;
}
+
+ ///
+ /// Save a discovered port that the monitor didn't report in its possible values.
+ /// This ensures ports like DisplayPort-1 are remembered even if the monitor firmware doesn't list them.
+ ///
+ public static void AddDiscoveredPort(string serialNumber, string monitorName, int vcpValue, string defaultName)
+ {
+ var config = Load();
+ var monitorConfig = config.Monitors.FirstOrDefault(m => m.SerialNumber == serialNumber);
+
+ if (monitorConfig == null)
+ {
+ monitorConfig = new MonitorConfig { SerialNumber = serialNumber, MonitorName = monitorName };
+ config.Monitors.Add(monitorConfig);
+ }
+
+ // Check if port already exists
+ if (monitorConfig.Ports.Any(p => p.VcpValue == vcpValue))
+ return;
+
+ // Add the discovered port
+ monitorConfig.Ports.Add(new PortConfig
+ {
+ VcpValue = vcpValue,
+ DefaultName = defaultName,
+ CustomLabel = "",
+ IsHidden = false,
+ ShowInQuickSwitch = false
+ });
+
+ Save(config);
+ }
+
+ ///
+ /// Get any discovered ports from config that the monitor didn't report.
+ ///
+ public static List GetDiscoveredPorts(string serialNumber, List reportedOptions)
+ {
+ var discovered = new List();
+ var monitorConfig = GetMonitorConfig(serialNumber);
+
+ foreach (var port in monitorConfig.Ports)
+ {
+ // If this port isn't in the reported options, it's a discovered port
+ if (!reportedOptions.Any(o => o.Value == port.VcpValue))
+ {
+ var name = !string.IsNullOrWhiteSpace(port.CustomLabel) ? port.CustomLabel : port.DefaultName;
+ discovered.Add(new InputSourceOption(port.VcpValue, name));
+ }
+ }
+
+ return discovered;
+ }
}
diff --git a/Library/Method/CMMCommand.cs b/Library/Method/CMMCommand.cs
index fc950af..1785bb5 100644
--- a/Library/Method/CMMCommand.cs
+++ b/Library/Method/CMMCommand.cs
@@ -39,17 +39,21 @@ public static class CMMCommand
CMMexe,
$"/GetValue {monitorSN} {vcpCode}");
- // Empty result means timeout - don't retry, monitor is unresponsive
- if (string.IsNullOrEmpty(output) && exitCode == -1)
+ // Timeout
+ if (exitCode == -1)
return string.Empty;
- // Parse output - ControlMyMonitor outputs the value directly
- var value = output?.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault()?.Trim();
+ // ControlMyMonitor returns the value as the exit code
+ // Exit code > 0 means success with that value
+ if (exitCode > 0)
+ return exitCode.ToString();
- if (!string.IsNullOrEmpty(value) && exitCode == 0)
+ // Also check stdout in case it outputs there
+ var value = output?.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).LastOrDefault()?.Trim();
+ if (!string.IsNullOrEmpty(value) && value != "0")
return value;
- // Only retry on non-timeout failures
+ // Only retry on failure
if (attempt < maxRetries)
await Task.Delay(300);
}