- Add VCP commands for brightness (10), contrast (12), input source (60) - Fix UTF-16 encoding for monitor data parsing - Add system tray app with monitor controls - Add localization for en, es, fr, de, zh, ja, pt, it, hi - Update to .NET 9.0 - Add LICENSE and README 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
172 lines
6.9 KiB
C#
172 lines
6.9 KiB
C#
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;
|
|
|
|
namespace DellMonitorControl;
|
|
|
|
public partial class MainWindow : Window
|
|
{
|
|
public MainWindow()
|
|
{
|
|
InitializeComponent();
|
|
PositionWindowNearTray();
|
|
Loaded += async (s, e) => await LoadMonitors();
|
|
}
|
|
|
|
private void PositionWindowNearTray()
|
|
{
|
|
var workArea = SystemParameters.WorkArea;
|
|
Left = workArea.Right - Width - 10;
|
|
Top = workArea.Bottom - Height - 10;
|
|
}
|
|
|
|
private static readonly string LogFile = System.IO.Path.Combine(
|
|
Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "MonitorControl.log");
|
|
|
|
private static void Log(string msg)
|
|
{
|
|
try { System.IO.File.AppendAllText(LogFile, $"{DateTime.Now:HH:mm:ss.fff} {msg}\n"); }
|
|
catch { }
|
|
}
|
|
|
|
private async Task LoadMonitors()
|
|
{
|
|
var newChildren = new List<UIElement>();
|
|
Log("LoadMonitors started");
|
|
|
|
try
|
|
{
|
|
Log("Calling ScanMonitor...");
|
|
await CMMCommand.ScanMonitor();
|
|
Log("ScanMonitor complete");
|
|
|
|
Log("Calling ReadMonitorsData...");
|
|
var monitors = (await CMMCommand.ReadMonitorsData()).ToList();
|
|
Log($"ReadMonitorsData complete, found {monitors.Count} monitors");
|
|
|
|
if (!monitors.Any())
|
|
{
|
|
Log("No monitors found");
|
|
newChildren.Add(new TextBlock { Text = "No DDC/CI monitors detected", Foreground = Brushes.White, FontSize = 14 });
|
|
}
|
|
else
|
|
{
|
|
foreach (var m in monitors)
|
|
{
|
|
Log($"Processing monitor: {m.MonitorName} ({m.SerialNumber})");
|
|
|
|
var brightness = await CMMCommand.GetBrightness(m.SerialNumber) ?? 50;
|
|
Log($" Brightness: {brightness}");
|
|
var contrast = await CMMCommand.GetContrast(m.SerialNumber) ?? 50;
|
|
Log($" Contrast: {contrast}");
|
|
var inputSource = await CMMCommand.GetInputSource(m.SerialNumber);
|
|
Log($" InputSource: {inputSource}");
|
|
var inputOptions = await CMMCommand.GetInputSourceOptions(m.SerialNumber);
|
|
Log($" InputOptions count: {inputOptions.Count}");
|
|
var powerStatus = await CMMCommand.GetMonPowerStatus(m.SerialNumber) ?? "Unknown";
|
|
Log($" PowerStatus: {powerStatus}");
|
|
|
|
newChildren.Add(new TextBlock
|
|
{
|
|
Text = m.MonitorName,
|
|
Foreground = Brushes.White,
|
|
FontSize = 14,
|
|
FontWeight = FontWeights.Bold,
|
|
Margin = new Thickness(0, 10, 0, 5)
|
|
});
|
|
|
|
newChildren.Add(CreateSliderRow("Brightness", brightness, m.SerialNumber, CMMCommand.SetBrightness));
|
|
newChildren.Add(CreateSliderRow("Contrast", contrast, m.SerialNumber, CMMCommand.SetContrast));
|
|
|
|
if (inputOptions.Count > 0)
|
|
newChildren.Add(CreateInputRow(inputSource, inputOptions, m.SerialNumber));
|
|
|
|
newChildren.Add(CreatePowerRow(powerStatus, m.SerialNumber));
|
|
}
|
|
}
|
|
Log($"Built {newChildren.Count} UI elements");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
Log($"ERROR: {ex.GetType().Name}: {ex.Message}\n{ex.StackTrace}");
|
|
newChildren.Clear();
|
|
newChildren.Add(new TextBlock { Text = $"Error: {ex.Message}", Foreground = Brushes.Red, FontSize = 12, TextWrapping = TextWrapping.Wrap });
|
|
}
|
|
|
|
Log("Updating UI...");
|
|
sp.Children.Clear();
|
|
foreach (var child in newChildren)
|
|
sp.Children.Add(child);
|
|
Log($"UI updated with {sp.Children.Count} children");
|
|
}
|
|
|
|
private StackPanel CreateSliderRow(string label, int value, string serialNumber, Func<string, int, Task> setCommand)
|
|
{
|
|
var row = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 3, 0, 3) };
|
|
row.Children.Add(new TextBlock { Text = label, Foreground = Brushes.LightGray, Width = 70, FontSize = 12, VerticalAlignment = VerticalAlignment.Center });
|
|
|
|
var slider = new Slider { Minimum = 0, Maximum = 100, Value = value, Width = 140, Tag = serialNumber };
|
|
var valueText = new TextBlock { Text = value.ToString(), Foreground = Brushes.White, Width = 30, FontSize = 12, Margin = new Thickness(5, 0, 0, 0) };
|
|
|
|
slider.ValueChanged += (s, e) => valueText.Text = ((int)e.NewValue).ToString();
|
|
slider.PreviewMouseUp += async (s, e) =>
|
|
{
|
|
if (s is Slider sl && sl.Tag is string sn)
|
|
await setCommand(sn, (int)sl.Value);
|
|
};
|
|
|
|
row.Children.Add(slider);
|
|
row.Children.Add(valueText);
|
|
return row;
|
|
}
|
|
|
|
private StackPanel CreateInputRow(int? currentInput, List<InputSourceOption> options, string serialNumber)
|
|
{
|
|
var row = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 3, 0, 3) };
|
|
row.Children.Add(new TextBlock { Text = "Input", Foreground = Brushes.LightGray, Width = 70, FontSize = 12, VerticalAlignment = VerticalAlignment.Center });
|
|
|
|
var combo = new ComboBox { Width = 170, ItemsSource = options, DisplayMemberPath = "Name", Tag = serialNumber };
|
|
if (currentInput.HasValue)
|
|
combo.SelectedItem = options.Find(o => o.Value == currentInput.Value);
|
|
|
|
combo.SelectionChanged += async (s, e) =>
|
|
{
|
|
if (s is ComboBox cb && cb.Tag is string sn && cb.SelectedItem is InputSourceOption opt)
|
|
await CMMCommand.SetInputSource(sn, opt.Value);
|
|
};
|
|
|
|
row.Children.Add(combo);
|
|
return row;
|
|
}
|
|
|
|
private StackPanel CreatePowerRow(string status, string serialNumber)
|
|
{
|
|
var row = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 3, 0, 3) };
|
|
row.Children.Add(new TextBlock { Text = "Power", Foreground = Brushes.LightGray, Width = 70, FontSize = 12, VerticalAlignment = VerticalAlignment.Center });
|
|
|
|
var btn = new Button { Content = status, Width = 170, Tag = serialNumber };
|
|
btn.Click += async (s, e) =>
|
|
{
|
|
if (s is Button b && b.Tag is string sn)
|
|
{
|
|
var current = await CMMCommand.GetMonPowerStatus(sn);
|
|
if (current == "Sleep" || current == "PowerOff")
|
|
await CMMCommand.PowerOn(sn);
|
|
else
|
|
await CMMCommand.Sleep(sn);
|
|
await Task.Delay(1000);
|
|
b.Content = await CMMCommand.GetMonPowerStatus(sn);
|
|
}
|
|
};
|
|
|
|
row.Children.Add(btn);
|
|
return row;
|
|
}
|
|
}
|