2
0
Files
controlmymonitormanagement/Library/Method/CMMCommand.cs

284 lines
8.8 KiB
C#
Raw Normal View History

2022-05-23 00:58:58 +08:00
using CMM.Library.Base;
using CMM.Library.Helpers;
using CMM.Library.ViewModel;
using System.IO;
2023-07-02 22:17:57 +08:00
namespace CMM.Library.Method;
/// <summary>
/// Control My Monitor Management Command
/// </summary>
public static class CMMCommand
2022-05-23 00:58:58 +08:00
{
static readonly string CMMTmpFolder = Path.Combine(Path.GetTempPath(), "CMM");
static readonly string CMMexe = Path.Combine(AppContext.BaseDirectory, "ControlMyMonitor.exe");
2023-07-02 22:17:57 +08:00
static readonly string CMMsMonitors = Path.Combine(CMMTmpFolder, "smonitors.tmp");
public static async Task ScanMonitor()
2022-05-23 00:58:58 +08:00
{
// Ensure temp folder exists for output files
Directory.CreateDirectory(CMMTmpFolder);
await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/smonitors {CMMsMonitors}");
2023-07-02 22:17:57 +08:00
}
2022-05-23 00:58:58 +08:00
public static async Task PowerOn(string monitorSN)
{
await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/SetValue {monitorSN} D6 1");
2023-07-02 22:17:57 +08:00
}
public static async Task Sleep(string monitorSN)
2023-07-02 22:17:57 +08:00
{
await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/SetValue {monitorSN} D6 4");
2023-07-02 22:17:57 +08:00
}
private static async Task<string> GetMonitorValue(string monitorSN, string vcpCode = "D6", int maxRetries = 2)
2023-07-02 22:17:57 +08:00
{
for (int attempt = 0; attempt <= maxRetries; attempt++)
2023-07-05 20:45:59 +08:00
{
// Execute directly without batch file wrapper
var (output, exitCode) = await ConsoleHelper.ExecuteExeAsync(
CMMexe,
$"/GetValue {monitorSN} {vcpCode}");
2023-07-05 20:45:59 +08:00
// Timeout
if (exitCode == -1)
return string.Empty;
2023-07-05 20:45:59 +08:00
// ControlMyMonitor returns the value as the exit code
// Exit code > 0 means success with that value
if (exitCode > 0)
return exitCode.ToString();
2023-07-05 20:45:59 +08:00
// 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 failure
if (attempt < maxRetries)
await Task.Delay(300);
}
return string.Empty;
2023-07-03 01:51:09 +08:00
}
#region Brightness (VCP Code 10)
public static async Task SetBrightness(string monitorSN, int value)
{
await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/SetValue {monitorSN} 10 {value}");
}
public static async Task<int?> GetBrightness(string monitorSN)
{
var value = await GetMonitorValue(monitorSN, "10");
return int.TryParse(value, out var result) ? result : null;
}
#endregion
#region Contrast (VCP Code 12)
public static async Task SetContrast(string monitorSN, int value)
{
await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/SetValue {monitorSN} 12 {value}");
}
public static async Task<int?> GetContrast(string monitorSN)
{
var value = await GetMonitorValue(monitorSN, "12");
return int.TryParse(value, out var result) ? result : null;
}
#endregion
#region Input Source (VCP Code 60)
public static async Task SetInputSource(string monitorSN, int value)
{
await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/SetValue {monitorSN} 60 {value}");
}
public static async Task<int?> GetInputSource(string monitorSN)
{
var value = await GetMonitorValue(monitorSN, "60");
return int.TryParse(value, out var result) ? result : null;
}
public static async Task<List<InputSourceOption>> GetInputSourceOptions(string monitorSN)
{
var options = new List<InputSourceOption>();
var savePath = Path.Combine(CMMTmpFolder, $"{monitorSN}_vcp.tmp");
await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/sjson {savePath} {monitorSN}");
if (!File.Exists(savePath)) return options;
var monitorModel = JsonHelper.JsonFormFile<IEnumerable<SMonitorModel>>(savePath);
var inputSourceVcp = monitorModel?.FirstOrDefault(m => m.VCPCode == "60");
if (inputSourceVcp?.PossibleValues != null)
{
var possibleValues = inputSourceVcp.PossibleValues
.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(v => int.TryParse(v.Trim(), out var val) ? val : (int?)null)
.Where(v => v.HasValue)
.Select(v => v.Value);
foreach (var value in possibleValues)
{
options.Add(new InputSourceOption(value, GetInputSourceName(value)));
}
}
return options;
}
public static string GetInputSourceName(int vcpValue)
{
return vcpValue switch
{
1 => "VGA-1",
2 => "VGA-2",
3 => "DVI-1",
4 => "DVI-2",
5 => "Composite-1",
6 => "Composite-2",
7 => "S-Video-1",
8 => "S-Video-2",
9 => "Tuner-1",
10 => "Tuner-2",
11 => "Tuner-3",
12 => "Component-1",
13 => "Component-2",
14 => "Component-3",
15 => "DisplayPort-1",
16 => "DisplayPort-2",
17 => "HDMI-1",
18 => "HDMI-2",
_ => $"Input-{vcpValue}"
};
}
#endregion
2023-07-03 01:51:09 +08:00
public static async Task<string> GetMonPowerStatus(string monitorSN)
{
var status = await GetMonitorValue(monitorSN);
return status switch
2022-05-23 00:58:58 +08:00
{
2023-07-03 01:51:09 +08:00
"1" => "PowerOn",
"4" => "Sleep",
"5" => "PowerOff",
_ => string.Empty
};
}
2022-05-23 00:58:58 +08:00
2023-07-03 01:51:09 +08:00
public static async Task ScanMonitorStatus(IEnumerable<XMonitor> monitors)
{
var taskList = monitors.Select(x =>
{
return ScanMonitorStatus($"{CMMTmpFolder}\\{x.SerialNumber}.tmp", x);
});
await Task.WhenAll(taskList);
2023-07-02 22:17:57 +08:00
}
2023-07-03 01:51:09 +08:00
static async Task ScanMonitorStatus(string savePath, XMonitor mon)
2023-07-02 22:17:57 +08:00
{
await ConsoleHelper.ExecuteExeAsync(CMMexe, $"/sjson {savePath} {mon.MonitorID}");
2023-07-03 01:51:09 +08:00
var monitorModel = JsonHelper.JsonFormFile<IEnumerable<SMonitorModel>>(savePath);
var status = monitorModel.ReadMonitorStatus();
mon.Status = new ObservableRangeCollection<XMonitorStatus>(status);
2023-07-02 22:17:57 +08:00
}
/// <summary>
/// 取得螢幕狀態
/// </summary>
2023-07-03 01:51:09 +08:00
public static IEnumerable<XMonitorStatus> ReadMonitorStatus(this IEnumerable<SMonitorModel> monitorModel)
2023-07-02 22:17:57 +08:00
{
2023-07-03 01:51:09 +08:00
foreach (var m in monitorModel)
{
yield return new XMonitorStatus
2022-05-23 00:58:58 +08:00
{
2023-07-03 01:51:09 +08:00
VCP_Code = m.VCPCode,
VCPCodeName = m.VCPCodeName,
Read_Write = m.ReadWrite,
CurrentValue = TryGetInt(m.CurrentValue),
MaximumValue = TryGetInt(m.MaximumValue),
PossibleValues = TryGetArrStr(m.PossibleValues),
};
2023-07-02 22:17:57 +08:00
}
2023-07-03 01:51:09 +08:00
IEnumerable<int> TryGetArrStr(string str)
2023-07-02 22:17:57 +08:00
{
2023-07-03 01:51:09 +08:00
return str.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(x => TryGetInt(x))
.Where(x => x != null)
.Select(x => (int)x)
.ToList();
2022-05-23 00:58:58 +08:00
}
2023-07-02 22:17:57 +08:00
int? TryGetInt(string str)
2022-05-23 00:58:58 +08:00
{
2023-07-03 01:51:09 +08:00
return int.TryParse(str, out var value)
? value
: null;
2023-07-02 22:17:57 +08:00
}
}
2022-05-23 00:58:58 +08:00
2023-07-02 22:17:57 +08:00
/// <summary>
/// 取得螢幕清單
/// </summary>
public static async Task<IEnumerable<XMonitor>> ReadMonitorsData()
{
var monitors = new List<XMonitor>();
2022-05-23 00:58:58 +08:00
2023-07-02 22:17:57 +08:00
if (!File.Exists(CMMsMonitors)) return monitors;
2022-05-23 00:58:58 +08:00
// Read with UTF-16 LE encoding (ControlMyMonitor outputs UTF-16)
var rawBytes = await File.ReadAllBytesAsync(CMMsMonitors);
var content = System.Text.Encoding.Unicode.GetString(rawBytes);
XMonitor? mon = null;
foreach (var line in content.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
2023-07-02 22:17:57 +08:00
{
var colonIdx = line.IndexOf(':');
if (colonIdx < 0) continue;
var key = line.Substring(0, colonIdx).Trim();
var val = line.Substring(colonIdx + 1).Trim().Trim('"');
2022-05-23 00:58:58 +08:00
if (key.Contains("Monitor Device Name"))
2022-05-23 00:58:58 +08:00
{
mon = new XMonitor { MonitorDeviceName = val };
2022-05-23 00:58:58 +08:00
}
else if (mon != null && key.Contains("Monitor Name"))
2022-05-23 00:58:58 +08:00
{
mon.MonitorName = val;
2022-05-23 00:58:58 +08:00
}
else if (mon != null && key.Contains("Serial Number"))
2023-07-02 22:17:57 +08:00
{
mon.SerialNumber = val;
2023-07-02 22:17:57 +08:00
}
else if (mon != null && key.Contains("Adapter Name"))
2022-05-23 00:58:58 +08:00
{
mon.AdapterName = val;
2022-05-23 00:58:58 +08:00
}
else if (mon != null && key.Contains("Monitor ID"))
2023-07-02 22:17:57 +08:00
{
mon.MonitorID = val;
if (!string.IsNullOrEmpty(mon.SerialNumber))
monitors.Add(mon);
mon = null;
2023-07-02 22:17:57 +08:00
}
2022-05-23 00:58:58 +08:00
}
2023-07-02 22:17:57 +08:00
return monitors;
}
2022-05-23 00:58:58 +08:00
}