From 14945c3e69fac6b334befad85733a47f759f58ed Mon Sep 17 00:00:00 2001
From: Quick <577652+markdwags@users.noreply.github.com>
Date: Wed, 7 Jan 2026 18:09:28 -0600
Subject: [PATCH 01/10] implement VCP feature support for brightness, contrast,
and input; add feature resolver and update command handling
---
DDCSwitch/DDCSwitch.csproj | 4 +-
DDCSwitch/FeatureResolver.cs | 171 ++++++++++++++++++++
DDCSwitch/JsonContext.cs | 8 +-
DDCSwitch/Monitor.cs | 114 ++++++++++++++
DDCSwitch/Program.cs | 295 +++++++++++++++++++++++++++--------
DDCSwitch/VcpFeature.cs | 83 ++++++++++
6 files changed, 606 insertions(+), 69 deletions(-)
create mode 100644 DDCSwitch/FeatureResolver.cs
create mode 100644 DDCSwitch/VcpFeature.cs
diff --git a/DDCSwitch/DDCSwitch.csproj b/DDCSwitch/DDCSwitch.csproj
index 04e5d93..a2ee008 100644
--- a/DDCSwitch/DDCSwitch.csproj
+++ b/DDCSwitch/DDCSwitch.csproj
@@ -19,7 +19,7 @@
$(MSBuildProjectDirectory)\..\CHANGELOG.md
-
+
@(ChangelogLines, '%0A')
@@ -33,7 +33,7 @@
$(Version)
$(Version)
-
+
diff --git a/DDCSwitch/FeatureResolver.cs b/DDCSwitch/FeatureResolver.cs
new file mode 100644
index 0000000..ffaa897
--- /dev/null
+++ b/DDCSwitch/FeatureResolver.cs
@@ -0,0 +1,171 @@
+using System.Globalization;
+
+namespace DDCSwitch;
+
+///
+/// Resolves feature names to VCP codes and handles value conversions
+///
+public static class FeatureResolver
+{
+ private static readonly Dictionary FeatureMap = new(StringComparer.OrdinalIgnoreCase)
+ {
+ { "brightness", VcpFeature.Brightness },
+ { "contrast", VcpFeature.Contrast },
+ { "input", VcpFeature.InputSource }
+ };
+
+ ///
+ /// Attempts to resolve a feature name or VCP code to a VcpFeature
+ ///
+ /// Feature name (brightness, contrast, input) or VCP code (0x10, 0x12, etc.)
+ /// The resolved VcpFeature if successful
+ /// True if the feature was resolved successfully
+ public static bool TryResolveFeature(string input, out VcpFeature? feature)
+ {
+ feature = null;
+
+ if (string.IsNullOrWhiteSpace(input))
+ {
+ return false;
+ }
+
+ // First try to resolve as a known feature name
+ if (FeatureMap.TryGetValue(input.Trim(), out feature))
+ {
+ return true;
+ }
+
+ // Try to parse as a VCP code
+ if (TryParseVcpCode(input, out byte vcpCode))
+ {
+ // Create a generic VCP feature for unknown codes
+ feature = new VcpFeature(vcpCode, $"VCP_{vcpCode:X2}", VcpFeatureType.ReadWrite, false);
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Converts a percentage value (0-100) to raw VCP value based on the maximum value
+ ///
+ /// Percentage value (0-100)
+ /// Maximum raw value supported by the monitor
+ /// Raw VCP value
+ public static uint ConvertPercentageToRaw(uint percentage, uint maxValue)
+ {
+ if (percentage > 100)
+ {
+ throw new ArgumentOutOfRangeException(nameof(percentage), "Percentage must be between 0 and 100");
+ }
+
+ if (maxValue == 0)
+ {
+ return 0;
+ }
+
+ // Convert percentage to raw value with proper rounding
+ return (uint)Math.Round((double)percentage * maxValue / 100.0);
+ }
+
+ ///
+ /// Converts a raw VCP value to percentage based on the maximum value
+ ///
+ /// Raw VCP value
+ /// Maximum raw value supported by the monitor
+ /// Percentage value (0-100)
+ public static uint ConvertRawToPercentage(uint rawValue, uint maxValue)
+ {
+ if (maxValue == 0)
+ {
+ return 0;
+ }
+
+ if (rawValue > maxValue)
+ {
+ rawValue = maxValue;
+ }
+
+ // Convert raw value to percentage with proper rounding
+ return (uint)Math.Round((double)rawValue * 100.0 / maxValue);
+ }
+
+ ///
+ /// Attempts to parse a VCP code from a string (supports hex format like 0x10 or decimal)
+ ///
+ /// Input string containing VCP code
+ /// Parsed VCP code if successful
+ /// True if parsing was successful
+ public static bool TryParseVcpCode(string input, out byte vcpCode)
+ {
+ vcpCode = 0;
+
+ if (string.IsNullOrWhiteSpace(input))
+ {
+ return false;
+ }
+
+ input = input.Trim();
+
+ // Try hex format (0x10, 0X10)
+ if (input.StartsWith("0x", StringComparison.OrdinalIgnoreCase) ||
+ input.StartsWith("0X", StringComparison.OrdinalIgnoreCase))
+ {
+ var hexPart = input.Substring(2);
+ return byte.TryParse(hexPart, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out vcpCode);
+ }
+
+ // Try decimal format
+ return byte.TryParse(input, NumberStyles.Integer, CultureInfo.InvariantCulture, out vcpCode);
+ }
+
+ ///
+ /// Attempts to parse a percentage value from a string (supports % suffix)
+ ///
+ /// Input string containing percentage value
+ /// Parsed percentage value if successful
+ /// True if parsing was successful and value is in valid range (0-100)
+ public static bool TryParsePercentage(string input, out uint percentage)
+ {
+ percentage = 0;
+
+ if (string.IsNullOrWhiteSpace(input))
+ {
+ return false;
+ }
+
+ input = input.Trim();
+
+ // Remove % suffix if present
+ if (input.EndsWith("%"))
+ {
+ input = input.Substring(0, input.Length - 1).Trim();
+ }
+
+ if (!uint.TryParse(input, NumberStyles.Integer, CultureInfo.InvariantCulture, out percentage))
+ {
+ return false;
+ }
+
+ // Validate range
+ return percentage <= 100;
+ }
+
+ ///
+ /// Gets all known feature names
+ ///
+ /// Collection of known feature names
+ public static IEnumerable GetKnownFeatureNames()
+ {
+ return FeatureMap.Keys;
+ }
+
+ ///
+ /// Gets all predefined VCP features
+ ///
+ /// Collection of predefined VCP features
+ public static IEnumerable GetPredefinedFeatures()
+ {
+ return FeatureMap.Values;
+ }
+}
\ No newline at end of file
diff --git a/DDCSwitch/JsonContext.cs b/DDCSwitch/JsonContext.cs
index f33dcde..18db944 100644
--- a/DDCSwitch/JsonContext.cs
+++ b/DDCSwitch/JsonContext.cs
@@ -5,8 +5,8 @@ namespace DDCSwitch;
[JsonSerializable(typeof(ErrorResponse))]
[JsonSerializable(typeof(ListMonitorsResponse))]
[JsonSerializable(typeof(MonitorInfo))]
-[JsonSerializable(typeof(GetInputResponse))]
-[JsonSerializable(typeof(SetInputResponse))]
+[JsonSerializable(typeof(GetVcpResponse))]
+[JsonSerializable(typeof(SetVcpResponse))]
[JsonSerializable(typeof(MonitorReference))]
[JsonSourceGenerationOptions(
WriteIndented = true,
@@ -20,8 +20,8 @@ internal partial class JsonContext : JsonSerializerContext
// Response models
internal record ErrorResponse(bool Success, string Error, MonitorReference? Monitor = null);
internal record ListMonitorsResponse(bool Success, List? Monitors = null, string? Error = null);
-internal record GetInputResponse(bool Success, MonitorReference Monitor, string CurrentInput, string CurrentInputCode, uint MaxValue);
-internal record SetInputResponse(bool Success, MonitorReference Monitor, string NewInput, string NewInputCode);
+internal record GetVcpResponse(bool Success, MonitorReference Monitor, string FeatureName, uint RawValue, uint MaxValue, uint? PercentageValue = null, string? ErrorMessage = null);
+internal record SetVcpResponse(bool Success, MonitorReference Monitor, string FeatureName, uint SetValue, uint? PercentageValue = null, string? ErrorMessage = null);
// Data models
internal record MonitorInfo(
diff --git a/DDCSwitch/Monitor.cs b/DDCSwitch/Monitor.cs
index ffcba81..1e9843d 100644
--- a/DDCSwitch/Monitor.cs
+++ b/DDCSwitch/Monitor.cs
@@ -1,3 +1,7 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
namespace DDCSwitch;
///
@@ -42,6 +46,116 @@ public bool TrySetInputSource(uint value)
return NativeMethods.SetVCPFeature(Handle, InputSource.VcpInputSource, value);
}
+ ///
+ /// Attempts to read a VCP feature value from the monitor
+ ///
+ /// VCP code to read (0x00-0xFF)
+ /// Current value of the VCP feature
+ /// Maximum value supported by the VCP feature
+ /// True if the operation was successful
+ public bool TryGetVcpFeature(byte vcpCode, out uint currentValue, out uint maxValue)
+ {
+ currentValue = 0;
+ maxValue = 0;
+
+ if (_disposed || Handle == IntPtr.Zero)
+ {
+ return false;
+ }
+
+ return NativeMethods.GetVCPFeatureAndVCPFeatureReply(
+ Handle,
+ vcpCode,
+ out _,
+ out currentValue,
+ out maxValue);
+ }
+
+ ///
+ /// Attempts to write a VCP feature value to the monitor
+ ///
+ /// VCP code to write (0x00-0xFF)
+ /// Value to set for the VCP feature
+ /// True if the operation was successful
+ public bool TrySetVcpFeature(byte vcpCode, uint value)
+ {
+ if (_disposed || Handle == IntPtr.Zero)
+ {
+ return false;
+ }
+
+ return NativeMethods.SetVCPFeature(Handle, vcpCode, value);
+ }
+
+ ///
+ /// Scans all VCP codes (0x00-0xFF) to discover supported features
+ ///
+ /// Dictionary mapping VCP codes to their feature information
+ public Dictionary ScanVcpFeatures()
+ {
+ var features = new Dictionary();
+
+ if (_disposed || Handle == IntPtr.Zero)
+ {
+ return features;
+ }
+
+ // Get predefined features for name lookup
+ var predefinedFeatures = FeatureResolver.GetPredefinedFeatures()
+ .ToDictionary(f => f.Code, f => f);
+
+ // Scan all possible VCP codes (0x00 to 0xFF)
+ for (int code = 0; code <= 255; code++)
+ {
+ byte vcpCode = (byte)code;
+
+ if (TryGetVcpFeature(vcpCode, out uint currentValue, out uint maxValue))
+ {
+ // Feature is supported - determine name and type
+ string name;
+ VcpFeatureType type;
+
+ if (predefinedFeatures.TryGetValue(vcpCode, out VcpFeature? predefined))
+ {
+ name = predefined.Name;
+ type = predefined.Type;
+ }
+ else
+ {
+ name = $"VCP_{vcpCode:X2}";
+ type = VcpFeatureType.ReadWrite; // Assume read-write for unknown codes
+ }
+
+ features[vcpCode] = new VcpFeatureInfo(
+ vcpCode,
+ name,
+ type,
+ currentValue,
+ maxValue,
+ true
+ );
+ }
+ else
+ {
+ // Feature is not supported - still add entry for completeness
+ string name = predefinedFeatures.TryGetValue(vcpCode, out VcpFeature? predefined)
+ ? predefined.Name
+ : $"VCP_{vcpCode:X2}";
+
+ features[vcpCode] = new VcpFeatureInfo(
+ vcpCode,
+ name,
+ VcpFeatureType.ReadWrite,
+ 0,
+ 0,
+ false
+ );
+ }
+ }
+
+ return features;
+ }
+
public void Dispose()
{
if (!_disposed && Handle != IntPtr.Zero)
diff --git a/DDCSwitch/Program.cs b/DDCSwitch/Program.cs
index 925e737..d3283fd 100644
--- a/DDCSwitch/Program.cs
+++ b/DDCSwitch/Program.cs
@@ -95,38 +95,14 @@ private static int ShowUsage()
AnsiConsole.Write(new FigletText("DDCSwitch").Color(Color.Blue));
AnsiConsole.MarkupLine($"[dim]Windows DDC/CI Monitor Input Switcher v{version}[/]\n");
- var table = new Table()
- .Border(TableBorder.Rounded)
- .AddColumn("Command")
- .AddColumn("Description")
- .AddColumn("Example");
-
- table.AddRow(
- "[yellow]list[/] or [yellow]ls[/]",
- "List all DDC/CI capable monitors",
- "[dim]DDCSwitch list[/]");
-
- table.AddRow(
- "[yellow]get[/] ",
- "Get current input source for a monitor",
- "[dim]DDCSwitch get 0[/]");
-
- table.AddRow(
- "[yellow]set[/] ",
- "Set input source for a monitor",
- "[dim]DDCSwitch set 0 HDMI1[/]");
-
- table.AddRow(
- "[yellow]version[/] or [yellow]-v[/]",
- "Display version information",
- "[dim]DDCSwitch version[/]");
-
- AnsiConsole.Write(table);
-
- AnsiConsole.MarkupLine("\n[bold]Monitor:[/] Monitor index (0, 1, 2...) or name pattern");
- AnsiConsole.MarkupLine("[bold]Input:[/] Input name (HDMI1, HDMI2, DP1, DP2, DVI1, VGA1) or hex code (0x11)");
- AnsiConsole.MarkupLine("\n[bold]Options:[/]");
- AnsiConsole.MarkupLine(" [yellow]--json[/] Output in JSON format");
+ AnsiConsole.MarkupLine("[yellow]Commands:[/]");
+ AnsiConsole.MarkupLine(" list - List all DDC/CI capable monitors");
+ AnsiConsole.MarkupLine(" get monitor feature - Get current value for a monitor feature");
+ AnsiConsole.MarkupLine(" set monitor feature value - Set value for a monitor feature");
+ AnsiConsole.MarkupLine(" version - Display version information");
+
+ AnsiConsole.MarkupLine("\nSupported features: brightness, contrast, input, or VCP codes like 0x10");
+ AnsiConsole.MarkupLine("Use --json flag for JSON output");
return 0;
}
@@ -267,17 +243,35 @@ private static int ListMonitors(bool jsonOutput)
private static int GetCurrentInput(string[] args, bool jsonOutput)
{
- if (args.Length < 2)
+ if (args.Length < 3)
{
if (jsonOutput)
{
- var error = new ErrorResponse(false, "Monitor identifier required");
+ var error = new ErrorResponse(false, "Monitor identifier and feature required");
Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
}
else
{
- AnsiConsole.MarkupLine("[red]Error:[/] Monitor identifier required.");
- AnsiConsole.MarkupLine("Usage: [yellow]DDCSwitch get [/]");
+ AnsiConsole.MarkupLine("[red]Error:[/] Monitor identifier and feature required.");
+ AnsiConsole.MarkupLine("Usage: [yellow]DDCSwitch get [/]");
+ }
+
+ return 1;
+ }
+
+ string featureInput = args[2];
+
+ if (!FeatureResolver.TryResolveFeature(featureInput, out VcpFeature? feature))
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, $"Invalid feature '{featureInput}'");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ AnsiConsole.MarkupLine($"[red]Error:[/] Invalid feature '{featureInput}'.");
+ AnsiConsole.MarkupLine("Valid features: brightness, contrast, input, or VCP code (0x10, 0x12, etc.)");
}
return 1;
@@ -324,20 +318,23 @@ private static int GetCurrentInput(string[] args, bool jsonOutput)
return 1;
}
- if (!monitor.TryGetInputSource(out uint current, out uint max))
+ // Use generic VCP method for all features
+ bool success = monitor.TryGetVcpFeature(feature!.Code, out uint current, out uint max);
+
+ if (!success)
{
if (jsonOutput)
{
var monitorRef =
new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- var error = new ErrorResponse(false, $"Failed to get input source from monitor '{monitor.Name}'",
+ var error = new ErrorResponse(false, $"Failed to get {feature.Name} from monitor '{monitor.Name}'",
monitorRef);
Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
}
else
{
- AnsiConsole.MarkupLine($"[red]Error:[/] Failed to get input source from monitor '{monitor.Name}'.");
- AnsiConsole.MarkupLine("The monitor may not support DDC/CI or requires administrator privileges.");
+ AnsiConsole.MarkupLine($"[red]Error:[/] Failed to get {feature.Name} from monitor '{monitor.Name}'.");
+ AnsiConsole.MarkupLine("The monitor may not support this feature or requires administrator privileges.");
}
// Cleanup
@@ -352,13 +349,42 @@ private static int GetCurrentInput(string[] args, bool jsonOutput)
if (jsonOutput)
{
var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- var result = new GetInputResponse(true, monitorRef, InputSource.GetName(current), $"0x{current:X2}", max);
- Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.GetInputResponse));
+
+ if (feature.Code == InputSource.VcpInputSource)
+ {
+ // Use generic VCP response for input as well
+ uint? percentageValue = feature.SupportsPercentage ? FeatureResolver.ConvertRawToPercentage(current, max) : null;
+ var result = new GetVcpResponse(true, monitorRef, feature.Name, current, max, percentageValue);
+ Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.GetVcpResponse));
+ }
+ else
+ {
+ // Use generic VCP response for other features
+ uint? percentageValue = feature.SupportsPercentage ? FeatureResolver.ConvertRawToPercentage(current, max) : null;
+ var result = new GetVcpResponse(true, monitorRef, feature.Name, current, max, percentageValue);
+ Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.GetVcpResponse));
+ }
}
else
{
AnsiConsole.MarkupLine($"[green]Monitor:[/] {monitor.Name} ({monitor.DeviceName})");
- AnsiConsole.MarkupLine($"[green]Current Input:[/] {InputSource.GetName(current)} (0x{current:X2})");
+
+ if (feature.Code == InputSource.VcpInputSource)
+ {
+ // Display input with name resolution
+ AnsiConsole.MarkupLine($"[green]Current {feature.Name}:[/] {InputSource.GetName(current)} (0x{current:X2})");
+ }
+ else if (feature.SupportsPercentage)
+ {
+ // Display percentage for brightness/contrast
+ uint percentage = FeatureResolver.ConvertRawToPercentage(current, max);
+ AnsiConsole.MarkupLine($"[green]Current {feature.Name}:[/] {percentage}% (raw: {current}/{max})");
+ }
+ else
+ {
+ // Display raw values for unknown VCP codes
+ AnsiConsole.MarkupLine($"[green]Current {feature.Name}:[/] {current} (max: {max})");
+ }
}
// Cleanup
@@ -372,34 +398,96 @@ private static int GetCurrentInput(string[] args, bool jsonOutput)
private static int SetInput(string[] args, bool jsonOutput)
{
- if (args.Length < 3)
+ // Require 4 arguments: set
+ if (args.Length < 4)
{
if (jsonOutput)
{
- var error = new ErrorResponse(false, "Monitor and input source required");
+ var error = new ErrorResponse(false, "Monitor, feature, and value required");
Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
}
else
{
- AnsiConsole.MarkupLine("[red]Error:[/] Monitor and input source required.");
- AnsiConsole.MarkupLine("Usage: [yellow]DDCSwitch set [/]");
+ AnsiConsole.MarkupLine("[red]Error:[/] Monitor, feature, and value required.");
+ AnsiConsole.MarkupLine("Usage: [yellow]DDCSwitch set [/]");
}
return 1;
}
- if (!InputSource.TryParse(args[2], out uint inputValue))
+ string featureInput = args[2];
+ string valueInput = args[3];
+
+ if (!FeatureResolver.TryResolveFeature(featureInput, out VcpFeature? feature))
{
if (jsonOutput)
{
- var error = new ErrorResponse(false, $"Invalid input source '{args[2]}'");
+ var error = new ErrorResponse(false, $"Invalid feature '{featureInput}'");
Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
}
else
{
- AnsiConsole.MarkupLine($"[red]Error:[/] Invalid input source '{args[2]}'.");
- AnsiConsole.MarkupLine(
- "Valid inputs: HDMI1, HDMI2, DP1, DP2, DVI1, DVI2, VGA1, VGA2, or hex code (0x11)");
+ AnsiConsole.MarkupLine($"[red]Error:[/] Invalid feature '{featureInput}'.");
+ AnsiConsole.MarkupLine("Valid features: brightness, contrast, input, or VCP code (0x10, 0x12, etc.)");
+ }
+
+ return 1;
+ }
+
+ // Parse the value based on feature type
+ uint setValue;
+ uint? percentageValue = null;
+
+ if (feature!.Code == InputSource.VcpInputSource)
+ {
+ // Use existing input source parsing for input feature
+ if (!InputSource.TryParse(valueInput, out setValue))
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, $"Invalid input source '{valueInput}'");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ AnsiConsole.MarkupLine($"[red]Error:[/] Invalid input source '{valueInput}'.");
+ AnsiConsole.MarkupLine(
+ "Valid inputs: HDMI1, HDMI2, DP1, DP2, DVI1, DVI2, VGA1, VGA2, or hex code (0x11)");
+ }
+
+ return 1;
+ }
+ }
+ else if (feature.SupportsPercentage && FeatureResolver.TryParsePercentage(valueInput, out uint percentage))
+ {
+ // Parse as percentage for brightness/contrast
+ percentageValue = percentage;
+ // We'll convert to raw value after getting monitor's max value
+ setValue = 0; // Placeholder
+ }
+ else if (uint.TryParse(valueInput, out uint rawValue))
+ {
+ // Parse as raw value - we'll validate range after getting monitor's max value
+ setValue = rawValue;
+ }
+ else
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, $"Invalid value '{valueInput}' for feature '{feature.Name}'");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ AnsiConsole.MarkupLine($"[red]Error:[/] Invalid value '{valueInput}' for feature '{feature.Name}'.");
+ if (feature.SupportsPercentage)
+ {
+ AnsiConsole.MarkupLine("Valid values: 0-100% or raw numeric value");
+ }
+ else
+ {
+ AnsiConsole.MarkupLine("Valid values: numeric value within monitor's supported range");
+ }
}
return 1;
@@ -446,34 +534,100 @@ private static int SetInput(string[] args, bool jsonOutput)
return 1;
}
+ // If we have a percentage value, we need to get the max value first to convert it
+ // For raw values, we also need to validate they're within the monitor's supported range
+ if (percentageValue.HasValue || (!feature!.SupportsPercentage && feature.Code != InputSource.VcpInputSource))
+ {
+ if (monitor.TryGetVcpFeature(feature.Code, out uint currentValue, out uint maxValue))
+ {
+ if (percentageValue.HasValue)
+ {
+ // Convert percentage to raw value
+ setValue = FeatureResolver.ConvertPercentageToRaw(percentageValue.Value, maxValue);
+ }
+ else if (feature.Code != InputSource.VcpInputSource)
+ {
+ // Validate raw value is within supported range
+ if (setValue > maxValue)
+ {
+ if (jsonOutput)
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+ var error = new ErrorResponse(false, $"Value {setValue} is out of range for {feature.Name}. Valid range: 0-{maxValue}", monitorRef);
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ AnsiConsole.MarkupLine($"[red]Error:[/] Value {setValue} is out of range for {feature.Name}.");
+ AnsiConsole.MarkupLine($"Valid range: 0-{maxValue}");
+ }
+
+ // Cleanup
+ foreach (var m in monitors)
+ {
+ m.Dispose();
+ }
+
+ return 1;
+ }
+ }
+ }
+ else
+ {
+ if (jsonOutput)
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+ var error = new ErrorResponse(false, $"Failed to read current {feature.Name} from monitor '{monitor.Name}' to validate range", monitorRef);
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ AnsiConsole.MarkupLine($"[red]Error:[/] Failed to read current {feature.Name} from monitor '{monitor.Name}' to validate range.");
+ AnsiConsole.MarkupLine("The monitor may not support this feature or requires administrator privileges.");
+ }
+
+ // Cleanup
+ foreach (var m in monitors)
+ {
+ m.Dispose();
+ }
+
+ return 1;
+ }
+ }
+
bool success = false;
string? errorMsg = null;
if (!jsonOutput)
{
+ string displayValue = percentageValue.HasValue ? $"{percentageValue}%" : setValue.ToString();
AnsiConsole.Status()
- .Start($"Switching {monitor.Name} to {InputSource.GetName(inputValue)}...", ctx =>
+ .Start($"Setting {monitor.Name} {feature.Name} to {displayValue}...", ctx =>
{
ctx.Spinner(Spinner.Known.Dots);
- if (!monitor.TrySetInputSource(inputValue))
+ if (!monitor.TrySetVcpFeature(feature!.Code, setValue))
{
- errorMsg =
- $"Failed to set input source on monitor '{monitor.Name}'. The monitor may not support this input or requires administrator privileges.";
+ errorMsg = $"Failed to set {feature.Name} on monitor '{monitor.Name}'. The monitor may not support this feature or requires administrator privileges.";
}
else
{
success = true;
- // Give the monitor a moment to switch
+ }
+
+ if (success)
+ {
+ // Give the monitor a moment to apply the change
Thread.Sleep(500);
}
});
}
else
{
- if (!monitor.TrySetInputSource(inputValue))
+ if (!monitor.TrySetVcpFeature(feature!.Code, setValue))
{
- errorMsg = $"Failed to set input source on monitor '{monitor.Name}'";
+ errorMsg = $"Failed to set {feature.Name} on monitor '{monitor.Name}'";
}
else
{
@@ -508,13 +662,28 @@ private static int SetInput(string[] args, bool jsonOutput)
if (jsonOutput)
{
var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- var result = new SetInputResponse(true, monitorRef, InputSource.GetName(inputValue), $"0x{inputValue:X2}");
- Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.SetInputResponse));
+
+ // Use generic VCP response for all features
+ var result = new SetVcpResponse(true, monitorRef, feature!.Name, setValue, percentageValue);
+ Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.SetVcpResponse));
}
else
{
- AnsiConsole.MarkupLine(
- $"[green]✓[/] Successfully switched {monitor.Name} to {InputSource.GetName(inputValue)}");
+ if (feature!.Code == InputSource.VcpInputSource)
+ {
+ // Display input with name resolution
+ AnsiConsole.MarkupLine($"[green]✓[/] Successfully set {monitor.Name} {feature.Name} to {InputSource.GetName(setValue)}");
+ }
+ else if (percentageValue.HasValue)
+ {
+ // Display percentage for brightness/contrast
+ AnsiConsole.MarkupLine($"[green]✓[/] Successfully set {monitor.Name} {feature.Name} to {percentageValue}%");
+ }
+ else
+ {
+ // Display raw value for unknown VCP codes
+ AnsiConsole.MarkupLine($"[green]✓[/] Successfully set {monitor.Name} {feature.Name} to {setValue}");
+ }
}
// Cleanup
diff --git a/DDCSwitch/VcpFeature.cs b/DDCSwitch/VcpFeature.cs
new file mode 100644
index 0000000..3a4cd4a
--- /dev/null
+++ b/DDCSwitch/VcpFeature.cs
@@ -0,0 +1,83 @@
+namespace DDCSwitch;
+
+///
+/// Defines the access type for a VCP feature
+///
+public enum VcpFeatureType
+{
+ ///
+ /// Feature can only be read
+ ///
+ ReadOnly,
+
+ ///
+ /// Feature can only be written
+ ///
+ WriteOnly,
+
+ ///
+ /// Feature can be both read and written
+ ///
+ ReadWrite
+}
+
+///
+/// Represents a VCP (Virtual Control Panel) feature with its properties
+///
+public class VcpFeature
+{
+ public byte Code { get; }
+ public string Name { get; }
+ public VcpFeatureType Type { get; }
+ public bool SupportsPercentage { get; }
+
+ public VcpFeature(byte code, string name, VcpFeatureType type, bool supportsPercentage)
+ {
+ Code = code;
+ Name = name;
+ Type = type;
+ SupportsPercentage = supportsPercentage;
+ }
+
+ ///
+ /// Brightness control (VCP 0x10)
+ ///
+ public static VcpFeature Brightness => new(0x10, "brightness", VcpFeatureType.ReadWrite, true);
+
+ ///
+ /// Contrast control (VCP 0x12)
+ ///
+ public static VcpFeature Contrast => new(0x12, "contrast", VcpFeatureType.ReadWrite, true);
+
+ ///
+ /// Input source selection (VCP 0x60)
+ ///
+ public static VcpFeature InputSource => new(0x60, "input", VcpFeatureType.ReadWrite, false);
+
+ public override string ToString()
+ {
+ return $"{Name} (0x{Code:X2})";
+ }
+
+ public override bool Equals(object? obj)
+ {
+ return obj is VcpFeature other && Code == other.Code;
+ }
+
+ public override int GetHashCode()
+ {
+ return Code.GetHashCode();
+ }
+}
+
+///
+/// Information about a VCP feature discovered during scanning
+///
+public record VcpFeatureInfo(
+ byte Code,
+ string Name,
+ VcpFeatureType Type,
+ uint CurrentValue,
+ uint MaxValue,
+ bool IsSupported
+);
\ No newline at end of file
From f50e7babf424d625e55350ff23d794409b0ea430 Mon Sep 17 00:00:00 2001
From: Quick <577652+markdwags@users.noreply.github.com>
Date: Wed, 7 Jan 2026 19:39:05 -0600
Subject: [PATCH 02/10] enhance VCP feature support with error handling; add
brightness and contrast controls, VCP scanning, and update README
---
DDCSwitch/FeatureResolver.cs | 127 +++-
DDCSwitch/JsonContext.cs | 8 +-
DDCSwitch/Monitor.cs | 57 +-
DDCSwitch/Program.cs | 610 ++++++++++++++++---
DDCSwitch/VcpErrorHandler.cs | 259 ++++++++
EXAMPLES.md | 1095 ++++++++++++++++++++++++++++------
README.md | 123 +++-
7 files changed, 2012 insertions(+), 267 deletions(-)
create mode 100644 DDCSwitch/VcpErrorHandler.cs
diff --git a/DDCSwitch/FeatureResolver.cs b/DDCSwitch/FeatureResolver.cs
index ffaa897..dd5aab2 100644
--- a/DDCSwitch/FeatureResolver.cs
+++ b/DDCSwitch/FeatureResolver.cs
@@ -95,7 +95,7 @@ public static uint ConvertRawToPercentage(uint rawValue, uint maxValue)
///
/// Input string containing VCP code
/// Parsed VCP code if successful
- /// True if parsing was successful
+ /// True if parsing was successful and VCP code is in valid range (0x00-0xFF)
public static bool TryParseVcpCode(string input, out byte vcpCode)
{
vcpCode = 0;
@@ -112,11 +112,26 @@ public static bool TryParseVcpCode(string input, out byte vcpCode)
input.StartsWith("0X", StringComparison.OrdinalIgnoreCase))
{
var hexPart = input.Substring(2);
- return byte.TryParse(hexPart, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out vcpCode);
+ if (byte.TryParse(hexPart, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out vcpCode))
+ {
+ // VCP codes are inherently valid for byte range (0x00-0xFF)
+ return true;
+ }
+ return false;
+ }
+
+ // Try decimal format - validate range for decimal input
+ if (uint.TryParse(input, NumberStyles.Integer, CultureInfo.InvariantCulture, out uint decimalValue))
+ {
+ // Validate VCP code is in valid range (0x00-0xFF)
+ if (decimalValue <= 255)
+ {
+ vcpCode = (byte)decimalValue;
+ return true;
+ }
}
- // Try decimal format
- return byte.TryParse(input, NumberStyles.Integer, CultureInfo.InvariantCulture, out vcpCode);
+ return false;
}
///
@@ -147,10 +162,112 @@ public static bool TryParsePercentage(string input, out uint percentage)
return false;
}
- // Validate range
+ // Validate range (0-100%)
+ return percentage <= 100;
+ }
+
+ ///
+ /// Validates that a raw VCP value is within the monitor's supported range
+ ///
+ /// Raw VCP value to validate
+ /// Maximum value supported by the monitor for this VCP code
+ /// True if the value is within the valid range (0 to maxValue)
+ public static bool IsValidRawVcpValue(uint value, uint maxValue)
+ {
+ return value <= maxValue;
+ }
+
+ ///
+ /// Validates that a percentage value is in the valid range
+ ///
+ /// Percentage value to validate
+ /// True if the percentage is between 0 and 100 inclusive
+ public static bool IsValidPercentage(uint percentage)
+ {
return percentage <= 100;
}
+ ///
+ /// Validates that a VCP code is in the valid range
+ ///
+ /// VCP code to validate
+ /// True if the VCP code is in the valid range (0x00-0xFF)
+ public static bool IsValidVcpCode(byte vcpCode)
+ {
+ // All byte values are valid VCP codes (0x00-0xFF)
+ return true;
+ }
+
+ ///
+ /// Gets a descriptive error message for invalid percentage values
+ ///
+ /// The invalid input that was provided
+ /// Error message describing the validation failure
+ public static string GetPercentageValidationError(string input)
+ {
+ if (string.IsNullOrWhiteSpace(input))
+ {
+ return "Percentage value cannot be empty";
+ }
+
+ var cleanInput = input.Trim();
+ if (cleanInput.EndsWith("%"))
+ {
+ cleanInput = cleanInput.Substring(0, cleanInput.Length - 1).Trim();
+ }
+
+ if (!uint.TryParse(cleanInput, out uint value))
+ {
+ return $"'{input}' is not a valid percentage value. Expected format: 0-100 or 0%-100%";
+ }
+
+ if (value > 100)
+ {
+ return $"Percentage value {value}% is out of range. Valid range: 0-100%";
+ }
+
+ return $"'{input}' is not a valid percentage value";
+ }
+
+ ///
+ /// Gets a descriptive error message for invalid VCP codes
+ ///
+ /// The invalid input that was provided
+ /// Error message describing the validation failure
+ public static string GetVcpCodeValidationError(string input)
+ {
+ if (string.IsNullOrWhiteSpace(input))
+ {
+ return "VCP code cannot be empty";
+ }
+
+ var cleanInput = input.Trim();
+
+ if (cleanInput.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
+ {
+ return $"'{input}' is not a valid VCP code. Expected hex format: 0x00-0xFF";
+ }
+
+ if (uint.TryParse(cleanInput, out uint value) && value > 255)
+ {
+ return $"VCP code {value} is out of range. Valid range: 0-255 (0x00-0xFF)";
+ }
+
+ return $"'{input}' is not a valid VCP code. Expected format: 0-255 or 0x00-0xFF";
+ }
+
+ ///
+ /// Gets a descriptive error message for invalid raw VCP values
+ ///
+ /// The invalid value that was provided
+ /// The maximum value supported by the monitor
+ /// The name of the VCP feature
+ /// Error message describing the validation failure
+ public static string GetRawValueValidationError(uint value, uint maxValue, string featureName)
+ {
+ return $"Value {value} is out of range for {featureName}. Valid range: 0-{maxValue}";
+ }
+
///
/// Gets all known feature names
///
diff --git a/DDCSwitch/JsonContext.cs b/DDCSwitch/JsonContext.cs
index 18db944..059cdf0 100644
--- a/DDCSwitch/JsonContext.cs
+++ b/DDCSwitch/JsonContext.cs
@@ -7,6 +7,9 @@ namespace DDCSwitch;
[JsonSerializable(typeof(MonitorInfo))]
[JsonSerializable(typeof(GetVcpResponse))]
[JsonSerializable(typeof(SetVcpResponse))]
+[JsonSerializable(typeof(VcpScanResponse))]
+[JsonSerializable(typeof(VcpFeatureInfo))]
+[JsonSerializable(typeof(VcpFeatureType))]
[JsonSerializable(typeof(MonitorReference))]
[JsonSourceGenerationOptions(
WriteIndented = true,
@@ -22,6 +25,7 @@ internal record ErrorResponse(bool Success, string Error, MonitorReference? Moni
internal record ListMonitorsResponse(bool Success, List? Monitors = null, string? Error = null);
internal record GetVcpResponse(bool Success, MonitorReference Monitor, string FeatureName, uint RawValue, uint MaxValue, uint? PercentageValue = null, string? ErrorMessage = null);
internal record SetVcpResponse(bool Success, MonitorReference Monitor, string FeatureName, uint SetValue, uint? PercentageValue = null, string? ErrorMessage = null);
+internal record VcpScanResponse(bool Success, MonitorReference Monitor, List Features, string? ErrorMessage = null);
// Data models
internal record MonitorInfo(
@@ -31,7 +35,9 @@ internal record MonitorInfo(
bool IsPrimary,
string? CurrentInput,
string? CurrentInputCode,
- string Status);
+ string Status,
+ string? Brightness = null,
+ string? Contrast = null);
internal record MonitorReference(int Index, string Name, string DeviceName, bool IsPrimary = false);
diff --git a/DDCSwitch/Monitor.cs b/DDCSwitch/Monitor.cs
index 1e9843d..b09cd19 100644
--- a/DDCSwitch/Monitor.cs
+++ b/DDCSwitch/Monitor.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
+using System.Runtime.InteropServices;
namespace DDCSwitch;
@@ -47,44 +48,88 @@ public bool TrySetInputSource(uint value)
}
///
- /// Attempts to read a VCP feature value from the monitor
+ /// Attempts to read a VCP feature value from the monitor with enhanced error detection
///
/// VCP code to read (0x00-0xFF)
/// Current value of the VCP feature
/// Maximum value supported by the VCP feature
+ /// Win32 error code if operation fails
/// True if the operation was successful
- public bool TryGetVcpFeature(byte vcpCode, out uint currentValue, out uint maxValue)
+ public bool TryGetVcpFeature(byte vcpCode, out uint currentValue, out uint maxValue, out int errorCode)
{
currentValue = 0;
maxValue = 0;
+ errorCode = 0;
if (_disposed || Handle == IntPtr.Zero)
{
+ errorCode = 0x00000006; // ERROR_INVALID_HANDLE
return false;
}
- return NativeMethods.GetVCPFeatureAndVCPFeatureReply(
+ bool success = NativeMethods.GetVCPFeatureAndVCPFeatureReply(
Handle,
vcpCode,
out _,
out currentValue,
out maxValue);
+
+ if (!success)
+ {
+ errorCode = Marshal.GetLastWin32Error();
+ }
+
+ return success;
+ }
+
+ ///
+ /// Attempts to read a VCP feature value from the monitor (legacy method for backward compatibility)
+ ///
+ /// VCP code to read (0x00-0xFF)
+ /// Current value of the VCP feature
+ /// Maximum value supported by the VCP feature
+ /// True if the operation was successful
+ public bool TryGetVcpFeature(byte vcpCode, out uint currentValue, out uint maxValue)
+ {
+ return TryGetVcpFeature(vcpCode, out currentValue, out maxValue, out _);
}
///
- /// Attempts to write a VCP feature value to the monitor
+ /// Attempts to write a VCP feature value to the monitor with enhanced error detection
///
/// VCP code to write (0x00-0xFF)
/// Value to set for the VCP feature
+ /// Win32 error code if operation fails
/// True if the operation was successful
- public bool TrySetVcpFeature(byte vcpCode, uint value)
+ public bool TrySetVcpFeature(byte vcpCode, uint value, out int errorCode)
{
+ errorCode = 0;
+
if (_disposed || Handle == IntPtr.Zero)
{
+ errorCode = 0x00000006; // ERROR_INVALID_HANDLE
return false;
}
- return NativeMethods.SetVCPFeature(Handle, vcpCode, value);
+ bool success = NativeMethods.SetVCPFeature(Handle, vcpCode, value);
+
+ if (!success)
+ {
+ errorCode = Marshal.GetLastWin32Error();
+ }
+
+ return success;
+ }
+
+ ///
+ /// Attempts to write a VCP feature value to the monitor (legacy method for backward compatibility)
+ ///
+ /// VCP code to write (0x00-0xFF)
+ /// Value to set for the VCP feature
+ /// True if the operation was successful
+ public bool TrySetVcpFeature(byte vcpCode, uint value)
+ {
+ return TrySetVcpFeature(vcpCode, value, out _);
}
///
diff --git a/DDCSwitch/Program.cs b/DDCSwitch/Program.cs
index d3283fd..5b2cf07 100644
--- a/DDCSwitch/Program.cs
+++ b/DDCSwitch/Program.cs
@@ -27,6 +27,14 @@ public static int Run(string[] args)
bool jsonOutput = args.Contains("--json", StringComparer.OrdinalIgnoreCase);
var filteredArgs = args.Where(a => !a.Equals("--json", StringComparison.OrdinalIgnoreCase)).ToArray();
+ // Check for --verbose flag
+ bool verboseOutput = filteredArgs.Contains("--verbose", StringComparer.OrdinalIgnoreCase);
+ filteredArgs = filteredArgs.Where(a => !a.Equals("--verbose", StringComparison.OrdinalIgnoreCase)).ToArray();
+
+ // Check for --scan flag
+ bool scanOutput = filteredArgs.Contains("--scan", StringComparer.OrdinalIgnoreCase);
+ filteredArgs = filteredArgs.Where(a => !a.Equals("--scan", StringComparison.OrdinalIgnoreCase)).ToArray();
+
if (filteredArgs.Length == 0)
{
ShowUsage();
@@ -39,7 +47,7 @@ public static int Run(string[] args)
{
return command switch
{
- "list" or "ls" => ListMonitors(jsonOutput),
+ "list" or "ls" => ListMonitors(jsonOutput, verboseOutput, scanOutput),
"get" => GetCurrentInput(filteredArgs, jsonOutput),
"set" => SetInput(filteredArgs, jsonOutput),
"version" or "-v" or "--version" => ShowVersion(jsonOutput),
@@ -96,13 +104,15 @@ private static int ShowUsage()
AnsiConsole.MarkupLine($"[dim]Windows DDC/CI Monitor Input Switcher v{version}[/]\n");
AnsiConsole.MarkupLine("[yellow]Commands:[/]");
- AnsiConsole.MarkupLine(" list - List all DDC/CI capable monitors");
- AnsiConsole.MarkupLine(" get monitor feature - Get current value for a monitor feature");
+ AnsiConsole.WriteLine(" list [--verbose] [--scan] - List all DDC/CI capable monitors");
+ AnsiConsole.WriteLine(" get monitor [feature] - Get current value for a monitor feature or scan all features");
AnsiConsole.MarkupLine(" set monitor feature value - Set value for a monitor feature");
AnsiConsole.MarkupLine(" version - Display version information");
AnsiConsole.MarkupLine("\nSupported features: brightness, contrast, input, or VCP codes like 0x10");
- AnsiConsole.MarkupLine("Use --json flag for JSON output");
+ AnsiConsole.MarkupLine("Use [yellow]--json[/] flag for JSON output");
+ AnsiConsole.MarkupLine("Use [yellow]--verbose[/] flag with list to include brightness and contrast");
+ AnsiConsole.MarkupLine("Use [yellow]--scan[/] flag with list to enumerate all VCP codes");
return 0;
}
@@ -123,15 +133,15 @@ private static int InvalidCommand(string command, bool jsonOutput)
return 1;
}
- private static int ListMonitors(bool jsonOutput)
+ private static int ListMonitors(bool jsonOutput, bool verboseOutput = false, bool scanOutput = false)
{
if (!jsonOutput)
{
AnsiConsole.Status()
- .Start("Enumerating monitors...", ctx =>
+ .Start(scanOutput ? "Scanning VCP features..." : "Enumerating monitors...", ctx =>
{
ctx.Spinner(Spinner.Known.Dots);
- Thread.Sleep(100); // Brief pause for visual feedback
+ Thread.Sleep(scanOutput ? 500 : 100); // Longer pause for VCP scanning
});
}
@@ -152,6 +162,12 @@ private static int ListMonitors(bool jsonOutput)
return 1;
}
+ // If scan mode is enabled, perform VCP scanning for each monitor
+ if (scanOutput)
+ {
+ return HandleVcpScan(monitors, jsonOutput);
+ }
+
if (jsonOutput)
{
var monitorList = monitors.Select(monitor =>
@@ -159,6 +175,8 @@ private static int ListMonitors(bool jsonOutput)
string? inputName = null;
uint? inputCode = null;
string status = "ok";
+ string? brightness = null;
+ string? contrast = null;
try
{
@@ -171,10 +189,41 @@ private static int ListMonitors(bool jsonOutput)
{
status = "no_ddc_ci";
}
+
+ // Get brightness and contrast if verbose mode is enabled
+ if (verboseOutput && status == "ok")
+ {
+ // Try to get brightness (VCP 0x10)
+ if (monitor.TryGetVcpFeature(VcpFeature.Brightness.Code, out uint brightnessCurrent, out uint brightnessMax))
+ {
+ uint brightnessPercentage = FeatureResolver.ConvertRawToPercentage(brightnessCurrent, brightnessMax);
+ brightness = $"{brightnessPercentage}%";
+ }
+ else
+ {
+ brightness = "N/A";
+ }
+
+ // Try to get contrast (VCP 0x12)
+ if (monitor.TryGetVcpFeature(VcpFeature.Contrast.Code, out uint contrastCurrent, out uint contrastMax))
+ {
+ uint contrastPercentage = FeatureResolver.ConvertRawToPercentage(contrastCurrent, contrastMax);
+ contrast = $"{contrastPercentage}%";
+ }
+ else
+ {
+ contrast = "N/A";
+ }
+ }
}
catch
{
status = "error";
+ if (verboseOutput)
+ {
+ brightness = "N/A";
+ contrast = "N/A";
+ }
}
return new MonitorInfo(
@@ -184,7 +233,9 @@ private static int ListMonitors(bool jsonOutput)
monitor.IsPrimary,
inputName,
inputCode != null ? $"0x{inputCode:X2}" : null,
- status);
+ status,
+ brightness,
+ contrast);
}).ToList();
var result = new ListMonitorsResponse(true, monitorList);
@@ -197,13 +248,23 @@ private static int ListMonitors(bool jsonOutput)
.AddColumn("Index")
.AddColumn("Monitor Name")
.AddColumn("Device")
- .AddColumn("Current Input")
- .AddColumn("Status");
+ .AddColumn("Current Input");
+
+ // Add brightness and contrast columns if verbose mode is enabled
+ if (verboseOutput)
+ {
+ table.AddColumn("Brightness");
+ table.AddColumn("Contrast");
+ }
+
+ table.AddColumn("Status");
foreach (var monitor in monitors)
{
string inputInfo = "N/A";
string status = "[green]OK[/]";
+ string brightnessInfo = "N/A";
+ string contrastInfo = "N/A";
try
{
@@ -215,18 +276,66 @@ private static int ListMonitors(bool jsonOutput)
{
status = "[yellow]No DDC/CI[/]";
}
+
+ // Get brightness and contrast if verbose mode is enabled and monitor supports DDC/CI
+ if (verboseOutput && status == "[green]OK[/]")
+ {
+ // Try to get brightness (VCP 0x10)
+ if (monitor.TryGetVcpFeature(VcpFeature.Brightness.Code, out uint brightnessCurrent, out uint brightnessMax))
+ {
+ uint brightnessPercentage = FeatureResolver.ConvertRawToPercentage(brightnessCurrent, brightnessMax);
+ brightnessInfo = $"{brightnessPercentage}%";
+ }
+ else
+ {
+ brightnessInfo = "[dim]N/A[/]";
+ }
+
+ // Try to get contrast (VCP 0x12)
+ if (monitor.TryGetVcpFeature(VcpFeature.Contrast.Code, out uint contrastCurrent, out uint contrastMax))
+ {
+ uint contrastPercentage = FeatureResolver.ConvertRawToPercentage(contrastCurrent, contrastMax);
+ contrastInfo = $"{contrastPercentage}%";
+ }
+ else
+ {
+ contrastInfo = "[dim]N/A[/]";
+ }
+ }
+ else if (verboseOutput)
+ {
+ brightnessInfo = "[dim]N/A[/]";
+ contrastInfo = "[dim]N/A[/]";
+ }
}
catch
{
status = "[red]Error[/]";
+ if (verboseOutput)
+ {
+ brightnessInfo = "[dim]N/A[/]";
+ contrastInfo = "[dim]N/A[/]";
+ }
}
- table.AddRow(
+ var row = new List
+ {
monitor.IsPrimary ? $"{monitor.Index} [yellow]*[/]" : monitor.Index.ToString(),
monitor.Name,
monitor.DeviceName,
- inputInfo,
- status);
+ inputInfo
+ };
+
+ // Add brightness and contrast columns if verbose mode is enabled
+ if (verboseOutput)
+ {
+ row.Add(brightnessInfo);
+ row.Add(contrastInfo);
+ }
+
+ row.Add(status);
+
+ table.AddRow(row.ToArray());
}
AnsiConsole.Write(table);
@@ -243,34 +352,54 @@ private static int ListMonitors(bool jsonOutput)
private static int GetCurrentInput(string[] args, bool jsonOutput)
{
- if (args.Length < 3)
+ if (args.Length < 2)
{
if (jsonOutput)
{
- var error = new ErrorResponse(false, "Monitor identifier and feature required");
+ var error = new ErrorResponse(false, "Monitor identifier required");
Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
}
else
{
- AnsiConsole.MarkupLine("[red]Error:[/] Monitor identifier and feature required.");
- AnsiConsole.MarkupLine("Usage: [yellow]DDCSwitch get [/]");
+ AnsiConsole.MarkupLine("[red]Error:[/] Monitor identifier required.");
+ AnsiConsole.WriteLine("Usage: DDCSwitch get [feature]");
}
return 1;
}
+ // If no feature is specified, perform VCP scan
+ if (args.Length == 2)
+ {
+ return HandleVcpScanForMonitor(args[1], jsonOutput);
+ }
+
string featureInput = args[2];
if (!FeatureResolver.TryResolveFeature(featureInput, out VcpFeature? feature))
{
+ string errorMessage;
+
+ // Provide specific error message based on input type
+ if (FeatureResolver.TryParseVcpCode(featureInput, out _))
+ {
+ // Valid VCP code but not in our predefined list
+ errorMessage = $"VCP code '{featureInput}' is valid but may not be supported by all monitors";
+ }
+ else
+ {
+ // Invalid feature name or VCP code
+ errorMessage = $"Invalid feature '{featureInput}'. {FeatureResolver.GetVcpCodeValidationError(featureInput)}";
+ }
+
if (jsonOutput)
{
- var error = new ErrorResponse(false, $"Invalid feature '{featureInput}'");
+ var error = new ErrorResponse(false, errorMessage);
Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
}
else
{
- AnsiConsole.MarkupLine($"[red]Error:[/] Invalid feature '{featureInput}'.");
+ AnsiConsole.MarkupLine($"[red]Error:[/] {errorMessage}");
AnsiConsole.MarkupLine("Valid features: brightness, contrast, input, or VCP code (0x10, 0x12, etc.)");
}
@@ -318,23 +447,40 @@ private static int GetCurrentInput(string[] args, bool jsonOutput)
return 1;
}
- // Use generic VCP method for all features
- bool success = monitor.TryGetVcpFeature(feature!.Code, out uint current, out uint max);
+ // Use generic VCP method for all features with enhanced error handling
+ bool success = monitor.TryGetVcpFeature(feature!.Code, out uint current, out uint max, out int errorCode);
if (!success)
{
+ string errorMessage;
+
+ if (VcpErrorHandler.IsTimeoutError(errorCode))
+ {
+ errorMessage = VcpErrorHandler.CreateTimeoutMessage(monitor, feature, "read");
+ }
+ else if (VcpErrorHandler.IsUnsupportedFeatureError(errorCode))
+ {
+ errorMessage = VcpErrorHandler.CreateUnsupportedFeatureMessage(monitor, feature);
+ }
+ else if (errorCode == 0x00000006) // ERROR_INVALID_HANDLE
+ {
+ errorMessage = VcpErrorHandler.CreateCommunicationFailureMessage(monitor);
+ }
+ else
+ {
+ errorMessage = VcpErrorHandler.CreateReadFailureMessage(monitor, feature);
+ }
+
if (jsonOutput)
{
var monitorRef =
new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- var error = new ErrorResponse(false, $"Failed to get {feature.Name} from monitor '{monitor.Name}'",
- monitorRef);
+ var error = new ErrorResponse(false, errorMessage, monitorRef);
Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
}
else
{
- AnsiConsole.MarkupLine($"[red]Error:[/] Failed to get {feature.Name} from monitor '{monitor.Name}'.");
- AnsiConsole.MarkupLine("The monitor may not support this feature or requires administrator privileges.");
+ AnsiConsole.MarkupLine($"[red]Error:[/] {errorMessage}");
}
// Cleanup
@@ -420,50 +566,60 @@ private static int SetInput(string[] args, bool jsonOutput)
if (!FeatureResolver.TryResolveFeature(featureInput, out VcpFeature? feature))
{
+ string errorMessage;
+
+ // Provide specific error message based on input type
+ if (FeatureResolver.TryParseVcpCode(featureInput, out _))
+ {
+ // Valid VCP code but not in our predefined list
+ errorMessage = $"VCP code '{featureInput}' is valid but may not be supported by all monitors";
+ }
+ else
+ {
+ // Invalid feature name or VCP code
+ errorMessage = $"Invalid feature '{featureInput}'. {FeatureResolver.GetVcpCodeValidationError(featureInput)}";
+ }
+
if (jsonOutput)
{
- var error = new ErrorResponse(false, $"Invalid feature '{featureInput}'");
+ var error = new ErrorResponse(false, errorMessage);
Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
}
else
{
- AnsiConsole.MarkupLine($"[red]Error:[/] Invalid feature '{featureInput}'.");
+ AnsiConsole.MarkupLine($"[red]Error:[/] {errorMessage}");
AnsiConsole.MarkupLine("Valid features: brightness, contrast, input, or VCP code (0x10, 0x12, etc.)");
}
return 1;
}
- // Parse the value based on feature type
- uint setValue;
+ // Parse and validate the value based on feature type
+ uint setValue = 0; // Initialize to avoid compiler error
uint? percentageValue = null;
+ string? validationError = null;
if (feature!.Code == InputSource.VcpInputSource)
{
// Use existing input source parsing for input feature
if (!InputSource.TryParse(valueInput, out setValue))
{
- if (jsonOutput)
- {
- var error = new ErrorResponse(false, $"Invalid input source '{valueInput}'");
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine($"[red]Error:[/] Invalid input source '{valueInput}'.");
- AnsiConsole.MarkupLine(
- "Valid inputs: HDMI1, HDMI2, DP1, DP2, DVI1, DVI2, VGA1, VGA2, or hex code (0x11)");
- }
-
- return 1;
+ validationError = $"Invalid input source '{valueInput}'. Valid inputs: HDMI1, HDMI2, DP1, DP2, DVI1, DVI2, VGA1, VGA2, or hex code (0x11)";
}
}
else if (feature.SupportsPercentage && FeatureResolver.TryParsePercentage(valueInput, out uint percentage))
{
- // Parse as percentage for brightness/contrast
- percentageValue = percentage;
- // We'll convert to raw value after getting monitor's max value
- setValue = 0; // Placeholder
+ // Parse as percentage for brightness/contrast - validate percentage range
+ if (!FeatureResolver.IsValidPercentage(percentage))
+ {
+ validationError = VcpErrorHandler.CreateRangeValidationMessage(feature, percentage, 100, true);
+ }
+ else
+ {
+ percentageValue = percentage;
+ // We'll convert to raw value after getting monitor's max value
+ setValue = 0; // Placeholder
+ }
}
else if (uint.TryParse(valueInput, out uint rawValue))
{
@@ -471,23 +627,29 @@ private static int SetInput(string[] args, bool jsonOutput)
setValue = rawValue;
}
else
+ {
+ // Invalid value format
+ if (feature.SupportsPercentage)
+ {
+ validationError = FeatureResolver.GetPercentageValidationError(valueInput);
+ }
+ else
+ {
+ validationError = $"Invalid value '{valueInput}' for feature '{feature.Name}'. Expected: numeric value within monitor's supported range";
+ }
+ }
+
+ // If we have a validation error, return it now
+ if (validationError != null)
{
if (jsonOutput)
{
- var error = new ErrorResponse(false, $"Invalid value '{valueInput}' for feature '{feature.Name}'");
+ var error = new ErrorResponse(false, validationError);
Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
}
else
{
- AnsiConsole.MarkupLine($"[red]Error:[/] Invalid value '{valueInput}' for feature '{feature.Name}'.");
- if (feature.SupportsPercentage)
- {
- AnsiConsole.MarkupLine("Valid values: 0-100% or raw numeric value");
- }
- else
- {
- AnsiConsole.MarkupLine("Valid values: numeric value within monitor's supported range");
- }
+ AnsiConsole.MarkupLine($"[red]Error:[/] {validationError}");
}
return 1;
@@ -534,11 +696,10 @@ private static int SetInput(string[] args, bool jsonOutput)
return 1;
}
- // If we have a percentage value, we need to get the max value first to convert it
- // For raw values, we also need to validate they're within the monitor's supported range
- if (percentageValue.HasValue || (!feature!.SupportsPercentage && feature.Code != InputSource.VcpInputSource))
+ // If we have a percentage value or need to validate raw value range, get the monitor's max value
+ if (percentageValue.HasValue || (feature!.Code != InputSource.VcpInputSource && !percentageValue.HasValue))
{
- if (monitor.TryGetVcpFeature(feature.Code, out uint currentValue, out uint maxValue))
+ if (monitor.TryGetVcpFeature(feature.Code, out uint currentValue, out uint maxValue, out int errorCode))
{
if (percentageValue.HasValue)
{
@@ -548,18 +709,19 @@ private static int SetInput(string[] args, bool jsonOutput)
else if (feature.Code != InputSource.VcpInputSource)
{
// Validate raw value is within supported range
- if (setValue > maxValue)
+ if (!FeatureResolver.IsValidRawVcpValue(setValue, maxValue))
{
+ string rangeError = VcpErrorHandler.CreateRangeValidationMessage(feature, setValue, maxValue);
+
if (jsonOutput)
{
var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- var error = new ErrorResponse(false, $"Value {setValue} is out of range for {feature.Name}. Valid range: 0-{maxValue}", monitorRef);
+ var error = new ErrorResponse(false, rangeError, monitorRef);
Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
}
else
{
- AnsiConsole.MarkupLine($"[red]Error:[/] Value {setValue} is out of range for {feature.Name}.");
- AnsiConsole.MarkupLine($"Valid range: 0-{maxValue}");
+ AnsiConsole.MarkupLine($"[red]Error:[/] {rangeError}");
}
// Cleanup
@@ -574,16 +736,34 @@ private static int SetInput(string[] args, bool jsonOutput)
}
else
{
+ string readError;
+
+ if (VcpErrorHandler.IsTimeoutError(errorCode))
+ {
+ readError = VcpErrorHandler.CreateTimeoutMessage(monitor, feature, "read");
+ }
+ else if (VcpErrorHandler.IsUnsupportedFeatureError(errorCode))
+ {
+ readError = VcpErrorHandler.CreateUnsupportedFeatureMessage(monitor, feature);
+ }
+ else if (errorCode == 0x00000006) // ERROR_INVALID_HANDLE
+ {
+ readError = VcpErrorHandler.CreateCommunicationFailureMessage(monitor);
+ }
+ else
+ {
+ readError = $"Failed to read current {feature.Name} from monitor '{monitor.Name}' to validate range. {VcpErrorHandler.CreateReadFailureMessage(monitor, feature)}";
+ }
+
if (jsonOutput)
{
var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- var error = new ErrorResponse(false, $"Failed to read current {feature.Name} from monitor '{monitor.Name}' to validate range", monitorRef);
+ var error = new ErrorResponse(false, readError, monitorRef);
Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
}
else
{
- AnsiConsole.MarkupLine($"[red]Error:[/] Failed to read current {feature.Name} from monitor '{monitor.Name}' to validate range.");
- AnsiConsole.MarkupLine("The monitor may not support this feature or requires administrator privileges.");
+ AnsiConsole.MarkupLine($"[red]Error:[/] {readError}");
}
// Cleanup
@@ -607,9 +787,24 @@ private static int SetInput(string[] args, bool jsonOutput)
{
ctx.Spinner(Spinner.Known.Dots);
- if (!monitor.TrySetVcpFeature(feature!.Code, setValue))
+ if (!monitor.TrySetVcpFeature(feature!.Code, setValue, out int errorCode))
{
- errorMsg = $"Failed to set {feature.Name} on monitor '{monitor.Name}'. The monitor may not support this feature or requires administrator privileges.";
+ if (VcpErrorHandler.IsTimeoutError(errorCode))
+ {
+ errorMsg = VcpErrorHandler.CreateTimeoutMessage(monitor, feature, "write");
+ }
+ else if (VcpErrorHandler.IsUnsupportedFeatureError(errorCode))
+ {
+ errorMsg = VcpErrorHandler.CreateUnsupportedFeatureMessage(monitor, feature);
+ }
+ else if (errorCode == 0x00000006) // ERROR_INVALID_HANDLE
+ {
+ errorMsg = VcpErrorHandler.CreateCommunicationFailureMessage(monitor);
+ }
+ else
+ {
+ errorMsg = VcpErrorHandler.CreateWriteFailureMessage(monitor, feature, setValue);
+ }
}
else
{
@@ -625,9 +820,24 @@ private static int SetInput(string[] args, bool jsonOutput)
}
else
{
- if (!monitor.TrySetVcpFeature(feature!.Code, setValue))
+ if (!monitor.TrySetVcpFeature(feature!.Code, setValue, out int errorCode))
{
- errorMsg = $"Failed to set {feature.Name} on monitor '{monitor.Name}'";
+ if (VcpErrorHandler.IsTimeoutError(errorCode))
+ {
+ errorMsg = VcpErrorHandler.CreateTimeoutMessage(monitor, feature, "write");
+ }
+ else if (VcpErrorHandler.IsUnsupportedFeatureError(errorCode))
+ {
+ errorMsg = VcpErrorHandler.CreateUnsupportedFeatureMessage(monitor, feature);
+ }
+ else if (errorCode == 0x00000006) // ERROR_INVALID_HANDLE
+ {
+ errorMsg = VcpErrorHandler.CreateCommunicationFailureMessage(monitor);
+ }
+ else
+ {
+ errorMsg = VcpErrorHandler.CreateWriteFailureMessage(monitor, feature, setValue);
+ }
}
else
{
@@ -694,4 +904,262 @@ private static int SetInput(string[] args, bool jsonOutput)
return 0;
}
+
+ private static int HandleVcpScan(List monitors, bool jsonOutput)
+ {
+ if (jsonOutput)
+ {
+ // JSON output for VCP scan
+ var scanResults = new List();
+
+ foreach (var monitor in monitors)
+ {
+ try
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+ var features = monitor.ScanVcpFeatures();
+
+ // Convert to list and filter only supported features for cleaner output
+ var supportedFeatures = features.Values
+ .Where(f => f.IsSupported)
+ .OrderBy(f => f.Code)
+ .ToList();
+
+ scanResults.Add(new VcpScanResponse(true, monitorRef, supportedFeatures));
+ }
+ catch (Exception ex)
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+ scanResults.Add(new VcpScanResponse(false, monitorRef, new List(), ex.Message));
+ }
+ }
+
+ // Output all scan results
+ foreach (var result in scanResults)
+ {
+ Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.VcpScanResponse));
+ }
+ }
+ else
+ {
+ // Table output for VCP scan
+ foreach (var monitor in monitors)
+ {
+ try
+ {
+ AnsiConsole.MarkupLine($"\n[bold blue]Monitor {monitor.Index}: {monitor.Name}[/] ({monitor.DeviceName})");
+
+ var features = monitor.ScanVcpFeatures();
+ var supportedFeatures = features.Values
+ .Where(f => f.IsSupported)
+ .OrderBy(f => f.Code)
+ .ToList();
+
+ if (supportedFeatures.Count == 0)
+ {
+ AnsiConsole.MarkupLine("[yellow] No supported VCP features found[/]");
+ continue;
+ }
+
+ var table = new Table()
+ .Border(TableBorder.Rounded)
+ .AddColumn("VCP Code")
+ .AddColumn("Feature Name")
+ .AddColumn("Access Type")
+ .AddColumn("Current Value")
+ .AddColumn("Max Value")
+ .AddColumn("Percentage");
+
+ foreach (var feature in supportedFeatures)
+ {
+ string vcpCode = $"0x{feature.Code:X2}";
+ string accessType = feature.Type switch
+ {
+ VcpFeatureType.ReadOnly => "[yellow]Read-Only[/]",
+ VcpFeatureType.WriteOnly => "[red]Write-Only[/]",
+ VcpFeatureType.ReadWrite => "[green]Read-Write[/]",
+ _ => "[dim]Unknown[/]"
+ };
+
+ string currentValue = feature.CurrentValue.ToString();
+ string maxValue = feature.MaxValue.ToString();
+
+ // Calculate percentage for known percentage-based features
+ string percentage = "N/A";
+ if ((feature.Code == VcpFeature.Brightness.Code || feature.Code == VcpFeature.Contrast.Code) && feature.MaxValue > 0)
+ {
+ uint percentageValue = FeatureResolver.ConvertRawToPercentage(feature.CurrentValue, feature.MaxValue);
+ percentage = $"{percentageValue}%";
+ }
+
+ table.AddRow(vcpCode, feature.Name, accessType, currentValue, maxValue, percentage);
+ }
+
+ AnsiConsole.Write(table);
+ }
+ catch (Exception ex)
+ {
+ AnsiConsole.MarkupLine($"[red]Error scanning monitor {monitor.Index} ({monitor.Name}): {ex.Message}[/]");
+ }
+ }
+ }
+
+ // Cleanup
+ foreach (var monitor in monitors)
+ {
+ monitor.Dispose();
+ }
+
+ return 0;
+ }
+
+ private static int HandleVcpScanForMonitor(string monitorIdentifier, bool jsonOutput)
+ {
+ if (!jsonOutput)
+ {
+ AnsiConsole.Status()
+ .Start("Scanning VCP features...", ctx =>
+ {
+ ctx.Spinner(Spinner.Known.Dots);
+ Thread.Sleep(500); // Pause for VCP scanning
+ });
+ }
+
+ var monitors = MonitorController.EnumerateMonitors();
+
+ if (monitors.Count == 0)
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, "No DDC/CI capable monitors found");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ AnsiConsole.MarkupLine("[red]Error:[/] No DDC/CI capable monitors found.");
+ }
+
+ return 1;
+ }
+
+ var monitor = MonitorController.FindMonitor(monitors, monitorIdentifier);
+
+ if (monitor == null)
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, $"Monitor '{monitorIdentifier}' not found");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ AnsiConsole.MarkupLine($"[red]Error:[/] Monitor '{monitorIdentifier}' not found.");
+ AnsiConsole.MarkupLine("Use [yellow]DDCSwitch list[/] to see available monitors.");
+ }
+
+ // Cleanup
+ foreach (var m in monitors)
+ {
+ m.Dispose();
+ }
+
+ return 1;
+ }
+
+ try
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+ var features = monitor.ScanVcpFeatures();
+
+ // Filter only supported features for cleaner output
+ var supportedFeatures = features.Values
+ .Where(f => f.IsSupported)
+ .OrderBy(f => f.Code)
+ .ToList();
+
+ if (jsonOutput)
+ {
+ // JSON output for VCP scan
+ var scanResult = new VcpScanResponse(true, monitorRef, supportedFeatures);
+ Console.WriteLine(JsonSerializer.Serialize(scanResult, JsonContext.Default.VcpScanResponse));
+ }
+ else
+ {
+ // Table output for VCP scan - consistent with verbose listing format
+ AnsiConsole.MarkupLine($"[bold blue]Monitor {monitor.Index}: {monitor.Name}[/] ({monitor.DeviceName})");
+
+ if (supportedFeatures.Count == 0)
+ {
+ AnsiConsole.MarkupLine("[yellow] No supported VCP features found[/]");
+ }
+ else
+ {
+ var table = new Table()
+ .Border(TableBorder.Rounded)
+ .AddColumn("VCP Code")
+ .AddColumn("Feature Name")
+ .AddColumn("Access Type")
+ .AddColumn("Current Value")
+ .AddColumn("Max Value")
+ .AddColumn("Percentage");
+
+ foreach (var feature in supportedFeatures)
+ {
+ string vcpCode = $"0x{feature.Code:X2}";
+ string accessType = feature.Type switch
+ {
+ VcpFeatureType.ReadOnly => "[yellow]Read-Only[/]",
+ VcpFeatureType.WriteOnly => "[red]Write-Only[/]",
+ VcpFeatureType.ReadWrite => "[green]Read-Write[/]",
+ _ => "[dim]Unknown[/]"
+ };
+
+ string currentValue = feature.CurrentValue.ToString();
+ string maxValue = feature.MaxValue.ToString();
+
+ // Calculate percentage for known percentage-based features
+ string percentage = "N/A";
+ if ((feature.Code == VcpFeature.Brightness.Code || feature.Code == VcpFeature.Contrast.Code) && feature.MaxValue > 0)
+ {
+ uint percentageValue = FeatureResolver.ConvertRawToPercentage(feature.CurrentValue, feature.MaxValue);
+ percentage = $"{percentageValue}%";
+ }
+
+ table.AddRow(vcpCode, feature.Name, accessType, currentValue, maxValue, percentage);
+ }
+
+ AnsiConsole.Write(table);
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ if (jsonOutput)
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+ var scanResult = new VcpScanResponse(false, monitorRef, new List(), ex.Message);
+ Console.WriteLine(JsonSerializer.Serialize(scanResult, JsonContext.Default.VcpScanResponse));
+ }
+ else
+ {
+ AnsiConsole.MarkupLine($"[red]Error scanning monitor {monitor.Index} ({monitor.Name}): {ex.Message}[/]");
+ }
+
+ // Cleanup
+ foreach (var m in monitors)
+ {
+ m.Dispose();
+ }
+
+ return 1;
+ }
+
+ // Cleanup
+ foreach (var m in monitors)
+ {
+ m.Dispose();
+ }
+
+ return 0;
+ }
}
\ No newline at end of file
diff --git a/DDCSwitch/VcpErrorHandler.cs b/DDCSwitch/VcpErrorHandler.cs
new file mode 100644
index 0000000..aac96a6
--- /dev/null
+++ b/DDCSwitch/VcpErrorHandler.cs
@@ -0,0 +1,259 @@
+using System.Runtime.InteropServices;
+
+namespace DDCSwitch;
+
+///
+/// Provides enhanced error handling for VCP operations with specific error messages and suggestions
+///
+public static class VcpErrorHandler
+{
+ ///
+ /// Creates a detailed error message for VCP read failures
+ ///
+ /// The monitor that failed
+ /// The VCP feature that failed
+ /// Detailed error message with suggestions
+ public static string CreateReadFailureMessage(Monitor monitor, VcpFeature feature)
+ {
+ var baseMessage = $"Failed to read {feature.Name} from monitor '{monitor.Name}'";
+ var suggestions = GetReadFailureSuggestions(feature);
+
+ return $"{baseMessage}. {suggestions}";
+ }
+
+ ///
+ /// Creates a detailed error message for VCP write failures
+ ///
+ /// The monitor that failed
+ /// The VCP feature that failed
+ /// The value that was attempted to be set
+ /// Detailed error message with suggestions
+ public static string CreateWriteFailureMessage(Monitor monitor, VcpFeature feature, uint attemptedValue)
+ {
+ var baseMessage = $"Failed to set {feature.Name} to {attemptedValue} on monitor '{monitor.Name}'";
+ var suggestions = GetWriteFailureSuggestions(feature, attemptedValue);
+
+ return $"{baseMessage}. {suggestions}";
+ }
+
+ ///
+ /// Creates a detailed error message for unsupported VCP features
+ ///
+ /// The monitor that doesn't support the feature
+ /// The unsupported VCP feature
+ /// Detailed error message with alternatives
+ public static string CreateUnsupportedFeatureMessage(Monitor monitor, VcpFeature feature)
+ {
+ var baseMessage = $"Monitor '{monitor.Name}' doesn't support {feature.Name} control (VCP 0x{feature.Code:X2})";
+ var alternatives = GetFeatureAlternatives(feature);
+
+ return $"{baseMessage}. {alternatives}";
+ }
+
+ ///
+ /// Creates a detailed error message for VCP communication timeouts
+ ///
+ /// The monitor that timed out
+ /// The VCP feature that timed out
+ /// The operation that timed out (read/write)
+ /// Detailed error message with troubleshooting steps
+ public static string CreateTimeoutMessage(Monitor monitor, VcpFeature feature, string operation)
+ {
+ var baseMessage = $"Timeout while trying to {operation} {feature.Name} on monitor '{monitor.Name}'";
+ var troubleshooting = GetTimeoutTroubleshooting();
+
+ return $"{baseMessage}. {troubleshooting}";
+ }
+
+ ///
+ /// Creates a detailed error message for DDC/CI communication failures
+ ///
+ /// The monitor with communication issues
+ /// Detailed error message with troubleshooting steps
+ public static string CreateCommunicationFailureMessage(Monitor monitor)
+ {
+ var baseMessage = $"DDC/CI communication failed with monitor '{monitor.Name}'";
+ var troubleshooting = GetCommunicationTroubleshooting();
+
+ return $"{baseMessage}. {troubleshooting}";
+ }
+
+ ///
+ /// Creates a detailed error message for value range validation failures
+ ///
+ /// The VCP feature
+ /// The invalid value
+ /// The maximum allowed value
+ /// Whether the value is a percentage
+ /// Detailed error message with valid range information
+ public static string CreateRangeValidationMessage(VcpFeature feature, uint attemptedValue, uint maxValue, bool isPercentage = false)
+ {
+ if (isPercentage)
+ {
+ return $"{feature.Name} value {attemptedValue}% is out of range. Valid range: 0-100%";
+ }
+
+ var baseMessage = $"{feature.Name} value {attemptedValue} is out of range for this monitor";
+ var validRange = $"Valid range: 0-{maxValue}";
+ var suggestion = GetRangeValidationSuggestion(feature, maxValue);
+
+ return $"{baseMessage}. {validRange}. {suggestion}";
+ }
+
+ ///
+ /// Determines if a VCP operation failure is likely due to a timeout
+ ///
+ /// The last Win32 error code
+ /// True if the error indicates a timeout
+ public static bool IsTimeoutError(int lastError)
+ {
+ // Common timeout-related error codes
+ return lastError switch
+ {
+ 0x00000102 => true, // ERROR_TIMEOUT
+ 0x00000121 => true, // ERROR_SEM_TIMEOUT
+ 0x000005B4 => true, // ERROR_TIMEOUT (alternative)
+ 0x00000079 => true, // ERROR_SEM_TIMEOUT (alternative)
+ _ => false
+ };
+ }
+
+ ///
+ /// Determines if a VCP operation failure is likely due to unsupported feature
+ ///
+ /// The last Win32 error code
+ /// True if the error indicates unsupported feature
+ public static bool IsUnsupportedFeatureError(int lastError)
+ {
+ // Common unsupported feature error codes
+ return lastError switch
+ {
+ 0x00000001 => true, // ERROR_INVALID_FUNCTION
+ 0x00000057 => true, // ERROR_INVALID_PARAMETER
+ 0x0000007A => true, // ERROR_INSUFFICIENT_BUFFER
+ 0x00000032 => true, // ERROR_NOT_SUPPORTED
+ _ => false
+ };
+ }
+
+ ///
+ /// Gets suggestions for VCP read failures
+ ///
+ private static string GetReadFailureSuggestions(VcpFeature feature)
+ {
+ var suggestions = new List();
+
+ if (feature.Code == VcpFeature.Brightness.Code || feature.Code == VcpFeature.Contrast.Code)
+ {
+ suggestions.Add("Some monitors require administrator privileges for brightness/contrast control");
+ suggestions.Add("Try running as administrator or check if the monitor supports DDC/CI for this feature");
+ }
+ else if (feature.Code == VcpFeature.InputSource.Code)
+ {
+ suggestions.Add("Ensure the monitor supports DDC/CI input switching");
+ suggestions.Add("Some monitors only support input switching when not in use");
+ }
+ else
+ {
+ suggestions.Add($"VCP code 0x{feature.Code:X2} may not be supported by this monitor");
+ suggestions.Add("Use 'DDCSwitch list --scan' to see all supported VCP codes");
+ }
+
+ suggestions.Add("Check that the monitor is properly connected and powered on");
+
+ return string.Join(". ", suggestions);
+ }
+
+ ///
+ /// Gets suggestions for VCP write failures
+ ///
+ private static string GetWriteFailureSuggestions(VcpFeature feature, uint attemptedValue)
+ {
+ var suggestions = new List();
+
+ if (feature.Code == VcpFeature.Brightness.Code || feature.Code == VcpFeature.Contrast.Code)
+ {
+ suggestions.Add("Some monitors require administrator privileges for brightness/contrast control");
+ suggestions.Add("Ensure the value is within the monitor's supported range (use 'get' command to check current range)");
+ }
+ else if (feature.Code == VcpFeature.InputSource.Code)
+ {
+ suggestions.Add("Verify the input source is available on this monitor");
+ suggestions.Add("Some monitors only allow input switching when the input is not active");
+ }
+ else
+ {
+ suggestions.Add($"VCP code 0x{feature.Code:X2} may not support write operations on this monitor");
+ suggestions.Add("Use 'DDCSwitch list --scan' to check if this VCP code supports write operations");
+ }
+
+ suggestions.Add("Try running as administrator if permission issues persist");
+
+ return string.Join(". ", suggestions);
+ }
+
+ ///
+ /// Gets alternative features when a feature is unsupported
+ ///
+ private static string GetFeatureAlternatives(VcpFeature feature)
+ {
+ return feature.Code switch
+ {
+ 0x10 => "Try using your monitor's physical buttons or on-screen display (OSD) to adjust brightness",
+ 0x12 => "Try using your monitor's physical buttons or on-screen display (OSD) to adjust contrast",
+ 0x60 => "Try using your monitor's physical input selection button or check if the monitor supports other input switching methods",
+ _ => "Use 'DDCSwitch list --scan' to see all supported VCP codes for this monitor"
+ };
+ }
+
+ ///
+ /// Gets troubleshooting steps for timeout errors
+ ///
+ private static string GetTimeoutTroubleshooting()
+ {
+ var steps = new List
+ {
+ "The monitor may be busy or slow to respond",
+ "Try waiting a moment and running the command again",
+ "Check that no other DDC/CI applications are accessing the monitor",
+ "Ensure the monitor cable supports DDC/CI communication (some cheap cables don't)",
+ "Try power cycling the monitor if the issue persists"
+ };
+
+ return string.Join(". ", steps);
+ }
+
+ ///
+ /// Gets troubleshooting steps for general communication failures
+ ///
+ private static string GetCommunicationTroubleshooting()
+ {
+ var steps = new List
+ {
+ "Ensure the monitor supports DDC/CI (check monitor documentation)",
+ "Verify the video cable supports DDC/CI (HDMI, DisplayPort, DVI-D, or VGA with DDC support)",
+ "Try running as administrator - some monitors require elevated privileges",
+ "Check if DDC/CI is enabled in the monitor's on-screen display (OSD) settings",
+ "Power cycle the monitor and try again"
+ };
+
+ return string.Join(". ", steps);
+ }
+
+ ///
+ /// Gets suggestions for range validation failures
+ ///
+ private static string GetRangeValidationSuggestion(VcpFeature feature, uint maxValue)
+ {
+ if (feature.SupportsPercentage)
+ {
+ return "For percentage values, use 0-100% format (e.g., '75%')";
+ }
+
+ return feature.Code switch
+ {
+ 0x60 => "For input sources, use names like 'HDMI1', 'DP1', or hex codes like '0x11'",
+ _ => $"Use 'DDCSwitch get {feature.Name}' to see the current value and valid range"
+ };
+ }
+}
\ No newline at end of file
diff --git a/EXAMPLES.md b/EXAMPLES.md
index 4dd0e27..f05054c 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -1,6 +1,6 @@
# DDCSwitch Examples
-This document contains detailed examples and use cases for DDCSwitch.
+This document contains detailed examples and use cases for DDCSwitch, including input switching, brightness/contrast control, and raw VCP access.
## Basic Usage Examples
@@ -12,21 +12,192 @@ DDCSwitch list
This will show all your monitors and indicate which ones support DDC/CI control. Monitors with "OK" status can be controlled.
-### Get Current Input of Primary Monitor
+### Verbose Monitor Information
+
+Get detailed information including brightness and contrast:
```powershell
-# If primary monitor is index 0
+DDCSwitch list --verbose
+```
+
+This shows current brightness and contrast levels for each monitor (displays "N/A" for unsupported features).
+
+### Get Current Settings
+
+```powershell
+# Get all VCP features for primary monitor (scans all supported features)
DDCSwitch get 0
+
+# Get specific features
+DDCSwitch get 0 input # Current input source
+DDCSwitch get 0 brightness # Current brightness
+DDCSwitch get 0 contrast # Current contrast
+
+# Get raw VCP value
+DDCSwitch get 0 0x10 # Brightness (raw)
```
-### Switch Between Two Inputs Quickly
+### Set Monitor Settings
```powershell
-# Switch to console (HDMI)
+# Switch input
DDCSwitch set 0 HDMI1
-# Switch to PC (DisplayPort)
-DDCSwitch set 0 DP1
+# Set brightness to 75%
+DDCSwitch set 0 brightness 75%
+
+# Set contrast to 80%
+DDCSwitch set 0 contrast 80%
+
+# Set raw VCP value
+DDCSwitch set 0 0x10 120 # Brightness (raw value)
+```
+
+## Brightness and Contrast Control
+
+### Basic Brightness Control
+
+```powershell
+# Set brightness to specific percentage
+DDCSwitch set 0 brightness 50%
+DDCSwitch set 0 brightness 75%
+DDCSwitch set 0 brightness 100%
+
+# Get current brightness
+DDCSwitch get 0 brightness
+# Output: Monitor: Generic PnP Monitor / Brightness: 75% (120/160)
+```
+
+### Basic Contrast Control
+
+```powershell
+# Set contrast to specific percentage
+DDCSwitch set 0 contrast 60%
+DDCSwitch set 0 contrast 85%
+DDCSwitch set 0 contrast 100%
+
+# Get current contrast
+DDCSwitch get 0 contrast
+# Output: Monitor: Generic PnP Monitor / Contrast: 85% (136/160)
+```
+
+### Brightness Presets
+
+Create quick brightness presets:
+
+```powershell
+# brightness-low.ps1
+DDCSwitch set 0 brightness 25%
+Write-Host "Brightness set to 25% (Low)" -ForegroundColor Green
+
+# brightness-medium.ps1
+DDCSwitch set 0 brightness 50%
+Write-Host "Brightness set to 50% (Medium)" -ForegroundColor Green
+
+# brightness-high.ps1
+DDCSwitch set 0 brightness 75%
+Write-Host "Brightness set to 75% (High)" -ForegroundColor Green
+
+# brightness-max.ps1
+DDCSwitch set 0 brightness 100%
+Write-Host "Brightness set to 100% (Maximum)" -ForegroundColor Green
+```
+
+### Time-Based Brightness Control
+
+Automatically adjust brightness based on time of day:
+
+```powershell
+# auto-brightness.ps1
+$hour = (Get-Date).Hour
+
+if ($hour -ge 6 -and $hour -lt 9) {
+ # Morning: Medium brightness
+ DDCSwitch set 0 brightness 60%
+ Write-Host "Morning brightness: 60%" -ForegroundColor Yellow
+} elseif ($hour -ge 9 -and $hour -lt 18) {
+ # Daytime: High brightness
+ DDCSwitch set 0 brightness 85%
+ Write-Host "Daytime brightness: 85%" -ForegroundColor Green
+} elseif ($hour -ge 18 -and $hour -lt 22) {
+ # Evening: Medium brightness
+ DDCSwitch set 0 brightness 50%
+ Write-Host "Evening brightness: 50%" -ForegroundColor Orange
+} else {
+ # Night: Low brightness
+ DDCSwitch set 0 brightness 25%
+ Write-Host "Night brightness: 25%" -ForegroundColor Blue
+}
+```
+
+### Gaming vs Work Profiles
+
+Create different brightness/contrast profiles:
+
+```powershell
+# gaming-profile.ps1
+Write-Host "Activating Gaming Profile..." -ForegroundColor Cyan
+DDCSwitch set 0 input HDMI1 # Switch to console
+DDCSwitch set 0 brightness 90% # High brightness for gaming
+DDCSwitch set 0 contrast 85% # High contrast for visibility
+Write-Host "Gaming profile activated!" -ForegroundColor Green
+
+# work-profile.ps1
+Write-Host "Activating Work Profile..." -ForegroundColor Cyan
+DDCSwitch set 0 input DP1 # Switch to PC
+DDCSwitch set 0 brightness 60% # Comfortable brightness for long work
+DDCSwitch set 0 contrast 75% # Balanced contrast for text
+Write-Host "Work profile activated!" -ForegroundColor Green
+```
+
+## Raw VCP Access Examples
+
+### Discover VCP Features
+
+```powershell
+# Use verbose listing to see all supported VCP features
+DDCSwitch list --verbose
+```
+
+### Common VCP Codes
+
+```powershell
+# Brightness (VCP 0x10)
+DDCSwitch get 0 0x10
+DDCSwitch set 0 0x10 120
+
+# Contrast (VCP 0x12)
+DDCSwitch get 0 0x12
+DDCSwitch set 0 0x12 140
+
+# Input Source (VCP 0x60)
+DDCSwitch get 0 0x60
+DDCSwitch set 0 0x60 0x11 # HDMI1
+
+# Color Temperature (VCP 0x14) - if supported
+DDCSwitch get 0 0x14
+DDCSwitch set 0 0x14 6500 # 6500K
+```
+
+### Test Unknown VCP Codes
+
+```powershell
+# test-vcp-codes.ps1 - Discover what VCP codes your monitor supports
+Write-Host "Testing VCP codes for Monitor 0" -ForegroundColor Cyan
+
+$commonCodes = @(0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x20, 0x30, 0x60, 0x62, 0x6C, 0x6E, 0x70)
+
+foreach ($code in $commonCodes) {
+ $hexCode = "0x{0:X2}" -f $code
+ try {
+ $result = DDCSwitch get 0 $hexCode 2>$null
+ if ($result -notmatch "error|failed") {
+ Write-Host "✓ VCP $hexCode supported: $result" -ForegroundColor Green
+ }
+ } catch {
+ Write-Host "✗ VCP $hexCode not supported" -ForegroundColor Red
+ }
+}
```
## JSON Output and Automation
@@ -35,38 +206,55 @@ All DDCSwitch commands support the `--json` flag for machine-readable output. Th
### PowerShell JSON Examples
-#### Example 1: Conditional Input Switching
+#### Example 1: Conditional Input Switching with Brightness Control
-Check the current input and switch only if needed:
+Check the current input and switch only if needed, then adjust brightness:
```powershell
-# Check if monitor is on HDMI1, switch to DP1 if not
+# Check if monitor is on HDMI1, switch to DP1 if not, then set work brightness
$result = DDCSwitch get 0 --json | ConvertFrom-Json
if ($result.success -and $result.currentInputCode -ne "0x11") {
Write-Host "Monitor is on $($result.currentInput), switching to HDMI1..."
DDCSwitch set 0 HDMI1 --json | Out-Null
+ DDCSwitch set 0 brightness 75% --json | Out-Null
+ Write-Host "Switched to HDMI1 and set brightness to 75%" -ForegroundColor Green
} else {
Write-Host "Monitor already on HDMI1"
}
```
-#### Example 2: Switch All Monitors with Error Handling
+#### Example 2: Complete Monitor Setup with All Features
```powershell
-# Switch all available monitors to HDMI1 with error handling
+# Switch all available monitors with full configuration
$listResult = DDCSwitch list --json | ConvertFrom-Json
if ($listResult.success) {
foreach ($monitor in $listResult.monitors) {
if ($monitor.status -eq "ok") {
- Write-Host "Switching $($monitor.name) to HDMI1..."
- $setResult = DDCSwitch set $monitor.index HDMI1 --json | ConvertFrom-Json
+ Write-Host "Configuring $($monitor.name)..." -ForegroundColor Cyan
+ # Set input
+ $setResult = DDCSwitch set $monitor.index HDMI1 --json | ConvertFrom-Json
if ($setResult.success) {
- Write-Host "✓ Successfully switched $($monitor.name)" -ForegroundColor Green
+ Write-Host " ✓ Input: HDMI1" -ForegroundColor Green
+ }
+
+ # Set brightness
+ $brightnessResult = DDCSwitch set $monitor.index brightness 75% --json | ConvertFrom-Json
+ if ($brightnessResult.success) {
+ Write-Host " ✓ Brightness: 75%" -ForegroundColor Green
+ } else {
+ Write-Host " ✗ Brightness not supported" -ForegroundColor Yellow
+ }
+
+ # Set contrast
+ $contrastResult = DDCSwitch set $monitor.index contrast 80% --json | ConvertFrom-Json
+ if ($contrastResult.success) {
+ Write-Host " ✓ Contrast: 80%" -ForegroundColor Green
} else {
- Write-Host "✗ Failed: $($setResult.error)" -ForegroundColor Red
+ Write-Host " ✗ Contrast not supported" -ForegroundColor Yellow
}
}
}
@@ -75,13 +263,13 @@ if ($listResult.success) {
}
```
-#### Example 3: Monitor Status Dashboard
+#### Example 3: Monitor Status Dashboard with VCP Features
-Create a simple dashboard showing all monitor states:
+Create a comprehensive dashboard showing all monitor states:
```powershell
# monitor-dashboard.ps1
-$result = DDCSwitch list --json | ConvertFrom-Json
+$result = DDCSwitch list --verbose --json | ConvertFrom-Json
if ($result.success) {
Write-Host "`n=== Monitor Status Dashboard ===" -ForegroundColor Cyan
@@ -93,32 +281,55 @@ if ($result.success) {
Write-Host " Device: $($monitor.deviceName)"
Write-Host " Input: $($monitor.currentInput) ($($monitor.currentInputCode))"
Write-Host " Status: $($monitor.status.ToUpper())"
+
+ if ($monitor.brightness) {
+ Write-Host " Brightness: $($monitor.brightness)" -ForegroundColor Green
+ } else {
+ Write-Host " Brightness: N/A" -ForegroundColor Gray
+ }
+
+ if ($monitor.contrast) {
+ Write-Host " Contrast: $($monitor.contrast)" -ForegroundColor Green
+ } else {
+ Write-Host " Contrast: N/A" -ForegroundColor Gray
+ }
Write-Host ""
}
}
```
-#### Example 4: Toggle Between Two Inputs
+#### Example 4: Smart Brightness Toggle
```powershell
-# toggle-input.ps1
+# smart-brightness-toggle.ps1
param([int]$MonitorIndex = 0)
-$result = DDCSwitch get $MonitorIndex --json | ConvertFrom-Json
+$result = DDCSwitch get $MonitorIndex brightness --json | ConvertFrom-Json
if ($result.success) {
- $newInput = if ($result.currentInputCode -eq "0x11") { "DP1" } else { "HDMI1" }
- $switchResult = DDCSwitch set $MonitorIndex $newInput --json | ConvertFrom-Json
+ $currentPercent = $result.percentageValue
+
+ # Toggle between 25%, 50%, 75%, 100%
+ $newBrightness = switch ($currentPercent) {
+ {$_ -le 25} { 50 }
+ {$_ -le 50} { 75 }
+ {$_ -le 75} { 100 }
+ default { 25 }
+ }
+
+ $switchResult = DDCSwitch set $MonitorIndex brightness "$newBrightness%" --json | ConvertFrom-Json
if ($switchResult.success) {
- Write-Host "Toggled from $($result.currentInput) to $($switchResult.newInput)" -ForegroundColor Green
+ Write-Host "Brightness: $currentPercent% → $newBrightness%" -ForegroundColor Green
}
+} else {
+ Write-Host "Brightness control not supported on this monitor" -ForegroundColor Yellow
}
```
### Python JSON Examples
-#### Example 1: Simple Monitor Switcher
+#### Example 1: Monitor Control with Brightness/Contrast
```python
#!/usr/bin/env python3
@@ -136,16 +347,34 @@ def run_ddc(args):
return json.loads(result.stdout)
def list_monitors():
- """List all monitors"""
- data = run_ddc(['list'])
+ """List all monitors with brightness/contrast info"""
+ data = run_ddc(['list', '--verbose'])
if data['success']:
for monitor in data['monitors']:
primary = " [PRIMARY]" if monitor['isPrimary'] else ""
print(f"{monitor['index']}: {monitor['name']}{primary}")
- print(f" Current: {monitor['currentInput']} ({monitor['currentInputCode']})")
+ print(f" Input: {monitor['currentInput']} ({monitor['currentInputCode']})")
+ print(f" Brightness: {monitor.get('brightness', 'N/A')}")
+ print(f" Contrast: {monitor.get('contrast', 'N/A')}")
else:
print(f"Error: {data['error']}", file=sys.stderr)
+def set_brightness(monitor_index, percentage):
+ """Set monitor brightness"""
+ data = run_ddc(['set', str(monitor_index), 'brightness', f'{percentage}%'])
+ if data['success']:
+ print(f"✓ Set brightness to {percentage}% on {data['monitor']['name']}")
+ else:
+ print(f"✗ Error: {data['error']}", file=sys.stderr)
+
+def set_contrast(monitor_index, percentage):
+ """Set monitor contrast"""
+ data = run_ddc(['set', str(monitor_index), 'contrast', f'{percentage}%'])
+ if data['success']:
+ print(f"✓ Set contrast to {percentage}% on {data['monitor']['name']}")
+ else:
+ print(f"✗ Error: {data['error']}", file=sys.stderr)
+
def switch_input(monitor_index, input_name):
"""Switch monitor input"""
data = run_ddc(['set', str(monitor_index), input_name])
@@ -158,79 +387,101 @@ def switch_input(monitor_index, input_name):
# Example usage
if __name__ == '__main__':
list_monitors()
+ # set_brightness(0, 75)
+ # set_contrast(0, 80)
# switch_input(0, 'HDMI1')
```
-#### Example 2: Gaming Mode Automation
+#### Example 2: Automated Brightness Control
```python
#!/usr/bin/env python3
"""
-Automatically switch monitors based on running applications
-Usage: python gaming_mode.py
+Automatically adjust monitor brightness based on time of day
+Usage: python auto_brightness.py
"""
import subprocess
import json
import time
-import psutil
+from datetime import datetime
def run_ddc(args):
result = subprocess.run(['DDCSwitch'] + args + ['--json'],
capture_output=True, text=True)
return json.loads(result.stdout)
-def switch_to_gaming():
- """Switch all monitors to HDMI (console inputs)"""
- print("🎮 Activating gaming mode...")
+def set_brightness_all(percentage):
+ """Set brightness on all supported monitors"""
+ print(f"🔆 Setting brightness to {percentage}%...")
data = run_ddc(['list'])
if data['success']:
for monitor in data['monitors']:
if monitor['status'] == 'ok':
- result = run_ddc(['set', str(monitor['index']), 'HDMI1'])
+ result = run_ddc(['set', str(monitor['index']), 'brightness', f'{percentage}%'])
if result['success']:
- print(f" ✓ {monitor['name']} → HDMI1")
+ print(f" ✓ {monitor['name']} → {percentage}%")
+ else:
+ print(f" ✗ {monitor['name']} → Brightness not supported")
-def switch_to_work():
- """Switch all monitors to DisplayPort (PC inputs)"""
+def get_brightness_for_time():
+ """Get appropriate brightness based on current time"""
+ hour = datetime.now().hour
+
+ if 6 <= hour < 9: # Morning
+ return 60
+ elif 9 <= hour < 18: # Daytime
+ return 85
+ elif 18 <= hour < 22: # Evening
+ return 50
+ else: # Night
+ return 25
+
+def gaming_mode():
+ """Switch to gaming setup with high brightness"""
+ print("🎮 Activating gaming mode...")
+ data = run_ddc(['list'])
+
+ if data['success']:
+ for monitor in data['monitors']:
+ if monitor['status'] == 'ok':
+ # Switch to HDMI (console)
+ input_result = run_ddc(['set', str(monitor['index']), 'HDMI1'])
+ if input_result['success']:
+ print(f" ✓ {monitor['name']} → HDMI1")
+
+ # Set high brightness for gaming
+ brightness_result = run_ddc(['set', str(monitor['index']), 'brightness', '90%'])
+ if brightness_result['success']:
+ print(f" ✓ {monitor['name']} → 90% brightness")
+
+def work_mode():
+ """Switch to work setup with comfortable brightness"""
print("💼 Activating work mode...")
data = run_ddc(['list'])
if data['success']:
for monitor in data['monitors']:
if monitor['status'] == 'ok':
- result = run_ddc(['set', str(monitor['index']), 'DP1'])
- if result['success']:
+ # Switch to DisplayPort (PC)
+ input_result = run_ddc(['set', str(monitor['index']), 'DP1'])
+ if input_result['success']:
print(f" ✓ {monitor['name']} → DP1")
+
+ # Set comfortable brightness for work
+ brightness_result = run_ddc(['set', str(monitor['index']), 'brightness', '60%'])
+ if brightness_result['success']:
+ print(f" ✓ {monitor['name']} → 60% brightness")
-def is_game_running():
- """Check if specific games are running"""
- game_processes = ['steam.exe', 'EpicGamesLauncher.exe']
- for proc in psutil.process_iter(['name']):
- if proc.info['name'] in game_processes:
- return True
- return False
-
-# Monitor and switch automatically
+# Auto-brightness based on time
if __name__ == '__main__':
- previous_state = None
-
- while True:
- gaming = is_game_running()
-
- if gaming != previous_state:
- if gaming:
- switch_to_gaming()
- else:
- switch_to_work()
- previous_state = gaming
-
- time.sleep(10) # Check every 10 seconds
+ brightness = get_brightness_for_time()
+ set_brightness_all(brightness)
```
### Node.js JSON Examples
-#### Example 1: Monitor Information API
+#### Example 1: Monitor Control API with VCP Features
```javascript
// monitor-api.js
@@ -244,38 +495,67 @@ class DDCSwitch {
return JSON.parse(output);
}
- static listMonitors() {
- return this.exec(['list']);
+ static listMonitors(verbose = false) {
+ const args = verbose ? ['list', '--verbose'] : ['list'];
+ return this.exec(args);
}
static getCurrentInput(monitorIndex) {
return this.exec(['get', monitorIndex]);
}
+ static getBrightness(monitorIndex) {
+ return this.exec(['get', monitorIndex, 'brightness']);
+ }
+
+ static getContrast(monitorIndex) {
+ return this.exec(['get', monitorIndex, 'contrast']);
+ }
+
static setInput(monitorIndex, input) {
return this.exec(['set', monitorIndex, input]);
}
+
+ static setBrightness(monitorIndex, percentage) {
+ return this.exec(['set', monitorIndex, 'brightness', `${percentage}%`]);
+ }
+
+ static setContrast(monitorIndex, percentage) {
+ return this.exec(['set', monitorIndex, 'contrast', `${percentage}%`]);
+ }
+
+ static setRawVcp(monitorIndex, vcpCode, value) {
+ return this.exec(['set', monitorIndex, vcpCode, value]);
+ }
}
// Usage example
-const monitors = DDCSwitch.listMonitors();
+const monitors = DDCSwitch.listMonitors(true);
console.log(`Found ${monitors.monitors.length} monitors`);
monitors.monitors.forEach(monitor => {
- console.log(`${monitor.index}: ${monitor.name} - ${monitor.currentInput}`);
+ console.log(`${monitor.index}: ${monitor.name}`);
+ console.log(` Input: ${monitor.currentInput}`);
+ console.log(` Brightness: ${monitor.brightness || 'N/A'}`);
+ console.log(` Contrast: ${monitor.contrast || 'N/A'}`);
});
-// Switch first monitor to HDMI1
-const result = DDCSwitch.setInput(0, 'HDMI1');
-if (result.success) {
- console.log(`✓ Switched to ${result.newInput}`);
+// Set brightness and contrast
+const brightnessResult = DDCSwitch.setBrightness(0, 75);
+if (brightnessResult.success) {
+ console.log(`✓ Set brightness to 75%`);
+}
+
+const contrastResult = DDCSwitch.setContrast(0, 80);
+if (contrastResult.success) {
+ console.log(`✓ Set contrast to 80%`);
}
```
-#### Example 2: Express.js REST API
+#### Example 2: Express.js REST API with VCP Support
```javascript
-// server.js - Web API for monitor control
+// server.js - Web API for complete monitor control
const express = require('express');
const { execSync } = require('child_process');
@@ -293,18 +573,32 @@ function runDDC(args) {
}
}
-// GET /monitors - List all monitors
+// GET /monitors - List all monitors (with verbose option)
app.get('/monitors', (req, res) => {
- const result = runDDC(['list']);
+ const verbose = req.query.verbose === 'true';
+ const args = verbose ? ['list', '--verbose'] : ['list'];
+ const result = runDDC(args);
res.json(result);
});
-// GET /monitors/:id - Get specific monitor
+// GET /monitors/:id - Get specific monitor info
app.get('/monitors/:id', (req, res) => {
const result = runDDC(['get', req.params.id]);
res.json(result);
});
+// GET /monitors/:id/brightness - Get brightness
+app.get('/monitors/:id/brightness', (req, res) => {
+ const result = runDDC(['get', req.params.id, 'brightness']);
+ res.json(result);
+});
+
+// GET /monitors/:id/contrast - Get contrast
+app.get('/monitors/:id/contrast', (req, res) => {
+ const result = runDDC(['get', req.params.id, 'contrast']);
+ res.json(result);
+});
+
// POST /monitors/:id/input - Set monitor input
app.post('/monitors/:id/input', (req, res) => {
const { input } = req.body;
@@ -312,8 +606,52 @@ app.post('/monitors/:id/input', (req, res) => {
res.json(result);
});
+// POST /monitors/:id/brightness - Set brightness
+app.post('/monitors/:id/brightness', (req, res) => {
+ const { percentage } = req.body;
+ const result = runDDC(['set', req.params.id, 'brightness', `${percentage}%`]);
+ res.json(result);
+});
+
+// POST /monitors/:id/contrast - Set contrast
+app.post('/monitors/:id/contrast', (req, res) => {
+ const { percentage } = req.body;
+ const result = runDDC(['set', req.params.id, 'contrast', `${percentage}%`]);
+ res.json(result);
+});
+
+// POST /monitors/:id/vcp - Set raw VCP value
+app.post('/monitors/:id/vcp', (req, res) => {
+ const { code, value } = req.body;
+ const result = runDDC(['set', req.params.id, code, value]);
+ res.json(result);
+});
+
+// POST /monitors/:id/profile - Apply complete profile
+app.post('/monitors/:id/profile', (req, res) => {
+ const { input, brightness, contrast } = req.body;
+ const results = {};
+
+ if (input) {
+ results.input = runDDC(['set', req.params.id, input]);
+ }
+ if (brightness) {
+ results.brightness = runDDC(['set', req.params.id, 'brightness', `${brightness}%`]);
+ }
+ if (contrast) {
+ results.contrast = runDDC(['set', req.params.id, 'contrast', `${contrast}%`]);
+ }
+
+ res.json({ success: true, results });
+});
+
app.listen(3000, () => {
console.log('DDCSwitch API running on http://localhost:3000');
+ console.log('Endpoints:');
+ console.log(' GET /monitors?verbose=true');
+ console.log(' GET /monitors/:id/brightness');
+ console.log(' POST /monitors/:id/brightness {"percentage": 75}');
+ console.log(' POST /monitors/:id/profile {"input": "HDMI1", "brightness": 75, "contrast": 80}');
});
```
@@ -321,24 +659,45 @@ app.listen(3000, () => {
```batch
@echo off
-REM check-and-switch.bat - Switch only if not already on target input
+REM complete-setup.bat - Set input, brightness, and contrast
-for /f "delims=" %%i in ('DDCSwitch get 0 --json') do set JSON_OUTPUT=%%i
+echo Setting up monitor configuration...
-REM Simple check if contains HDMI1
-echo %JSON_OUTPUT% | find "0x11" >nul
-if errorlevel 1 (
- echo Switching to HDMI1...
- DDCSwitch set 0 HDMI1
+REM Switch to HDMI1
+for /f "delims=" %%i in ('DDCSwitch set 0 HDMI1 --json') do set INPUT_RESULT=%%i
+echo %INPUT_RESULT% | find "\"success\":true" >nul
+if not errorlevel 1 (
+ echo ✓ Switched to HDMI1
+) else (
+ echo ✗ Failed to switch input
+)
+
+REM Set brightness to 75%
+for /f "delims=" %%i in ('DDCSwitch set 0 brightness 75%% --json') do set BRIGHTNESS_RESULT=%%i
+echo %BRIGHTNESS_RESULT% | find "\"success\":true" >nul
+if not errorlevel 1 (
+ echo ✓ Set brightness to 75%%
+) else (
+ echo ✗ Brightness not supported or failed
+)
+
+REM Set contrast to 80%
+for /f "delims=" %%i in ('DDCSwitch set 0 contrast 80%% --json') do set CONTRAST_RESULT=%%i
+echo %CONTRAST_RESULT% | find "\"success\":true" >nul
+if not errorlevel 1 (
+ echo ✓ Set contrast to 80%%
) else (
- echo Already on HDMI1
+ echo ✗ Contrast not supported or failed
)
+
+echo Monitor setup complete!
+pause
```
### Rust JSON Example
```rust
-// monitor_switcher.rs
+// monitor_controller.rs
use serde::{Deserialize, Serialize};
use std::process::Command;
@@ -354,6 +713,8 @@ struct MonitorInfo {
current_input: Option,
#[serde(rename = "currentInputCode")]
current_input_code: Option,
+ brightness: Option,
+ contrast: Option,
status: String,
}
@@ -364,26 +725,103 @@ struct ListResponse {
error: Option,
}
-fn main() {
+#[derive(Debug, Deserialize)]
+struct SetResponse {
+ success: bool,
+ #[serde(rename = "percentageValue")]
+ percentage_value: Option,
+ #[serde(rename = "rawValue")]
+ raw_value: Option,
+ error: Option,
+}
+
+fn run_ddc_command(args: &[&str]) -> Result> {
+ let mut cmd_args = args.to_vec();
+ cmd_args.push("--json");
+
let output = Command::new("DDCSwitch")
- .args(&["list", "--json"])
- .output()
- .expect("Failed to execute DDCSwitch");
+ .args(&cmd_args)
+ .output()?;
+
+ Ok(String::from_utf8_lossy(&output.stdout).to_string())
+}
- let json_str = String::from_utf8_lossy(&output.stdout);
- let result: ListResponse = serde_json::from_str(&json_str)
- .expect("Failed to parse JSON");
+fn list_monitors(verbose: bool) -> Result> {
+ let args = if verbose {
+ vec!["list", "--verbose"]
+ } else {
+ vec!["list"]
+ };
+
+ let json_str = run_ddc_command(&args)?;
+ let result: ListResponse = serde_json::from_str(&json_str)?;
+ Ok(result)
+}
+
+fn set_brightness(monitor_index: u32, percentage: u32) -> Result> {
+ let args = vec!["set", &monitor_index.to_string(), "brightness", &format!("{}%", percentage)];
+ let json_str = run_ddc_command(&args)?;
+ let result: SetResponse = serde_json::from_str(&json_str)?;
+ Ok(result)
+}
+
+fn set_contrast(monitor_index: u32, percentage: u32) -> Result> {
+ let args = vec!["set", &monitor_index.to_string(), "contrast", &format!("{}%", percentage)];
+ let json_str = run_ddc_command(&args)?;
+ let result: SetResponse = serde_json::from_str(&json_str)?;
+ Ok(result)
+}
+
+fn main() -> Result<(), Box> {
+ // List monitors with verbose info
+ let result = list_monitors(true)?;
if result.success {
if let Some(monitors) = result.monitors {
- for monitor in monitors {
- println!("{}: {} - {:?}",
+ println!("Found {} monitors:", monitors.len());
+ for monitor in &monitors {
+ println!("{}: {} - Input: {:?}",
monitor.index,
monitor.name,
monitor.current_input);
+ println!(" Brightness: {:?}", monitor.brightness);
+ println!(" Contrast: {:?}", monitor.contrast);
+ }
+
+ // Set brightness and contrast on first monitor
+ if !monitors.is_empty() {
+ let monitor_index = monitors[0].index;
+
+ match set_brightness(monitor_index, 75) {
+ Ok(response) if response.success => {
+ println!("✓ Set brightness to 75%");
+ }
+ Ok(response) => {
+ println!("✗ Failed to set brightness: {:?}", response.error);
+ }
+ Err(e) => {
+ println!("✗ Error setting brightness: {}", e);
+ }
+ }
+
+ match set_contrast(monitor_index, 80) {
+ Ok(response) if response.success => {
+ println!("✓ Set contrast to 80%");
+ }
+ Ok(response) => {
+ println!("✗ Failed to set contrast: {:?}", response.error);
+ }
+ Err(e) => {
+ println!("✗ Error setting contrast: {}", e);
+ }
+ }
}
}
+ } else {
+ println!("Error: {:?}", result.error);
}
+
+ Ok(())
}
```
@@ -392,34 +830,53 @@ fn main() {
### Multi-Monitor Setup Scripts
#### Scenario: Work Setup
-Switch all monitors to PC inputs:
+Switch all monitors to PC inputs with comfortable brightness:
```powershell
# work-setup.ps1
Write-Host "Switching to work setup..." -ForegroundColor Cyan
DDCSwitch set 0 DP1
+DDCSwitch set 0 brightness 60%
+DDCSwitch set 0 contrast 75%
DDCSwitch set 1 DP2
-DDCSwitch set 2 HDMI1
+DDCSwitch set 1 brightness 60%
+DDCSwitch set 1 contrast 75%
Write-Host "Work setup ready!" -ForegroundColor Green
```
#### Scenario: Gaming Setup
-Switch monitors to console inputs:
+Switch monitors to console inputs with high brightness:
```powershell
# gaming-setup.ps1
Write-Host "Switching to gaming setup..." -ForegroundColor Cyan
DDCSwitch set 0 HDMI1 # Main monitor to PS5
+DDCSwitch set 0 brightness 90%
+DDCSwitch set 0 contrast 85%
DDCSwitch set 1 HDMI2 # Secondary to Switch
+DDCSwitch set 1 brightness 85%
+DDCSwitch set 1 contrast 80%
Write-Host "Gaming setup ready!" -ForegroundColor Green
```
+#### Scenario: Movie/Media Setup
+Optimize for media consumption:
+
+```powershell
+# media-setup.ps1
+Write-Host "Switching to media setup..." -ForegroundColor Cyan
+DDCSwitch set 0 HDMI1 # Media device
+DDCSwitch set 0 brightness 40% # Lower brightness for comfortable viewing
+DDCSwitch set 0 contrast 90% # High contrast for better blacks
+Write-Host "Media setup ready!" -ForegroundColor Green
+```
+
### AutoHotkey Integration
-Create a comprehensive input switching system:
+Create a comprehensive input switching and brightness control system:
```autohotkey
-; DDCSwitch AutoHotkey Script
+; DDCSwitch AutoHotkey Script with VCP Support
; Place DDCSwitch.exe in C:\Tools\ or update path below
; Global variables
@@ -431,6 +888,7 @@ RunDDCSwitch(args) {
Run, %DDCSwitchPath% %args%, , Hide
}
+; Input switching hotkeys
; Ctrl+Alt+1: Switch monitor 0 to HDMI1
^!1::
RunDDCSwitch("set 0 HDMI1")
@@ -449,48 +907,133 @@ RunDDCSwitch(args) {
TrayTip, DDCSwitch, Switched to DisplayPort, 1
return
-; Ctrl+Alt+W: Work setup (all monitors to PC)
+; Brightness control hotkeys
+; Ctrl+Alt+Plus: Increase brightness by 10%
+^!NumpadAdd::
+^!=::
+ RunDDCSwitch("set 0 brightness +10%")
+ TrayTip, DDCSwitch, Brightness increased, 1
+ return
+
+; Ctrl+Alt+Minus: Decrease brightness by 10%
+^!NumpadSub::
+^!-::
+ RunDDCSwitch("set 0 brightness -10%")
+ TrayTip, DDCSwitch, Brightness decreased, 1
+ return
+
+; Brightness presets
+; Ctrl+Alt+F1: 25% brightness (night mode)
+^!F1::
+ RunDDCSwitch("set 0 brightness 25%")
+ TrayTip, DDCSwitch, Night Mode (25%), 1
+ return
+
+; Ctrl+Alt+F2: 50% brightness (comfortable)
+^!F2::
+ RunDDCSwitch("set 0 brightness 50%")
+ TrayTip, DDCSwitch, Comfortable (50%), 1
+ return
+
+; Ctrl+Alt+F3: 75% brightness (bright)
+^!F3::
+ RunDDCSwitch("set 0 brightness 75%")
+ TrayTip, DDCSwitch, Bright (75%), 1
+ return
+
+; Ctrl+Alt+F4: 100% brightness (maximum)
+^!F4::
+ RunDDCSwitch("set 0 brightness 100%")
+ TrayTip, DDCSwitch, Maximum (100%), 1
+ return
+
+; Profile hotkeys
+; Ctrl+Alt+W: Work setup (all monitors to PC with comfortable settings)
^!w::
RunDDCSwitch("set 0 DP1")
Sleep 500
+ RunDDCSwitch("set 0 brightness 60%")
+ Sleep 500
+ RunDDCSwitch("set 0 contrast 75%")
+ Sleep 500
RunDDCSwitch("set 1 DP2")
TrayTip, DDCSwitch, Work Setup Activated, 1
return
-; Ctrl+Alt+G: Gaming setup (all monitors to console)
+; Ctrl+Alt+G: Gaming setup (all monitors to console with high brightness)
^!g::
RunDDCSwitch("set 0 HDMI1")
Sleep 500
+ RunDDCSwitch("set 0 brightness 90%")
+ Sleep 500
+ RunDDCSwitch("set 0 contrast 85%")
+ Sleep 500
RunDDCSwitch("set 1 HDMI2")
TrayTip, DDCSwitch, Gaming Setup Activated, 1
return
-; Ctrl+Alt+L: List all monitors
+; Ctrl+Alt+M: Media setup (HDMI with low brightness, high contrast)
+^!m::
+ RunDDCSwitch("set 0 HDMI1")
+ Sleep 500
+ RunDDCSwitch("set 0 brightness 40%")
+ Sleep 500
+ RunDDCSwitch("set 0 contrast 90%")
+ TrayTip, DDCSwitch, Media Setup Activated, 1
+ return
+
+; Ctrl+Alt+L: List all monitors with verbose info
^!l::
- Run, cmd /k DDCSwitch.exe list
+ Run, cmd /k DDCSwitch.exe list --verbose
+ return
+
+; Ctrl+Alt+I: Show current monitor info
+^!i::
+ Run, cmd /k "DDCSwitch.exe get 0 && DDCSwitch.exe get 0 brightness && DDCSwitch.exe get 0 contrast && pause"
return
```
### Stream Deck Integration
-If you use Elgato Stream Deck, create a "System" action with these commands:
+If you use Elgato Stream Deck, create actions for complete monitor control:
-**Button 1: PC Input**
+**Button 1: PC Mode**
```
Title: PC Mode
Command: C:\Tools\DDCSwitch.exe set 0 DP1
+Arguments: && C:\Tools\DDCSwitch.exe set 0 brightness 60%
```
-**Button 2: Console Input**
+**Button 2: Console Mode**
```
Title: Console Mode
Command: C:\Tools\DDCSwitch.exe set 0 HDMI1
+Arguments: && C:\Tools\DDCSwitch.exe set 0 brightness 90%
```
-**Button 3: List Monitors**
+**Button 3: Brightness Low**
+```
+Title: 🔅 Low
+Command: C:\Tools\DDCSwitch.exe set 0 brightness 25%
+```
+
+**Button 4: Brightness High**
+```
+Title: 🔆 High
+Command: C:\Tools\DDCSwitch.exe set 0 brightness 85%
+```
+
+**Button 5: Monitor Info**
```
Title: Monitor Info
-Command: cmd /k C:\Tools\DDCSwitch.exe list
+Command: cmd /k C:\Tools\DDCSwitch.exe list --verbose
+```
+
+**Button 6: Gaming Profile**
+```
+Title: 🎮 Gaming
+Command: C:\Tools\DDCSwitch.exe set 0 HDMI1
+Arguments: && timeout /t 1 && C:\Tools\DDCSwitch.exe set 0 brightness 90% && C:\Tools\DDCSwitch.exe set 0 contrast 85%
```
### Task Scheduler Integration
@@ -515,51 +1058,212 @@ Same steps, but with trigger at 6:00 PM and arguments: `set 0 HDMI1`
Add to your PowerShell profile (`$PROFILE`):
```powershell
-# DDCSwitch aliases
-function ddc-list { DDCSwitch list }
+# DDCSwitch aliases for complete monitor control
+function ddc-list { DDCSwitch list --verbose }
function ddc-work {
DDCSwitch set 0 DP1
+ DDCSwitch set 0 brightness 60%
+ DDCSwitch set 0 contrast 75%
DDCSwitch set 1 DP2
Write-Host "✓ Work setup activated" -ForegroundColor Green
}
function ddc-game {
DDCSwitch set 0 HDMI1
+ DDCSwitch set 0 brightness 90%
+ DDCSwitch set 0 contrast 85%
DDCSwitch set 1 HDMI2
Write-Host "✓ Gaming setup activated" -ForegroundColor Green
}
+function ddc-media {
+ DDCSwitch set 0 HDMI1
+ DDCSwitch set 0 brightness 40%
+ DDCSwitch set 0 contrast 90%
+ Write-Host "✓ Media setup activated" -ForegroundColor Green
+}
function ddc-hdmi { DDCSwitch set 0 HDMI1 }
function ddc-dp { DDCSwitch set 0 DP1 }
+function ddc-bright([int]$level) { DDCSwitch set 0 brightness "$level%" }
+function ddc-contrast([int]$level) { DDCSwitch set 0 contrast "$level%" }
+
+# Brightness shortcuts
+function ddc-dim { DDCSwitch set 0 brightness 25% }
+function ddc-normal { DDCSwitch set 0 brightness 60% }
+function ddc-bright { DDCSwitch set 0 brightness 85% }
+function ddc-max { DDCSwitch set 0 brightness 100% }
-# Then use: ddc-work, ddc-game, ddc-hdmi, ddc-dp, ddc-list
+# Then use: ddc-work, ddc-game, ddc-media, ddc-bright 75, ddc-list
```
-### KVM Switch Replacement
+### Complete Monitor Control
-Use DDCSwitch as a software KVM (without USB switching):
+Use DDCSwitch as a comprehensive monitor management solution:
```powershell
-# kvm-to-pc1.ps1
-DDCSwitch set 0 DP1
-DDCSwitch set 1 DP1
-Write-Host "Switched all monitors to PC1" -ForegroundColor Green
+# complete-monitor-control.ps1
+param(
+ [string]$Profile = "work", # work, gaming, media, custom
+ [int]$Monitor = 0,
+ [string]$Input,
+ [int]$Brightness,
+ [int]$Contrast
+)
-# kvm-to-pc2.ps1
-DDCSwitch set 0 DP2
-DDCSwitch set 1 DP2
-Write-Host "Switched all monitors to PC2" -ForegroundColor Green
+function Apply-Profile {
+ param($ProfileName, $MonitorIndex)
+
+ switch ($ProfileName.ToLower()) {
+ "work" {
+ DDCSwitch set $MonitorIndex DP1
+ DDCSwitch set $MonitorIndex brightness 60%
+ DDCSwitch set $MonitorIndex contrast 75%
+ Write-Host "✓ Applied work profile" -ForegroundColor Green
+ }
+ "gaming" {
+ DDCSwitch set $MonitorIndex HDMI1
+ DDCSwitch set $MonitorIndex brightness 90%
+ DDCSwitch set $MonitorIndex contrast 85%
+ Write-Host "✓ Applied gaming profile" -ForegroundColor Green
+ }
+ "media" {
+ DDCSwitch set $MonitorIndex HDMI1
+ DDCSwitch set $MonitorIndex brightness 40%
+ DDCSwitch set $MonitorIndex contrast 90%
+ Write-Host "✓ Applied media profile" -ForegroundColor Green
+ }
+ "custom" {
+ if ($Input) { DDCSwitch set $MonitorIndex $Input }
+ if ($Brightness) { DDCSwitch set $MonitorIndex brightness "$Brightness%" }
+ if ($Contrast) { DDCSwitch set $MonitorIndex contrast "$Contrast%" }
+ Write-Host "✓ Applied custom settings" -ForegroundColor Green
+ }
+ }
+}
+
+Apply-Profile -ProfileName $Profile -MonitorIndex $Monitor
+
+# Usage examples:
+# .\complete-monitor-control.ps1 -Profile work
+# .\complete-monitor-control.ps1 -Profile gaming -Monitor 1
+# .\complete-monitor-control.ps1 -Profile custom -Input HDMI2 -Brightness 75 -Contrast 80
```
-### Testing Monitor Compatibility
+### Testing Monitor VCP Support
-If your monitor doesn't respond to standard codes, try discovering the correct codes:
+Test what VCP features your monitor supports:
```powershell
-# Test different input codes
+# test-vcp-support.ps1 - Test brightness, contrast, and other VCP features
$monitor = 0
-foreach ($code in 0x01, 0x03, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15) {
- Write-Host "Testing code 0x$($code.ToString('X2'))..."
- DDCSwitch set $monitor "0x$($code.ToString('X2'))"
- Start-Sleep -Seconds 3
+
+Write-Host "Testing VCP feature support for Monitor $monitor" -ForegroundColor Cyan
+Write-Host "=" * 50
+
+# Test brightness support
+Write-Host "`nTesting Brightness (VCP 0x10)..." -ForegroundColor Yellow
+try {
+ $brightness = DDCSwitch get $monitor brightness 2>$null
+ if ($brightness -match "Brightness:") {
+ Write-Host "✓ Brightness supported: $brightness" -ForegroundColor Green
+
+ # Test setting brightness
+ DDCSwitch set $monitor brightness 50% | Out-Null
+ Start-Sleep -Seconds 1
+ $newBrightness = DDCSwitch get $monitor brightness
+ Write-Host "✓ Brightness control works: $newBrightness" -ForegroundColor Green
+ } else {
+ Write-Host "✗ Brightness not supported" -ForegroundColor Red
+ }
+} catch {
+ Write-Host "✗ Brightness not supported" -ForegroundColor Red
+}
+
+# Test contrast support
+Write-Host "`nTesting Contrast (VCP 0x12)..." -ForegroundColor Yellow
+try {
+ $contrast = DDCSwitch get $monitor contrast 2>$null
+ if ($contrast -match "Contrast:") {
+ Write-Host "✓ Contrast supported: $contrast" -ForegroundColor Green
+
+ # Test setting contrast
+ DDCSwitch set $monitor contrast 75% | Out-Null
+ Start-Sleep -Seconds 1
+ $newContrast = DDCSwitch get $monitor contrast
+ Write-Host "✓ Contrast control works: $newContrast" -ForegroundColor Green
+ } else {
+ Write-Host "✗ Contrast not supported" -ForegroundColor Red
+ }
+} catch {
+ Write-Host "✗ Contrast not supported" -ForegroundColor Red
+}
+
+# Test raw VCP codes
+Write-Host "`nTesting Raw VCP Codes..." -ForegroundColor Yellow
+$vcpCodes = @{
+ "0x10" = "Brightness"
+ "0x12" = "Contrast"
+ "0x14" = "Color Temperature"
+ "0x16" = "Red Gain"
+ "0x18" = "Green Gain"
+ "0x1A" = "Blue Gain"
+ "0x60" = "Input Source"
+ "0x62" = "Audio Volume"
+ "0x6C" = "Red Black Level"
+ "0x6E" = "Green Black Level"
+ "0x70" = "Blue Black Level"
+}
+
+foreach ($code in $vcpCodes.Keys) {
+ try {
+ $result = DDCSwitch get $monitor $code 2>$null
+ if ($result -and $result -notmatch "error|failed|not supported") {
+ Write-Host "✓ VCP $code ($($vcpCodes[$code])): $result" -ForegroundColor Green
+ } else {
+ Write-Host "✗ VCP $code ($($vcpCodes[$code])): Not supported" -ForegroundColor Gray
+ }
+ } catch {
+ Write-Host "✗ VCP $code ($($vcpCodes[$code])): Not supported" -ForegroundColor Gray
+ }
+}
+
+Write-Host "`nVCP Support Test Complete!" -ForegroundColor Cyan
+```
+
+### Finding Optimal Settings
+
+Find the best brightness and contrast settings for different scenarios:
+
+```powershell
+# find-optimal-settings.ps1
+Write-Host "Monitor Calibration Helper" -ForegroundColor Cyan
+Write-Host "This script will cycle through different brightness/contrast combinations"
+Write-Host "Press any key after each setting to continue, or Ctrl+C to stop"
+Write-Host ""
+
+$monitor = 0
+$brightnessLevels = @(25, 40, 50, 60, 75, 85, 100)
+$contrastLevels = @(60, 70, 75, 80, 85, 90, 95)
+
+foreach ($brightness in $brightnessLevels) {
+ foreach ($contrast in $contrastLevels) {
+ Write-Host "Setting: Brightness $brightness%, Contrast $contrast%" -ForegroundColor Yellow
+
+ DDCSwitch set $monitor brightness "$brightness%" | Out-Null
+ Start-Sleep -Milliseconds 500
+ DDCSwitch set $monitor contrast "$contrast%" | Out-Null
+
+ Write-Host "How does this look? (Press Enter to continue, 'q' to quit, 's' to save this setting)" -ForegroundColor Green
+ $input = Read-Host
+
+ if ($input -eq 'q') {
+ Write-Host "Calibration stopped." -ForegroundColor Red
+ break
+ } elseif ($input -eq 's') {
+ Write-Host "Saved setting: Brightness $brightness%, Contrast $contrast%" -ForegroundColor Cyan
+ Write-Host "Command to reproduce: DDCSwitch set $monitor brightness $brightness%; DDCSwitch set $monitor contrast $contrast%" -ForegroundColor White
+ Read-Host "Press Enter to continue or Ctrl+C to stop"
+ }
+ }
+ if ($input -eq 'q') { break }
}
```
@@ -700,43 +1404,83 @@ Make them double-clickable for quick access!
## Common Patterns
-### Pattern 1: Toggle Between Two Inputs
+### Pattern 1: Toggle Between Settings
```powershell
-# toggle-input.ps1
-$current = (DDCSwitch get 0 | Select-String -Pattern "0x([0-9A-F]{2})").Matches[0].Groups[1].Value
-$currentDecimal = [Convert]::ToInt32($current, 16)
+# toggle-brightness.ps1 - Toggle between low/medium/high brightness
+$current = DDCSwitch get 0 brightness --json | ConvertFrom-Json
-if ($currentDecimal -eq 0x11) { # Currently HDMI1
- DDCSwitch set 0 DP1
- Write-Host "Switched to DisplayPort"
+if ($current.success) {
+ $currentPercent = $current.percentageValue
+
+ # Cycle through 25% → 50% → 75% → 100% → 25%
+ $newBrightness = switch ($currentPercent) {
+ {$_ -le 25} { 50 }
+ {$_ -le 50} { 75 }
+ {$_ -le 75} { 100 }
+ default { 25 }
+ }
+
+ DDCSwitch set 0 brightness "$newBrightness%"
+ Write-Host "Brightness: $currentPercent% → $newBrightness%" -ForegroundColor Green
} else {
- DDCSwitch set 0 HDMI1
- Write-Host "Switched to HDMI1"
+ Write-Host "Brightness control not supported" -ForegroundColor Red
}
```
-### Pattern 2: Check Before Switch
+### Pattern 2: Check Before Set
```powershell
-# smart-switch.ps1
-param([string]$Input = "HDMI1")
+# smart-profile-switch.ps1
+param([string]$Profile = "work")
+
+# Get current settings
+$inputResult = DDCSwitch get 0 --json | ConvertFrom-Json
+$brightnessResult = DDCSwitch get 0 brightness --json | ConvertFrom-Json
-$monitors = DDCSwitch list
-if ($monitors -match "No DDC/CI") {
- Write-Error "No compatible monitors found"
+if (-not $inputResult.success) {
+ Write-Error "Monitor not accessible"
exit 1
}
-DDCSwitch set 0 $Input
-Write-Host "Successfully switched to $Input" -ForegroundColor Green
+# Define profiles
+$profiles = @{
+ "work" = @{ input = "DP1"; brightness = 60; contrast = 75 }
+ "gaming" = @{ input = "HDMI1"; brightness = 90; contrast = 85 }
+ "media" = @{ input = "HDMI1"; brightness = 40; contrast = 90 }
+}
+
+if (-not $profiles.ContainsKey($Profile)) {
+ Write-Error "Unknown profile: $Profile. Available: work, gaming, media"
+ exit 1
+}
+
+$targetProfile = $profiles[$Profile]
+
+# Apply settings only if different
+if ($inputResult.currentInputCode -ne $targetProfile.input) {
+ DDCSwitch set 0 $targetProfile.input
+ Write-Host "✓ Input: $($targetProfile.input)" -ForegroundColor Green
+}
+
+if ($brightnessResult.success -and $brightnessResult.percentageValue -ne $targetProfile.brightness) {
+ DDCSwitch set 0 brightness "$($targetProfile.brightness)%"
+ Write-Host "✓ Brightness: $($targetProfile.brightness)%" -ForegroundColor Green
+}
+
+DDCSwitch set 0 contrast "$($targetProfile.contrast)%"
+Write-Host "✓ Profile '$Profile' applied" -ForegroundColor Cyan
```
-### Pattern 3: Switch All Monitors to Same Input
+### Pattern 3: Sync All Monitors
```powershell
-# sync-all.ps1 - Using JSON for reliable monitor enumeration
-param([string]$Input = "HDMI1")
+# sync-all-monitors.ps1 - Apply same settings to all monitors
+param(
+ [string]$Input = "HDMI1",
+ [int]$Brightness = 75,
+ [int]$Contrast = 80
+)
$result = DDCSwitch list --json | ConvertFrom-Json
@@ -748,30 +1492,37 @@ if (-not $result.success) {
$okMonitors = $result.monitors | Where-Object { $_.status -eq "ok" }
foreach ($monitor in $okMonitors) {
- Write-Host "Switching monitor $($monitor.index) ($($monitor.name)) to $Input..."
- DDCSwitch set $monitor.index $Input
- Start-Sleep -Milliseconds 500
-}
-
-Write-Host "Switched $($okMonitors.Count) monitors to $Input" -ForegroundColor Green
-```
-
-**Alternative (without JSON):**
-```powershell
-# sync-all-legacy.ps1
-param([string]$Input = "HDMI1")
-
-$output = DDCSwitch list
-$monitorCount = ($output | Select-String -Pattern "^\│ \d+" -AllMatches).Matches.Count
-
-for ($i = 0; $i -lt $monitorCount; $i++) {
- Write-Host "Switching monitor $i to $Input..."
- DDCSwitch set $i $Input
- Start-Sleep -Milliseconds 500
+ Write-Host "Configuring monitor $($monitor.index) ($($monitor.name))..." -ForegroundColor Cyan
+
+ # Set input
+ $inputResult = DDCSwitch set $monitor.index $Input --json | ConvertFrom-Json
+ if ($inputResult.success) {
+ Write-Host " ✓ Input: $Input" -ForegroundColor Green
+ } else {
+ Write-Host " ✗ Input failed: $($inputResult.error)" -ForegroundColor Red
+ }
+
+ # Set brightness
+ $brightnessResult = DDCSwitch set $monitor.index brightness "$Brightness%" --json | ConvertFrom-Json
+ if ($brightnessResult.success) {
+ Write-Host " ✓ Brightness: $Brightness%" -ForegroundColor Green
+ } else {
+ Write-Host " ✗ Brightness not supported" -ForegroundColor Yellow
+ }
+
+ # Set contrast
+ $contrastResult = DDCSwitch set $monitor.index contrast "$Contrast%" --json | ConvertFrom-Json
+ if ($contrastResult.success) {
+ Write-Host " ✓ Contrast: $Contrast%" -ForegroundColor Green
+ } else {
+ Write-Host " ✗ Contrast not supported" -ForegroundColor Yellow
+ }
+
+ Start-Sleep -Milliseconds 500 # Prevent DDC/CI overload
}
-Write-Host "All monitors switched to $Input" -ForegroundColor Green
+Write-Host "Synchronized $($okMonitors.Count) monitors" -ForegroundColor Cyan
```
-Happy switching!
+Happy switching and brightness controlling!
diff --git a/README.md b/README.md
index 5b00607..abd1128 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
[](https://dotnet.microsoft.com/)
[](https://github.com/markdwags/DDCSwitch#json-output-for-automation)
-A Windows command-line utility to control monitor input sources via DDC/CI (Display Data Channel Command Interface). Switch between HDMI, DisplayPort, DVI, and VGA inputs without touching physical buttons.
+A Windows command-line utility to control monitor settings via DDC/CI (Display Data Channel Command Interface). Control input sources, brightness, contrast, and other VCP features without touching physical buttons.
📚 **[Examples](EXAMPLES.md)** | 📝 **[Changelog](CHANGELOG.md)**
@@ -14,6 +14,9 @@ A Windows command-line utility to control monitor input sources via DDC/CI (Disp
- 🖥️ **List all DDC/CI capable monitors** with their current input sources
- 🔄 **Switch monitor inputs** programmatically (HDMI, DisplayPort, DVI, VGA, etc.)
+- 🔆 **Control brightness and contrast** with percentage values (0-100%)
+- 🎛️ **Raw VCP access** for advanced users to control any monitor feature
+- 🔍 **VCP scanning** to discover all supported monitor features
- 🎯 **Simple CLI interface** perfect for scripts, shortcuts, and hotkeys
- 📊 **JSON output support** - Machine-readable output for automation and integration
- ⚡ **Fast and lightweight** - NativeAOT compiled for instant startup
@@ -65,19 +68,52 @@ Example output:
╰───────┴─────────────────────┴──────────────┴───────────────────────────┴────────╯
```
+#### Verbose Listing
+
+Add `--verbose` to include brightness and contrast information:
+
+```powershell
+DDCSwitch list --verbose
+```
+
+Example output:
+```
+╭───────┬─────────────────────┬──────────────┬───────────────────────────┬────────┬────────────┬──────────╮
+│ Index │ Monitor Name │ Device │ Current Input │ Status │ Brightness │ Contrast │
+├───────┼─────────────────────┼──────────────┼───────────────────────────┼────────┼────────────┼──────────┤
+│ 0 │ Generic PnP Monitor │ \\.\DISPLAY2 │ HDMI1 (0x11) │ OK │ 75% │ 80% │
+│ 1* │ VG270U P │ \\.\DISPLAY1 │ DisplayPort1 (DP1) (0x0F) │ OK │ N/A │ N/A │
+╰───────┴─────────────────────┴──────────────┴───────────────────────────┴────────┴────────────┴──────────╯
+```
+
Add `--json` for machine-readable output (see [EXAMPLES.md](EXAMPLES.md) for automation examples).
-### Get Current Input
+### Get Current Settings
-Get the current input source for a specific monitor:
+Get all VCP features for a specific monitor:
```powershell
DDCSwitch get 0
```
-Output: `Monitor: Generic PnP Monitor (\\.\DISPLAY2)` / `Current Input: HDMI1 (0x11)`
+This will scan and display all supported VCP features for monitor 0, showing their names, access types, current values, and maximum values.
-### Set Input Source
+Get a specific feature:
+
+```powershell
+# Get current input source
+DDCSwitch get 0 input
+
+# Get brightness as percentage
+DDCSwitch get 0 brightness
+
+# Get contrast as percentage
+DDCSwitch get 0 contrast
+```
+
+Output: `Monitor: Generic PnP Monitor` / `Brightness: 75% (120/160)`
+
+### Set Monitor Settings
Switch a monitor to a different input:
@@ -89,10 +125,43 @@ DDCSwitch set 0 HDMI1
DDCSwitch set "LG ULTRAGEAR" HDMI2
```
-Output: `✓ Successfully switched Generic PnP Monitor to HDMI1`
+Set brightness or contrast with percentage values:
+
+```powershell
+# Set brightness to 75%
+DDCSwitch set 0 brightness 75%
+
+# Set contrast to 80%
+DDCSwitch set 0 contrast 80%
+```
+
+Output: `✓ Successfully set brightness to 75% (120/160)`
-### Supported Input Names
+### Raw VCP Access
+For advanced users, access any VCP feature by code:
+
+```powershell
+# Get raw VCP value (e.g., VCP code 0x10 for brightness)
+DDCSwitch get 0 0x10
+
+# Set raw VCP value
+DDCSwitch set 0 0x10 120
+```
+
+### VCP Feature Scanning
+
+Discover all supported VCP features on a monitor:
+
+```powershell
+DDCSwitch list --verbose
+```
+
+This scans all VCP codes (0x00-0xFF) and displays supported features with their current values, maximum values, and access types (read-only, write-only, read-write).
+
+### Supported Features
+
+#### Input Sources
- **HDMI**: `HDMI1`, `HDMI2`
- **DisplayPort**: `DP1`, `DP2`, `DisplayPort1`, `DisplayPort2`
- **DVI**: `DVI1`, `DVI2`
@@ -100,6 +169,16 @@ Output: `✓ Successfully switched Generic PnP Monitor to HDMI1`
- **Other**: `SVideo1`, `SVideo2`, `Tuner1`, `ComponentVideo1`, etc.
- **Custom codes**: Use hex values like `0x11` for manufacturer-specific inputs
+#### Common VCP Features
+- **Brightness**: `brightness` (VCP 0x10) - accepts percentage values (0-100%)
+- **Contrast**: `contrast` (VCP 0x12) - accepts percentage values (0-100%)
+- **Input Source**: `input` (VCP 0x60) - existing functionality maintained
+
+#### Raw VCP Codes
+- Any VCP code from `0x00` to `0xFF`
+- Values must be within the monitor's supported range
+- Use hex format: `0x10`, `0x12`, etc.
+
## Use Cases
### Quick Examples
@@ -110,13 +189,27 @@ DDCSwitch set 0 HDMI1
DDCSwitch set 1 DP1
```
+**Control brightness and contrast:**
+```powershell
+DDCSwitch set 0 brightness 75%
+DDCSwitch set 0 contrast 80%
+DDCSwitch get 0 brightness
+```
+
+**Raw VCP access:**
+```powershell
+DDCSwitch get 0 0x10 # Get brightness (raw)
+DDCSwitch set 0 0x10 120 # Set brightness (raw value)
+```
+
**Desktop shortcut:**
-Create a shortcut with target: `C:\Path\To\DDCSwitch.exe set 0 HDMI1`
+Create a shortcut with target: `C:\Path\To\DDCSwitch.exe set 0 brightness 50%`
**AutoHotkey:**
```autohotkey
-^!h::Run, DDCSwitch.exe set 0 HDMI1 ; Ctrl+Alt+H for HDMI1
-^!d::Run, DDCSwitch.exe set 0 DP1 ; Ctrl+Alt+D for DisplayPort
+^!h::Run, DDCSwitch.exe set 0 HDMI1 ; Ctrl+Alt+H for HDMI1
+^!d::Run, DDCSwitch.exe set 0 DP1 ; Ctrl+Alt+D for DisplayPort
+^!b::Run, DDCSwitch.exe set 0 brightness 75% ; Ctrl+Alt+B for 75% brightness
```
### JSON Output for Automation
@@ -171,11 +264,17 @@ If you need to verify DDC/CI values or troubleshoot monitor-specific issues, try
## Technical Details
-DDCSwitch uses the Windows DXVA2 API to communicate with monitors via DDC/CI protocol. It reads/writes VCP (Virtual Control Panel) feature 0x60 (Input Source) following the MCCS specification.
+DDCSwitch uses the Windows DXVA2 API to communicate with monitors via DDC/CI protocol. It reads/writes VCP (Virtual Control Panel) features following the MCCS specification.
-**Common VCP Input Codes:**
+**Common VCP Codes:**
+- `0x10` Brightness, `0x12` Contrast, `0x60` Input Source
- `0x01` VGA, `0x03` DVI, `0x0F` DisplayPort 1, `0x10` DisplayPort 2, `0x11` HDMI 1, `0x12` HDMI 2
+**VCP Feature Types:**
+- **Read-Write**: Can get and set values (brightness, contrast, input)
+- **Read-Only**: Can only read current value (some monitor info)
+- **Write-Only**: Can only set values (some calibration features)
+
**NativeAOT Compatible:** Uses source generators for JSON, `DllImport` for P/Invoke, and zero reflection for reliable AOT compilation.
## Why Windows Only?
From 5bc61d42f8f9e57e23a4fcbccc88beb42a9d46a9 Mon Sep 17 00:00:00 2001
From: Quick <577652+markdwags@users.noreply.github.com>
Date: Wed, 7 Jan 2026 20:17:28 -0600
Subject: [PATCH 03/10] add full DDC/CI support for all compliant monitors;
enhance error handling, improve documentation, and optimize input switching
performance
---
CHANGELOG.md | 8 +
DDCSwitch/FeatureResolver.cs | 112 ++-
DDCSwitch/Monitor.cs | 4 +
DDCSwitch/Program.cs | 86 +-
DDCSwitch/Properties/launchSettings.json | 29 +
DDCSwitch/VcpFeature.cs | 989 ++++++++++++++++++++++-
EXAMPLES.md | 202 ++++-
README.md | 68 +-
8 files changed, 1454 insertions(+), 44 deletions(-)
create mode 100644 DDCSwitch/Properties/launchSettings.json
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d0f9366..60827e3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,14 @@
All notable changes to DDCSwitch will be documented in this file.
+## [1.0.2] - 2026-01-07
+
+### Added
+- Full support for all DDC/CI compliant monitors
+- Improved error handling for unsupported monitors
+- Enhanced documentation with additional examples
+- Optimized performance for input switching operations
+
## [1.0.1] - 2026-01-07
### Added
diff --git a/DDCSwitch/FeatureResolver.cs b/DDCSwitch/FeatureResolver.cs
index dd5aab2..ffb3f51 100644
--- a/DDCSwitch/FeatureResolver.cs
+++ b/DDCSwitch/FeatureResolver.cs
@@ -7,12 +7,45 @@ namespace DDCSwitch;
///
public static class FeatureResolver
{
- private static readonly Dictionary FeatureMap = new(StringComparer.OrdinalIgnoreCase)
+ private static readonly Dictionary FeatureMap = BuildFeatureMap();
+ private static readonly Dictionary CodeMap = BuildCodeMap();
+
+ ///
+ /// Builds the feature name to VcpFeature mapping including aliases
+ ///
+ private static Dictionary BuildFeatureMap()
+ {
+ var map = new Dictionary(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var feature in VcpFeature.AllFeatures)
+ {
+ // Add primary name
+ map[feature.Name] = feature;
+
+ // Add aliases
+ foreach (var alias in feature.Aliases)
+ {
+ map[alias] = feature;
+ }
+ }
+
+ return map;
+ }
+
+ ///
+ /// Builds the VCP code to VcpFeature mapping
+ ///
+ private static Dictionary BuildCodeMap()
{
- { "brightness", VcpFeature.Brightness },
- { "contrast", VcpFeature.Contrast },
- { "input", VcpFeature.InputSource }
- };
+ var map = new Dictionary();
+
+ foreach (var feature in VcpFeature.AllFeatures)
+ {
+ map[feature.Code] = feature;
+ }
+
+ return map;
+ }
///
/// Attempts to resolve a feature name or VCP code to a VcpFeature
@@ -29,7 +62,7 @@ public static bool TryResolveFeature(string input, out VcpFeature? feature)
return false;
}
- // First try to resolve as a known feature name
+ // First try to resolve as a known feature name or alias
if (FeatureMap.TryGetValue(input.Trim(), out feature))
{
return true;
@@ -38,14 +71,75 @@ public static bool TryResolveFeature(string input, out VcpFeature? feature)
// Try to parse as a VCP code
if (TryParseVcpCode(input, out byte vcpCode))
{
+ // Check if we have a predefined feature for this code
+ if (CodeMap.TryGetValue(vcpCode, out feature))
+ {
+ return true;
+ }
+
// Create a generic VCP feature for unknown codes
- feature = new VcpFeature(vcpCode, $"VCP_{vcpCode:X2}", VcpFeatureType.ReadWrite, false);
+ feature = new VcpFeature(vcpCode, $"VCP_{vcpCode:X2}", $"Unknown VCP feature 0x{vcpCode:X2}", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
return true;
}
return false;
}
+ ///
+ /// Gets VCP features by category
+ ///
+ /// The category to filter by
+ /// Array of VCP features in the specified category
+ public static VcpFeature[] GetFeaturesByCategory(VcpFeatureCategory category)
+ {
+ return VcpFeature.AllFeatures.Where(f => f.Category == category).ToArray();
+ }
+
+ ///
+ /// Searches for VCP features by partial name matching
+ ///
+ /// Partial name to search for
+ /// Array of VCP features matching the partial name
+ public static VcpFeature[] SearchFeatures(string partialName)
+ {
+ if (string.IsNullOrWhiteSpace(partialName))
+ {
+ return Array.Empty();
+ }
+
+ var searchTerm = partialName.Trim();
+ return VcpFeature.AllFeatures
+ .Where(f => f.Name.Contains(searchTerm, StringComparison.OrdinalIgnoreCase) ||
+ f.Description.Contains(searchTerm, StringComparison.OrdinalIgnoreCase) ||
+ f.Aliases.Any(alias => alias.Contains(searchTerm, StringComparison.OrdinalIgnoreCase)))
+ .ToArray();
+ }
+
+ ///
+ /// Gets a VCP feature by its code
+ ///
+ /// VCP code
+ /// VCP feature if found, otherwise a generic feature for the code
+ public static VcpFeature GetFeatureByCode(byte code)
+ {
+ if (CodeMap.TryGetValue(code, out var feature))
+ {
+ return feature;
+ }
+
+ // Return a generic feature for unknown codes
+ return new VcpFeature(code, $"VCP_{code:X2}", $"Unknown VCP feature 0x{code:X2}", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+ }
+
+ ///
+ /// Gets all available VCP feature categories
+ ///
+ /// Array of category names
+ public static string[] GetAllCategories()
+ {
+ return Enum.GetNames();
+ }
+
///
/// Converts a percentage value (0-100) to raw VCP value based on the maximum value
///
@@ -269,7 +363,7 @@ public static string GetRawValueValidationError(uint value, uint maxValue, strin
}
///
- /// Gets all known feature names
+ /// Gets all known feature names including aliases
///
/// Collection of known feature names
public static IEnumerable GetKnownFeatureNames()
@@ -283,6 +377,6 @@ public static IEnumerable GetKnownFeatureNames()
/// Collection of predefined VCP features
public static IEnumerable GetPredefinedFeatures()
{
- return FeatureMap.Values;
+ return VcpFeature.AllFeatures;
}
}
\ No newline at end of file
diff --git a/DDCSwitch/Monitor.cs b/DDCSwitch/Monitor.cs
index b09cd19..00dab9a 100644
--- a/DDCSwitch/Monitor.cs
+++ b/DDCSwitch/Monitor.cs
@@ -174,7 +174,9 @@ public Dictionary ScanVcpFeatures()
features[vcpCode] = new VcpFeatureInfo(
vcpCode,
name,
+ predefined?.Description ?? $"VCP feature {name}",
type,
+ predefined?.Category ?? VcpFeatureCategory.Miscellaneous,
currentValue,
maxValue,
true
@@ -190,7 +192,9 @@ public Dictionary ScanVcpFeatures()
features[vcpCode] = new VcpFeatureInfo(
vcpCode,
name,
+ predefined?.Description ?? $"VCP feature {name}",
VcpFeatureType.ReadWrite,
+ predefined?.Category ?? VcpFeatureCategory.Miscellaneous,
0,
0,
false
diff --git a/DDCSwitch/Program.cs b/DDCSwitch/Program.cs
index 5b2cf07..39c675e 100644
--- a/DDCSwitch/Program.cs
+++ b/DDCSwitch/Program.cs
@@ -135,17 +135,22 @@ private static int InvalidCommand(string command, bool jsonOutput)
private static int ListMonitors(bool jsonOutput, bool verboseOutput = false, bool scanOutput = false)
{
+ List monitors;
+
if (!jsonOutput)
{
+ monitors = null!;
AnsiConsole.Status()
.Start(scanOutput ? "Scanning VCP features..." : "Enumerating monitors...", ctx =>
{
ctx.Spinner(Spinner.Known.Dots);
- Thread.Sleep(scanOutput ? 500 : 100); // Longer pause for VCP scanning
+ monitors = MonitorController.EnumerateMonitors();
});
}
-
- var monitors = MonitorController.EnumerateMonitors();
+ else
+ {
+ monitors = MonitorController.EnumerateMonitors();
+ }
if (monitors.Count == 0)
{
@@ -448,7 +453,24 @@ private static int GetCurrentInput(string[] args, bool jsonOutput)
}
// Use generic VCP method for all features with enhanced error handling
- bool success = monitor.TryGetVcpFeature(feature!.Code, out uint current, out uint max, out int errorCode);
+ bool success = false;
+ uint current = 0;
+ uint max = 0;
+ int errorCode = 0;
+
+ if (!jsonOutput)
+ {
+ AnsiConsole.Status()
+ .Start($"Reading {feature!.Name} from {monitor.Name}...", ctx =>
+ {
+ ctx.Spinner(Spinner.Known.Dots);
+ success = monitor.TryGetVcpFeature(feature.Code, out current, out max, out errorCode);
+ });
+ }
+ else
+ {
+ success = monitor.TryGetVcpFeature(feature!.Code, out current, out max, out errorCode);
+ }
if (!success)
{
@@ -496,20 +518,9 @@ private static int GetCurrentInput(string[] args, bool jsonOutput)
{
var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- if (feature.Code == InputSource.VcpInputSource)
- {
- // Use generic VCP response for input as well
- uint? percentageValue = feature.SupportsPercentage ? FeatureResolver.ConvertRawToPercentage(current, max) : null;
- var result = new GetVcpResponse(true, monitorRef, feature.Name, current, max, percentageValue);
- Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.GetVcpResponse));
- }
- else
- {
- // Use generic VCP response for other features
- uint? percentageValue = feature.SupportsPercentage ? FeatureResolver.ConvertRawToPercentage(current, max) : null;
- var result = new GetVcpResponse(true, monitorRef, feature.Name, current, max, percentageValue);
- Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.GetVcpResponse));
- }
+ uint? percentageValue = feature.SupportsPercentage ? FeatureResolver.ConvertRawToPercentage(current, max) : null;
+ var result = new GetVcpResponse(true, monitorRef, feature.Name, current, max, percentageValue);
+ Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.GetVcpResponse));
}
else
{
@@ -949,7 +960,14 @@ private static int HandleVcpScan(List monitors, bool jsonOutp
{
AnsiConsole.MarkupLine($"\n[bold blue]Monitor {monitor.Index}: {monitor.Name}[/] ({monitor.DeviceName})");
- var features = monitor.ScanVcpFeatures();
+ Dictionary features = null!;
+ AnsiConsole.Status()
+ .Start($"Scanning VCP features for {monitor.Name}...", ctx =>
+ {
+ ctx.Spinner(Spinner.Known.Dots);
+ features = monitor.ScanVcpFeatures();
+ });
+
var supportedFeatures = features.Values
.Where(f => f.IsSupported)
.OrderBy(f => f.Code)
@@ -1015,17 +1033,22 @@ private static int HandleVcpScan(List monitors, bool jsonOutp
private static int HandleVcpScanForMonitor(string monitorIdentifier, bool jsonOutput)
{
+ List monitors;
+
if (!jsonOutput)
{
+ monitors = null!;
AnsiConsole.Status()
- .Start("Scanning VCP features...", ctx =>
+ .Start("Enumerating monitors...", ctx =>
{
ctx.Spinner(Spinner.Known.Dots);
- Thread.Sleep(500); // Pause for VCP scanning
+ monitors = MonitorController.EnumerateMonitors();
});
}
-
- var monitors = MonitorController.EnumerateMonitors();
+ else
+ {
+ monitors = MonitorController.EnumerateMonitors();
+ }
if (monitors.Count == 0)
{
@@ -1069,7 +1092,22 @@ private static int HandleVcpScanForMonitor(string monitorIdentifier, bool jsonOu
try
{
var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- var features = monitor.ScanVcpFeatures();
+ Dictionary features;
+
+ if (!jsonOutput)
+ {
+ features = null!;
+ AnsiConsole.Status()
+ .Start($"Scanning VCP features for {monitor.Name}...", ctx =>
+ {
+ ctx.Spinner(Spinner.Known.Dots);
+ features = monitor.ScanVcpFeatures();
+ });
+ }
+ else
+ {
+ features = monitor.ScanVcpFeatures();
+ }
// Filter only supported features for cleaner output
var supportedFeatures = features.Values
diff --git a/DDCSwitch/Properties/launchSettings.json b/DDCSwitch/Properties/launchSettings.json
new file mode 100644
index 0000000..7d604ca
--- /dev/null
+++ b/DDCSwitch/Properties/launchSettings.json
@@ -0,0 +1,29 @@
+{
+ "profiles": {
+ "DDCSwitch (get 0)": {
+ "commandName": "Project",
+ "commandLineArgs": "get 0"
+ },
+ "DDCSwitch (list)": {
+ "commandName": "Project",
+ "commandLineArgs": "list"
+ },
+ "DDCSwitch (list --verbose)": {
+ "commandName": "Project",
+ "commandLineArgs": "list --verbose"
+ },
+ "DDCSwitch (set)": {
+ "commandName": "Project",
+ "commandLineArgs": "set 0 input HDMI1"
+ },
+ "DDCSwitch (version)": {
+ "commandName": "Project",
+ "commandLineArgs": "version"
+ },
+ "DDCSwitch (help)": {
+ "commandName": "Project",
+ "commandLineArgs": "help"
+ }
+ }
+}
+
diff --git a/DDCSwitch/VcpFeature.cs b/DDCSwitch/VcpFeature.cs
index 3a4cd4a..b82093b 100644
--- a/DDCSwitch/VcpFeature.cs
+++ b/DDCSwitch/VcpFeature.cs
@@ -21,6 +21,42 @@ public enum VcpFeatureType
ReadWrite
}
+///
+/// Defines the functional category of a VCP feature
+///
+public enum VcpFeatureCategory
+{
+ ///
+ /// Image adjustment features (brightness, contrast, sharpness, etc.)
+ ///
+ ImageAdjustment,
+
+ ///
+ /// Color control features (RGB gains, color temperature, etc.)
+ ///
+ ColorControl,
+
+ ///
+ /// Geometry features (position, size, pincushion - mainly CRT)
+ ///
+ Geometry,
+
+ ///
+ /// Audio features (volume, mute, balance, etc.)
+ ///
+ Audio,
+
+ ///
+ /// Preset and factory features (restore defaults, degauss, etc.)
+ ///
+ Preset,
+
+ ///
+ /// Miscellaneous features that don't fit other categories
+ ///
+ Miscellaneous
+}
+
///
/// Represents a VCP (Virtual Control Panel) feature with its properties
///
@@ -28,31 +64,974 @@ public class VcpFeature
{
public byte Code { get; }
public string Name { get; }
+ public string Description { get; }
public VcpFeatureType Type { get; }
+ public VcpFeatureCategory Category { get; }
public bool SupportsPercentage { get; }
+ public string[] Aliases { get; }
- public VcpFeature(byte code, string name, VcpFeatureType type, bool supportsPercentage)
+ public VcpFeature(byte code, string name, string description, VcpFeatureType type, VcpFeatureCategory category, bool supportsPercentage, params string[] aliases)
{
Code = code;
Name = name;
+ Description = description;
Type = type;
+ Category = category;
SupportsPercentage = supportsPercentage;
+ Aliases = aliases ?? Array.Empty();
}
+ // Legacy constructor for backward compatibility
+ public VcpFeature(byte code, string name, VcpFeatureType type, bool supportsPercentage)
+ : this(code, name, $"VCP feature {name} (0x{code:X2})", type, VcpFeatureCategory.Miscellaneous, supportsPercentage)
+ {
+ }
+
+ // ===== IMAGE ADJUSTMENT FEATURES =====
+
///
/// Brightness control (VCP 0x10)
///
- public static VcpFeature Brightness => new(0x10, "brightness", VcpFeatureType.ReadWrite, true);
+ public static VcpFeature Brightness => new(0x10, "brightness", "Brightness control", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true, "bright");
+
+ ///
+ /// Flesh tone enhancement (VCP 0x11)
+ ///
+ public static VcpFeature FleshToneEnhancement => new(0x11, "flesh-tone-enhancement", "Flesh tone enhancement", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
///
/// Contrast control (VCP 0x12)
///
- public static VcpFeature Contrast => new(0x12, "contrast", VcpFeatureType.ReadWrite, true);
+ public static VcpFeature Contrast => new(0x12, "contrast", "Contrast control", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Backlight control (VCP 0x13)
+ ///
+ public static VcpFeature Backlight => new(0x13, "backlight", "Backlight control", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Select color preset (VCP 0x14)
+ ///
+ public static VcpFeature SelectColorPreset => new(0x14, "select-color-preset", "Select color preset", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false, "color-preset");
+
+ ///
+ /// Focus (VCP 0x1C)
+ ///
+ public static VcpFeature Focus => new(0x1C, "focus", "Focus", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Auto setup (VCP 0x1E)
+ ///
+ public static VcpFeature AutoSetup => new(0x1E, "auto-setup", "Auto setup", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Auto color setup (VCP 0x1F)
+ ///
+ public static VcpFeature AutoColorSetup => new(0x1F, "auto-color-setup", "Auto color setup", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Gray scale expansion (VCP 0x2E)
+ ///
+ public static VcpFeature GrayScaleExpansion => new(0x2E, "gray-scale-expansion", "Gray scale expansion", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Horizontal Moire (VCP 0x56)
+ ///
+ public static VcpFeature HorizontalMoire => new(0x56, "horizontal-moire", "Horizontal Moire", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Vertical Moire (VCP 0x58)
+ ///
+ public static VcpFeature VerticalMoire => new(0x58, "vertical-moire", "Vertical Moire", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
///
/// Input source selection (VCP 0x60)
///
- public static VcpFeature InputSource => new(0x60, "input", VcpFeatureType.ReadWrite, false);
+ public static VcpFeature InputSource => new(0x60, "input", "Input source selection", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false, "source");
+
+ ///
+ /// Adjust Focal Plane (VCP 0x7A)
+ ///
+ public static VcpFeature AdjustFocalPlane => new(0x7A, "adjust-focal-plane", "Adjust Focal Plane", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Adjust Zoom (VCP 0x7C)
+ ///
+ public static VcpFeature AdjustZoom => new(0x7C, "adjust-zoom", "Adjust Zoom", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Trapezoid (VCP 0x7E)
+ ///
+ public static VcpFeature Trapezoid => new(0x7E, "trapezoid", "Trapezoid", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Keystone (VCP 0x80)
+ ///
+ public static VcpFeature Keystone => new(0x80, "keystone", "Keystone", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Horizontal Mirror (Flip) (VCP 0x82)
+ ///
+ public static VcpFeature HorizontalMirror => new(0x82, "horizontal-mirror", "Horizontal Mirror (Flip)", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Vertical Mirror (Flip) (VCP 0x84)
+ ///
+ public static VcpFeature VerticalMirror => new(0x84, "vertical-mirror", "Vertical Mirror (Flip)", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Display Scaling (VCP 0x86)
+ ///
+ public static VcpFeature DisplayScaling => new(0x86, "display-scaling", "Display Scaling", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Sharpness control (VCP 0x87)
+ ///
+ public static VcpFeature Sharpness => new(0x87, "sharpness", "Sharpness control", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true, "sharp");
+
+ ///
+ /// Velocity Scan Modulation (VCP 0x88)
+ ///
+ public static VcpFeature VelocityScanModulation => new(0x88, "velocity-scan-modulation", "Velocity Scan Modulation", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// TV Sharpness (VCP 0x8C)
+ ///
+ public static VcpFeature TVSharpness => new(0x8C, "tv-sharpness", "TV Sharpness", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// TV Contrast (VCP 0x8E)
+ ///
+ public static VcpFeature TVContrast => new(0x8E, "tv-contrast", "TV Contrast", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// TV Black level/Luminesence (VCP 0x92)
+ ///
+ public static VcpFeature TVBlackLevel => new(0x92, "tv-black-level", "TV Black level/Luminesence", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Window background (VCP 0x9A)
+ ///
+ public static VcpFeature WindowBackground => new(0x9A, "window-background", "Window background", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Auto setup on/off (VCP 0xA2)
+ ///
+ public static VcpFeature AutoSetupOnOff => new(0xA2, "auto-setup-on-off", "Auto setup on/off", VcpFeatureType.WriteOnly, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Window mask control (VCP 0xA4)
+ ///
+ public static VcpFeature WindowMaskControl => new(0xA4, "window-mask-control", "Window mask control", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Change the selected window (VCP 0xA5)
+ ///
+ public static VcpFeature ChangeSelectedWindow => new(0xA5, "change-selected-window", "Change the selected window", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Screen Orientation (VCP 0xAA)
+ ///
+ public static VcpFeature ScreenOrientation => new(0xAA, "screen-orientation", "Screen Orientation", VcpFeatureType.ReadOnly, VcpFeatureCategory.ImageAdjustment, false, "orientation");
+
+ ///
+ /// Stereo video mode (VCP 0xD4)
+ ///
+ public static VcpFeature StereoVideoMode => new(0xD4, "stereo-video-mode", "Stereo video mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Scan mode (VCP 0xDA)
+ ///
+ public static VcpFeature ScanMode => new(0xDA, "scan-mode", "Scan mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Image Mode (VCP 0xDB)
+ ///
+ public static VcpFeature ImageMode => new(0xDB, "image-mode", "Image Mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false, "mode");
+
+ ///
+ /// Display Mode (VCP 0xDC)
+ ///
+ public static VcpFeature DisplayMode => new(0xDC, "display-mode", "Display Mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ // ===== COLOR CONTROL FEATURES =====
+
+ ///
+ /// Red video gain (VCP 0x16)
+ ///
+ public static VcpFeature RedGain => new(0x16, "red-gain", "Video gain: Red", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true, "red");
+
+ ///
+ /// User color vision compensation (VCP 0x17)
+ ///
+ public static VcpFeature UserColorVisionCompensation => new(0x17, "user-color-vision-compensation", "User color vision compensation", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Green video gain (VCP 0x18)
+ ///
+ public static VcpFeature GreenGain => new(0x18, "green-gain", "Video gain: Green", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true, "green");
+
+ ///
+ /// Blue video gain (VCP 0x1A)
+ ///
+ public static VcpFeature BlueGain => new(0x1A, "blue-gain", "Video gain: Blue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true, "blue");
+
+ ///
+ /// 6 axis saturation: Red (VCP 0x59)
+ ///
+ public static VcpFeature SixAxisSaturationRed => new(0x59, "6-axis-saturation-red", "6 axis saturation: Red", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis saturation: Yellow (VCP 0x5A)
+ ///
+ public static VcpFeature SixAxisSaturationYellow => new(0x5A, "6-axis-saturation-yellow", "6 axis saturation: Yellow", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis saturation: Green (VCP 0x5B)
+ ///
+ public static VcpFeature SixAxisSaturationGreen => new(0x5B, "6-axis-saturation-green", "6 axis saturation: Green", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis saturation: Cyan (VCP 0x5C)
+ ///
+ public static VcpFeature SixAxisSaturationCyan => new(0x5C, "6-axis-saturation-cyan", "6 axis saturation: Cyan", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis saturation: Blue (VCP 0x5D)
+ ///
+ public static VcpFeature SixAxisSaturationBlue => new(0x5D, "6-axis-saturation-blue", "6 axis saturation: Blue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis saturation: Magenta (VCP 0x5E)
+ ///
+ public static VcpFeature SixAxisSaturationMagenta => new(0x5E, "6-axis-saturation-magenta", "6 axis saturation: Magenta", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Backlight Level: White (VCP 0x6B)
+ ///
+ public static VcpFeature BacklightLevelWhite => new(0x6B, "backlight-level-white", "Backlight Level: White", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Red video black level (VCP 0x6C)
+ ///
+ public static VcpFeature RedBlackLevel => new(0x6C, "red-black-level", "Video black level: Red", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Backlight Level: Red (VCP 0x6D)
+ ///
+ public static VcpFeature BacklightLevelRed => new(0x6D, "backlight-level-red", "Backlight Level: Red", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Green video black level (VCP 0x6E)
+ ///
+ public static VcpFeature GreenBlackLevel => new(0x6E, "green-black-level", "Video black level: Green", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Backlight Level: Green (VCP 0x6F)
+ ///
+ public static VcpFeature BacklightLevelGreen => new(0x6F, "backlight-level-green", "Backlight Level: Green", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Blue video black level (VCP 0x70)
+ ///
+ public static VcpFeature BlueBlackLevel => new(0x70, "blue-black-level", "Video black level: Blue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Backlight Level: Blue (VCP 0x71)
+ ///
+ public static VcpFeature BacklightLevelBlue => new(0x71, "backlight-level-blue", "Backlight Level: Blue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Gamma (VCP 0x72)
+ ///
+ public static VcpFeature Gamma => new(0x72, "gamma", "Gamma", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, false);
+
+ ///
+ /// Color Saturation (VCP 0x8A)
+ ///
+ public static VcpFeature ColorSaturation => new(0x8A, "color-saturation", "Color Saturation", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true, "saturation", "sat");
+
+ ///
+ /// Hue (VCP 0x90)
+ ///
+ public static VcpFeature Hue => new(0x90, "hue", "Hue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis hue control: Red (VCP 0x9B)
+ ///
+ public static VcpFeature SixAxisHueRed => new(0x9B, "6-axis-hue-red", "6 axis hue control: Red", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis hue control: Yellow (VCP 0x9C)
+ ///
+ public static VcpFeature SixAxisHueYellow => new(0x9C, "6-axis-hue-yellow", "6 axis hue control: Yellow", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis hue control: Green (VCP 0x9D)
+ ///
+ public static VcpFeature SixAxisHueGreen => new(0x9D, "6-axis-hue-green", "6 axis hue control: Green", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis hue control: Cyan (VCP 0x9E)
+ ///
+ public static VcpFeature SixAxisHueCyan => new(0x9E, "6-axis-hue-cyan", "6 axis hue control: Cyan", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis hue control: Blue (VCP 0x9F)
+ ///
+ public static VcpFeature SixAxisHueBlue => new(0x9F, "6-axis-hue-blue", "6 axis hue control: Blue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis hue control: Magenta (VCP 0xA0)
+ ///
+ public static VcpFeature SixAxisHueMagenta => new(0xA0, "6-axis-hue-magenta", "6 axis hue control: Magenta", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ // ===== GEOMETRY FEATURES (mainly CRT) =====
+
+ ///
+ /// Horizontal position (VCP 0x20)
+ ///
+ public static VcpFeature HorizontalPosition => new(0x20, "h-position", "Horizontal Position (Phase)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false, "h-pos");
+
+ ///
+ /// Horizontal size (VCP 0x22)
+ ///
+ public static VcpFeature HorizontalSize => new(0x22, "h-size", "Horizontal Size", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Horizontal pincushion (VCP 0x24)
+ ///
+ public static VcpFeature HorizontalPincushion => new(0x24, "h-pincushion", "Horizontal Pincushion", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Horizontal pincushion balance (VCP 0x26)
+ ///
+ public static VcpFeature HorizontalPincushionBalance => new(0x26, "h-pincushion-balance", "Horizontal Pincushion Balance", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Horizontal convergence R/B (VCP 0x28)
+ ///
+ public static VcpFeature HorizontalConvergenceRB => new(0x28, "h-convergence-rb", "Horizontal Convergence R/B", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Horizontal convergence M/G (VCP 0x29)
+ ///
+ public static VcpFeature HorizontalConvergenceMG => new(0x29, "h-convergence-mg", "Horizontal Convergence M/G", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Horizontal linearity (VCP 0x2A)
+ ///
+ public static VcpFeature HorizontalLinearity => new(0x2A, "h-linearity", "Horizontal Linearity", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Horizontal linearity balance (VCP 0x2C)
+ ///
+ public static VcpFeature HorizontalLinearityBalance => new(0x2C, "h-linearity-balance", "Horizontal Linearity Balance", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical position (VCP 0x30)
+ ///
+ public static VcpFeature VerticalPosition => new(0x30, "v-position", "Vertical Position (Phase)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false, "v-pos");
+
+ ///
+ /// Vertical size (VCP 0x32)
+ ///
+ public static VcpFeature VerticalSize => new(0x32, "v-size", "Vertical Size", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical pincushion (VCP 0x34)
+ ///
+ public static VcpFeature VerticalPincushion => new(0x34, "v-pincushion", "Vertical Pincushion", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical pincushion balance (VCP 0x36)
+ ///
+ public static VcpFeature VerticalPincushionBalance => new(0x36, "v-pincushion-balance", "Vertical Pincushion Balance", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical convergence R/B (VCP 0x38)
+ ///
+ public static VcpFeature VerticalConvergenceRB => new(0x38, "v-convergence-rb", "Vertical Convergence R/B", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical convergence M/G (VCP 0x39)
+ ///
+ public static VcpFeature VerticalConvergenceMG => new(0x39, "v-convergence-mg", "Vertical Convergence M/G", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical linearity (VCP 0x3A)
+ ///
+ public static VcpFeature VerticalLinearity => new(0x3A, "v-linearity", "Vertical Linearity", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical linearity balance (VCP 0x3C)
+ ///
+ public static VcpFeature VerticalLinearityBalance => new(0x3C, "v-linearity-balance", "Vertical Linearity Balance", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Clock phase (VCP 0x3E)
+ ///
+ public static VcpFeature ClockPhase => new(0x3E, "clock-phase", "Clock Phase", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false, "phase");
+
+ ///
+ /// Horizontal Parallelogram (VCP 0x40)
+ ///
+ public static VcpFeature HorizontalParallelogram => new(0x40, "horizontal-parallelogram", "Horizontal Parallelogram", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical Parallelogram (VCP 0x41)
+ ///
+ public static VcpFeature VerticalParallelogram => new(0x41, "vertical-parallelogram", "Vertical Parallelogram", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Horizontal Keystone (VCP 0x42)
+ ///
+ public static VcpFeature HorizontalKeystone => new(0x42, "horizontal-keystone", "Horizontal Keystone", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical Keystone (VCP 0x43)
+ ///
+ public static VcpFeature VerticalKeystone => new(0x43, "vertical-keystone", "Vertical Keystone", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Rotation (VCP 0x44)
+ ///
+ public static VcpFeature Rotation => new(0x44, "rotation", "Rotation", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Top Corner Flare (VCP 0x46)
+ ///
+ public static VcpFeature TopCornerFlare => new(0x46, "top-corner-flare", "Top Corner Flare", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Top Corner Hook (VCP 0x48)
+ ///
+ public static VcpFeature TopCornerHook => new(0x48, "top-corner-hook", "Top Corner Hook", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Bottom Corner Flare (VCP 0x4A)
+ ///
+ public static VcpFeature BottomCornerFlare => new(0x4A, "bottom-corner-flare", "Bottom Corner Flare", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Bottom Corner Hook (VCP 0x4C)
+ ///
+ public static VcpFeature BottomCornerHook => new(0x4C, "bottom-corner-hook", "Bottom Corner Hook", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Window Position(TL_X) (VCP 0x95)
+ ///
+ public static VcpFeature WindowPositionTLX => new(0x95, "window-position-tl-x", "Window Position(TL_X)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Window Position(TL_Y) (VCP 0x96)
+ ///
+ public static VcpFeature WindowPositionTLY => new(0x96, "window-position-tl-y", "Window Position(TL_Y)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Window Position(BR_X) (VCP 0x97)
+ ///
+ public static VcpFeature WindowPositionBRX => new(0x97, "window-position-br-x", "Window Position(BR_X)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Window Position(BR_Y) (VCP 0x98)
+ ///
+ public static VcpFeature WindowPositionBRY => new(0x98, "window-position-br-y", "Window Position(BR_Y)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Window control on/off (VCP 0x99)
+ ///
+ public static VcpFeature WindowControlOnOff => new(0x99, "window-control-on-off", "Window control on/off", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ // ===== AUDIO FEATURES =====
+
+ ///
+ /// Audio speaker volume (VCP 0x62)
+ ///
+ public static VcpFeature AudioVolume => new(0x62, "volume", "Audio speaker volume", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, true, "vol");
+
+ ///
+ /// Speaker Select (VCP 0x63)
+ ///
+ public static VcpFeature SpeakerSelect => new(0x63, "speaker-select", "Speaker Select", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, false);
+
+ ///
+ /// Audio: Microphone Volume (VCP 0x64)
+ ///
+ public static VcpFeature MicrophoneVolume => new(0x64, "microphone-volume", "Audio: Microphone Volume", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, true, "mic-volume");
+
+ ///
+ /// Audio mute/Screen blank (VCP 0x8D)
+ ///
+ public static VcpFeature AudioMute => new(0x8D, "mute", "Audio mute/Screen blank", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, false);
+
+ ///
+ /// Audio treble (VCP 0x8F)
+ ///
+ public static VcpFeature AudioTreble => new(0x8F, "audio-treble", "Audio treble", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, true, "treble");
+
+ ///
+ /// Audio bass (VCP 0x91)
+ ///
+ public static VcpFeature AudioBass => new(0x91, "audio-bass", "Audio bass", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, true, "bass");
+
+ ///
+ /// Audio balance L/R (VCP 0x93)
+ ///
+ public static VcpFeature AudioBalance => new(0x93, "audio-balance", "Audio balance L/R", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, true, "balance");
+
+ ///
+ /// Audio processor mode (VCP 0x94)
+ ///
+ public static VcpFeature AudioProcessorMode => new(0x94, "audio-processor-mode", "Audio processor mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, false);
+
+ // ===== PRESET FEATURES =====
+
+ ///
+ /// Degauss (VCP 0x01)
+ ///
+ public static VcpFeature Degauss => new(0x01, "degauss", "Degauss", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false);
+
+ ///
+ /// New control value (VCP 0x02)
+ ///
+ public static VcpFeature NewControlValue => new(0x02, "new-control-value", "New control value", VcpFeatureType.ReadWrite, VcpFeatureCategory.Preset, false);
+
+ ///
+ /// Soft controls (VCP 0x03)
+ ///
+ public static VcpFeature SoftControls => new(0x03, "soft-controls", "Soft controls", VcpFeatureType.ReadWrite, VcpFeatureCategory.Preset, false);
+
+ ///
+ /// Restore factory defaults (VCP 0x04)
+ ///
+ public static VcpFeature RestoreDefaults => new(0x04, "restore-defaults", "Restore factory defaults", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false, "factory-reset");
+
+ ///
+ /// Restore factory brightness/contrast defaults (VCP 0x05)
+ ///
+ public static VcpFeature RestoreBrightnessContrastDefaults => new(0x05, "restore-brightness-contrast", "Restore factory brightness/contrast defaults", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false);
+
+ ///
+ /// Restore factory geometry defaults (VCP 0x06)
+ ///
+ public static VcpFeature RestoreGeometryDefaults => new(0x06, "restore-geometry", "Restore factory geometry defaults", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false);
+
+ ///
+ /// Restore factory color defaults (VCP 0x08)
+ ///
+ public static VcpFeature RestoreColorDefaults => new(0x08, "restore-color", "Restore factory color defaults", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false);
+
+ ///
+ /// Restore factory TV defaults (VCP 0x0A)
+ ///
+ public static VcpFeature RestoreTVDefaults => new(0x0A, "restore-tv-defaults", "Restore factory TV defaults", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false);
+
+ ///
+ /// Color temperature increment (VCP 0x0B)
+ ///
+ public static VcpFeature ColorTemperatureIncrement => new(0x0B, "color-temp-increment", "Color temperature increment", VcpFeatureType.ReadOnly, VcpFeatureCategory.ColorControl, false);
+
+ ///
+ /// Color temperature request (VCP 0x0C)
+ ///
+ public static VcpFeature ColorTemperatureRequest => new(0x0C, "color-temp-request", "Color temperature request", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, false, "color-temp");
+
+ ///
+ /// Clock (VCP 0x0E)
+ ///
+ public static VcpFeature Clock => new(0x0E, "clock", "Clock", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ // ===== MISCELLANEOUS FEATURES =====
+
+ ///
+ /// Active control (VCP 0x52)
+ ///
+ public static VcpFeature ActiveControl => new(0x52, "active-control", "Active control", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Performance Preservation (VCP 0x54)
+ ///
+ public static VcpFeature PerformancePreservation => new(0x54, "performance-preservation", "Performance Preservation", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Ambient light sensor (VCP 0x66)
+ ///
+ public static VcpFeature AmbientLightSensor => new(0x66, "ambient-light-sensor", "Ambient light sensor", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// LUT Size (VCP 0x73)
+ ///
+ public static VcpFeature LUTSize => new(0x73, "lut-size", "LUT Size", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Single point LUT operation (VCP 0x74)
+ ///
+ public static VcpFeature SinglePointLUTOperation => new(0x74, "single-point-lut-operation", "Single point LUT operation", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Block LUT operation (VCP 0x75)
+ ///
+ public static VcpFeature BlockLUTOperation => new(0x75, "block-lut-operation", "Block LUT operation", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Remote Procedure Call (VCP 0x76)
+ ///
+ public static VcpFeature RemoteProcedureCall => new(0x76, "remote-procedure-call", "Remote Procedure Call", VcpFeatureType.WriteOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Display Identification Operation (VCP 0x78)
+ ///
+ public static VcpFeature DisplayIdentificationOperation => new(0x78, "display-identification-operation", "Display Identification Operation", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// TV Channel Up/Down (VCP 0x8B)
+ ///
+ public static VcpFeature TVChannelUpDown => new(0x8B, "tv-channel-up-down", "TV Channel Up/Down", VcpFeatureType.WriteOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Horizontal frequency (VCP 0xAC)
+ ///
+ public static VcpFeature HorizontalFrequency => new(0xAC, "horizontal-frequency", "Horizontal frequency", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false, "h-freq");
+
+ ///
+ /// Vertical frequency (VCP 0xAE)
+ ///
+ public static VcpFeature VerticalFrequency => new(0xAE, "vertical-frequency", "Vertical frequency", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false, "v-freq");
+
+ ///
+ /// Settings (VCP 0xB0)
+ ///
+ public static VcpFeature Settings => new(0xB0, "settings", "Settings", VcpFeatureType.WriteOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Flat panel sub-pixel layout (VCP 0xB2)
+ ///
+ public static VcpFeature FlatPanelSubPixelLayout => new(0xB2, "flat-panel-sub-pixel-layout", "Flat panel sub-pixel layout", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Source Timing Mode (VCP 0xB4)
+ ///
+ public static VcpFeature SourceTimingMode => new(0xB4, "source-timing-mode", "Source Timing Mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Display technology type (VCP 0xB6)
+ ///
+ public static VcpFeature DisplayTechnologyType => new(0xB6, "display-technology-type", "Display technology type", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Monitor status (VCP 0xB7)
+ ///
+ public static VcpFeature MonitorStatus => new(0xB7, "monitor-status", "Monitor status", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Packet count (VCP 0xB8)
+ ///
+ public static VcpFeature PacketCount => new(0xB8, "packet-count", "Packet count", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Monitor X origin (VCP 0xB9)
+ ///
+ public static VcpFeature MonitorXOrigin => new(0xB9, "monitor-x-origin", "Monitor X origin", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Monitor Y origin (VCP 0xBA)
+ ///
+ public static VcpFeature MonitorYOrigin => new(0xBA, "monitor-y-origin", "Monitor Y origin", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Header error count (VCP 0xBB)
+ ///
+ public static VcpFeature HeaderErrorCount => new(0xBB, "header-error-count", "Header error count", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Body CRC error count (VCP 0xBC)
+ ///
+ public static VcpFeature BodyCRCErrorCount => new(0xBC, "body-crc-error-count", "Body CRC error count", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Client ID (VCP 0xBD)
+ ///
+ public static VcpFeature ClientID => new(0xBD, "client-id", "Client ID", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Link control (VCP 0xBE)
+ ///
+ public static VcpFeature LinkControl => new(0xBE, "link-control", "Link control", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Display usage time (VCP 0xC0)
+ ///
+ public static VcpFeature DisplayUsageTime => new(0xC0, "display-usage-time", "Display usage time", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Display descriptor length (VCP 0xC2)
+ ///
+ public static VcpFeature DisplayDescriptorLength => new(0xC2, "display-descriptor-length", "Display descriptor length", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Transmit display descriptor (VCP 0xC3)
+ ///
+ public static VcpFeature TransmitDisplayDescriptor => new(0xC3, "transmit-display-descriptor", "Transmit display descriptor", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Enable display of 'display descriptor' (VCP 0xC4)
+ ///
+ public static VcpFeature EnableDisplayOfDisplayDescriptor => new(0xC4, "enable-display-of-display-descriptor", "Enable display of 'display descriptor'", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Application enable key (VCP 0xC6)
+ ///
+ public static VcpFeature ApplicationEnableKey => new(0xC6, "application-enable-key", "Application enable key", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Display controller type (VCP 0xC8)
+ ///
+ public static VcpFeature DisplayControllerType => new(0xC8, "display-controller-type", "Display controller type", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Display firmware level (VCP 0xC9)
+ ///
+ public static VcpFeature DisplayFirmwareLevel => new(0xC9, "display-firmware-level", "Display firmware level", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false, "firmware");
+
+ ///
+ /// OSD/Button Control (VCP 0xCA)
+ ///
+ public static VcpFeature OSDButtonControl => new(0xCA, "osd-button-control", "OSD/Button Control", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false, "osd");
+
+ ///
+ /// OSD Language (VCP 0xCC)
+ ///
+ public static VcpFeature OSDLanguage => new(0xCC, "osd-language", "OSD Language", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false, "language");
+
+ ///
+ /// Status Indicators (VCP 0xCD)
+ ///
+ public static VcpFeature StatusIndicators => new(0xCD, "status-indicators", "Status Indicators", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Auxiliary display size (VCP 0xCE)
+ ///
+ public static VcpFeature AuxiliaryDisplaySize => new(0xCE, "auxiliary-display-size", "Auxiliary display size", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Auxiliary display data (VCP 0xCF)
+ ///
+ public static VcpFeature AuxiliaryDisplayData => new(0xCF, "auxiliary-display-data", "Auxiliary display data", VcpFeatureType.WriteOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Output select (VCP 0xD0)
+ ///
+ public static VcpFeature OutputSelect => new(0xD0, "output-select", "Output select", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Asset Tag (VCP 0xD2)
+ ///
+ public static VcpFeature AssetTag => new(0xD2, "asset-tag", "Asset Tag", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Power mode (VCP 0xD6)
+ ///
+ public static VcpFeature PowerMode => new(0xD6, "power-mode", "Power mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false, "power");
+
+ ///
+ /// Auxiliary power output (VCP 0xD7)
+ ///
+ public static VcpFeature AuxiliaryPowerOutput => new(0xD7, "auxiliary-power-output", "Auxiliary power output", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Scratch Pad (VCP 0xDE)
+ ///
+ public static VcpFeature ScratchPad => new(0xDE, "scratch-pad", "Scratch Pad", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// VCP Version (VCP 0xDF)
+ ///
+ public static VcpFeature VcpVersion => new(0xDF, "vcp-version", "VCP Version", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false, "version");
+
+ ///
+ /// All features registry - contains all predefined MCCS features
+ ///
+ public static IReadOnlyList AllFeatures { get; } = BuildAllFeatures();
+
+ ///
+ /// Builds the complete registry of all MCCS VCP features
+ ///
+ private static List BuildAllFeatures()
+ {
+ return new List
+ {
+ // Preset Features (0x01-0x0F range)
+ Degauss,
+ NewControlValue,
+ SoftControls,
+ RestoreDefaults,
+ RestoreBrightnessContrastDefaults,
+ RestoreGeometryDefaults,
+ RestoreColorDefaults,
+ RestoreTVDefaults,
+ ColorTemperatureIncrement,
+ ColorTemperatureRequest,
+ Clock,
+
+ // Image Adjustment Features (0x10-0x1F range)
+ Brightness,
+ FleshToneEnhancement,
+ Contrast,
+ Backlight,
+ SelectColorPreset,
+ RedGain,
+ UserColorVisionCompensation,
+ GreenGain,
+ BlueGain,
+ Focus,
+ AutoSetup,
+ AutoColorSetup,
+
+ // Geometry Features (0x20-0x4F range)
+ HorizontalPosition,
+ HorizontalSize,
+ HorizontalPincushion,
+ HorizontalPincushionBalance,
+ HorizontalConvergenceRB,
+ HorizontalConvergenceMG,
+ HorizontalLinearity,
+ HorizontalLinearityBalance,
+ GrayScaleExpansion,
+ VerticalPosition,
+ VerticalSize,
+ VerticalPincushion,
+ VerticalPincushionBalance,
+ VerticalConvergenceRB,
+ VerticalConvergenceMG,
+ VerticalLinearity,
+ VerticalLinearityBalance,
+ ClockPhase,
+ HorizontalParallelogram,
+ VerticalParallelogram,
+ HorizontalKeystone,
+ VerticalKeystone,
+ Rotation,
+ TopCornerFlare,
+ TopCornerHook,
+ BottomCornerFlare,
+ BottomCornerHook,
+
+ // Control and Performance Features (0x50-0x5F range)
+ ActiveControl,
+ PerformancePreservation,
+ HorizontalMoire,
+ VerticalMoire,
+ SixAxisSaturationRed,
+ SixAxisSaturationYellow,
+ SixAxisSaturationGreen,
+ SixAxisSaturationCyan,
+ SixAxisSaturationBlue,
+ SixAxisSaturationMagenta,
+
+ // Input and Audio Features (0x60-0x6F range)
+ InputSource,
+ AudioVolume,
+ SpeakerSelect,
+ MicrophoneVolume,
+ AmbientLightSensor,
+ BacklightLevelWhite,
+ RedBlackLevel,
+ BacklightLevelRed,
+ GreenBlackLevel,
+ BacklightLevelGreen,
+ BlueBlackLevel,
+ BacklightLevelBlue,
+
+ // Advanced Color and LUT Features (0x70-0x7F range)
+ Gamma,
+ LUTSize,
+ SinglePointLUTOperation,
+ BlockLUTOperation,
+ RemoteProcedureCall,
+ DisplayIdentificationOperation,
+ AdjustFocalPlane,
+ AdjustZoom,
+ Trapezoid,
+
+ // Image Processing Features (0x80-0x9F range)
+ Keystone,
+ HorizontalMirror,
+ VerticalMirror,
+ DisplayScaling,
+ Sharpness,
+ VelocityScanModulation,
+ ColorSaturation,
+ TVChannelUpDown,
+ TVSharpness,
+ AudioMute,
+ TVContrast,
+ AudioTreble,
+ Hue,
+ AudioBass,
+ TVBlackLevel,
+ AudioBalance,
+ AudioProcessorMode,
+ WindowPositionTLX,
+ WindowPositionTLY,
+ WindowPositionBRX,
+ WindowPositionBRY,
+ WindowControlOnOff,
+ WindowBackground,
+ SixAxisHueRed,
+ SixAxisHueYellow,
+ SixAxisHueGreen,
+ SixAxisHueCyan,
+ SixAxisHueBlue,
+ SixAxisHueMagenta,
+
+ // Setup and Window Features (0xA0-0xAF range)
+ AutoSetupOnOff,
+ WindowMaskControl,
+ ChangeSelectedWindow,
+ ScreenOrientation,
+ HorizontalFrequency,
+ VerticalFrequency,
+
+ // System Information Features (0xB0-0xBF range)
+ Settings,
+ FlatPanelSubPixelLayout,
+ SourceTimingMode,
+ DisplayTechnologyType,
+ MonitorStatus,
+ PacketCount,
+ MonitorXOrigin,
+ MonitorYOrigin,
+ HeaderErrorCount,
+ BodyCRCErrorCount,
+ ClientID,
+ LinkControl,
+
+ // Display Management Features (0xC0-0xDF range)
+ DisplayUsageTime,
+ DisplayDescriptorLength,
+ TransmitDisplayDescriptor,
+ EnableDisplayOfDisplayDescriptor,
+ ApplicationEnableKey,
+ DisplayControllerType,
+ DisplayFirmwareLevel,
+ OSDButtonControl,
+ OSDLanguage,
+ StatusIndicators,
+ AuxiliaryDisplaySize,
+ AuxiliaryDisplayData,
+ OutputSelect,
+ AssetTag,
+ StereoVideoMode,
+ PowerMode,
+ AuxiliaryPowerOutput,
+ ScanMode,
+ ImageMode,
+ DisplayMode,
+ ScratchPad,
+ VcpVersion
+ };
+ }
public override string ToString()
{
@@ -76,7 +1055,9 @@ public override int GetHashCode()
public record VcpFeatureInfo(
byte Code,
string Name,
+ string Description,
VcpFeatureType Type,
+ VcpFeatureCategory Category,
uint CurrentValue,
uint MaxValue,
bool IsSupported
diff --git a/EXAMPLES.md b/EXAMPLES.md
index f05054c..24ae3ec 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -1,6 +1,206 @@
# DDCSwitch Examples
-This document contains detailed examples and use cases for DDCSwitch, including input switching, brightness/contrast control, and raw VCP access.
+This document contains detailed examples and use cases for DDCSwitch, including input switching, brightness/contrast control, comprehensive VCP feature access, and automation.
+
+## Comprehensive VCP Feature Examples
+
+DDCSwitch now supports all MCCS (Monitor Control Command Set) standardized VCP features, organized by categories for easy discovery.
+
+### VCP Feature Categories
+
+List all available feature categories:
+
+```powershell
+DDCSwitch list --categories
+```
+
+Output:
+```
+Available VCP Feature Categories:
+- ImageAdjustment: Brightness, contrast, sharpness, backlight controls
+- ColorControl: RGB gains, color temperature, gamma, hue, saturation
+- Geometry: Position, size, pincushion controls (mainly CRT monitors)
+- Audio: Volume, mute, balance, treble, bass controls
+- Preset: Factory defaults, degauss, calibration features
+- Miscellaneous: Power mode, OSD settings, firmware information
+```
+
+### Browse Features by Category
+
+```powershell
+# Image adjustment features
+DDCSwitch list --category image
+
+# Color control features
+DDCSwitch list --category color
+
+# Audio features
+DDCSwitch list --category audio
+```
+
+### Color Control Examples
+
+Control RGB gains for color calibration:
+
+```powershell
+# Set individual RGB gains (percentage values)
+DDCSwitch set 0 red-gain 95%
+DDCSwitch set 0 green-gain 90%
+DDCSwitch set 0 blue-gain 85%
+
+# Get current RGB values
+DDCSwitch get 0 red-gain
+DDCSwitch get 0 green-gain
+DDCSwitch get 0 blue-gain
+
+# Color temperature control (if supported)
+DDCSwitch set 0 color-temp-request 6500
+DDCSwitch get 0 color-temp-request
+
+# Gamma control
+DDCSwitch set 0 gamma 2.2
+DDCSwitch get 0 gamma
+
+# Hue and saturation
+DDCSwitch set 0 hue 50%
+DDCSwitch set 0 saturation 80%
+```
+
+### Audio Control Examples
+
+Control monitor speakers (if supported):
+
+```powershell
+# Volume control (percentage)
+DDCSwitch set 0 volume 75%
+DDCSwitch get 0 volume
+
+# Mute/unmute
+DDCSwitch set 0 mute 1 # Mute
+DDCSwitch set 0 mute 0 # Unmute
+
+# Audio balance (if supported)
+DDCSwitch set 0 audio-balance 50% # Centered
+
+# Treble and bass (if supported)
+DDCSwitch set 0 audio-treble 60%
+DDCSwitch set 0 audio-bass 70%
+```
+
+### Advanced Image Controls
+
+Beyond basic brightness and contrast:
+
+```powershell
+# Sharpness control
+DDCSwitch set 0 sharpness 75%
+DDCSwitch get 0 sharpness
+
+# Backlight control (LED monitors)
+DDCSwitch set 0 backlight 80%
+DDCSwitch get 0 backlight
+
+# Image orientation (if supported)
+DDCSwitch set 0 image-orientation 0 # Normal
+DDCSwitch set 0 image-orientation 1 # 90° rotation
+
+# Image mode presets (if supported)
+DDCSwitch set 0 image-mode 1 # Standard
+DDCSwitch set 0 image-mode 2 # Movie
+DDCSwitch set 0 image-mode 3 # Game
+```
+
+### Factory Reset and Calibration
+
+```powershell
+# Restore all factory defaults
+DDCSwitch set 0 restore-defaults 1
+
+# Restore specific defaults
+DDCSwitch set 0 restore-brightness-contrast 1
+DDCSwitch set 0 restore-color 1
+DDCSwitch set 0 restore-geometry 1
+
+# Degauss (CRT monitors)
+DDCSwitch set 0 degauss 1
+
+# Auto calibration features (if supported)
+DDCSwitch set 0 auto-color-setup 1
+DDCSwitch set 0 auto-size-center 1
+```
+
+### Complete Monitor Profile Examples
+
+Create comprehensive monitor profiles using all available features:
+
+```powershell
+# gaming-profile-advanced.ps1
+Write-Host "Activating Advanced Gaming Profile..." -ForegroundColor Cyan
+
+# Input and basic settings
+DDCSwitch set 0 input HDMI1
+DDCSwitch set 0 brightness 90%
+DDCSwitch set 0 contrast 85%
+
+# Color optimization for gaming
+DDCSwitch set 0 red-gain 100%
+DDCSwitch set 0 green-gain 95%
+DDCSwitch set 0 blue-gain 90%
+DDCSwitch set 0 saturation 110% # Enhanced colors
+DDCSwitch set 0 sharpness 80% # Crisp details
+
+# Audio settings
+DDCSwitch set 0 volume 60%
+DDCSwitch set 0 mute 0
+
+Write-Host "Advanced Gaming Profile Activated!" -ForegroundColor Green
+```
+
+```powershell
+# work-profile-advanced.ps1
+Write-Host "Activating Advanced Work Profile..." -ForegroundColor Cyan
+
+# Input and basic settings
+DDCSwitch set 0 input DP1
+DDCSwitch set 0 brightness 60%
+DDCSwitch set 0 contrast 75%
+
+# Color optimization for text work
+DDCSwitch set 0 red-gain 85%
+DDCSwitch set 0 green-gain 90%
+DDCSwitch set 0 blue-gain 95%
+DDCSwitch set 0 saturation 70% # Reduced saturation for comfort
+DDCSwitch set 0 sharpness 60% # Softer for long reading
+
+# Audio settings
+DDCSwitch set 0 volume 40% # Lower for office environment
+DDCSwitch set 0 mute 0
+
+Write-Host "Advanced Work Profile Activated!" -ForegroundColor Green
+```
+
+```powershell
+# photo-editing-profile.ps1
+Write-Host "Activating Photo Editing Profile..." -ForegroundColor Cyan
+
+# Input and basic settings
+DDCSwitch set 0 input DP1
+DDCSwitch set 0 brightness 70%
+DDCSwitch set 0 contrast 80%
+
+# Accurate color reproduction
+DDCSwitch set 0 red-gain 90%
+DDCSwitch set 0 green-gain 90%
+DDCSwitch set 0 blue-gain 90%
+DDCSwitch set 0 saturation 100% # Natural saturation
+DDCSwitch set 0 gamma 2.2 # Standard gamma
+DDCSwitch set 0 color-temp-request 6500 # D65 standard
+
+# Disable audio to avoid distractions
+DDCSwitch set 0 mute 1
+
+Write-Host "Photo Editing Profile Activated!" -ForegroundColor Green
+```
## Basic Usage Examples
diff --git a/README.md b/README.md
index abd1128..6cd32e7 100644
--- a/README.md
+++ b/README.md
@@ -15,7 +15,8 @@ A Windows command-line utility to control monitor settings via DDC/CI (Display D
- 🖥️ **List all DDC/CI capable monitors** with their current input sources
- 🔄 **Switch monitor inputs** programmatically (HDMI, DisplayPort, DVI, VGA, etc.)
- 🔆 **Control brightness and contrast** with percentage values (0-100%)
-- 🎛️ **Raw VCP access** for advanced users to control any monitor feature
+- 🎛️ **Comprehensive VCP feature support** - Access all MCCS standardized monitor controls
+- 🏷️ **Feature categories and discovery** - Browse VCP features by category (Image, Color, Geometry, Audio, etc.)
- 🔍 **VCP scanning** to discover all supported monitor features
- 🎯 **Simple CLI interface** perfect for scripts, shortcuts, and hotkeys
- 📊 **JSON output support** - Machine-readable output for automation and integration
@@ -159,6 +160,34 @@ DDCSwitch list --verbose
This scans all VCP codes (0x00-0xFF) and displays supported features with their current values, maximum values, and access types (read-only, write-only, read-write).
+### VCP Feature Categories and Discovery
+
+Discover and browse VCP features by category:
+
+```powershell
+# List all available categories
+DDCSwitch list --categories
+
+# List features in a specific category
+DDCSwitch list --category image
+DDCSwitch list --category color
+DDCSwitch list --category audio
+```
+
+Example output:
+```
+Image Adjustment Features:
+- brightness (0x10): Brightness control
+- contrast (0x12): Contrast control
+- sharpness (0x87): Sharpness control
+- backlight (0x13): Backlight control
+
+Color Control Features:
+- red-gain (0x16): Video gain: Red
+- green-gain (0x18): Video gain: Green
+- blue-gain (0x1A): Video gain: Blue
+```
+
### Supported Features
#### Input Sources
@@ -173,6 +202,18 @@ This scans all VCP codes (0x00-0xFF) and displays supported features with their
- **Brightness**: `brightness` (VCP 0x10) - accepts percentage values (0-100%)
- **Contrast**: `contrast` (VCP 0x12) - accepts percentage values (0-100%)
- **Input Source**: `input` (VCP 0x60) - existing functionality maintained
+- **Color Controls**: `red-gain`, `green-gain`, `blue-gain` (VCP 0x16, 0x18, 0x1A)
+- **Audio**: `volume`, `mute` (VCP 0x62, 0x8D) - volume accepts percentage values
+- **Geometry**: `h-position`, `v-position`, `clock`, `phase` (mainly for CRT monitors)
+- **Presets**: `restore-defaults`, `degauss` (VCP 0x04, 0x01)
+
+#### VCP Feature Categories
+- **Image Adjustment**: brightness, contrast, sharpness, backlight, etc.
+- **Color Control**: RGB gains, color temperature, gamma, hue, saturation
+- **Geometry**: position, size, pincushion controls (mainly CRT)
+- **Audio**: volume, mute, balance, treble, bass
+- **Preset**: factory defaults, degauss, calibration
+- **Miscellaneous**: power mode, OSD settings, firmware info
#### Raw VCP Codes
- Any VCP code from `0x00` to `0xFF`
@@ -189,17 +230,32 @@ DDCSwitch set 0 HDMI1
DDCSwitch set 1 DP1
```
-**Control brightness and contrast:**
+**Control comprehensive VCP features:**
```powershell
DDCSwitch set 0 brightness 75%
DDCSwitch set 0 contrast 80%
DDCSwitch get 0 brightness
+
+# Color controls
+DDCSwitch set 0 red-gain 90%
+DDCSwitch set 0 green-gain 85%
+DDCSwitch set 0 blue-gain 95%
+
+# Audio controls (if supported)
+DDCSwitch set 0 volume 50%
+DDCSwitch set 0 mute 1
```
-**Raw VCP access:**
+**VCP feature discovery:**
```powershell
-DDCSwitch get 0 0x10 # Get brightness (raw)
-DDCSwitch set 0 0x10 120 # Set brightness (raw value)
+# List all available VCP feature categories
+DDCSwitch list --categories
+
+# List features in a specific category
+DDCSwitch list --category color
+
+# Search for features by name
+DDCSwitch get 0 bright # Matches "brightness"
```
**Desktop shortcut:**
@@ -291,6 +347,6 @@ MIT License - see LICENSE file for details
## Acknowledgments
-- Inspired by `ddcutil` for Linux
+- Inspired by [ddcutil](https://www.ddcutil.com) for Linux
- Uses Spectre.Console for beautiful terminal output
From c81489a869dbc5134afd10e652b34092b3d3b2fa Mon Sep 17 00:00:00 2001
From: Quick <577652+markdwags@users.noreply.github.com>
Date: Wed, 7 Jan 2026 20:38:25 -0600
Subject: [PATCH 04/10] add VCP feature generation script and data; embed VCP
feature data JSON, create build script for NativeAOT, and update project
files
---
.gitignore | 4 +-
DDCSwitch/DDCSwitch.csproj | 12 +
DDCSwitch/VcpFeature.Generated.cs | 935 +++++++++++++++++
DDCSwitch/VcpFeature.cs | 959 +-----------------
DDCSwitch/VcpFeatureData.json | 1561 +++++++++++++++++++++++++++++
build.cmd | 53 +
tools/GenerateVcpFeatures.py | 114 +++
tools/Regenerate.ps1 | 30 +
8 files changed, 2716 insertions(+), 952 deletions(-)
create mode 100644 DDCSwitch/VcpFeature.Generated.cs
create mode 100644 DDCSwitch/VcpFeatureData.json
create mode 100644 build.cmd
create mode 100644 tools/GenerateVcpFeatures.py
create mode 100644 tools/Regenerate.ps1
diff --git a/.gitignore b/.gitignore
index be2f63d..dd4c524 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,4 +37,6 @@ artifacts/
# NuGet Packages
*.nupkg
*.snupkg
-**/packages/*
\ No newline at end of file
+**/packages/*
+
+dist/
\ No newline at end of file
diff --git a/DDCSwitch/DDCSwitch.csproj b/DDCSwitch/DDCSwitch.csproj
index a2ee008..2b27bce 100644
--- a/DDCSwitch/DDCSwitch.csproj
+++ b/DDCSwitch/DDCSwitch.csproj
@@ -40,5 +40,17 @@
+
+
+
+
+
+
+ True
+ True
+ VcpFeatureData.json
+
+
+
diff --git a/DDCSwitch/VcpFeature.Generated.cs b/DDCSwitch/VcpFeature.Generated.cs
new file mode 100644
index 0000000..554118c
--- /dev/null
+++ b/DDCSwitch/VcpFeature.Generated.cs
@@ -0,0 +1,935 @@
+//
+// This file is automatically generated from VcpFeatureData.json
+// Do not edit this file directly. Instead, edit VcpFeatureData.json and regenerate.
+
+namespace DDCSwitch;
+
+public partial class VcpFeature
+{
+ // ===== GENERATED VCP FEATURES =====
+
+ // ===== PRESET FEATURES =====
+
+ ///
+ /// Degauss (VCP 0x01)
+ ///
+ public static VcpFeature Degauss => new(0x01, "degauss", "Degauss", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false);
+
+ ///
+ /// New control value (VCP 0x02)
+ ///
+ public static VcpFeature NewControlValue => new(0x02, "new-control-value", "New control value", VcpFeatureType.ReadWrite, VcpFeatureCategory.Preset, false);
+
+ ///
+ /// Soft controls (VCP 0x03)
+ ///
+ public static VcpFeature SoftControls => new(0x03, "soft-controls", "Soft controls", VcpFeatureType.ReadWrite, VcpFeatureCategory.Preset, false);
+
+ ///
+ /// Restore factory defaults (VCP 0x04)
+ ///
+ public static VcpFeature RestoreDefaults => new(0x04, "restore-defaults", "Restore factory defaults", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false, "factory-reset");
+
+ ///
+ /// Restore factory brightness/contrast defaults (VCP 0x05)
+ ///
+ public static VcpFeature RestoreBrightnessContrastDefaults => new(0x05, "restore-brightness-contrast", "Restore factory brightness/contrast defaults", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false);
+
+ ///
+ /// Restore factory geometry defaults (VCP 0x06)
+ ///
+ public static VcpFeature RestoreGeometryDefaults => new(0x06, "restore-geometry", "Restore factory geometry defaults", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false);
+
+ ///
+ /// Restore factory color defaults (VCP 0x08)
+ ///
+ public static VcpFeature RestoreColorDefaults => new(0x08, "restore-color", "Restore factory color defaults", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false);
+
+ ///
+ /// Restore factory TV defaults (VCP 0x0A)
+ ///
+ public static VcpFeature RestoreTVDefaults => new(0x0A, "restore-tv-defaults", "Restore factory TV defaults", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false);
+
+
+ // ===== COLORCONTROL FEATURES =====
+
+ ///
+ /// Color temperature increment (VCP 0x0B)
+ ///
+ public static VcpFeature ColorTemperatureIncrement => new(0x0B, "color-temp-increment", "Color temperature increment", VcpFeatureType.ReadOnly, VcpFeatureCategory.ColorControl, false);
+
+ ///
+ /// Color temperature request (VCP 0x0C)
+ ///
+ public static VcpFeature ColorTemperatureRequest => new(0x0C, "color-temp-request", "Color temperature request", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, false, "color-temp");
+
+ ///
+ /// Video gain: Red (VCP 0x16)
+ ///
+ public static VcpFeature RedGain => new(0x16, "red-gain", "Video gain: Red", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true, "red");
+
+ ///
+ /// User color vision compensation (VCP 0x17)
+ ///
+ public static VcpFeature UserColorVisionCompensation => new(0x17, "user-color-vision-compensation", "User color vision compensation", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Video gain: Green (VCP 0x18)
+ ///
+ public static VcpFeature GreenGain => new(0x18, "green-gain", "Video gain: Green", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true, "green");
+
+ ///
+ /// Video gain: Blue (VCP 0x1A)
+ ///
+ public static VcpFeature BlueGain => new(0x1A, "blue-gain", "Video gain: Blue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true, "blue");
+
+ ///
+ /// 6 axis saturation: Red (VCP 0x59)
+ ///
+ public static VcpFeature SixAxisSaturationRed => new(0x59, "6-axis-saturation-red", "6 axis saturation: Red", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis saturation: Yellow (VCP 0x5A)
+ ///
+ public static VcpFeature SixAxisSaturationYellow => new(0x5A, "6-axis-saturation-yellow", "6 axis saturation: Yellow", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis saturation: Green (VCP 0x5B)
+ ///
+ public static VcpFeature SixAxisSaturationGreen => new(0x5B, "6-axis-saturation-green", "6 axis saturation: Green", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis saturation: Cyan (VCP 0x5C)
+ ///
+ public static VcpFeature SixAxisSaturationCyan => new(0x5C, "6-axis-saturation-cyan", "6 axis saturation: Cyan", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis saturation: Blue (VCP 0x5D)
+ ///
+ public static VcpFeature SixAxisSaturationBlue => new(0x5D, "6-axis-saturation-blue", "6 axis saturation: Blue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis saturation: Magenta (VCP 0x5E)
+ ///
+ public static VcpFeature SixAxisSaturationMagenta => new(0x5E, "6-axis-saturation-magenta", "6 axis saturation: Magenta", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Backlight Level: White (VCP 0x6B)
+ ///
+ public static VcpFeature BacklightLevelWhite => new(0x6B, "backlight-level-white", "Backlight Level: White", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Video black level: Red (VCP 0x6C)
+ ///
+ public static VcpFeature RedBlackLevel => new(0x6C, "red-black-level", "Video black level: Red", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Backlight Level: Red (VCP 0x6D)
+ ///
+ public static VcpFeature BacklightLevelRed => new(0x6D, "backlight-level-red", "Backlight Level: Red", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Video black level: Green (VCP 0x6E)
+ ///
+ public static VcpFeature GreenBlackLevel => new(0x6E, "green-black-level", "Video black level: Green", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Backlight Level: Green (VCP 0x6F)
+ ///
+ public static VcpFeature BacklightLevelGreen => new(0x6F, "backlight-level-green", "Backlight Level: Green", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Video black level: Blue (VCP 0x70)
+ ///
+ public static VcpFeature BlueBlackLevel => new(0x70, "blue-black-level", "Video black level: Blue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Backlight Level: Blue (VCP 0x71)
+ ///
+ public static VcpFeature BacklightLevelBlue => new(0x71, "backlight-level-blue", "Backlight Level: Blue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// Gamma (VCP 0x72)
+ ///
+ public static VcpFeature Gamma => new(0x72, "gamma", "Gamma", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, false);
+
+ ///
+ /// Color Saturation (VCP 0x8A)
+ ///
+ public static VcpFeature ColorSaturation => new(0x8A, "color-saturation", "Color Saturation", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true, "saturation", "sat");
+
+ ///
+ /// Hue (VCP 0x90)
+ ///
+ public static VcpFeature Hue => new(0x90, "hue", "Hue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis hue control: Red (VCP 0x9B)
+ ///
+ public static VcpFeature SixAxisHueRed => new(0x9B, "6-axis-hue-red", "6 axis hue control: Red", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis hue control: Yellow (VCP 0x9C)
+ ///
+ public static VcpFeature SixAxisHueYellow => new(0x9C, "6-axis-hue-yellow", "6 axis hue control: Yellow", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis hue control: Green (VCP 0x9D)
+ ///
+ public static VcpFeature SixAxisHueGreen => new(0x9D, "6-axis-hue-green", "6 axis hue control: Green", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis hue control: Cyan (VCP 0x9E)
+ ///
+ public static VcpFeature SixAxisHueCyan => new(0x9E, "6-axis-hue-cyan", "6 axis hue control: Cyan", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis hue control: Blue (VCP 0x9F)
+ ///
+ public static VcpFeature SixAxisHueBlue => new(0x9F, "6-axis-hue-blue", "6 axis hue control: Blue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+ ///
+ /// 6 axis hue control: Magenta (VCP 0xA0)
+ ///
+ public static VcpFeature SixAxisHueMagenta => new(0xA0, "6-axis-hue-magenta", "6 axis hue control: Magenta", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
+
+
+ // ===== GEOMETRY FEATURES =====
+
+ ///
+ /// Clock (VCP 0x0E)
+ ///
+ public static VcpFeature Clock => new(0x0E, "clock", "Clock", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Horizontal Position (Phase) (VCP 0x20)
+ ///
+ public static VcpFeature HorizontalPosition => new(0x20, "h-position", "Horizontal Position (Phase)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false, "h-pos");
+
+ ///
+ /// Horizontal Size (VCP 0x22)
+ ///
+ public static VcpFeature HorizontalSize => new(0x22, "h-size", "Horizontal Size", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Horizontal Pincushion (VCP 0x24)
+ ///
+ public static VcpFeature HorizontalPincushion => new(0x24, "h-pincushion", "Horizontal Pincushion", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Horizontal Pincushion Balance (VCP 0x26)
+ ///
+ public static VcpFeature HorizontalPincushionBalance => new(0x26, "h-pincushion-balance", "Horizontal Pincushion Balance", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Horizontal Convergence R/B (VCP 0x28)
+ ///
+ public static VcpFeature HorizontalConvergenceRB => new(0x28, "h-convergence-rb", "Horizontal Convergence R/B", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Horizontal Convergence M/G (VCP 0x29)
+ ///
+ public static VcpFeature HorizontalConvergenceMG => new(0x29, "h-convergence-mg", "Horizontal Convergence M/G", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Horizontal Linearity (VCP 0x2A)
+ ///
+ public static VcpFeature HorizontalLinearity => new(0x2A, "h-linearity", "Horizontal Linearity", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Horizontal Linearity Balance (VCP 0x2C)
+ ///
+ public static VcpFeature HorizontalLinearityBalance => new(0x2C, "h-linearity-balance", "Horizontal Linearity Balance", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical Position (Phase) (VCP 0x30)
+ ///
+ public static VcpFeature VerticalPosition => new(0x30, "v-position", "Vertical Position (Phase)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false, "v-pos");
+
+ ///
+ /// Vertical Size (VCP 0x32)
+ ///
+ public static VcpFeature VerticalSize => new(0x32, "v-size", "Vertical Size", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical Pincushion (VCP 0x34)
+ ///
+ public static VcpFeature VerticalPincushion => new(0x34, "v-pincushion", "Vertical Pincushion", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical Pincushion Balance (VCP 0x36)
+ ///
+ public static VcpFeature VerticalPincushionBalance => new(0x36, "v-pincushion-balance", "Vertical Pincushion Balance", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical Convergence R/B (VCP 0x38)
+ ///
+ public static VcpFeature VerticalConvergenceRB => new(0x38, "v-convergence-rb", "Vertical Convergence R/B", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical Convergence M/G (VCP 0x39)
+ ///
+ public static VcpFeature VerticalConvergenceMG => new(0x39, "v-convergence-mg", "Vertical Convergence M/G", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical Linearity (VCP 0x3A)
+ ///
+ public static VcpFeature VerticalLinearity => new(0x3A, "v-linearity", "Vertical Linearity", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical Linearity Balance (VCP 0x3C)
+ ///
+ public static VcpFeature VerticalLinearityBalance => new(0x3C, "v-linearity-balance", "Vertical Linearity Balance", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Clock Phase (VCP 0x3E)
+ ///
+ public static VcpFeature ClockPhase => new(0x3E, "clock-phase", "Clock Phase", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false, "phase");
+
+ ///
+ /// Horizontal Parallelogram (VCP 0x40)
+ ///
+ public static VcpFeature HorizontalParallelogram => new(0x40, "horizontal-parallelogram", "Horizontal Parallelogram", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical Parallelogram (VCP 0x41)
+ ///
+ public static VcpFeature VerticalParallelogram => new(0x41, "vertical-parallelogram", "Vertical Parallelogram", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Horizontal Keystone (VCP 0x42)
+ ///
+ public static VcpFeature HorizontalKeystone => new(0x42, "horizontal-keystone", "Horizontal Keystone", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Vertical Keystone (VCP 0x43)
+ ///
+ public static VcpFeature VerticalKeystone => new(0x43, "vertical-keystone", "Vertical Keystone", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Rotation (VCP 0x44)
+ ///
+ public static VcpFeature Rotation => new(0x44, "rotation", "Rotation", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Top Corner Flare (VCP 0x46)
+ ///
+ public static VcpFeature TopCornerFlare => new(0x46, "top-corner-flare", "Top Corner Flare", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Top Corner Hook (VCP 0x48)
+ ///
+ public static VcpFeature TopCornerHook => new(0x48, "top-corner-hook", "Top Corner Hook", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Bottom Corner Flare (VCP 0x4A)
+ ///
+ public static VcpFeature BottomCornerFlare => new(0x4A, "bottom-corner-flare", "Bottom Corner Flare", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Bottom Corner Hook (VCP 0x4C)
+ ///
+ public static VcpFeature BottomCornerHook => new(0x4C, "bottom-corner-hook", "Bottom Corner Hook", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Window Position(TL_X) (VCP 0x95)
+ ///
+ public static VcpFeature WindowPositionTLX => new(0x95, "window-position-tl-x", "Window Position(TL_X)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Window Position(TL_Y) (VCP 0x96)
+ ///
+ public static VcpFeature WindowPositionTLY => new(0x96, "window-position-tl-y", "Window Position(TL_Y)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Window Position(BR_X) (VCP 0x97)
+ ///
+ public static VcpFeature WindowPositionBRX => new(0x97, "window-position-br-x", "Window Position(BR_X)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Window Position(BR_Y) (VCP 0x98)
+ ///
+ public static VcpFeature WindowPositionBRY => new(0x98, "window-position-br-y", "Window Position(BR_Y)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+ ///
+ /// Window control on/off (VCP 0x99)
+ ///
+ public static VcpFeature WindowControlOnOff => new(0x99, "window-control-on-off", "Window control on/off", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
+
+
+ // ===== IMAGEADJUSTMENT FEATURES =====
+
+ ///
+ /// Brightness control (VCP 0x10)
+ ///
+ public static VcpFeature Brightness => new(0x10, "brightness", "Brightness control", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true, "bright");
+
+ ///
+ /// Flesh tone enhancement (VCP 0x11)
+ ///
+ public static VcpFeature FleshToneEnhancement => new(0x11, "flesh-tone-enhancement", "Flesh tone enhancement", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Contrast control (VCP 0x12)
+ ///
+ public static VcpFeature Contrast => new(0x12, "contrast", "Contrast control", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Backlight control (VCP 0x13)
+ ///
+ public static VcpFeature Backlight => new(0x13, "backlight", "Backlight control", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Select color preset (VCP 0x14)
+ ///
+ public static VcpFeature SelectColorPreset => new(0x14, "select-color-preset", "Select color preset", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false, "color-preset");
+
+ ///
+ /// Focus (VCP 0x1C)
+ ///
+ public static VcpFeature Focus => new(0x1C, "focus", "Focus", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Auto setup (VCP 0x1E)
+ ///
+ public static VcpFeature AutoSetup => new(0x1E, "auto-setup", "Auto setup", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Auto color setup (VCP 0x1F)
+ ///
+ public static VcpFeature AutoColorSetup => new(0x1F, "auto-color-setup", "Auto color setup", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Gray scale expansion (VCP 0x2E)
+ ///
+ public static VcpFeature GrayScaleExpansion => new(0x2E, "gray-scale-expansion", "Gray scale expansion", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Horizontal Moire (VCP 0x56)
+ ///
+ public static VcpFeature HorizontalMoire => new(0x56, "horizontal-moire", "Horizontal Moire", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Vertical Moire (VCP 0x58)
+ ///
+ public static VcpFeature VerticalMoire => new(0x58, "vertical-moire", "Vertical Moire", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Input source selection (VCP 0x60)
+ ///
+ public static VcpFeature InputSource => new(0x60, "input", "Input source selection", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false, "source");
+
+ ///
+ /// Adjust Focal Plane (VCP 0x7A)
+ ///
+ public static VcpFeature AdjustFocalPlane => new(0x7A, "adjust-focal-plane", "Adjust Focal Plane", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Adjust Zoom (VCP 0x7C)
+ ///
+ public static VcpFeature AdjustZoom => new(0x7C, "adjust-zoom", "Adjust Zoom", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Trapezoid (VCP 0x7E)
+ ///
+ public static VcpFeature Trapezoid => new(0x7E, "trapezoid", "Trapezoid", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Keystone (VCP 0x80)
+ ///
+ public static VcpFeature Keystone => new(0x80, "keystone", "Keystone", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Horizontal Mirror (Flip) (VCP 0x82)
+ ///
+ public static VcpFeature HorizontalMirror => new(0x82, "horizontal-mirror", "Horizontal Mirror (Flip)", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Vertical Mirror (Flip) (VCP 0x84)
+ ///
+ public static VcpFeature VerticalMirror => new(0x84, "vertical-mirror", "Vertical Mirror (Flip)", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Display Scaling (VCP 0x86)
+ ///
+ public static VcpFeature DisplayScaling => new(0x86, "display-scaling", "Display Scaling", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Sharpness control (VCP 0x87)
+ ///
+ public static VcpFeature Sharpness => new(0x87, "sharpness", "Sharpness control", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true, "sharp");
+
+ ///
+ /// Velocity Scan Modulation (VCP 0x88)
+ ///
+ public static VcpFeature VelocityScanModulation => new(0x88, "velocity-scan-modulation", "Velocity Scan Modulation", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// TV Sharpness (VCP 0x8C)
+ ///
+ public static VcpFeature TVSharpness => new(0x8C, "tv-sharpness", "TV Sharpness", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// TV Contrast (VCP 0x8E)
+ ///
+ public static VcpFeature TVContrast => new(0x8E, "tv-contrast", "TV Contrast", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// TV Black level/Luminesence (VCP 0x92)
+ ///
+ public static VcpFeature TVBlackLevel => new(0x92, "tv-black-level", "TV Black level/Luminesence", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Window background (VCP 0x9A)
+ ///
+ public static VcpFeature WindowBackground => new(0x9A, "window-background", "Window background", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
+
+ ///
+ /// Auto setup on/off (VCP 0xA2)
+ ///
+ public static VcpFeature AutoSetupOnOff => new(0xA2, "auto-setup-on-off", "Auto setup on/off", VcpFeatureType.WriteOnly, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Window mask control (VCP 0xA4)
+ ///
+ public static VcpFeature WindowMaskControl => new(0xA4, "window-mask-control", "Window mask control", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Change the selected window (VCP 0xA5)
+ ///
+ public static VcpFeature ChangeSelectedWindow => new(0xA5, "change-selected-window", "Change the selected window", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Screen Orientation (VCP 0xAA)
+ ///
+ public static VcpFeature ScreenOrientation => new(0xAA, "screen-orientation", "Screen Orientation", VcpFeatureType.ReadOnly, VcpFeatureCategory.ImageAdjustment, false, "orientation");
+
+ ///
+ /// Stereo video mode (VCP 0xD4)
+ ///
+ public static VcpFeature StereoVideoMode => new(0xD4, "stereo-video-mode", "Stereo video mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Scan mode (VCP 0xDA)
+ ///
+ public static VcpFeature ScanMode => new(0xDA, "scan-mode", "Scan mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+ ///
+ /// Image Mode (VCP 0xDB)
+ ///
+ public static VcpFeature ImageMode => new(0xDB, "image-mode", "Image Mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false, "mode");
+
+ ///
+ /// Display Mode (VCP 0xDC)
+ ///
+ public static VcpFeature DisplayMode => new(0xDC, "display-mode", "Display Mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
+
+
+ // ===== MISCELLANEOUS FEATURES =====
+
+ ///
+ /// Active control (VCP 0x52)
+ ///
+ public static VcpFeature ActiveControl => new(0x52, "active-control", "Active control", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Performance Preservation (VCP 0x54)
+ ///
+ public static VcpFeature PerformancePreservation => new(0x54, "performance-preservation", "Performance Preservation", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Ambient light sensor (VCP 0x66)
+ ///
+ public static VcpFeature AmbientLightSensor => new(0x66, "ambient-light-sensor", "Ambient light sensor", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// LUT Size (VCP 0x73)
+ ///
+ public static VcpFeature LUTSize => new(0x73, "lut-size", "LUT Size", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Single point LUT operation (VCP 0x74)
+ ///
+ public static VcpFeature SinglePointLUTOperation => new(0x74, "single-point-lut-operation", "Single point LUT operation", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Block LUT operation (VCP 0x75)
+ ///
+ public static VcpFeature BlockLUTOperation => new(0x75, "block-lut-operation", "Block LUT operation", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Remote Procedure Call (VCP 0x76)
+ ///
+ public static VcpFeature RemoteProcedureCall => new(0x76, "remote-procedure-call", "Remote Procedure Call", VcpFeatureType.WriteOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Display Identification Operation (VCP 0x78)
+ ///
+ public static VcpFeature DisplayIdentificationOperation => new(0x78, "display-identification-operation", "Display Identification Operation", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// TV Channel Up/Down (VCP 0x8B)
+ ///
+ public static VcpFeature TVChannelUpDown => new(0x8B, "tv-channel-up-down", "TV Channel Up/Down", VcpFeatureType.WriteOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Horizontal frequency (VCP 0xAC)
+ ///
+ public static VcpFeature HorizontalFrequency => new(0xAC, "horizontal-frequency", "Horizontal frequency", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false, "h-freq");
+
+ ///
+ /// Vertical frequency (VCP 0xAE)
+ ///
+ public static VcpFeature VerticalFrequency => new(0xAE, "vertical-frequency", "Vertical frequency", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false, "v-freq");
+
+ ///
+ /// Settings (VCP 0xB0)
+ ///
+ public static VcpFeature Settings => new(0xB0, "settings", "Settings", VcpFeatureType.WriteOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Flat panel sub-pixel layout (VCP 0xB2)
+ ///
+ public static VcpFeature FlatPanelSubPixelLayout => new(0xB2, "flat-panel-sub-pixel-layout", "Flat panel sub-pixel layout", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Source Timing Mode (VCP 0xB4)
+ ///
+ public static VcpFeature SourceTimingMode => new(0xB4, "source-timing-mode", "Source Timing Mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Display technology type (VCP 0xB6)
+ ///
+ public static VcpFeature DisplayTechnologyType => new(0xB6, "display-technology-type", "Display technology type", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Monitor status (VCP 0xB7)
+ ///
+ public static VcpFeature MonitorStatus => new(0xB7, "monitor-status", "Monitor status", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Packet count (VCP 0xB8)
+ ///
+ public static VcpFeature PacketCount => new(0xB8, "packet-count", "Packet count", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Monitor X origin (VCP 0xB9)
+ ///
+ public static VcpFeature MonitorXOrigin => new(0xB9, "monitor-x-origin", "Monitor X origin", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Monitor Y origin (VCP 0xBA)
+ ///
+ public static VcpFeature MonitorYOrigin => new(0xBA, "monitor-y-origin", "Monitor Y origin", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Header error count (VCP 0xBB)
+ ///
+ public static VcpFeature HeaderErrorCount => new(0xBB, "header-error-count", "Header error count", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Body CRC error count (VCP 0xBC)
+ ///
+ public static VcpFeature BodyCRCErrorCount => new(0xBC, "body-crc-error-count", "Body CRC error count", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Client ID (VCP 0xBD)
+ ///
+ public static VcpFeature ClientID => new(0xBD, "client-id", "Client ID", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Link control (VCP 0xBE)
+ ///
+ public static VcpFeature LinkControl => new(0xBE, "link-control", "Link control", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Display usage time (VCP 0xC0)
+ ///
+ public static VcpFeature DisplayUsageTime => new(0xC0, "display-usage-time", "Display usage time", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Display descriptor length (VCP 0xC2)
+ ///
+ public static VcpFeature DisplayDescriptorLength => new(0xC2, "display-descriptor-length", "Display descriptor length", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Transmit display descriptor (VCP 0xC3)
+ ///
+ public static VcpFeature TransmitDisplayDescriptor => new(0xC3, "transmit-display-descriptor", "Transmit display descriptor", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Enable display of 'display descriptor' (VCP 0xC4)
+ ///
+ public static VcpFeature EnableDisplayOfDisplayDescriptor => new(0xC4, "enable-display-of-display-descriptor", "Enable display of 'display descriptor'", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Application enable key (VCP 0xC6)
+ ///
+ public static VcpFeature ApplicationEnableKey => new(0xC6, "application-enable-key", "Application enable key", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Display controller type (VCP 0xC8)
+ ///
+ public static VcpFeature DisplayControllerType => new(0xC8, "display-controller-type", "Display controller type", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Display firmware level (VCP 0xC9)
+ ///
+ public static VcpFeature DisplayFirmwareLevel => new(0xC9, "display-firmware-level", "Display firmware level", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false, "firmware");
+
+ ///
+ /// OSD/Button Control (VCP 0xCA)
+ ///
+ public static VcpFeature OSDButtonControl => new(0xCA, "osd-button-control", "OSD/Button Control", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false, "osd");
+
+ ///
+ /// OSD Language (VCP 0xCC)
+ ///
+ public static VcpFeature OSDLanguage => new(0xCC, "osd-language", "OSD Language", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false, "language");
+
+ ///
+ /// Status Indicators (VCP 0xCD)
+ ///
+ public static VcpFeature StatusIndicators => new(0xCD, "status-indicators", "Status Indicators", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Auxiliary display size (VCP 0xCE)
+ ///
+ public static VcpFeature AuxiliaryDisplaySize => new(0xCE, "auxiliary-display-size", "Auxiliary display size", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Auxiliary display data (VCP 0xCF)
+ ///
+ public static VcpFeature AuxiliaryDisplayData => new(0xCF, "auxiliary-display-data", "Auxiliary display data", VcpFeatureType.WriteOnly, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Output select (VCP 0xD0)
+ ///
+ public static VcpFeature OutputSelect => new(0xD0, "output-select", "Output select", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Asset Tag (VCP 0xD2)
+ ///
+ public static VcpFeature AssetTag => new(0xD2, "asset-tag", "Asset Tag", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Power mode (VCP 0xD6)
+ ///
+ public static VcpFeature PowerMode => new(0xD6, "power-mode", "Power mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false, "power");
+
+ ///
+ /// Auxiliary power output (VCP 0xD7)
+ ///
+ public static VcpFeature AuxiliaryPowerOutput => new(0xD7, "auxiliary-power-output", "Auxiliary power output", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// Scratch Pad (VCP 0xDE)
+ ///
+ public static VcpFeature ScratchPad => new(0xDE, "scratch-pad", "Scratch Pad", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
+
+ ///
+ /// VCP Version (VCP 0xDF)
+ ///
+ public static VcpFeature VcpVersion => new(0xDF, "vcp-version", "VCP Version", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false, "version");
+
+
+ // ===== AUDIO FEATURES =====
+
+ ///
+ /// Audio speaker volume (VCP 0x62)
+ ///
+ public static VcpFeature AudioVolume => new(0x62, "volume", "Audio speaker volume", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, true, "vol");
+
+ ///
+ /// Speaker Select (VCP 0x63)
+ ///
+ public static VcpFeature SpeakerSelect => new(0x63, "speaker-select", "Speaker Select", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, false);
+
+ ///
+ /// Audio: Microphone Volume (VCP 0x64)
+ ///
+ public static VcpFeature MicrophoneVolume => new(0x64, "microphone-volume", "Audio: Microphone Volume", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, true, "mic-volume");
+
+ ///
+ /// Audio mute/Screen blank (VCP 0x8D)
+ ///
+ public static VcpFeature AudioMute => new(0x8D, "mute", "Audio mute/Screen blank", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, false);
+
+ ///
+ /// Audio treble (VCP 0x8F)
+ ///
+ public static VcpFeature AudioTreble => new(0x8F, "audio-treble", "Audio treble", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, true, "treble");
+
+ ///
+ /// Audio bass (VCP 0x91)
+ ///
+ public static VcpFeature AudioBass => new(0x91, "audio-bass", "Audio bass", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, true, "bass");
+
+ ///
+ /// Audio balance L/R (VCP 0x93)
+ ///
+ public static VcpFeature AudioBalance => new(0x93, "audio-balance", "Audio balance L/R", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, true, "balance");
+
+ ///
+ /// Audio processor mode (VCP 0x94)
+ ///
+ public static VcpFeature AudioProcessorMode => new(0x94, "audio-processor-mode", "Audio processor mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, false);
+
+
+ ///
+ /// All features registry - contains all predefined MCCS features
+ ///
+ public static IReadOnlyList AllFeatures { get; } = new List
+ {
+ Degauss,
+ NewControlValue,
+ SoftControls,
+ RestoreDefaults,
+ RestoreBrightnessContrastDefaults,
+ RestoreGeometryDefaults,
+ RestoreColorDefaults,
+ RestoreTVDefaults,
+ ColorTemperatureIncrement,
+ ColorTemperatureRequest,
+ Clock,
+ Brightness,
+ FleshToneEnhancement,
+ Contrast,
+ Backlight,
+ SelectColorPreset,
+ RedGain,
+ UserColorVisionCompensation,
+ GreenGain,
+ BlueGain,
+ Focus,
+ AutoSetup,
+ AutoColorSetup,
+ HorizontalPosition,
+ HorizontalSize,
+ HorizontalPincushion,
+ HorizontalPincushionBalance,
+ HorizontalConvergenceRB,
+ HorizontalConvergenceMG,
+ HorizontalLinearity,
+ HorizontalLinearityBalance,
+ GrayScaleExpansion,
+ VerticalPosition,
+ VerticalSize,
+ VerticalPincushion,
+ VerticalPincushionBalance,
+ VerticalConvergenceRB,
+ VerticalConvergenceMG,
+ VerticalLinearity,
+ VerticalLinearityBalance,
+ ClockPhase,
+ HorizontalParallelogram,
+ VerticalParallelogram,
+ HorizontalKeystone,
+ VerticalKeystone,
+ Rotation,
+ TopCornerFlare,
+ TopCornerHook,
+ BottomCornerFlare,
+ BottomCornerHook,
+ ActiveControl,
+ PerformancePreservation,
+ HorizontalMoire,
+ VerticalMoire,
+ SixAxisSaturationRed,
+ SixAxisSaturationYellow,
+ SixAxisSaturationGreen,
+ SixAxisSaturationCyan,
+ SixAxisSaturationBlue,
+ SixAxisSaturationMagenta,
+ InputSource,
+ AudioVolume,
+ SpeakerSelect,
+ MicrophoneVolume,
+ AmbientLightSensor,
+ BacklightLevelWhite,
+ RedBlackLevel,
+ BacklightLevelRed,
+ GreenBlackLevel,
+ BacklightLevelGreen,
+ BlueBlackLevel,
+ BacklightLevelBlue,
+ Gamma,
+ LUTSize,
+ SinglePointLUTOperation,
+ BlockLUTOperation,
+ RemoteProcedureCall,
+ DisplayIdentificationOperation,
+ AdjustFocalPlane,
+ AdjustZoom,
+ Trapezoid,
+ Keystone,
+ HorizontalMirror,
+ VerticalMirror,
+ DisplayScaling,
+ Sharpness,
+ VelocityScanModulation,
+ ColorSaturation,
+ TVChannelUpDown,
+ TVSharpness,
+ AudioMute,
+ TVContrast,
+ AudioTreble,
+ Hue,
+ AudioBass,
+ TVBlackLevel,
+ AudioBalance,
+ AudioProcessorMode,
+ WindowPositionTLX,
+ WindowPositionTLY,
+ WindowPositionBRX,
+ WindowPositionBRY,
+ WindowControlOnOff,
+ WindowBackground,
+ SixAxisHueRed,
+ SixAxisHueYellow,
+ SixAxisHueGreen,
+ SixAxisHueCyan,
+ SixAxisHueBlue,
+ SixAxisHueMagenta,
+ AutoSetupOnOff,
+ WindowMaskControl,
+ ChangeSelectedWindow,
+ ScreenOrientation,
+ HorizontalFrequency,
+ VerticalFrequency,
+ Settings,
+ FlatPanelSubPixelLayout,
+ SourceTimingMode,
+ DisplayTechnologyType,
+ MonitorStatus,
+ PacketCount,
+ MonitorXOrigin,
+ MonitorYOrigin,
+ HeaderErrorCount,
+ BodyCRCErrorCount,
+ ClientID,
+ LinkControl,
+ DisplayUsageTime,
+ DisplayDescriptorLength,
+ TransmitDisplayDescriptor,
+ EnableDisplayOfDisplayDescriptor,
+ ApplicationEnableKey,
+ DisplayControllerType,
+ DisplayFirmwareLevel,
+ OSDButtonControl,
+ OSDLanguage,
+ StatusIndicators,
+ AuxiliaryDisplaySize,
+ AuxiliaryDisplayData,
+ OutputSelect,
+ AssetTag,
+ StereoVideoMode,
+ PowerMode,
+ AuxiliaryPowerOutput,
+ ScanMode,
+ ImageMode,
+ DisplayMode,
+ ScratchPad,
+ VcpVersion
+ };
+}
diff --git a/DDCSwitch/VcpFeature.cs b/DDCSwitch/VcpFeature.cs
index b82093b..74c19c1 100644
--- a/DDCSwitch/VcpFeature.cs
+++ b/DDCSwitch/VcpFeature.cs
@@ -1,4 +1,4 @@
-namespace DDCSwitch;
+namespace DDCSwitch;
///
/// Defines the access type for a VCP feature
@@ -58,9 +58,11 @@ public enum VcpFeatureCategory
}
///
-/// Represents a VCP (Virtual Control Panel) feature with its properties
+/// Represents a VCP (Virtual Control Panel) feature with its properties.
+/// This is a partial class - feature definitions are in VcpFeature.Generated.cs
+/// which is auto-generated from VcpFeatureData.json.
///
-public class VcpFeature
+public partial class VcpFeature
{
public byte Code { get; }
public string Name { get; }
@@ -78,7 +80,7 @@ public VcpFeature(byte code, string name, string description, VcpFeatureType typ
Type = type;
Category = category;
SupportsPercentage = supportsPercentage;
- Aliases = aliases ?? Array.Empty();
+ Aliases = aliases;
}
// Legacy constructor for backward compatibility
@@ -87,952 +89,6 @@ public VcpFeature(byte code, string name, VcpFeatureType type, bool supportsPerc
{
}
- // ===== IMAGE ADJUSTMENT FEATURES =====
-
- ///
- /// Brightness control (VCP 0x10)
- ///
- public static VcpFeature Brightness => new(0x10, "brightness", "Brightness control", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true, "bright");
-
- ///
- /// Flesh tone enhancement (VCP 0x11)
- ///
- public static VcpFeature FleshToneEnhancement => new(0x11, "flesh-tone-enhancement", "Flesh tone enhancement", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
-
- ///
- /// Contrast control (VCP 0x12)
- ///
- public static VcpFeature Contrast => new(0x12, "contrast", "Contrast control", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
-
- ///
- /// Backlight control (VCP 0x13)
- ///
- public static VcpFeature Backlight => new(0x13, "backlight", "Backlight control", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
-
- ///
- /// Select color preset (VCP 0x14)
- ///
- public static VcpFeature SelectColorPreset => new(0x14, "select-color-preset", "Select color preset", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false, "color-preset");
-
- ///
- /// Focus (VCP 0x1C)
- ///
- public static VcpFeature Focus => new(0x1C, "focus", "Focus", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
-
- ///
- /// Auto setup (VCP 0x1E)
- ///
- public static VcpFeature AutoSetup => new(0x1E, "auto-setup", "Auto setup", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
-
- ///
- /// Auto color setup (VCP 0x1F)
- ///
- public static VcpFeature AutoColorSetup => new(0x1F, "auto-color-setup", "Auto color setup", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
-
- ///
- /// Gray scale expansion (VCP 0x2E)
- ///
- public static VcpFeature GrayScaleExpansion => new(0x2E, "gray-scale-expansion", "Gray scale expansion", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
-
- ///
- /// Horizontal Moire (VCP 0x56)
- ///
- public static VcpFeature HorizontalMoire => new(0x56, "horizontal-moire", "Horizontal Moire", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
-
- ///
- /// Vertical Moire (VCP 0x58)
- ///
- public static VcpFeature VerticalMoire => new(0x58, "vertical-moire", "Vertical Moire", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
-
- ///
- /// Input source selection (VCP 0x60)
- ///
- public static VcpFeature InputSource => new(0x60, "input", "Input source selection", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false, "source");
-
- ///
- /// Adjust Focal Plane (VCP 0x7A)
- ///
- public static VcpFeature AdjustFocalPlane => new(0x7A, "adjust-focal-plane", "Adjust Focal Plane", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
-
- ///
- /// Adjust Zoom (VCP 0x7C)
- ///
- public static VcpFeature AdjustZoom => new(0x7C, "adjust-zoom", "Adjust Zoom", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
-
- ///
- /// Trapezoid (VCP 0x7E)
- ///
- public static VcpFeature Trapezoid => new(0x7E, "trapezoid", "Trapezoid", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
-
- ///
- /// Keystone (VCP 0x80)
- ///
- public static VcpFeature Keystone => new(0x80, "keystone", "Keystone", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
-
- ///
- /// Horizontal Mirror (Flip) (VCP 0x82)
- ///
- public static VcpFeature HorizontalMirror => new(0x82, "horizontal-mirror", "Horizontal Mirror (Flip)", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
-
- ///
- /// Vertical Mirror (Flip) (VCP 0x84)
- ///
- public static VcpFeature VerticalMirror => new(0x84, "vertical-mirror", "Vertical Mirror (Flip)", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
-
- ///
- /// Display Scaling (VCP 0x86)
- ///
- public static VcpFeature DisplayScaling => new(0x86, "display-scaling", "Display Scaling", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
-
- ///
- /// Sharpness control (VCP 0x87)
- ///
- public static VcpFeature Sharpness => new(0x87, "sharpness", "Sharpness control", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true, "sharp");
-
- ///
- /// Velocity Scan Modulation (VCP 0x88)
- ///
- public static VcpFeature VelocityScanModulation => new(0x88, "velocity-scan-modulation", "Velocity Scan Modulation", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
-
- ///
- /// TV Sharpness (VCP 0x8C)
- ///
- public static VcpFeature TVSharpness => new(0x8C, "tv-sharpness", "TV Sharpness", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
-
- ///
- /// TV Contrast (VCP 0x8E)
- ///
- public static VcpFeature TVContrast => new(0x8E, "tv-contrast", "TV Contrast", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
-
- ///
- /// TV Black level/Luminesence (VCP 0x92)
- ///
- public static VcpFeature TVBlackLevel => new(0x92, "tv-black-level", "TV Black level/Luminesence", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
-
- ///
- /// Window background (VCP 0x9A)
- ///
- public static VcpFeature WindowBackground => new(0x9A, "window-background", "Window background", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, true);
-
- ///
- /// Auto setup on/off (VCP 0xA2)
- ///
- public static VcpFeature AutoSetupOnOff => new(0xA2, "auto-setup-on-off", "Auto setup on/off", VcpFeatureType.WriteOnly, VcpFeatureCategory.ImageAdjustment, false);
-
- ///
- /// Window mask control (VCP 0xA4)
- ///
- public static VcpFeature WindowMaskControl => new(0xA4, "window-mask-control", "Window mask control", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
-
- ///
- /// Change the selected window (VCP 0xA5)
- ///
- public static VcpFeature ChangeSelectedWindow => new(0xA5, "change-selected-window", "Change the selected window", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
-
- ///
- /// Screen Orientation (VCP 0xAA)
- ///
- public static VcpFeature ScreenOrientation => new(0xAA, "screen-orientation", "Screen Orientation", VcpFeatureType.ReadOnly, VcpFeatureCategory.ImageAdjustment, false, "orientation");
-
- ///
- /// Stereo video mode (VCP 0xD4)
- ///
- public static VcpFeature StereoVideoMode => new(0xD4, "stereo-video-mode", "Stereo video mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
-
- ///
- /// Scan mode (VCP 0xDA)
- ///
- public static VcpFeature ScanMode => new(0xDA, "scan-mode", "Scan mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
-
- ///
- /// Image Mode (VCP 0xDB)
- ///
- public static VcpFeature ImageMode => new(0xDB, "image-mode", "Image Mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false, "mode");
-
- ///
- /// Display Mode (VCP 0xDC)
- ///
- public static VcpFeature DisplayMode => new(0xDC, "display-mode", "Display Mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.ImageAdjustment, false);
-
- // ===== COLOR CONTROL FEATURES =====
-
- ///
- /// Red video gain (VCP 0x16)
- ///
- public static VcpFeature RedGain => new(0x16, "red-gain", "Video gain: Red", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true, "red");
-
- ///
- /// User color vision compensation (VCP 0x17)
- ///
- public static VcpFeature UserColorVisionCompensation => new(0x17, "user-color-vision-compensation", "User color vision compensation", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// Green video gain (VCP 0x18)
- ///
- public static VcpFeature GreenGain => new(0x18, "green-gain", "Video gain: Green", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true, "green");
-
- ///
- /// Blue video gain (VCP 0x1A)
- ///
- public static VcpFeature BlueGain => new(0x1A, "blue-gain", "Video gain: Blue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true, "blue");
-
- ///
- /// 6 axis saturation: Red (VCP 0x59)
- ///
- public static VcpFeature SixAxisSaturationRed => new(0x59, "6-axis-saturation-red", "6 axis saturation: Red", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// 6 axis saturation: Yellow (VCP 0x5A)
- ///
- public static VcpFeature SixAxisSaturationYellow => new(0x5A, "6-axis-saturation-yellow", "6 axis saturation: Yellow", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// 6 axis saturation: Green (VCP 0x5B)
- ///
- public static VcpFeature SixAxisSaturationGreen => new(0x5B, "6-axis-saturation-green", "6 axis saturation: Green", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// 6 axis saturation: Cyan (VCP 0x5C)
- ///
- public static VcpFeature SixAxisSaturationCyan => new(0x5C, "6-axis-saturation-cyan", "6 axis saturation: Cyan", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// 6 axis saturation: Blue (VCP 0x5D)
- ///
- public static VcpFeature SixAxisSaturationBlue => new(0x5D, "6-axis-saturation-blue", "6 axis saturation: Blue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// 6 axis saturation: Magenta (VCP 0x5E)
- ///
- public static VcpFeature SixAxisSaturationMagenta => new(0x5E, "6-axis-saturation-magenta", "6 axis saturation: Magenta", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// Backlight Level: White (VCP 0x6B)
- ///
- public static VcpFeature BacklightLevelWhite => new(0x6B, "backlight-level-white", "Backlight Level: White", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// Red video black level (VCP 0x6C)
- ///
- public static VcpFeature RedBlackLevel => new(0x6C, "red-black-level", "Video black level: Red", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// Backlight Level: Red (VCP 0x6D)
- ///
- public static VcpFeature BacklightLevelRed => new(0x6D, "backlight-level-red", "Backlight Level: Red", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// Green video black level (VCP 0x6E)
- ///
- public static VcpFeature GreenBlackLevel => new(0x6E, "green-black-level", "Video black level: Green", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// Backlight Level: Green (VCP 0x6F)
- ///
- public static VcpFeature BacklightLevelGreen => new(0x6F, "backlight-level-green", "Backlight Level: Green", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// Blue video black level (VCP 0x70)
- ///
- public static VcpFeature BlueBlackLevel => new(0x70, "blue-black-level", "Video black level: Blue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// Backlight Level: Blue (VCP 0x71)
- ///
- public static VcpFeature BacklightLevelBlue => new(0x71, "backlight-level-blue", "Backlight Level: Blue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// Gamma (VCP 0x72)
- ///
- public static VcpFeature Gamma => new(0x72, "gamma", "Gamma", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, false);
-
- ///
- /// Color Saturation (VCP 0x8A)
- ///
- public static VcpFeature ColorSaturation => new(0x8A, "color-saturation", "Color Saturation", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true, "saturation", "sat");
-
- ///
- /// Hue (VCP 0x90)
- ///
- public static VcpFeature Hue => new(0x90, "hue", "Hue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// 6 axis hue control: Red (VCP 0x9B)
- ///
- public static VcpFeature SixAxisHueRed => new(0x9B, "6-axis-hue-red", "6 axis hue control: Red", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// 6 axis hue control: Yellow (VCP 0x9C)
- ///
- public static VcpFeature SixAxisHueYellow => new(0x9C, "6-axis-hue-yellow", "6 axis hue control: Yellow", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// 6 axis hue control: Green (VCP 0x9D)
- ///
- public static VcpFeature SixAxisHueGreen => new(0x9D, "6-axis-hue-green", "6 axis hue control: Green", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// 6 axis hue control: Cyan (VCP 0x9E)
- ///
- public static VcpFeature SixAxisHueCyan => new(0x9E, "6-axis-hue-cyan", "6 axis hue control: Cyan", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// 6 axis hue control: Blue (VCP 0x9F)
- ///
- public static VcpFeature SixAxisHueBlue => new(0x9F, "6-axis-hue-blue", "6 axis hue control: Blue", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- ///
- /// 6 axis hue control: Magenta (VCP 0xA0)
- ///
- public static VcpFeature SixAxisHueMagenta => new(0xA0, "6-axis-hue-magenta", "6 axis hue control: Magenta", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, true);
-
- // ===== GEOMETRY FEATURES (mainly CRT) =====
-
- ///
- /// Horizontal position (VCP 0x20)
- ///
- public static VcpFeature HorizontalPosition => new(0x20, "h-position", "Horizontal Position (Phase)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false, "h-pos");
-
- ///
- /// Horizontal size (VCP 0x22)
- ///
- public static VcpFeature HorizontalSize => new(0x22, "h-size", "Horizontal Size", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Horizontal pincushion (VCP 0x24)
- ///
- public static VcpFeature HorizontalPincushion => new(0x24, "h-pincushion", "Horizontal Pincushion", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Horizontal pincushion balance (VCP 0x26)
- ///
- public static VcpFeature HorizontalPincushionBalance => new(0x26, "h-pincushion-balance", "Horizontal Pincushion Balance", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Horizontal convergence R/B (VCP 0x28)
- ///
- public static VcpFeature HorizontalConvergenceRB => new(0x28, "h-convergence-rb", "Horizontal Convergence R/B", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Horizontal convergence M/G (VCP 0x29)
- ///
- public static VcpFeature HorizontalConvergenceMG => new(0x29, "h-convergence-mg", "Horizontal Convergence M/G", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Horizontal linearity (VCP 0x2A)
- ///
- public static VcpFeature HorizontalLinearity => new(0x2A, "h-linearity", "Horizontal Linearity", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Horizontal linearity balance (VCP 0x2C)
- ///
- public static VcpFeature HorizontalLinearityBalance => new(0x2C, "h-linearity-balance", "Horizontal Linearity Balance", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Vertical position (VCP 0x30)
- ///
- public static VcpFeature VerticalPosition => new(0x30, "v-position", "Vertical Position (Phase)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false, "v-pos");
-
- ///
- /// Vertical size (VCP 0x32)
- ///
- public static VcpFeature VerticalSize => new(0x32, "v-size", "Vertical Size", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Vertical pincushion (VCP 0x34)
- ///
- public static VcpFeature VerticalPincushion => new(0x34, "v-pincushion", "Vertical Pincushion", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Vertical pincushion balance (VCP 0x36)
- ///
- public static VcpFeature VerticalPincushionBalance => new(0x36, "v-pincushion-balance", "Vertical Pincushion Balance", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Vertical convergence R/B (VCP 0x38)
- ///
- public static VcpFeature VerticalConvergenceRB => new(0x38, "v-convergence-rb", "Vertical Convergence R/B", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Vertical convergence M/G (VCP 0x39)
- ///
- public static VcpFeature VerticalConvergenceMG => new(0x39, "v-convergence-mg", "Vertical Convergence M/G", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Vertical linearity (VCP 0x3A)
- ///
- public static VcpFeature VerticalLinearity => new(0x3A, "v-linearity", "Vertical Linearity", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Vertical linearity balance (VCP 0x3C)
- ///
- public static VcpFeature VerticalLinearityBalance => new(0x3C, "v-linearity-balance", "Vertical Linearity Balance", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Clock phase (VCP 0x3E)
- ///
- public static VcpFeature ClockPhase => new(0x3E, "clock-phase", "Clock Phase", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false, "phase");
-
- ///
- /// Horizontal Parallelogram (VCP 0x40)
- ///
- public static VcpFeature HorizontalParallelogram => new(0x40, "horizontal-parallelogram", "Horizontal Parallelogram", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Vertical Parallelogram (VCP 0x41)
- ///
- public static VcpFeature VerticalParallelogram => new(0x41, "vertical-parallelogram", "Vertical Parallelogram", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Horizontal Keystone (VCP 0x42)
- ///
- public static VcpFeature HorizontalKeystone => new(0x42, "horizontal-keystone", "Horizontal Keystone", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Vertical Keystone (VCP 0x43)
- ///
- public static VcpFeature VerticalKeystone => new(0x43, "vertical-keystone", "Vertical Keystone", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Rotation (VCP 0x44)
- ///
- public static VcpFeature Rotation => new(0x44, "rotation", "Rotation", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Top Corner Flare (VCP 0x46)
- ///
- public static VcpFeature TopCornerFlare => new(0x46, "top-corner-flare", "Top Corner Flare", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Top Corner Hook (VCP 0x48)
- ///
- public static VcpFeature TopCornerHook => new(0x48, "top-corner-hook", "Top Corner Hook", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Bottom Corner Flare (VCP 0x4A)
- ///
- public static VcpFeature BottomCornerFlare => new(0x4A, "bottom-corner-flare", "Bottom Corner Flare", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Bottom Corner Hook (VCP 0x4C)
- ///
- public static VcpFeature BottomCornerHook => new(0x4C, "bottom-corner-hook", "Bottom Corner Hook", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Window Position(TL_X) (VCP 0x95)
- ///
- public static VcpFeature WindowPositionTLX => new(0x95, "window-position-tl-x", "Window Position(TL_X)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Window Position(TL_Y) (VCP 0x96)
- ///
- public static VcpFeature WindowPositionTLY => new(0x96, "window-position-tl-y", "Window Position(TL_Y)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Window Position(BR_X) (VCP 0x97)
- ///
- public static VcpFeature WindowPositionBRX => new(0x97, "window-position-br-x", "Window Position(BR_X)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Window Position(BR_Y) (VCP 0x98)
- ///
- public static VcpFeature WindowPositionBRY => new(0x98, "window-position-br-y", "Window Position(BR_Y)", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- ///
- /// Window control on/off (VCP 0x99)
- ///
- public static VcpFeature WindowControlOnOff => new(0x99, "window-control-on-off", "Window control on/off", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- // ===== AUDIO FEATURES =====
-
- ///
- /// Audio speaker volume (VCP 0x62)
- ///
- public static VcpFeature AudioVolume => new(0x62, "volume", "Audio speaker volume", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, true, "vol");
-
- ///
- /// Speaker Select (VCP 0x63)
- ///
- public static VcpFeature SpeakerSelect => new(0x63, "speaker-select", "Speaker Select", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, false);
-
- ///
- /// Audio: Microphone Volume (VCP 0x64)
- ///
- public static VcpFeature MicrophoneVolume => new(0x64, "microphone-volume", "Audio: Microphone Volume", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, true, "mic-volume");
-
- ///
- /// Audio mute/Screen blank (VCP 0x8D)
- ///
- public static VcpFeature AudioMute => new(0x8D, "mute", "Audio mute/Screen blank", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, false);
-
- ///
- /// Audio treble (VCP 0x8F)
- ///
- public static VcpFeature AudioTreble => new(0x8F, "audio-treble", "Audio treble", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, true, "treble");
-
- ///
- /// Audio bass (VCP 0x91)
- ///
- public static VcpFeature AudioBass => new(0x91, "audio-bass", "Audio bass", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, true, "bass");
-
- ///
- /// Audio balance L/R (VCP 0x93)
- ///
- public static VcpFeature AudioBalance => new(0x93, "audio-balance", "Audio balance L/R", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, true, "balance");
-
- ///
- /// Audio processor mode (VCP 0x94)
- ///
- public static VcpFeature AudioProcessorMode => new(0x94, "audio-processor-mode", "Audio processor mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.Audio, false);
-
- // ===== PRESET FEATURES =====
-
- ///
- /// Degauss (VCP 0x01)
- ///
- public static VcpFeature Degauss => new(0x01, "degauss", "Degauss", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false);
-
- ///
- /// New control value (VCP 0x02)
- ///
- public static VcpFeature NewControlValue => new(0x02, "new-control-value", "New control value", VcpFeatureType.ReadWrite, VcpFeatureCategory.Preset, false);
-
- ///
- /// Soft controls (VCP 0x03)
- ///
- public static VcpFeature SoftControls => new(0x03, "soft-controls", "Soft controls", VcpFeatureType.ReadWrite, VcpFeatureCategory.Preset, false);
-
- ///
- /// Restore factory defaults (VCP 0x04)
- ///
- public static VcpFeature RestoreDefaults => new(0x04, "restore-defaults", "Restore factory defaults", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false, "factory-reset");
-
- ///
- /// Restore factory brightness/contrast defaults (VCP 0x05)
- ///
- public static VcpFeature RestoreBrightnessContrastDefaults => new(0x05, "restore-brightness-contrast", "Restore factory brightness/contrast defaults", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false);
-
- ///
- /// Restore factory geometry defaults (VCP 0x06)
- ///
- public static VcpFeature RestoreGeometryDefaults => new(0x06, "restore-geometry", "Restore factory geometry defaults", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false);
-
- ///
- /// Restore factory color defaults (VCP 0x08)
- ///
- public static VcpFeature RestoreColorDefaults => new(0x08, "restore-color", "Restore factory color defaults", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false);
-
- ///
- /// Restore factory TV defaults (VCP 0x0A)
- ///
- public static VcpFeature RestoreTVDefaults => new(0x0A, "restore-tv-defaults", "Restore factory TV defaults", VcpFeatureType.WriteOnly, VcpFeatureCategory.Preset, false);
-
- ///
- /// Color temperature increment (VCP 0x0B)
- ///
- public static VcpFeature ColorTemperatureIncrement => new(0x0B, "color-temp-increment", "Color temperature increment", VcpFeatureType.ReadOnly, VcpFeatureCategory.ColorControl, false);
-
- ///
- /// Color temperature request (VCP 0x0C)
- ///
- public static VcpFeature ColorTemperatureRequest => new(0x0C, "color-temp-request", "Color temperature request", VcpFeatureType.ReadWrite, VcpFeatureCategory.ColorControl, false, "color-temp");
-
- ///
- /// Clock (VCP 0x0E)
- ///
- public static VcpFeature Clock => new(0x0E, "clock", "Clock", VcpFeatureType.ReadWrite, VcpFeatureCategory.Geometry, false);
-
- // ===== MISCELLANEOUS FEATURES =====
-
- ///
- /// Active control (VCP 0x52)
- ///
- public static VcpFeature ActiveControl => new(0x52, "active-control", "Active control", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Performance Preservation (VCP 0x54)
- ///
- public static VcpFeature PerformancePreservation => new(0x54, "performance-preservation", "Performance Preservation", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Ambient light sensor (VCP 0x66)
- ///
- public static VcpFeature AmbientLightSensor => new(0x66, "ambient-light-sensor", "Ambient light sensor", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// LUT Size (VCP 0x73)
- ///
- public static VcpFeature LUTSize => new(0x73, "lut-size", "LUT Size", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Single point LUT operation (VCP 0x74)
- ///
- public static VcpFeature SinglePointLUTOperation => new(0x74, "single-point-lut-operation", "Single point LUT operation", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Block LUT operation (VCP 0x75)
- ///
- public static VcpFeature BlockLUTOperation => new(0x75, "block-lut-operation", "Block LUT operation", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Remote Procedure Call (VCP 0x76)
- ///
- public static VcpFeature RemoteProcedureCall => new(0x76, "remote-procedure-call", "Remote Procedure Call", VcpFeatureType.WriteOnly, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Display Identification Operation (VCP 0x78)
- ///
- public static VcpFeature DisplayIdentificationOperation => new(0x78, "display-identification-operation", "Display Identification Operation", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// TV Channel Up/Down (VCP 0x8B)
- ///
- public static VcpFeature TVChannelUpDown => new(0x8B, "tv-channel-up-down", "TV Channel Up/Down", VcpFeatureType.WriteOnly, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Horizontal frequency (VCP 0xAC)
- ///
- public static VcpFeature HorizontalFrequency => new(0xAC, "horizontal-frequency", "Horizontal frequency", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false, "h-freq");
-
- ///
- /// Vertical frequency (VCP 0xAE)
- ///
- public static VcpFeature VerticalFrequency => new(0xAE, "vertical-frequency", "Vertical frequency", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false, "v-freq");
-
- ///
- /// Settings (VCP 0xB0)
- ///
- public static VcpFeature Settings => new(0xB0, "settings", "Settings", VcpFeatureType.WriteOnly, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Flat panel sub-pixel layout (VCP 0xB2)
- ///
- public static VcpFeature FlatPanelSubPixelLayout => new(0xB2, "flat-panel-sub-pixel-layout", "Flat panel sub-pixel layout", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Source Timing Mode (VCP 0xB4)
- ///
- public static VcpFeature SourceTimingMode => new(0xB4, "source-timing-mode", "Source Timing Mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Display technology type (VCP 0xB6)
- ///
- public static VcpFeature DisplayTechnologyType => new(0xB6, "display-technology-type", "Display technology type", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Monitor status (VCP 0xB7)
- ///
- public static VcpFeature MonitorStatus => new(0xB7, "monitor-status", "Monitor status", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Packet count (VCP 0xB8)
- ///
- public static VcpFeature PacketCount => new(0xB8, "packet-count", "Packet count", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Monitor X origin (VCP 0xB9)
- ///
- public static VcpFeature MonitorXOrigin => new(0xB9, "monitor-x-origin", "Monitor X origin", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Monitor Y origin (VCP 0xBA)
- ///
- public static VcpFeature MonitorYOrigin => new(0xBA, "monitor-y-origin", "Monitor Y origin", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Header error count (VCP 0xBB)
- ///
- public static VcpFeature HeaderErrorCount => new(0xBB, "header-error-count", "Header error count", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Body CRC error count (VCP 0xBC)
- ///
- public static VcpFeature BodyCRCErrorCount => new(0xBC, "body-crc-error-count", "Body CRC error count", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Client ID (VCP 0xBD)
- ///
- public static VcpFeature ClientID => new(0xBD, "client-id", "Client ID", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Link control (VCP 0xBE)
- ///
- public static VcpFeature LinkControl => new(0xBE, "link-control", "Link control", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Display usage time (VCP 0xC0)
- ///
- public static VcpFeature DisplayUsageTime => new(0xC0, "display-usage-time", "Display usage time", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Display descriptor length (VCP 0xC2)
- ///
- public static VcpFeature DisplayDescriptorLength => new(0xC2, "display-descriptor-length", "Display descriptor length", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Transmit display descriptor (VCP 0xC3)
- ///
- public static VcpFeature TransmitDisplayDescriptor => new(0xC3, "transmit-display-descriptor", "Transmit display descriptor", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Enable display of 'display descriptor' (VCP 0xC4)
- ///
- public static VcpFeature EnableDisplayOfDisplayDescriptor => new(0xC4, "enable-display-of-display-descriptor", "Enable display of 'display descriptor'", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Application enable key (VCP 0xC6)
- ///
- public static VcpFeature ApplicationEnableKey => new(0xC6, "application-enable-key", "Application enable key", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Display controller type (VCP 0xC8)
- ///
- public static VcpFeature DisplayControllerType => new(0xC8, "display-controller-type", "Display controller type", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Display firmware level (VCP 0xC9)
- ///
- public static VcpFeature DisplayFirmwareLevel => new(0xC9, "display-firmware-level", "Display firmware level", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false, "firmware");
-
- ///
- /// OSD/Button Control (VCP 0xCA)
- ///
- public static VcpFeature OSDButtonControl => new(0xCA, "osd-button-control", "OSD/Button Control", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false, "osd");
-
- ///
- /// OSD Language (VCP 0xCC)
- ///
- public static VcpFeature OSDLanguage => new(0xCC, "osd-language", "OSD Language", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false, "language");
-
- ///
- /// Status Indicators (VCP 0xCD)
- ///
- public static VcpFeature StatusIndicators => new(0xCD, "status-indicators", "Status Indicators", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Auxiliary display size (VCP 0xCE)
- ///
- public static VcpFeature AuxiliaryDisplaySize => new(0xCE, "auxiliary-display-size", "Auxiliary display size", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Auxiliary display data (VCP 0xCF)
- ///
- public static VcpFeature AuxiliaryDisplayData => new(0xCF, "auxiliary-display-data", "Auxiliary display data", VcpFeatureType.WriteOnly, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Output select (VCP 0xD0)
- ///
- public static VcpFeature OutputSelect => new(0xD0, "output-select", "Output select", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Asset Tag (VCP 0xD2)
- ///
- public static VcpFeature AssetTag => new(0xD2, "asset-tag", "Asset Tag", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Power mode (VCP 0xD6)
- ///
- public static VcpFeature PowerMode => new(0xD6, "power-mode", "Power mode", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false, "power");
-
- ///
- /// Auxiliary power output (VCP 0xD7)
- ///
- public static VcpFeature AuxiliaryPowerOutput => new(0xD7, "auxiliary-power-output", "Auxiliary power output", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// Scratch Pad (VCP 0xDE)
- ///
- public static VcpFeature ScratchPad => new(0xDE, "scratch-pad", "Scratch Pad", VcpFeatureType.ReadWrite, VcpFeatureCategory.Miscellaneous, false);
-
- ///
- /// VCP Version (VCP 0xDF)
- ///
- public static VcpFeature VcpVersion => new(0xDF, "vcp-version", "VCP Version", VcpFeatureType.ReadOnly, VcpFeatureCategory.Miscellaneous, false, "version");
-
- ///
- /// All features registry - contains all predefined MCCS features
- ///
- public static IReadOnlyList AllFeatures { get; } = BuildAllFeatures();
-
- ///
- /// Builds the complete registry of all MCCS VCP features
- ///
- private static List BuildAllFeatures()
- {
- return new List
- {
- // Preset Features (0x01-0x0F range)
- Degauss,
- NewControlValue,
- SoftControls,
- RestoreDefaults,
- RestoreBrightnessContrastDefaults,
- RestoreGeometryDefaults,
- RestoreColorDefaults,
- RestoreTVDefaults,
- ColorTemperatureIncrement,
- ColorTemperatureRequest,
- Clock,
-
- // Image Adjustment Features (0x10-0x1F range)
- Brightness,
- FleshToneEnhancement,
- Contrast,
- Backlight,
- SelectColorPreset,
- RedGain,
- UserColorVisionCompensation,
- GreenGain,
- BlueGain,
- Focus,
- AutoSetup,
- AutoColorSetup,
-
- // Geometry Features (0x20-0x4F range)
- HorizontalPosition,
- HorizontalSize,
- HorizontalPincushion,
- HorizontalPincushionBalance,
- HorizontalConvergenceRB,
- HorizontalConvergenceMG,
- HorizontalLinearity,
- HorizontalLinearityBalance,
- GrayScaleExpansion,
- VerticalPosition,
- VerticalSize,
- VerticalPincushion,
- VerticalPincushionBalance,
- VerticalConvergenceRB,
- VerticalConvergenceMG,
- VerticalLinearity,
- VerticalLinearityBalance,
- ClockPhase,
- HorizontalParallelogram,
- VerticalParallelogram,
- HorizontalKeystone,
- VerticalKeystone,
- Rotation,
- TopCornerFlare,
- TopCornerHook,
- BottomCornerFlare,
- BottomCornerHook,
-
- // Control and Performance Features (0x50-0x5F range)
- ActiveControl,
- PerformancePreservation,
- HorizontalMoire,
- VerticalMoire,
- SixAxisSaturationRed,
- SixAxisSaturationYellow,
- SixAxisSaturationGreen,
- SixAxisSaturationCyan,
- SixAxisSaturationBlue,
- SixAxisSaturationMagenta,
-
- // Input and Audio Features (0x60-0x6F range)
- InputSource,
- AudioVolume,
- SpeakerSelect,
- MicrophoneVolume,
- AmbientLightSensor,
- BacklightLevelWhite,
- RedBlackLevel,
- BacklightLevelRed,
- GreenBlackLevel,
- BacklightLevelGreen,
- BlueBlackLevel,
- BacklightLevelBlue,
-
- // Advanced Color and LUT Features (0x70-0x7F range)
- Gamma,
- LUTSize,
- SinglePointLUTOperation,
- BlockLUTOperation,
- RemoteProcedureCall,
- DisplayIdentificationOperation,
- AdjustFocalPlane,
- AdjustZoom,
- Trapezoid,
-
- // Image Processing Features (0x80-0x9F range)
- Keystone,
- HorizontalMirror,
- VerticalMirror,
- DisplayScaling,
- Sharpness,
- VelocityScanModulation,
- ColorSaturation,
- TVChannelUpDown,
- TVSharpness,
- AudioMute,
- TVContrast,
- AudioTreble,
- Hue,
- AudioBass,
- TVBlackLevel,
- AudioBalance,
- AudioProcessorMode,
- WindowPositionTLX,
- WindowPositionTLY,
- WindowPositionBRX,
- WindowPositionBRY,
- WindowControlOnOff,
- WindowBackground,
- SixAxisHueRed,
- SixAxisHueYellow,
- SixAxisHueGreen,
- SixAxisHueCyan,
- SixAxisHueBlue,
- SixAxisHueMagenta,
-
- // Setup and Window Features (0xA0-0xAF range)
- AutoSetupOnOff,
- WindowMaskControl,
- ChangeSelectedWindow,
- ScreenOrientation,
- HorizontalFrequency,
- VerticalFrequency,
-
- // System Information Features (0xB0-0xBF range)
- Settings,
- FlatPanelSubPixelLayout,
- SourceTimingMode,
- DisplayTechnologyType,
- MonitorStatus,
- PacketCount,
- MonitorXOrigin,
- MonitorYOrigin,
- HeaderErrorCount,
- BodyCRCErrorCount,
- ClientID,
- LinkControl,
-
- // Display Management Features (0xC0-0xDF range)
- DisplayUsageTime,
- DisplayDescriptorLength,
- TransmitDisplayDescriptor,
- EnableDisplayOfDisplayDescriptor,
- ApplicationEnableKey,
- DisplayControllerType,
- DisplayFirmwareLevel,
- OSDButtonControl,
- OSDLanguage,
- StatusIndicators,
- AuxiliaryDisplaySize,
- AuxiliaryDisplayData,
- OutputSelect,
- AssetTag,
- StereoVideoMode,
- PowerMode,
- AuxiliaryPowerOutput,
- ScanMode,
- ImageMode,
- DisplayMode,
- ScratchPad,
- VcpVersion
- };
- }
-
public override string ToString()
{
return $"{Name} (0x{Code:X2})";
@@ -1061,4 +117,5 @@ public record VcpFeatureInfo(
uint CurrentValue,
uint MaxValue,
bool IsSupported
-);
\ No newline at end of file
+);
+
diff --git a/DDCSwitch/VcpFeatureData.json b/DDCSwitch/VcpFeatureData.json
new file mode 100644
index 0000000..e555c5e
--- /dev/null
+++ b/DDCSwitch/VcpFeatureData.json
@@ -0,0 +1,1561 @@
+{
+ "$schema": "vcp-feature-schema",
+ "description": "VCP (VESA Display Data Channel Command Interface) feature definitions from MCCS specification",
+ "features": [
+ {
+ "code": "0x01",
+ "property": "Degauss",
+ "name": "degauss",
+ "description": "Degauss",
+ "type": "WriteOnly",
+ "category": "Preset",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x02",
+ "property": "NewControlValue",
+ "name": "new-control-value",
+ "description": "New control value",
+ "type": "ReadWrite",
+ "category": "Preset",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x03",
+ "property": "SoftControls",
+ "name": "soft-controls",
+ "description": "Soft controls",
+ "type": "ReadWrite",
+ "category": "Preset",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x04",
+ "property": "RestoreDefaults",
+ "name": "restore-defaults",
+ "description": "Restore factory defaults",
+ "type": "WriteOnly",
+ "category": "Preset",
+ "supportsPercentage": false,
+ "aliases": [
+ "factory-reset"
+ ]
+ },
+ {
+ "code": "0x05",
+ "property": "RestoreBrightnessContrastDefaults",
+ "name": "restore-brightness-contrast",
+ "description": "Restore factory brightness/contrast defaults",
+ "type": "WriteOnly",
+ "category": "Preset",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x06",
+ "property": "RestoreGeometryDefaults",
+ "name": "restore-geometry",
+ "description": "Restore factory geometry defaults",
+ "type": "WriteOnly",
+ "category": "Preset",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x08",
+ "property": "RestoreColorDefaults",
+ "name": "restore-color",
+ "description": "Restore factory color defaults",
+ "type": "WriteOnly",
+ "category": "Preset",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x0A",
+ "property": "RestoreTVDefaults",
+ "name": "restore-tv-defaults",
+ "description": "Restore factory TV defaults",
+ "type": "WriteOnly",
+ "category": "Preset",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x0B",
+ "property": "ColorTemperatureIncrement",
+ "name": "color-temp-increment",
+ "description": "Color temperature increment",
+ "type": "ReadOnly",
+ "category": "ColorControl",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x0C",
+ "property": "ColorTemperatureRequest",
+ "name": "color-temp-request",
+ "description": "Color temperature request",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": false,
+ "aliases": [
+ "color-temp"
+ ]
+ },
+ {
+ "code": "0x0E",
+ "property": "Clock",
+ "name": "clock",
+ "description": "Clock",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x10",
+ "property": "Brightness",
+ "name": "brightness",
+ "description": "Brightness control",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": [
+ "bright"
+ ]
+ },
+ {
+ "code": "0x11",
+ "property": "FleshToneEnhancement",
+ "name": "flesh-tone-enhancement",
+ "description": "Flesh tone enhancement",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x12",
+ "property": "Contrast",
+ "name": "contrast",
+ "description": "Contrast control",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x13",
+ "property": "Backlight",
+ "name": "backlight",
+ "description": "Backlight control",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x14",
+ "property": "SelectColorPreset",
+ "name": "select-color-preset",
+ "description": "Select color preset",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": [
+ "color-preset"
+ ]
+ },
+ {
+ "code": "0x16",
+ "property": "RedGain",
+ "name": "red-gain",
+ "description": "Video gain: Red",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": [
+ "red"
+ ]
+ },
+ {
+ "code": "0x17",
+ "property": "UserColorVisionCompensation",
+ "name": "user-color-vision-compensation",
+ "description": "User color vision compensation",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x18",
+ "property": "GreenGain",
+ "name": "green-gain",
+ "description": "Video gain: Green",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": [
+ "green"
+ ]
+ },
+ {
+ "code": "0x1A",
+ "property": "BlueGain",
+ "name": "blue-gain",
+ "description": "Video gain: Blue",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": [
+ "blue"
+ ]
+ },
+ {
+ "code": "0x1C",
+ "property": "Focus",
+ "name": "focus",
+ "description": "Focus",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x1E",
+ "property": "AutoSetup",
+ "name": "auto-setup",
+ "description": "Auto setup",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x1F",
+ "property": "AutoColorSetup",
+ "name": "auto-color-setup",
+ "description": "Auto color setup",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x20",
+ "property": "HorizontalPosition",
+ "name": "h-position",
+ "description": "Horizontal Position (Phase)",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": [
+ "h-pos"
+ ]
+ },
+ {
+ "code": "0x22",
+ "property": "HorizontalSize",
+ "name": "h-size",
+ "description": "Horizontal Size",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x24",
+ "property": "HorizontalPincushion",
+ "name": "h-pincushion",
+ "description": "Horizontal Pincushion",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x26",
+ "property": "HorizontalPincushionBalance",
+ "name": "h-pincushion-balance",
+ "description": "Horizontal Pincushion Balance",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x28",
+ "property": "HorizontalConvergenceRB",
+ "name": "h-convergence-rb",
+ "description": "Horizontal Convergence R/B",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x29",
+ "property": "HorizontalConvergenceMG",
+ "name": "h-convergence-mg",
+ "description": "Horizontal Convergence M/G",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x2A",
+ "property": "HorizontalLinearity",
+ "name": "h-linearity",
+ "description": "Horizontal Linearity",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x2C",
+ "property": "HorizontalLinearityBalance",
+ "name": "h-linearity-balance",
+ "description": "Horizontal Linearity Balance",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x2E",
+ "property": "GrayScaleExpansion",
+ "name": "gray-scale-expansion",
+ "description": "Gray scale expansion",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x30",
+ "property": "VerticalPosition",
+ "name": "v-position",
+ "description": "Vertical Position (Phase)",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": [
+ "v-pos"
+ ]
+ },
+ {
+ "code": "0x32",
+ "property": "VerticalSize",
+ "name": "v-size",
+ "description": "Vertical Size",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x34",
+ "property": "VerticalPincushion",
+ "name": "v-pincushion",
+ "description": "Vertical Pincushion",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x36",
+ "property": "VerticalPincushionBalance",
+ "name": "v-pincushion-balance",
+ "description": "Vertical Pincushion Balance",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x38",
+ "property": "VerticalConvergenceRB",
+ "name": "v-convergence-rb",
+ "description": "Vertical Convergence R/B",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x39",
+ "property": "VerticalConvergenceMG",
+ "name": "v-convergence-mg",
+ "description": "Vertical Convergence M/G",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x3A",
+ "property": "VerticalLinearity",
+ "name": "v-linearity",
+ "description": "Vertical Linearity",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x3C",
+ "property": "VerticalLinearityBalance",
+ "name": "v-linearity-balance",
+ "description": "Vertical Linearity Balance",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x3E",
+ "property": "ClockPhase",
+ "name": "clock-phase",
+ "description": "Clock Phase",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": [
+ "phase"
+ ]
+ },
+ {
+ "code": "0x40",
+ "property": "HorizontalParallelogram",
+ "name": "horizontal-parallelogram",
+ "description": "Horizontal Parallelogram",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x41",
+ "property": "VerticalParallelogram",
+ "name": "vertical-parallelogram",
+ "description": "Vertical Parallelogram",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x42",
+ "property": "HorizontalKeystone",
+ "name": "horizontal-keystone",
+ "description": "Horizontal Keystone",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x43",
+ "property": "VerticalKeystone",
+ "name": "vertical-keystone",
+ "description": "Vertical Keystone",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x44",
+ "property": "Rotation",
+ "name": "rotation",
+ "description": "Rotation",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x46",
+ "property": "TopCornerFlare",
+ "name": "top-corner-flare",
+ "description": "Top Corner Flare",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x48",
+ "property": "TopCornerHook",
+ "name": "top-corner-hook",
+ "description": "Top Corner Hook",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x4A",
+ "property": "BottomCornerFlare",
+ "name": "bottom-corner-flare",
+ "description": "Bottom Corner Flare",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x4C",
+ "property": "BottomCornerHook",
+ "name": "bottom-corner-hook",
+ "description": "Bottom Corner Hook",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x52",
+ "property": "ActiveControl",
+ "name": "active-control",
+ "description": "Active control",
+ "type": "ReadOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x54",
+ "property": "PerformancePreservation",
+ "name": "performance-preservation",
+ "description": "Performance Preservation",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x56",
+ "property": "HorizontalMoire",
+ "name": "horizontal-moire",
+ "description": "Horizontal Moire",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x58",
+ "property": "VerticalMoire",
+ "name": "vertical-moire",
+ "description": "Vertical Moire",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x59",
+ "property": "SixAxisSaturationRed",
+ "name": "6-axis-saturation-red",
+ "description": "6 axis saturation: Red",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x5A",
+ "property": "SixAxisSaturationYellow",
+ "name": "6-axis-saturation-yellow",
+ "description": "6 axis saturation: Yellow",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x5B",
+ "property": "SixAxisSaturationGreen",
+ "name": "6-axis-saturation-green",
+ "description": "6 axis saturation: Green",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x5C",
+ "property": "SixAxisSaturationCyan",
+ "name": "6-axis-saturation-cyan",
+ "description": "6 axis saturation: Cyan",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x5D",
+ "property": "SixAxisSaturationBlue",
+ "name": "6-axis-saturation-blue",
+ "description": "6 axis saturation: Blue",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x5E",
+ "property": "SixAxisSaturationMagenta",
+ "name": "6-axis-saturation-magenta",
+ "description": "6 axis saturation: Magenta",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x60",
+ "property": "InputSource",
+ "name": "input",
+ "description": "Input source selection",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": [
+ "source"
+ ]
+ },
+ {
+ "code": "0x62",
+ "property": "AudioVolume",
+ "name": "volume",
+ "description": "Audio speaker volume",
+ "type": "ReadWrite",
+ "category": "Audio",
+ "supportsPercentage": true,
+ "aliases": [
+ "vol"
+ ]
+ },
+ {
+ "code": "0x63",
+ "property": "SpeakerSelect",
+ "name": "speaker-select",
+ "description": "Speaker Select",
+ "type": "ReadWrite",
+ "category": "Audio",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x64",
+ "property": "MicrophoneVolume",
+ "name": "microphone-volume",
+ "description": "Audio: Microphone Volume",
+ "type": "ReadWrite",
+ "category": "Audio",
+ "supportsPercentage": true,
+ "aliases": [
+ "mic-volume"
+ ]
+ },
+ {
+ "code": "0x66",
+ "property": "AmbientLightSensor",
+ "name": "ambient-light-sensor",
+ "description": "Ambient light sensor",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x6B",
+ "property": "BacklightLevelWhite",
+ "name": "backlight-level-white",
+ "description": "Backlight Level: White",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x6C",
+ "property": "RedBlackLevel",
+ "name": "red-black-level",
+ "description": "Video black level: Red",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x6D",
+ "property": "BacklightLevelRed",
+ "name": "backlight-level-red",
+ "description": "Backlight Level: Red",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x6E",
+ "property": "GreenBlackLevel",
+ "name": "green-black-level",
+ "description": "Video black level: Green",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x6F",
+ "property": "BacklightLevelGreen",
+ "name": "backlight-level-green",
+ "description": "Backlight Level: Green",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x70",
+ "property": "BlueBlackLevel",
+ "name": "blue-black-level",
+ "description": "Video black level: Blue",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x71",
+ "property": "BacklightLevelBlue",
+ "name": "backlight-level-blue",
+ "description": "Backlight Level: Blue",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x72",
+ "property": "Gamma",
+ "name": "gamma",
+ "description": "Gamma",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x73",
+ "property": "LUTSize",
+ "name": "lut-size",
+ "description": "LUT Size",
+ "type": "ReadOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x74",
+ "property": "SinglePointLUTOperation",
+ "name": "single-point-lut-operation",
+ "description": "Single point LUT operation",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x75",
+ "property": "BlockLUTOperation",
+ "name": "block-lut-operation",
+ "description": "Block LUT operation",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x76",
+ "property": "RemoteProcedureCall",
+ "name": "remote-procedure-call",
+ "description": "Remote Procedure Call",
+ "type": "WriteOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x78",
+ "property": "DisplayIdentificationOperation",
+ "name": "display-identification-operation",
+ "description": "Display Identification Operation",
+ "type": "ReadOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x7A",
+ "property": "AdjustFocalPlane",
+ "name": "adjust-focal-plane",
+ "description": "Adjust Focal Plane",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x7C",
+ "property": "AdjustZoom",
+ "name": "adjust-zoom",
+ "description": "Adjust Zoom",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x7E",
+ "property": "Trapezoid",
+ "name": "trapezoid",
+ "description": "Trapezoid",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x80",
+ "property": "Keystone",
+ "name": "keystone",
+ "description": "Keystone",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x82",
+ "property": "HorizontalMirror",
+ "name": "horizontal-mirror",
+ "description": "Horizontal Mirror (Flip)",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x84",
+ "property": "VerticalMirror",
+ "name": "vertical-mirror",
+ "description": "Vertical Mirror (Flip)",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x86",
+ "property": "DisplayScaling",
+ "name": "display-scaling",
+ "description": "Display Scaling",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x87",
+ "property": "Sharpness",
+ "name": "sharpness",
+ "description": "Sharpness control",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": [
+ "sharp"
+ ]
+ },
+ {
+ "code": "0x88",
+ "property": "VelocityScanModulation",
+ "name": "velocity-scan-modulation",
+ "description": "Velocity Scan Modulation",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x8A",
+ "property": "ColorSaturation",
+ "name": "color-saturation",
+ "description": "Color Saturation",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": [
+ "saturation",
+ "sat"
+ ]
+ },
+ {
+ "code": "0x8B",
+ "property": "TVChannelUpDown",
+ "name": "tv-channel-up-down",
+ "description": "TV Channel Up/Down",
+ "type": "WriteOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x8C",
+ "property": "TVSharpness",
+ "name": "tv-sharpness",
+ "description": "TV Sharpness",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x8D",
+ "property": "AudioMute",
+ "name": "mute",
+ "description": "Audio mute/Screen blank",
+ "type": "ReadWrite",
+ "category": "Audio",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x8E",
+ "property": "TVContrast",
+ "name": "tv-contrast",
+ "description": "TV Contrast",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x8F",
+ "property": "AudioTreble",
+ "name": "audio-treble",
+ "description": "Audio treble",
+ "type": "ReadWrite",
+ "category": "Audio",
+ "supportsPercentage": true,
+ "aliases": [
+ "treble"
+ ]
+ },
+ {
+ "code": "0x90",
+ "property": "Hue",
+ "name": "hue",
+ "description": "Hue",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x91",
+ "property": "AudioBass",
+ "name": "audio-bass",
+ "description": "Audio bass",
+ "type": "ReadWrite",
+ "category": "Audio",
+ "supportsPercentage": true,
+ "aliases": [
+ "bass"
+ ]
+ },
+ {
+ "code": "0x92",
+ "property": "TVBlackLevel",
+ "name": "tv-black-level",
+ "description": "TV Black level/Luminesence",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x93",
+ "property": "AudioBalance",
+ "name": "audio-balance",
+ "description": "Audio balance L/R",
+ "type": "ReadWrite",
+ "category": "Audio",
+ "supportsPercentage": true,
+ "aliases": [
+ "balance"
+ ]
+ },
+ {
+ "code": "0x94",
+ "property": "AudioProcessorMode",
+ "name": "audio-processor-mode",
+ "description": "Audio processor mode",
+ "type": "ReadWrite",
+ "category": "Audio",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x95",
+ "property": "WindowPositionTLX",
+ "name": "window-position-tl-x",
+ "description": "Window Position(TL_X)",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x96",
+ "property": "WindowPositionTLY",
+ "name": "window-position-tl-y",
+ "description": "Window Position(TL_Y)",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x97",
+ "property": "WindowPositionBRX",
+ "name": "window-position-br-x",
+ "description": "Window Position(BR_X)",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x98",
+ "property": "WindowPositionBRY",
+ "name": "window-position-br-y",
+ "description": "Window Position(BR_Y)",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x99",
+ "property": "WindowControlOnOff",
+ "name": "window-control-on-off",
+ "description": "Window control on/off",
+ "type": "ReadWrite",
+ "category": "Geometry",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0x9A",
+ "property": "WindowBackground",
+ "name": "window-background",
+ "description": "Window background",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x9B",
+ "property": "SixAxisHueRed",
+ "name": "6-axis-hue-red",
+ "description": "6 axis hue control: Red",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x9C",
+ "property": "SixAxisHueYellow",
+ "name": "6-axis-hue-yellow",
+ "description": "6 axis hue control: Yellow",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x9D",
+ "property": "SixAxisHueGreen",
+ "name": "6-axis-hue-green",
+ "description": "6 axis hue control: Green",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x9E",
+ "property": "SixAxisHueCyan",
+ "name": "6-axis-hue-cyan",
+ "description": "6 axis hue control: Cyan",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0x9F",
+ "property": "SixAxisHueBlue",
+ "name": "6-axis-hue-blue",
+ "description": "6 axis hue control: Blue",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0xA0",
+ "property": "SixAxisHueMagenta",
+ "name": "6-axis-hue-magenta",
+ "description": "6 axis hue control: Magenta",
+ "type": "ReadWrite",
+ "category": "ColorControl",
+ "supportsPercentage": true,
+ "aliases": []
+ },
+ {
+ "code": "0xA2",
+ "property": "AutoSetupOnOff",
+ "name": "auto-setup-on-off",
+ "description": "Auto setup on/off",
+ "type": "WriteOnly",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xA4",
+ "property": "WindowMaskControl",
+ "name": "window-mask-control",
+ "description": "Window mask control",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xA5",
+ "property": "ChangeSelectedWindow",
+ "name": "change-selected-window",
+ "description": "Change the selected window",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xAA",
+ "property": "ScreenOrientation",
+ "name": "screen-orientation",
+ "description": "Screen Orientation",
+ "type": "ReadOnly",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": [
+ "orientation"
+ ]
+ },
+ {
+ "code": "0xAC",
+ "property": "HorizontalFrequency",
+ "name": "horizontal-frequency",
+ "description": "Horizontal frequency",
+ "type": "ReadOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": [
+ "h-freq"
+ ]
+ },
+ {
+ "code": "0xAE",
+ "property": "VerticalFrequency",
+ "name": "vertical-frequency",
+ "description": "Vertical frequency",
+ "type": "ReadOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": [
+ "v-freq"
+ ]
+ },
+ {
+ "code": "0xB0",
+ "property": "Settings",
+ "name": "settings",
+ "description": "Settings",
+ "type": "WriteOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xB2",
+ "property": "FlatPanelSubPixelLayout",
+ "name": "flat-panel-sub-pixel-layout",
+ "description": "Flat panel sub-pixel layout",
+ "type": "ReadOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xB4",
+ "property": "SourceTimingMode",
+ "name": "source-timing-mode",
+ "description": "Source Timing Mode",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xB6",
+ "property": "DisplayTechnologyType",
+ "name": "display-technology-type",
+ "description": "Display technology type",
+ "type": "ReadOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xB7",
+ "property": "MonitorStatus",
+ "name": "monitor-status",
+ "description": "Monitor status",
+ "type": "ReadOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xB8",
+ "property": "PacketCount",
+ "name": "packet-count",
+ "description": "Packet count",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xB9",
+ "property": "MonitorXOrigin",
+ "name": "monitor-x-origin",
+ "description": "Monitor X origin",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xBA",
+ "property": "MonitorYOrigin",
+ "name": "monitor-y-origin",
+ "description": "Monitor Y origin",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xBB",
+ "property": "HeaderErrorCount",
+ "name": "header-error-count",
+ "description": "Header error count",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xBC",
+ "property": "BodyCRCErrorCount",
+ "name": "body-crc-error-count",
+ "description": "Body CRC error count",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xBD",
+ "property": "ClientID",
+ "name": "client-id",
+ "description": "Client ID",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xBE",
+ "property": "LinkControl",
+ "name": "link-control",
+ "description": "Link control",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xC0",
+ "property": "DisplayUsageTime",
+ "name": "display-usage-time",
+ "description": "Display usage time",
+ "type": "ReadOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xC2",
+ "property": "DisplayDescriptorLength",
+ "name": "display-descriptor-length",
+ "description": "Display descriptor length",
+ "type": "ReadOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xC3",
+ "property": "TransmitDisplayDescriptor",
+ "name": "transmit-display-descriptor",
+ "description": "Transmit display descriptor",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xC4",
+ "property": "EnableDisplayOfDisplayDescriptor",
+ "name": "enable-display-of-display-descriptor",
+ "description": "Enable display of 'display descriptor'",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xC6",
+ "property": "ApplicationEnableKey",
+ "name": "application-enable-key",
+ "description": "Application enable key",
+ "type": "ReadOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xC8",
+ "property": "DisplayControllerType",
+ "name": "display-controller-type",
+ "description": "Display controller type",
+ "type": "ReadOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xC9",
+ "property": "DisplayFirmwareLevel",
+ "name": "display-firmware-level",
+ "description": "Display firmware level",
+ "type": "ReadOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": [
+ "firmware"
+ ]
+ },
+ {
+ "code": "0xCA",
+ "property": "OSDButtonControl",
+ "name": "osd-button-control",
+ "description": "OSD/Button Control",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": [
+ "osd"
+ ]
+ },
+ {
+ "code": "0xCC",
+ "property": "OSDLanguage",
+ "name": "osd-language",
+ "description": "OSD Language",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": [
+ "language"
+ ]
+ },
+ {
+ "code": "0xCD",
+ "property": "StatusIndicators",
+ "name": "status-indicators",
+ "description": "Status Indicators",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xCE",
+ "property": "AuxiliaryDisplaySize",
+ "name": "auxiliary-display-size",
+ "description": "Auxiliary display size",
+ "type": "ReadOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xCF",
+ "property": "AuxiliaryDisplayData",
+ "name": "auxiliary-display-data",
+ "description": "Auxiliary display data",
+ "type": "WriteOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xD0",
+ "property": "OutputSelect",
+ "name": "output-select",
+ "description": "Output select",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xD2",
+ "property": "AssetTag",
+ "name": "asset-tag",
+ "description": "Asset Tag",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xD4",
+ "property": "StereoVideoMode",
+ "name": "stereo-video-mode",
+ "description": "Stereo video mode",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xD6",
+ "property": "PowerMode",
+ "name": "power-mode",
+ "description": "Power mode",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": [
+ "power"
+ ]
+ },
+ {
+ "code": "0xD7",
+ "property": "AuxiliaryPowerOutput",
+ "name": "auxiliary-power-output",
+ "description": "Auxiliary power output",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xDA",
+ "property": "ScanMode",
+ "name": "scan-mode",
+ "description": "Scan mode",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xDB",
+ "property": "ImageMode",
+ "name": "image-mode",
+ "description": "Image Mode",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": [
+ "mode"
+ ]
+ },
+ {
+ "code": "0xDC",
+ "property": "DisplayMode",
+ "name": "display-mode",
+ "description": "Display Mode",
+ "type": "ReadWrite",
+ "category": "ImageAdjustment",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xDE",
+ "property": "ScratchPad",
+ "name": "scratch-pad",
+ "description": "Scratch Pad",
+ "type": "ReadWrite",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": []
+ },
+ {
+ "code": "0xDF",
+ "property": "VcpVersion",
+ "name": "vcp-version",
+ "description": "VCP Version",
+ "type": "ReadOnly",
+ "category": "Miscellaneous",
+ "supportsPercentage": false,
+ "aliases": [
+ "version"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/build.cmd b/build.cmd
new file mode 100644
index 0000000..0122b57
--- /dev/null
+++ b/build.cmd
@@ -0,0 +1,53 @@
+@echo off
+setlocal enabledelayedexpansion
+
+echo ========================================
+echo Building DDCSwitch with NativeAOT
+echo ========================================
+echo.
+
+REM Clean previous build
+echo Cleaning previous build...
+dotnet clean DDCSwitch\DDCSwitch.csproj -c Release
+if errorlevel 1 (
+ echo ERROR: Clean failed
+ exit /b 1
+)
+echo.
+
+REM Build with NativeAOT
+echo Building with NativeAOT...
+dotnet publish DDCSwitch\DDCSwitch.csproj -c Release -r win-x64 --self-contained
+if errorlevel 1 (
+ echo ERROR: Build failed
+ exit /b 1
+)
+echo.
+
+REM Create dist folder
+echo Creating dist folder...
+if not exist "dist" mkdir "dist"
+
+REM Copy the NativeAOT executable
+echo Copying executable to dist folder...
+copy /Y "DDCSwitch\bin\Release\net10.0\win-x64\publish\DDCSwitch.exe" "dist\DDCSwitch.exe"
+if errorlevel 1 (
+ echo ERROR: Failed to copy executable
+ exit /b 1
+)
+
+echo.
+echo ========================================
+echo Build completed successfully!
+echo Output: dist\DDCSwitch.exe
+echo ========================================
+
+REM Display file size
+for %%A in ("dist\DDCSwitch.exe") do (
+ set size=%%~zA
+ set /a sizeMB=!size! / 1048576
+ echo File size: !sizeMB! MB
+)
+
+endlocal
+
diff --git a/tools/GenerateVcpFeatures.py b/tools/GenerateVcpFeatures.py
new file mode 100644
index 0000000..f2c5355
--- /dev/null
+++ b/tools/GenerateVcpFeatures.py
@@ -0,0 +1,114 @@
+#!/usr/bin/env python3
+"""
+Generates VcpFeature static properties from VcpFeatureData.json.
+This creates a partial class that can be combined with the core VcpFeature class.
+"""
+
+import json
+from pathlib import Path
+
+def generate_feature_property(feature):
+ """Generate a C# static property for a VCP feature."""
+ code = feature['code']
+ prop_name = feature['property']
+ name = feature['name']
+ description = feature['description']
+ feature_type = feature['type']
+ category = feature['category']
+ supports_percentage = 'true' if feature['supportsPercentage'] else 'false'
+ aliases = feature.get('aliases', [])
+
+ # Generate XML doc comment
+ lines = [
+ f" /// ",
+ f" /// {description} (VCP {code})",
+ f" /// "
+ ]
+
+ # Generate property
+ if aliases:
+ aliases_str = ', '.join(f'"{alias}"' for alias in aliases)
+ lines.append(f' public static VcpFeature {prop_name} => new({code}, "{name}", "{description}", VcpFeatureType.{feature_type}, VcpFeatureCategory.{category}, {supports_percentage}, {aliases_str});')
+ else:
+ lines.append(f' public static VcpFeature {prop_name} => new({code}, "{name}", "{description}", VcpFeatureType.{feature_type}, VcpFeatureCategory.{category}, {supports_percentage});')
+
+ return '\n'.join(lines)
+
+def generate_all_features_list(features):
+ """Generate the AllFeatures list."""
+ lines = [
+ " /// ",
+ " /// All features registry - contains all predefined MCCS features",
+ " /// ",
+ " public static IReadOnlyList AllFeatures { get; } = new List",
+ " {"
+ ]
+
+ for i, feature in enumerate(features):
+ comma = ',' if i < len(features) - 1 else ''
+ lines.append(f" {feature['property']}{comma}")
+
+ lines.append(" };")
+ return '\n'.join(lines)
+
+def main():
+ script_dir = Path(__file__).parent
+ json_file = script_dir.parent / 'DDCSwitch' / 'VcpFeatureData.json'
+ output_file = script_dir.parent / 'DDCSwitch' / 'VcpFeature.Generated.cs'
+
+ print(f"Loading {json_file}...")
+ with open(json_file, 'r', encoding='utf-8') as f:
+ data = json.load(f)
+
+ features = data['features']
+ print(f"Found {len(features)} features")
+
+ # Generate the file
+ lines = [
+ "// ",
+ "// This file is automatically generated from VcpFeatureData.json",
+ "// Do not edit this file directly. Instead, edit VcpFeatureData.json and regenerate.",
+ "",
+ "namespace DDCSwitch;",
+ "",
+ "public partial class VcpFeature",
+ "{",
+ " // ===== GENERATED VCP FEATURES ====="
+ ]
+
+ # Group features by category
+ categories = {}
+ for feature in features:
+ category = feature['category']
+ if category not in categories:
+ categories[category] = []
+ categories[category].append(feature)
+
+ # Generate properties grouped by category
+ for category, category_features in categories.items():
+ lines.append("")
+ lines.append(f" // ===== {category.upper()} FEATURES =====")
+ lines.append("")
+ for feature in category_features:
+ lines.append(generate_feature_property(feature))
+ lines.append("")
+
+ # Generate AllFeatures list
+ lines.append("")
+ lines.append(generate_all_features_list(features))
+
+ lines.append("}")
+ lines.append("")
+
+ content = '\n'.join(lines)
+
+ print(f"Writing to {output_file}...")
+ with open(output_file, 'w', encoding='utf-8') as f:
+ f.write(content)
+
+ print("Done!")
+ print(f"Generated {len(features)} feature properties")
+
+if __name__ == '__main__':
+ main()
+
diff --git a/tools/Regenerate.ps1 b/tools/Regenerate.ps1
new file mode 100644
index 0000000..763b982
--- /dev/null
+++ b/tools/Regenerate.ps1
@@ -0,0 +1,30 @@
+# Regenerate VCP Feature Code
+# Run this after editing VcpFeatureData.json
+
+Write-Host "=" * 60
+Write-Host "VCP Feature Code Generator"
+Write-Host "=" * 60
+
+$scriptPath = Split-Path -Parent $MyInvocation.MyCommand.Path
+$projectRoot = Split-Path -Parent $scriptPath
+
+Push-Location $projectRoot
+
+try {
+ Write-Host "`nGenerating VcpFeature.Generated.cs from VcpFeatureData.json..."
+ python "$scriptPath\GenerateVcpFeatures.py"
+
+ if ($LASTEXITCODE -eq 0) {
+ Write-Host "`n✓ Generation successful!" -ForegroundColor Green
+ Write-Host "`nNext steps:"
+ Write-Host " 1. Review the changes in VcpFeature.Generated.cs"
+ Write-Host " 2. Build the project: dotnet build"
+ Write-Host " 3. Test the changes"
+ } else {
+ Write-Host "`n✗ Generation failed!" -ForegroundColor Red
+ exit 1
+ }
+} finally {
+ Pop-Location
+}
+
From a660ebc260a8ed1012a3b04ac8d670f8c5ec95bd Mon Sep 17 00:00:00 2001
From: Quick <577652+markdwags@users.noreply.github.com>
Date: Wed, 7 Jan 2026 20:45:18 -0600
Subject: [PATCH 05/10] Refactor how commands are handled,
---
DDCSwitch/Commands/CommandRouter.cs | 79 ++
DDCSwitch/Commands/ConsoleOutputFormatter.cs | 22 +
DDCSwitch/Commands/GetCommand.cs | 229 ++++
DDCSwitch/Commands/HelpCommand.cs | 53 +
DDCSwitch/Commands/ListCommand.cs | 240 ++++
DDCSwitch/Commands/SetCommand.cs | 388 ++++++
DDCSwitch/Commands/VcpScanCommand.cs | 278 ++++
DDCSwitch/Program.cs | 1203 +-----------------
8 files changed, 1291 insertions(+), 1201 deletions(-)
create mode 100644 DDCSwitch/Commands/CommandRouter.cs
create mode 100644 DDCSwitch/Commands/ConsoleOutputFormatter.cs
create mode 100644 DDCSwitch/Commands/GetCommand.cs
create mode 100644 DDCSwitch/Commands/HelpCommand.cs
create mode 100644 DDCSwitch/Commands/ListCommand.cs
create mode 100644 DDCSwitch/Commands/SetCommand.cs
create mode 100644 DDCSwitch/Commands/VcpScanCommand.cs
diff --git a/DDCSwitch/Commands/CommandRouter.cs b/DDCSwitch/Commands/CommandRouter.cs
new file mode 100644
index 0000000..a376628
--- /dev/null
+++ b/DDCSwitch/Commands/CommandRouter.cs
@@ -0,0 +1,79 @@
+using System.Text.Json;
+
+namespace DDCSwitch.Commands;
+
+internal static class CommandRouter
+{
+ public static int Route(string[] args)
+ {
+ if (args.Length == 0)
+ {
+ HelpCommand.ShowUsage();
+ return 1;
+ }
+
+ // Check for --json flag
+ bool jsonOutput = args.Contains("--json", StringComparer.OrdinalIgnoreCase);
+ var filteredArgs = args.Where(a => !a.Equals("--json", StringComparison.OrdinalIgnoreCase)).ToArray();
+
+ // Check for --verbose flag
+ bool verboseOutput = filteredArgs.Contains("--verbose", StringComparer.OrdinalIgnoreCase);
+ filteredArgs = filteredArgs.Where(a => !a.Equals("--verbose", StringComparison.OrdinalIgnoreCase)).ToArray();
+
+ // Check for --scan flag
+ bool scanOutput = filteredArgs.Contains("--scan", StringComparer.OrdinalIgnoreCase);
+ filteredArgs = filteredArgs.Where(a => !a.Equals("--scan", StringComparison.OrdinalIgnoreCase)).ToArray();
+
+ if (filteredArgs.Length == 0)
+ {
+ HelpCommand.ShowUsage();
+ return 1;
+ }
+
+ var command = filteredArgs[0].ToLowerInvariant();
+
+ try
+ {
+ return command switch
+ {
+ "list" or "ls" => ListCommand.Execute(jsonOutput, verboseOutput, scanOutput),
+ "get" => GetCommand.Execute(filteredArgs, jsonOutput),
+ "set" => SetCommand.Execute(filteredArgs, jsonOutput),
+ "version" or "-v" or "--version" => HelpCommand.ShowVersion(jsonOutput),
+ "help" or "-h" or "--help" or "/?" => HelpCommand.ShowUsage(),
+ _ => InvalidCommand(filteredArgs[0], jsonOutput)
+ };
+ }
+ catch (Exception ex)
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, ex.Message);
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError(ex.Message);
+ }
+
+ return 1;
+ }
+ }
+
+ private static int InvalidCommand(string command, bool jsonOutput)
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, $"Unknown command: {command}");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError($"Unknown command: {command}");
+ ConsoleOutputFormatter.WriteInfo("Run DDCSwitch help for usage information.");
+ }
+
+ return 1;
+ }
+}
+
diff --git a/DDCSwitch/Commands/ConsoleOutputFormatter.cs b/DDCSwitch/Commands/ConsoleOutputFormatter.cs
new file mode 100644
index 0000000..f58ed3c
--- /dev/null
+++ b/DDCSwitch/Commands/ConsoleOutputFormatter.cs
@@ -0,0 +1,22 @@
+using Spectre.Console;
+
+namespace DDCSwitch.Commands;
+
+internal static class ConsoleOutputFormatter
+{
+ public static void WriteError(string message)
+ {
+ AnsiConsole.MarkupLine($"[red]Error:[/] {message}");
+ }
+
+ public static void WriteInfo(string message)
+ {
+ AnsiConsole.MarkupLine(message);
+ }
+
+ public static void WriteSuccess(string message)
+ {
+ AnsiConsole.MarkupLine($"[green]✓[/] {message}");
+ }
+}
+
diff --git a/DDCSwitch/Commands/GetCommand.cs b/DDCSwitch/Commands/GetCommand.cs
new file mode 100644
index 0000000..83718c9
--- /dev/null
+++ b/DDCSwitch/Commands/GetCommand.cs
@@ -0,0 +1,229 @@
+using Spectre.Console;
+using System.Text.Json;
+
+namespace DDCSwitch.Commands;
+
+internal static class GetCommand
+{
+ public static int Execute(string[] args, bool jsonOutput)
+ {
+ if (args.Length < 2)
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, "Monitor identifier required");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError("Monitor identifier required.");
+ AnsiConsole.WriteLine("Usage: DDCSwitch get [feature]");
+ }
+
+ return 1;
+ }
+
+ // If no feature is specified, perform VCP scan
+ if (args.Length == 2)
+ {
+ return VcpScanCommand.ScanSingleMonitor(args[1], jsonOutput);
+ }
+
+ string featureInput = args[2];
+
+ if (!FeatureResolver.TryResolveFeature(featureInput, out VcpFeature? feature))
+ {
+ return HandleInvalidFeature(featureInput, jsonOutput);
+ }
+
+ var monitors = MonitorController.EnumerateMonitors();
+
+ if (monitors.Count == 0)
+ {
+ return HandleNoMonitors(jsonOutput);
+ }
+
+ var monitor = MonitorController.FindMonitor(monitors, args[1]);
+
+ if (monitor == null)
+ {
+ return HandleMonitorNotFound(monitors, args[1], jsonOutput);
+ }
+
+ int result = ReadFeatureValue(monitor, feature!, jsonOutput);
+
+ // Cleanup
+ foreach (var m in monitors)
+ {
+ m.Dispose();
+ }
+
+ return result;
+ }
+
+ private static int HandleInvalidFeature(string featureInput, bool jsonOutput)
+ {
+ string errorMessage;
+
+ // Provide specific error message based on input type
+ if (FeatureResolver.TryParseVcpCode(featureInput, out _))
+ {
+ // Valid VCP code but not in our predefined list
+ errorMessage = $"VCP code '{featureInput}' is valid but may not be supported by all monitors";
+ }
+ else
+ {
+ // Invalid feature name or VCP code
+ errorMessage = $"Invalid feature '{featureInput}'. {FeatureResolver.GetVcpCodeValidationError(featureInput)}";
+ }
+
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, errorMessage);
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError(errorMessage);
+ AnsiConsole.MarkupLine("Valid features: brightness, contrast, input, or VCP code (0x10, 0x12, etc.)");
+ }
+
+ return 1;
+ }
+
+ private static int HandleNoMonitors(bool jsonOutput)
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, "No DDC/CI capable monitors found");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError("No DDC/CI capable monitors found.");
+ }
+
+ return 1;
+ }
+
+ private static int HandleMonitorNotFound(List monitors, string monitorId, bool jsonOutput)
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, $"Monitor '{monitorId}' not found");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError($"Monitor '{monitorId}' not found.");
+ AnsiConsole.MarkupLine("Use [yellow]DDCSwitch list[/] to see available monitors.");
+ }
+
+ // Cleanup
+ foreach (var m in monitors)
+ {
+ m.Dispose();
+ }
+
+ return 1;
+ }
+
+ private static int ReadFeatureValue(Monitor monitor, VcpFeature feature, bool jsonOutput)
+ {
+ bool success = false;
+ uint current = 0;
+ uint max = 0;
+ int errorCode = 0;
+
+ if (!jsonOutput)
+ {
+ AnsiConsole.Status()
+ .Start($"Reading {feature.Name} from {monitor.Name}...", ctx =>
+ {
+ ctx.Spinner(Spinner.Known.Dots);
+ success = monitor.TryGetVcpFeature(feature.Code, out current, out max, out errorCode);
+ });
+ }
+ else
+ {
+ success = monitor.TryGetVcpFeature(feature.Code, out current, out max, out errorCode);
+ }
+
+ if (!success)
+ {
+ return HandleReadFailure(monitor, feature, errorCode, jsonOutput);
+ }
+
+ OutputFeatureValue(monitor, feature, current, max, jsonOutput);
+ return 0;
+ }
+
+ private static int HandleReadFailure(Monitor monitor, VcpFeature feature, int errorCode, bool jsonOutput)
+ {
+ string errorMessage;
+
+ if (VcpErrorHandler.IsTimeoutError(errorCode))
+ {
+ errorMessage = VcpErrorHandler.CreateTimeoutMessage(monitor, feature, "read");
+ }
+ else if (VcpErrorHandler.IsUnsupportedFeatureError(errorCode))
+ {
+ errorMessage = VcpErrorHandler.CreateUnsupportedFeatureMessage(monitor, feature);
+ }
+ else if (errorCode == 0x00000006) // ERROR_INVALID_HANDLE
+ {
+ errorMessage = VcpErrorHandler.CreateCommunicationFailureMessage(monitor);
+ }
+ else
+ {
+ errorMessage = VcpErrorHandler.CreateReadFailureMessage(monitor, feature);
+ }
+
+ if (jsonOutput)
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+ var error = new ErrorResponse(false, errorMessage, monitorRef);
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError(errorMessage);
+ }
+
+ return 1;
+ }
+
+ private static void OutputFeatureValue(Monitor monitor, VcpFeature feature, uint current, uint max, bool jsonOutput)
+ {
+ if (jsonOutput)
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+
+ uint? percentageValue = feature.SupportsPercentage ? FeatureResolver.ConvertRawToPercentage(current, max) : null;
+ var result = new GetVcpResponse(true, monitorRef, feature.Name, current, max, percentageValue);
+ Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.GetVcpResponse));
+ }
+ else
+ {
+ AnsiConsole.MarkupLine($"[green]Monitor:[/] {monitor.Name} ({monitor.DeviceName})");
+
+ if (feature.Code == InputSource.VcpInputSource)
+ {
+ // Display input with name resolution
+ AnsiConsole.MarkupLine($"[green]Current {feature.Name}:[/] {InputSource.GetName(current)} (0x{current:X2})");
+ }
+ else if (feature.SupportsPercentage)
+ {
+ // Display percentage for brightness/contrast
+ uint percentage = FeatureResolver.ConvertRawToPercentage(current, max);
+ AnsiConsole.MarkupLine($"[green]Current {feature.Name}:[/] {percentage}% (raw: {current}/{max})");
+ }
+ else
+ {
+ // Display raw values for unknown VCP codes
+ AnsiConsole.MarkupLine($"[green]Current {feature.Name}:[/] {current} (max: {max})");
+ }
+ }
+ }
+}
+
diff --git a/DDCSwitch/Commands/HelpCommand.cs b/DDCSwitch/Commands/HelpCommand.cs
new file mode 100644
index 0000000..1c52dde
--- /dev/null
+++ b/DDCSwitch/Commands/HelpCommand.cs
@@ -0,0 +1,53 @@
+using Spectre.Console;
+
+namespace DDCSwitch.Commands;
+
+internal static class HelpCommand
+{
+ public static string GetVersion()
+ {
+ var version = typeof(HelpCommand).Assembly
+ .GetName().Version?.ToString(3) ?? "0.0.0";
+ return version;
+ }
+
+ public static int ShowVersion(bool jsonOutput)
+ {
+ var version = GetVersion();
+
+ if (jsonOutput)
+ {
+ Console.WriteLine($"{{\"version\":\"{version}\"}}");
+ }
+ else
+ {
+ AnsiConsole.Write(new FigletText("DDCSwitch").Color(Color.Blue));
+ AnsiConsole.MarkupLine($"[bold]Version:[/] [green]{version}[/]");
+ AnsiConsole.MarkupLine("[dim]Windows DDC/CI Monitor Input Switcher[/]");
+ }
+
+ return 0;
+ }
+
+ public static int ShowUsage()
+ {
+ var version = GetVersion();
+
+ AnsiConsole.Write(new FigletText("DDCSwitch").Color(Color.Blue));
+ AnsiConsole.MarkupLine($"[dim]Windows DDC/CI Monitor Input Switcher v{version}[/]\n");
+
+ AnsiConsole.MarkupLine("[yellow]Commands:[/]");
+ AnsiConsole.WriteLine(" list [--verbose] [--scan] - List all DDC/CI capable monitors");
+ AnsiConsole.WriteLine(" get monitor [feature] - Get current value for a monitor feature or scan all features");
+ AnsiConsole.MarkupLine(" set monitor feature value - Set value for a monitor feature");
+ AnsiConsole.MarkupLine(" version - Display version information");
+
+ AnsiConsole.MarkupLine("\nSupported features: brightness, contrast, input, or VCP codes like 0x10");
+ AnsiConsole.MarkupLine("Use [yellow]--json[/] flag for JSON output");
+ AnsiConsole.MarkupLine("Use [yellow]--verbose[/] flag with list to include brightness and contrast");
+ AnsiConsole.MarkupLine("Use [yellow]--scan[/] flag with list to enumerate all VCP codes");
+
+ return 0;
+ }
+}
+
diff --git a/DDCSwitch/Commands/ListCommand.cs b/DDCSwitch/Commands/ListCommand.cs
new file mode 100644
index 0000000..9d02c22
--- /dev/null
+++ b/DDCSwitch/Commands/ListCommand.cs
@@ -0,0 +1,240 @@
+using Spectre.Console;
+using System.Text.Json;
+
+namespace DDCSwitch.Commands;
+
+internal static class ListCommand
+{
+ public static int Execute(bool jsonOutput, bool verboseOutput, bool scanOutput)
+ {
+ List monitors;
+
+ if (!jsonOutput)
+ {
+ monitors = null!;
+ AnsiConsole.Status()
+ .Start(scanOutput ? "Scanning VCP features..." : "Enumerating monitors...", ctx =>
+ {
+ ctx.Spinner(Spinner.Known.Dots);
+ monitors = MonitorController.EnumerateMonitors();
+ });
+ }
+ else
+ {
+ monitors = MonitorController.EnumerateMonitors();
+ }
+
+ if (monitors.Count == 0)
+ {
+ if (jsonOutput)
+ {
+ var result = new ListMonitorsResponse(false, null, "No DDC/CI capable monitors found");
+ Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.ListMonitorsResponse));
+ }
+ else
+ {
+ AnsiConsole.MarkupLine("[yellow]No DDC/CI capable monitors found.[/]");
+ }
+
+ return 1;
+ }
+
+ // If scan mode is enabled, perform VCP scanning for each monitor
+ if (scanOutput)
+ {
+ return VcpScanCommand.ScanAllMonitors(monitors, jsonOutput);
+ }
+
+ if (jsonOutput)
+ {
+ OutputJsonList(monitors, verboseOutput);
+ }
+ else
+ {
+ OutputTableList(monitors, verboseOutput);
+ }
+
+ // Cleanup
+ foreach (var monitor in monitors)
+ {
+ monitor.Dispose();
+ }
+
+ return 0;
+ }
+
+ private static void OutputJsonList(List monitors, bool verboseOutput)
+ {
+ var monitorList = monitors.Select(monitor =>
+ {
+ string? inputName = null;
+ uint? inputCode = null;
+ string status = "ok";
+ string? brightness = null;
+ string? contrast = null;
+
+ try
+ {
+ if (monitor.TryGetInputSource(out uint current, out _))
+ {
+ inputName = InputSource.GetName(current);
+ inputCode = current;
+ }
+ else
+ {
+ status = "no_ddc_ci";
+ }
+
+ // Get brightness and contrast if verbose mode is enabled
+ if (verboseOutput && status == "ok")
+ {
+ // Try to get brightness (VCP 0x10)
+ if (monitor.TryGetVcpFeature(VcpFeature.Brightness.Code, out uint brightnessCurrent, out uint brightnessMax))
+ {
+ uint brightnessPercentage = FeatureResolver.ConvertRawToPercentage(brightnessCurrent, brightnessMax);
+ brightness = $"{brightnessPercentage}%";
+ }
+ else
+ {
+ brightness = "N/A";
+ }
+
+ // Try to get contrast (VCP 0x12)
+ if (monitor.TryGetVcpFeature(VcpFeature.Contrast.Code, out uint contrastCurrent, out uint contrastMax))
+ {
+ uint contrastPercentage = FeatureResolver.ConvertRawToPercentage(contrastCurrent, contrastMax);
+ contrast = $"{contrastPercentage}%";
+ }
+ else
+ {
+ contrast = "N/A";
+ }
+ }
+ }
+ catch
+ {
+ status = "error";
+ if (verboseOutput)
+ {
+ brightness = "N/A";
+ contrast = "N/A";
+ }
+ }
+
+ return new MonitorInfo(
+ monitor.Index,
+ monitor.Name,
+ monitor.DeviceName,
+ monitor.IsPrimary,
+ inputName,
+ inputCode != null ? $"0x{inputCode:X2}" : null,
+ status,
+ brightness,
+ contrast);
+ }).ToList();
+
+ var result = new ListMonitorsResponse(true, monitorList);
+ Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.ListMonitorsResponse));
+ }
+
+ private static void OutputTableList(List monitors, bool verboseOutput)
+ {
+ var table = new Table()
+ .Border(TableBorder.Rounded)
+ .AddColumn("Index")
+ .AddColumn("Monitor Name")
+ .AddColumn("Device")
+ .AddColumn("Current Input");
+
+ // Add brightness and contrast columns if verbose mode is enabled
+ if (verboseOutput)
+ {
+ table.AddColumn("Brightness");
+ table.AddColumn("Contrast");
+ }
+
+ table.AddColumn("Status");
+
+ foreach (var monitor in monitors)
+ {
+ string inputInfo = "N/A";
+ string status = "[green]OK[/]";
+ string brightnessInfo = "N/A";
+ string contrastInfo = "N/A";
+
+ try
+ {
+ if (monitor.TryGetInputSource(out uint current, out _))
+ {
+ inputInfo = $"{InputSource.GetName(current)} (0x{current:X2})";
+ }
+ else
+ {
+ status = "[yellow]No DDC/CI[/]";
+ }
+
+ // Get brightness and contrast if verbose mode is enabled and monitor supports DDC/CI
+ if (verboseOutput && status == "[green]OK[/]")
+ {
+ // Try to get brightness (VCP 0x10)
+ if (monitor.TryGetVcpFeature(VcpFeature.Brightness.Code, out uint brightnessCurrent, out uint brightnessMax))
+ {
+ uint brightnessPercentage = FeatureResolver.ConvertRawToPercentage(brightnessCurrent, brightnessMax);
+ brightnessInfo = $"{brightnessPercentage}%";
+ }
+ else
+ {
+ brightnessInfo = "[dim]N/A[/]";
+ }
+
+ // Try to get contrast (VCP 0x12)
+ if (monitor.TryGetVcpFeature(VcpFeature.Contrast.Code, out uint contrastCurrent, out uint contrastMax))
+ {
+ uint contrastPercentage = FeatureResolver.ConvertRawToPercentage(contrastCurrent, contrastMax);
+ contrastInfo = $"{contrastPercentage}%";
+ }
+ else
+ {
+ contrastInfo = "[dim]N/A[/]";
+ }
+ }
+ else if (verboseOutput)
+ {
+ brightnessInfo = "[dim]N/A[/]";
+ contrastInfo = "[dim]N/A[/]";
+ }
+ }
+ catch
+ {
+ status = "[red]Error[/]";
+ if (verboseOutput)
+ {
+ brightnessInfo = "[dim]N/A[/]";
+ contrastInfo = "[dim]N/A[/]";
+ }
+ }
+
+ var row = new List
+ {
+ monitor.IsPrimary ? $"{monitor.Index} [yellow]*[/]" : monitor.Index.ToString(),
+ monitor.Name,
+ monitor.DeviceName,
+ inputInfo
+ };
+
+ // Add brightness and contrast columns if verbose mode is enabled
+ if (verboseOutput)
+ {
+ row.Add(brightnessInfo);
+ row.Add(contrastInfo);
+ }
+
+ row.Add(status);
+
+ table.AddRow(row.ToArray());
+ }
+
+ AnsiConsole.Write(table);
+ }
+}
+
diff --git a/DDCSwitch/Commands/SetCommand.cs b/DDCSwitch/Commands/SetCommand.cs
new file mode 100644
index 0000000..bcbaafa
--- /dev/null
+++ b/DDCSwitch/Commands/SetCommand.cs
@@ -0,0 +1,388 @@
+using Spectre.Console;
+using System.Text.Json;
+
+namespace DDCSwitch.Commands;
+
+internal static class SetCommand
+{
+ public static int Execute(string[] args, bool jsonOutput)
+ {
+ // Require 4 arguments: set
+ if (args.Length < 4)
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, "Monitor, feature, and value required");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError("Monitor, feature, and value required.");
+ AnsiConsole.MarkupLine("Usage: [yellow]DDCSwitch set [/]");
+ }
+
+ return 1;
+ }
+
+ string featureInput = args[2];
+ string valueInput = args[3];
+
+ if (!FeatureResolver.TryResolveFeature(featureInput, out VcpFeature? feature))
+ {
+ return HandleInvalidFeature(featureInput, jsonOutput);
+ }
+
+ var (setValue, percentageValue, validationError) = ParseAndValidateValue(feature!, valueInput);
+
+ if (validationError != null)
+ {
+ return HandleValidationError(validationError, jsonOutput);
+ }
+
+ var monitors = MonitorController.EnumerateMonitors();
+
+ if (monitors.Count == 0)
+ {
+ return HandleNoMonitors(jsonOutput);
+ }
+
+ var monitor = MonitorController.FindMonitor(monitors, args[1]);
+
+ if (monitor == null)
+ {
+ return HandleMonitorNotFound(monitors, args[1], jsonOutput);
+ }
+
+ int result = SetFeatureValue(monitor, feature!, setValue, percentageValue, jsonOutput);
+
+ // Cleanup
+ foreach (var m in monitors)
+ {
+ m.Dispose();
+ }
+
+ return result;
+ }
+
+ private static int HandleInvalidFeature(string featureInput, bool jsonOutput)
+ {
+ string errorMessage;
+
+ // Provide specific error message based on input type
+ if (FeatureResolver.TryParseVcpCode(featureInput, out _))
+ {
+ // Valid VCP code but not in our predefined list
+ errorMessage = $"VCP code '{featureInput}' is valid but may not be supported by all monitors";
+ }
+ else
+ {
+ // Invalid feature name or VCP code
+ errorMessage = $"Invalid feature '{featureInput}'. {FeatureResolver.GetVcpCodeValidationError(featureInput)}";
+ }
+
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, errorMessage);
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError(errorMessage);
+ AnsiConsole.MarkupLine("Valid features: brightness, contrast, input, or VCP code (0x10, 0x12, etc.)");
+ }
+
+ return 1;
+ }
+
+ private static (uint setValue, uint? percentageValue, string? validationError) ParseAndValidateValue(VcpFeature feature, string valueInput)
+ {
+ uint setValue = 0;
+ uint? percentageValue = null;
+ string? validationError = null;
+
+ if (feature.Code == InputSource.VcpInputSource)
+ {
+ // Use existing input source parsing for input feature
+ if (!InputSource.TryParse(valueInput, out setValue))
+ {
+ validationError = $"Invalid input source '{valueInput}'. Valid inputs: HDMI1, HDMI2, DP1, DP2, DVI1, DVI2, VGA1, VGA2, or hex code (0x11)";
+ }
+ }
+ else if (feature.SupportsPercentage && FeatureResolver.TryParsePercentage(valueInput, out uint percentage))
+ {
+ // Parse as percentage for brightness/contrast - validate percentage range
+ if (!FeatureResolver.IsValidPercentage(percentage))
+ {
+ validationError = VcpErrorHandler.CreateRangeValidationMessage(feature, percentage, 100, true);
+ }
+ else
+ {
+ percentageValue = percentage;
+ // We'll convert to raw value after getting monitor's max value
+ setValue = 0; // Placeholder
+ }
+ }
+ else if (uint.TryParse(valueInput, out uint rawValue))
+ {
+ // Parse as raw value - we'll validate range after getting monitor's max value
+ setValue = rawValue;
+ }
+ else
+ {
+ // Invalid value format
+ if (feature.SupportsPercentage)
+ {
+ validationError = FeatureResolver.GetPercentageValidationError(valueInput);
+ }
+ else
+ {
+ validationError = $"Invalid value '{valueInput}' for feature '{feature.Name}'. Expected: numeric value within monitor's supported range";
+ }
+ }
+
+ return (setValue, percentageValue, validationError);
+ }
+
+ private static int HandleValidationError(string validationError, bool jsonOutput)
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, validationError);
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError(validationError);
+ }
+
+ return 1;
+ }
+
+ private static int HandleNoMonitors(bool jsonOutput)
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, "No DDC/CI capable monitors found");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError("No DDC/CI capable monitors found.");
+ }
+
+ return 1;
+ }
+
+ private static int HandleMonitorNotFound(List monitors, string monitorId, bool jsonOutput)
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, $"Monitor '{monitorId}' not found");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError($"Monitor '{monitorId}' not found.");
+ AnsiConsole.MarkupLine("Use [yellow]DDCSwitch list[/] to see available monitors.");
+ }
+
+ // Cleanup
+ foreach (var m in monitors)
+ {
+ m.Dispose();
+ }
+
+ return 1;
+ }
+
+ private static int SetFeatureValue(Monitor monitor, VcpFeature feature, uint setValue, uint? percentageValue, bool jsonOutput)
+ {
+ // If we have a percentage value or need to validate raw value range, get the monitor's max value
+ if (percentageValue.HasValue || (feature.Code != InputSource.VcpInputSource && !percentageValue.HasValue))
+ {
+ if (monitor.TryGetVcpFeature(feature.Code, out _, out uint maxValue, out int errorCode))
+ {
+ if (percentageValue.HasValue)
+ {
+ // Convert percentage to raw value
+ setValue = FeatureResolver.ConvertPercentageToRaw(percentageValue.Value, maxValue);
+ }
+ else if (feature.Code != InputSource.VcpInputSource)
+ {
+ // Validate raw value is within supported range
+ if (!FeatureResolver.IsValidRawVcpValue(setValue, maxValue))
+ {
+ string rangeError = VcpErrorHandler.CreateRangeValidationMessage(feature, setValue, maxValue);
+
+ if (jsonOutput)
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+ var error = new ErrorResponse(false, rangeError, monitorRef);
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError(rangeError);
+ }
+
+ return 1;
+ }
+ }
+ }
+ else
+ {
+ return HandleReadErrorDuringValidation(monitor, feature, errorCode, jsonOutput);
+ }
+ }
+
+ return WriteFeatureValue(monitor, feature, setValue, percentageValue, jsonOutput);
+ }
+
+ private static int HandleReadErrorDuringValidation(Monitor monitor, VcpFeature feature, int errorCode, bool jsonOutput)
+ {
+ string readError;
+
+ if (VcpErrorHandler.IsTimeoutError(errorCode))
+ {
+ readError = VcpErrorHandler.CreateTimeoutMessage(monitor, feature, "read");
+ }
+ else if (VcpErrorHandler.IsUnsupportedFeatureError(errorCode))
+ {
+ readError = VcpErrorHandler.CreateUnsupportedFeatureMessage(monitor, feature);
+ }
+ else if (errorCode == 0x00000006) // ERROR_INVALID_HANDLE
+ {
+ readError = VcpErrorHandler.CreateCommunicationFailureMessage(monitor);
+ }
+ else
+ {
+ readError = $"Failed to read current {feature.Name} from monitor '{monitor.Name}' to validate range. {VcpErrorHandler.CreateReadFailureMessage(monitor, feature)}";
+ }
+
+ if (jsonOutput)
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+ var error = new ErrorResponse(false, readError, monitorRef);
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError(readError);
+ }
+
+ return 1;
+ }
+
+ private static int WriteFeatureValue(Monitor monitor, VcpFeature feature, uint setValue, uint? percentageValue, bool jsonOutput)
+ {
+ bool success = false;
+ string? errorMsg = null;
+
+ if (!jsonOutput)
+ {
+ string displayValue = percentageValue.HasValue ? $"{percentageValue}%" : setValue.ToString();
+ AnsiConsole.Status()
+ .Start($"Setting {monitor.Name} {feature.Name} to {displayValue}...", ctx =>
+ {
+ ctx.Spinner(Spinner.Known.Dots);
+
+ if (!monitor.TrySetVcpFeature(feature.Code, setValue, out int errorCode))
+ {
+ errorMsg = GetWriteErrorMessage(monitor, feature, setValue, errorCode);
+ }
+ else
+ {
+ success = true;
+ }
+
+ if (success)
+ {
+ // Give the monitor a moment to apply the change
+ Thread.Sleep(500);
+ }
+ });
+ }
+ else
+ {
+ if (!monitor.TrySetVcpFeature(feature.Code, setValue, out int errorCode))
+ {
+ errorMsg = GetWriteErrorMessage(monitor, feature, setValue, errorCode);
+ }
+ else
+ {
+ success = true;
+ Thread.Sleep(500);
+ }
+ }
+
+ if (!success)
+ {
+ if (jsonOutput)
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+ var error = new ErrorResponse(false, errorMsg!, monitorRef);
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError(errorMsg!);
+ }
+
+ return 1;
+ }
+
+ OutputSuccess(monitor, feature, setValue, percentageValue, jsonOutput);
+ return 0;
+ }
+
+ private static string GetWriteErrorMessage(Monitor monitor, VcpFeature feature, uint setValue, int errorCode)
+ {
+ if (VcpErrorHandler.IsTimeoutError(errorCode))
+ {
+ return VcpErrorHandler.CreateTimeoutMessage(monitor, feature, "write");
+ }
+ else if (VcpErrorHandler.IsUnsupportedFeatureError(errorCode))
+ {
+ return VcpErrorHandler.CreateUnsupportedFeatureMessage(monitor, feature);
+ }
+ else if (errorCode == 0x00000006) // ERROR_INVALID_HANDLE
+ {
+ return VcpErrorHandler.CreateCommunicationFailureMessage(monitor);
+ }
+ else
+ {
+ return VcpErrorHandler.CreateWriteFailureMessage(monitor, feature, setValue);
+ }
+ }
+
+ private static void OutputSuccess(Monitor monitor, VcpFeature feature, uint setValue, uint? percentageValue, bool jsonOutput)
+ {
+ if (jsonOutput)
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+
+ // Use generic VCP response for all features
+ var result = new SetVcpResponse(true, monitorRef, feature.Name, setValue, percentageValue);
+ Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.SetVcpResponse));
+ }
+ else
+ {
+ if (feature.Code == InputSource.VcpInputSource)
+ {
+ // Display input with name resolution
+ ConsoleOutputFormatter.WriteSuccess($"Successfully set {monitor.Name} {feature.Name} to {InputSource.GetName(setValue)}");
+ }
+ else if (percentageValue.HasValue)
+ {
+ // Display percentage for brightness/contrast
+ ConsoleOutputFormatter.WriteSuccess($"Successfully set {monitor.Name} {feature.Name} to {percentageValue}%");
+ }
+ else
+ {
+ // Display raw value for unknown VCP codes
+ ConsoleOutputFormatter.WriteSuccess($"Successfully set {monitor.Name} {feature.Name} to {setValue}");
+ }
+ }
+ }
+}
+
diff --git a/DDCSwitch/Commands/VcpScanCommand.cs b/DDCSwitch/Commands/VcpScanCommand.cs
new file mode 100644
index 0000000..101115e
--- /dev/null
+++ b/DDCSwitch/Commands/VcpScanCommand.cs
@@ -0,0 +1,278 @@
+using Spectre.Console;
+using System.Text.Json;
+
+namespace DDCSwitch.Commands;
+
+internal static class VcpScanCommand
+{
+ public static int ScanAllMonitors(List monitors, bool jsonOutput)
+ {
+ if (jsonOutput)
+ {
+ OutputJsonScanAll(monitors);
+ }
+ else
+ {
+ OutputTableScanAll(monitors);
+ }
+
+ // Cleanup
+ foreach (var monitor in monitors)
+ {
+ monitor.Dispose();
+ }
+
+ return 0;
+ }
+
+ public static int ScanSingleMonitor(string monitorIdentifier, bool jsonOutput)
+ {
+ List monitors;
+
+ if (!jsonOutput)
+ {
+ monitors = null!;
+ AnsiConsole.Status()
+ .Start("Enumerating monitors...", ctx =>
+ {
+ ctx.Spinner(Spinner.Known.Dots);
+ monitors = MonitorController.EnumerateMonitors();
+ });
+ }
+ else
+ {
+ monitors = MonitorController.EnumerateMonitors();
+ }
+
+ if (monitors.Count == 0)
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, "No DDC/CI capable monitors found");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError("No DDC/CI capable monitors found.");
+ }
+
+ return 1;
+ }
+
+ var monitor = MonitorController.FindMonitor(monitors, monitorIdentifier);
+
+ if (monitor == null)
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, $"Monitor '{monitorIdentifier}' not found");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError($"Monitor '{monitorIdentifier}' not found.");
+ AnsiConsole.MarkupLine("Use [yellow]DDCSwitch list[/] to see available monitors.");
+ }
+
+ // Cleanup
+ foreach (var m in monitors)
+ {
+ m.Dispose();
+ }
+
+ return 1;
+ }
+
+ int result;
+
+ try
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+ Dictionary features;
+
+ if (!jsonOutput)
+ {
+ features = null!;
+ AnsiConsole.Status()
+ .Start($"Scanning VCP features for {monitor.Name}...", ctx =>
+ {
+ ctx.Spinner(Spinner.Known.Dots);
+ features = monitor.ScanVcpFeatures();
+ });
+ }
+ else
+ {
+ features = monitor.ScanVcpFeatures();
+ }
+
+ // Filter only supported features for cleaner output
+ var supportedFeatures = features.Values
+ .Where(f => f.IsSupported)
+ .OrderBy(f => f.Code)
+ .ToList();
+
+ if (jsonOutput)
+ {
+ OutputJsonScanSingle(monitorRef, supportedFeatures);
+ }
+ else
+ {
+ OutputTableScanSingle(monitor, supportedFeatures);
+ }
+
+ result = 0;
+ }
+ catch (Exception ex)
+ {
+ if (jsonOutput)
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+ var scanResult = new VcpScanResponse(false, monitorRef, new List(), ex.Message);
+ Console.WriteLine(JsonSerializer.Serialize(scanResult, JsonContext.Default.VcpScanResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError($"Error scanning monitor {monitor.Index} ({monitor.Name}): {ex.Message}");
+ }
+
+ result = 1;
+ }
+
+ // Cleanup
+ foreach (var m in monitors)
+ {
+ m.Dispose();
+ }
+
+ return result;
+ }
+
+ private static void OutputJsonScanAll(List monitors)
+ {
+ var scanResults = new List();
+
+ foreach (var monitor in monitors)
+ {
+ try
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+ var features = monitor.ScanVcpFeatures();
+
+ // Convert to list and filter only supported features for cleaner output
+ var supportedFeatures = features.Values
+ .Where(f => f.IsSupported)
+ .OrderBy(f => f.Code)
+ .ToList();
+
+ scanResults.Add(new VcpScanResponse(true, monitorRef, supportedFeatures));
+ }
+ catch (Exception ex)
+ {
+ var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
+ scanResults.Add(new VcpScanResponse(false, monitorRef, new List(), ex.Message));
+ }
+ }
+
+ // Output all scan results
+ foreach (var result in scanResults)
+ {
+ Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.VcpScanResponse));
+ }
+ }
+
+ private static void OutputTableScanAll(List monitors)
+ {
+ foreach (var monitor in monitors)
+ {
+ try
+ {
+ AnsiConsole.MarkupLine($"\n[bold blue]Monitor {monitor.Index}: {monitor.Name}[/] ({monitor.DeviceName})");
+
+ Dictionary features = null!;
+ AnsiConsole.Status()
+ .Start($"Scanning VCP features for {monitor.Name}...", ctx =>
+ {
+ ctx.Spinner(Spinner.Known.Dots);
+ features = monitor.ScanVcpFeatures();
+ });
+
+ var supportedFeatures = features.Values
+ .Where(f => f.IsSupported)
+ .OrderBy(f => f.Code)
+ .ToList();
+
+ if (supportedFeatures.Count == 0)
+ {
+ AnsiConsole.MarkupLine("[yellow] No supported VCP features found[/]");
+ continue;
+ }
+
+ OutputFeatureTable(supportedFeatures);
+ }
+ catch (Exception ex)
+ {
+ ConsoleOutputFormatter.WriteError($"Error scanning monitor {monitor.Index} ({monitor.Name}): {ex.Message}");
+ }
+ }
+ }
+
+ private static void OutputJsonScanSingle(MonitorReference monitorRef, List supportedFeatures)
+ {
+ var scanResult = new VcpScanResponse(true, monitorRef, supportedFeatures);
+ Console.WriteLine(JsonSerializer.Serialize(scanResult, JsonContext.Default.VcpScanResponse));
+ }
+
+ private static void OutputTableScanSingle(Monitor monitor, List supportedFeatures)
+ {
+ AnsiConsole.MarkupLine($"[bold blue]Monitor {monitor.Index}: {monitor.Name}[/] ({monitor.DeviceName})");
+
+ if (supportedFeatures.Count == 0)
+ {
+ AnsiConsole.MarkupLine("[yellow] No supported VCP features found[/]");
+ }
+ else
+ {
+ OutputFeatureTable(supportedFeatures);
+ }
+ }
+
+ private static void OutputFeatureTable(List supportedFeatures)
+ {
+ var table = new Table()
+ .Border(TableBorder.Rounded)
+ .AddColumn("VCP Code")
+ .AddColumn("Feature Name")
+ .AddColumn("Access Type")
+ .AddColumn("Current Value")
+ .AddColumn("Max Value")
+ .AddColumn("Percentage");
+
+ foreach (var feature in supportedFeatures)
+ {
+ string vcpCode = $"0x{feature.Code:X2}";
+ string accessType = feature.Type switch
+ {
+ VcpFeatureType.ReadOnly => "[yellow]Read-Only[/]",
+ VcpFeatureType.WriteOnly => "[red]Write-Only[/]",
+ VcpFeatureType.ReadWrite => "[green]Read-Write[/]",
+ _ => "[dim]Unknown[/]"
+ };
+
+ string currentValue = feature.CurrentValue.ToString();
+ string maxValue = feature.MaxValue.ToString();
+
+ // Calculate percentage for known percentage-based features
+ string percentage = "N/A";
+ if ((feature.Code == VcpFeature.Brightness.Code || feature.Code == VcpFeature.Contrast.Code) && feature.MaxValue > 0)
+ {
+ uint percentageValue = FeatureResolver.ConvertRawToPercentage(feature.CurrentValue, feature.MaxValue);
+ percentage = $"{percentageValue}%";
+ }
+
+ table.AddRow(vcpCode, feature.Name, accessType, currentValue, maxValue, percentage);
+ }
+
+ AnsiConsole.Write(table);
+ }
+}
+
diff --git a/DDCSwitch/Program.cs b/DDCSwitch/Program.cs
index 39c675e..0cac76e 100644
--- a/DDCSwitch/Program.cs
+++ b/DDCSwitch/Program.cs
@@ -1,1203 +1,4 @@
-using DDCSwitch;
-using Spectre.Console;
-using System.Text.Json;
-using System.Text.Json.Serialization;
+using DDCSwitch.Commands;
-return DDCSwitchProgram.Run(args);
+return CommandRouter.Route(args);
-static class DDCSwitchProgram
-{
- private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web)
- {
- WriteIndented = true,
- PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
- DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
- TypeInfoResolver = JsonContext.Default
- };
-
- public static int Run(string[] args)
- {
- if (args.Length == 0)
- {
- ShowUsage();
- return 1;
- }
-
- // Check for --json flag
- bool jsonOutput = args.Contains("--json", StringComparer.OrdinalIgnoreCase);
- var filteredArgs = args.Where(a => !a.Equals("--json", StringComparison.OrdinalIgnoreCase)).ToArray();
-
- // Check for --verbose flag
- bool verboseOutput = filteredArgs.Contains("--verbose", StringComparer.OrdinalIgnoreCase);
- filteredArgs = filteredArgs.Where(a => !a.Equals("--verbose", StringComparison.OrdinalIgnoreCase)).ToArray();
-
- // Check for --scan flag
- bool scanOutput = filteredArgs.Contains("--scan", StringComparer.OrdinalIgnoreCase);
- filteredArgs = filteredArgs.Where(a => !a.Equals("--scan", StringComparison.OrdinalIgnoreCase)).ToArray();
-
- if (filteredArgs.Length == 0)
- {
- ShowUsage();
- return 1;
- }
-
- var command = filteredArgs[0].ToLowerInvariant();
-
- try
- {
- return command switch
- {
- "list" or "ls" => ListMonitors(jsonOutput, verboseOutput, scanOutput),
- "get" => GetCurrentInput(filteredArgs, jsonOutput),
- "set" => SetInput(filteredArgs, jsonOutput),
- "version" or "-v" or "--version" => ShowVersion(jsonOutput),
- "help" or "-h" or "--help" or "/?" => ShowUsage(),
- _ => InvalidCommand(filteredArgs[0], jsonOutput)
- };
- }
- catch (Exception ex)
- {
- if (jsonOutput)
- {
- var error = new ErrorResponse(false, ex.Message);
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine($"[red]Error:[/] {ex.Message}");
- }
-
- return 1;
- }
- }
-
- private static string GetVersion()
- {
- var version = typeof(DDCSwitchProgram).Assembly
- .GetName().Version?.ToString(3) ?? "0.0.0";
- return version;
- }
-
- private static int ShowVersion(bool jsonOutput)
- {
- var version = GetVersion();
-
- if (jsonOutput)
- {
- Console.WriteLine($"{{\"version\":\"{version}\"}}");
- }
- else
- {
- AnsiConsole.Write(new FigletText("DDCSwitch").Color(Color.Blue));
- AnsiConsole.MarkupLine($"[bold]Version:[/] [green]{version}[/]");
- AnsiConsole.MarkupLine("[dim]Windows DDC/CI Monitor Input Switcher[/]");
- }
-
- return 0;
- }
-
- private static int ShowUsage()
- {
- var version = GetVersion();
-
- AnsiConsole.Write(new FigletText("DDCSwitch").Color(Color.Blue));
- AnsiConsole.MarkupLine($"[dim]Windows DDC/CI Monitor Input Switcher v{version}[/]\n");
-
- AnsiConsole.MarkupLine("[yellow]Commands:[/]");
- AnsiConsole.WriteLine(" list [--verbose] [--scan] - List all DDC/CI capable monitors");
- AnsiConsole.WriteLine(" get monitor [feature] - Get current value for a monitor feature or scan all features");
- AnsiConsole.MarkupLine(" set monitor feature value - Set value for a monitor feature");
- AnsiConsole.MarkupLine(" version - Display version information");
-
- AnsiConsole.MarkupLine("\nSupported features: brightness, contrast, input, or VCP codes like 0x10");
- AnsiConsole.MarkupLine("Use [yellow]--json[/] flag for JSON output");
- AnsiConsole.MarkupLine("Use [yellow]--verbose[/] flag with list to include brightness and contrast");
- AnsiConsole.MarkupLine("Use [yellow]--scan[/] flag with list to enumerate all VCP codes");
-
- return 0;
- }
-
- private static int InvalidCommand(string command, bool jsonOutput)
- {
- if (jsonOutput)
- {
- var error = new ErrorResponse(false, $"Unknown command: {command}");
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine($"[red]Unknown command:[/] {command}");
- AnsiConsole.MarkupLine("Run [yellow]DDCSwitch help[/] for usage information.");
- }
-
- return 1;
- }
-
- private static int ListMonitors(bool jsonOutput, bool verboseOutput = false, bool scanOutput = false)
- {
- List monitors;
-
- if (!jsonOutput)
- {
- monitors = null!;
- AnsiConsole.Status()
- .Start(scanOutput ? "Scanning VCP features..." : "Enumerating monitors...", ctx =>
- {
- ctx.Spinner(Spinner.Known.Dots);
- monitors = MonitorController.EnumerateMonitors();
- });
- }
- else
- {
- monitors = MonitorController.EnumerateMonitors();
- }
-
- if (monitors.Count == 0)
- {
- if (jsonOutput)
- {
- var result = new ListMonitorsResponse(false, null, "No DDC/CI capable monitors found");
- Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.ListMonitorsResponse));
- }
- else
- {
- AnsiConsole.MarkupLine("[yellow]No DDC/CI capable monitors found.[/]");
- }
-
- return 1;
- }
-
- // If scan mode is enabled, perform VCP scanning for each monitor
- if (scanOutput)
- {
- return HandleVcpScan(monitors, jsonOutput);
- }
-
- if (jsonOutput)
- {
- var monitorList = monitors.Select(monitor =>
- {
- string? inputName = null;
- uint? inputCode = null;
- string status = "ok";
- string? brightness = null;
- string? contrast = null;
-
- try
- {
- if (monitor.TryGetInputSource(out uint current, out uint max))
- {
- inputName = InputSource.GetName(current);
- inputCode = current;
- }
- else
- {
- status = "no_ddc_ci";
- }
-
- // Get brightness and contrast if verbose mode is enabled
- if (verboseOutput && status == "ok")
- {
- // Try to get brightness (VCP 0x10)
- if (monitor.TryGetVcpFeature(VcpFeature.Brightness.Code, out uint brightnessCurrent, out uint brightnessMax))
- {
- uint brightnessPercentage = FeatureResolver.ConvertRawToPercentage(brightnessCurrent, brightnessMax);
- brightness = $"{brightnessPercentage}%";
- }
- else
- {
- brightness = "N/A";
- }
-
- // Try to get contrast (VCP 0x12)
- if (monitor.TryGetVcpFeature(VcpFeature.Contrast.Code, out uint contrastCurrent, out uint contrastMax))
- {
- uint contrastPercentage = FeatureResolver.ConvertRawToPercentage(contrastCurrent, contrastMax);
- contrast = $"{contrastPercentage}%";
- }
- else
- {
- contrast = "N/A";
- }
- }
- }
- catch
- {
- status = "error";
- if (verboseOutput)
- {
- brightness = "N/A";
- contrast = "N/A";
- }
- }
-
- return new MonitorInfo(
- monitor.Index,
- monitor.Name,
- monitor.DeviceName,
- monitor.IsPrimary,
- inputName,
- inputCode != null ? $"0x{inputCode:X2}" : null,
- status,
- brightness,
- contrast);
- }).ToList();
-
- var result = new ListMonitorsResponse(true, monitorList);
- Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.ListMonitorsResponse));
- }
- else
- {
- var table = new Table()
- .Border(TableBorder.Rounded)
- .AddColumn("Index")
- .AddColumn("Monitor Name")
- .AddColumn("Device")
- .AddColumn("Current Input");
-
- // Add brightness and contrast columns if verbose mode is enabled
- if (verboseOutput)
- {
- table.AddColumn("Brightness");
- table.AddColumn("Contrast");
- }
-
- table.AddColumn("Status");
-
- foreach (var monitor in monitors)
- {
- string inputInfo = "N/A";
- string status = "[green]OK[/]";
- string brightnessInfo = "N/A";
- string contrastInfo = "N/A";
-
- try
- {
- if (monitor.TryGetInputSource(out uint current, out uint max))
- {
- inputInfo = $"{InputSource.GetName(current)} (0x{current:X2})";
- }
- else
- {
- status = "[yellow]No DDC/CI[/]";
- }
-
- // Get brightness and contrast if verbose mode is enabled and monitor supports DDC/CI
- if (verboseOutput && status == "[green]OK[/]")
- {
- // Try to get brightness (VCP 0x10)
- if (monitor.TryGetVcpFeature(VcpFeature.Brightness.Code, out uint brightnessCurrent, out uint brightnessMax))
- {
- uint brightnessPercentage = FeatureResolver.ConvertRawToPercentage(brightnessCurrent, brightnessMax);
- brightnessInfo = $"{brightnessPercentage}%";
- }
- else
- {
- brightnessInfo = "[dim]N/A[/]";
- }
-
- // Try to get contrast (VCP 0x12)
- if (monitor.TryGetVcpFeature(VcpFeature.Contrast.Code, out uint contrastCurrent, out uint contrastMax))
- {
- uint contrastPercentage = FeatureResolver.ConvertRawToPercentage(contrastCurrent, contrastMax);
- contrastInfo = $"{contrastPercentage}%";
- }
- else
- {
- contrastInfo = "[dim]N/A[/]";
- }
- }
- else if (verboseOutput)
- {
- brightnessInfo = "[dim]N/A[/]";
- contrastInfo = "[dim]N/A[/]";
- }
- }
- catch
- {
- status = "[red]Error[/]";
- if (verboseOutput)
- {
- brightnessInfo = "[dim]N/A[/]";
- contrastInfo = "[dim]N/A[/]";
- }
- }
-
- var row = new List
- {
- monitor.IsPrimary ? $"{monitor.Index} [yellow]*[/]" : monitor.Index.ToString(),
- monitor.Name,
- monitor.DeviceName,
- inputInfo
- };
-
- // Add brightness and contrast columns if verbose mode is enabled
- if (verboseOutput)
- {
- row.Add(brightnessInfo);
- row.Add(contrastInfo);
- }
-
- row.Add(status);
-
- table.AddRow(row.ToArray());
- }
-
- AnsiConsole.Write(table);
- }
-
- // Cleanup
- foreach (var monitor in monitors)
- {
- monitor.Dispose();
- }
-
- return 0;
- }
-
- private static int GetCurrentInput(string[] args, bool jsonOutput)
- {
- if (args.Length < 2)
- {
- if (jsonOutput)
- {
- var error = new ErrorResponse(false, "Monitor identifier required");
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine("[red]Error:[/] Monitor identifier required.");
- AnsiConsole.WriteLine("Usage: DDCSwitch get [feature]");
- }
-
- return 1;
- }
-
- // If no feature is specified, perform VCP scan
- if (args.Length == 2)
- {
- return HandleVcpScanForMonitor(args[1], jsonOutput);
- }
-
- string featureInput = args[2];
-
- if (!FeatureResolver.TryResolveFeature(featureInput, out VcpFeature? feature))
- {
- string errorMessage;
-
- // Provide specific error message based on input type
- if (FeatureResolver.TryParseVcpCode(featureInput, out _))
- {
- // Valid VCP code but not in our predefined list
- errorMessage = $"VCP code '{featureInput}' is valid but may not be supported by all monitors";
- }
- else
- {
- // Invalid feature name or VCP code
- errorMessage = $"Invalid feature '{featureInput}'. {FeatureResolver.GetVcpCodeValidationError(featureInput)}";
- }
-
- if (jsonOutput)
- {
- var error = new ErrorResponse(false, errorMessage);
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine($"[red]Error:[/] {errorMessage}");
- AnsiConsole.MarkupLine("Valid features: brightness, contrast, input, or VCP code (0x10, 0x12, etc.)");
- }
-
- return 1;
- }
-
- var monitors = MonitorController.EnumerateMonitors();
-
- if (monitors.Count == 0)
- {
- if (jsonOutput)
- {
- var error = new ErrorResponse(false, "No DDC/CI capable monitors found");
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine("[red]Error:[/] No DDC/CI capable monitors found.");
- }
-
- return 1;
- }
-
- var monitor = MonitorController.FindMonitor(monitors, args[1]);
-
- if (monitor == null)
- {
- if (jsonOutput)
- {
- var error = new ErrorResponse(false, $"Monitor '{args[1]}' not found");
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine($"[red]Error:[/] Monitor '{args[1]}' not found.");
- AnsiConsole.MarkupLine("Use [yellow]DDCSwitch list[/] to see available monitors.");
- }
-
- // Cleanup
- foreach (var m in monitors)
- {
- m.Dispose();
- }
-
- return 1;
- }
-
- // Use generic VCP method for all features with enhanced error handling
- bool success = false;
- uint current = 0;
- uint max = 0;
- int errorCode = 0;
-
- if (!jsonOutput)
- {
- AnsiConsole.Status()
- .Start($"Reading {feature!.Name} from {monitor.Name}...", ctx =>
- {
- ctx.Spinner(Spinner.Known.Dots);
- success = monitor.TryGetVcpFeature(feature.Code, out current, out max, out errorCode);
- });
- }
- else
- {
- success = monitor.TryGetVcpFeature(feature!.Code, out current, out max, out errorCode);
- }
-
- if (!success)
- {
- string errorMessage;
-
- if (VcpErrorHandler.IsTimeoutError(errorCode))
- {
- errorMessage = VcpErrorHandler.CreateTimeoutMessage(monitor, feature, "read");
- }
- else if (VcpErrorHandler.IsUnsupportedFeatureError(errorCode))
- {
- errorMessage = VcpErrorHandler.CreateUnsupportedFeatureMessage(monitor, feature);
- }
- else if (errorCode == 0x00000006) // ERROR_INVALID_HANDLE
- {
- errorMessage = VcpErrorHandler.CreateCommunicationFailureMessage(monitor);
- }
- else
- {
- errorMessage = VcpErrorHandler.CreateReadFailureMessage(monitor, feature);
- }
-
- if (jsonOutput)
- {
- var monitorRef =
- new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- var error = new ErrorResponse(false, errorMessage, monitorRef);
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine($"[red]Error:[/] {errorMessage}");
- }
-
- // Cleanup
- foreach (var m in monitors)
- {
- m.Dispose();
- }
-
- return 1;
- }
-
- if (jsonOutput)
- {
- var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
-
- uint? percentageValue = feature.SupportsPercentage ? FeatureResolver.ConvertRawToPercentage(current, max) : null;
- var result = new GetVcpResponse(true, monitorRef, feature.Name, current, max, percentageValue);
- Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.GetVcpResponse));
- }
- else
- {
- AnsiConsole.MarkupLine($"[green]Monitor:[/] {monitor.Name} ({monitor.DeviceName})");
-
- if (feature.Code == InputSource.VcpInputSource)
- {
- // Display input with name resolution
- AnsiConsole.MarkupLine($"[green]Current {feature.Name}:[/] {InputSource.GetName(current)} (0x{current:X2})");
- }
- else if (feature.SupportsPercentage)
- {
- // Display percentage for brightness/contrast
- uint percentage = FeatureResolver.ConvertRawToPercentage(current, max);
- AnsiConsole.MarkupLine($"[green]Current {feature.Name}:[/] {percentage}% (raw: {current}/{max})");
- }
- else
- {
- // Display raw values for unknown VCP codes
- AnsiConsole.MarkupLine($"[green]Current {feature.Name}:[/] {current} (max: {max})");
- }
- }
-
- // Cleanup
- foreach (var m in monitors)
- {
- m.Dispose();
- }
-
- return 0;
- }
-
- private static int SetInput(string[] args, bool jsonOutput)
- {
- // Require 4 arguments: set
- if (args.Length < 4)
- {
- if (jsonOutput)
- {
- var error = new ErrorResponse(false, "Monitor, feature, and value required");
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine("[red]Error:[/] Monitor, feature, and value required.");
- AnsiConsole.MarkupLine("Usage: [yellow]DDCSwitch set [/]");
- }
-
- return 1;
- }
-
- string featureInput = args[2];
- string valueInput = args[3];
-
- if (!FeatureResolver.TryResolveFeature(featureInput, out VcpFeature? feature))
- {
- string errorMessage;
-
- // Provide specific error message based on input type
- if (FeatureResolver.TryParseVcpCode(featureInput, out _))
- {
- // Valid VCP code but not in our predefined list
- errorMessage = $"VCP code '{featureInput}' is valid but may not be supported by all monitors";
- }
- else
- {
- // Invalid feature name or VCP code
- errorMessage = $"Invalid feature '{featureInput}'. {FeatureResolver.GetVcpCodeValidationError(featureInput)}";
- }
-
- if (jsonOutput)
- {
- var error = new ErrorResponse(false, errorMessage);
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine($"[red]Error:[/] {errorMessage}");
- AnsiConsole.MarkupLine("Valid features: brightness, contrast, input, or VCP code (0x10, 0x12, etc.)");
- }
-
- return 1;
- }
-
- // Parse and validate the value based on feature type
- uint setValue = 0; // Initialize to avoid compiler error
- uint? percentageValue = null;
- string? validationError = null;
-
- if (feature!.Code == InputSource.VcpInputSource)
- {
- // Use existing input source parsing for input feature
- if (!InputSource.TryParse(valueInput, out setValue))
- {
- validationError = $"Invalid input source '{valueInput}'. Valid inputs: HDMI1, HDMI2, DP1, DP2, DVI1, DVI2, VGA1, VGA2, or hex code (0x11)";
- }
- }
- else if (feature.SupportsPercentage && FeatureResolver.TryParsePercentage(valueInput, out uint percentage))
- {
- // Parse as percentage for brightness/contrast - validate percentage range
- if (!FeatureResolver.IsValidPercentage(percentage))
- {
- validationError = VcpErrorHandler.CreateRangeValidationMessage(feature, percentage, 100, true);
- }
- else
- {
- percentageValue = percentage;
- // We'll convert to raw value after getting monitor's max value
- setValue = 0; // Placeholder
- }
- }
- else if (uint.TryParse(valueInput, out uint rawValue))
- {
- // Parse as raw value - we'll validate range after getting monitor's max value
- setValue = rawValue;
- }
- else
- {
- // Invalid value format
- if (feature.SupportsPercentage)
- {
- validationError = FeatureResolver.GetPercentageValidationError(valueInput);
- }
- else
- {
- validationError = $"Invalid value '{valueInput}' for feature '{feature.Name}'. Expected: numeric value within monitor's supported range";
- }
- }
-
- // If we have a validation error, return it now
- if (validationError != null)
- {
- if (jsonOutput)
- {
- var error = new ErrorResponse(false, validationError);
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine($"[red]Error:[/] {validationError}");
- }
-
- return 1;
- }
-
- var monitors = MonitorController.EnumerateMonitors();
-
- if (monitors.Count == 0)
- {
- if (jsonOutput)
- {
- var error = new ErrorResponse(false, "No DDC/CI capable monitors found");
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine("[red]Error:[/] No DDC/CI capable monitors found.");
- }
-
- return 1;
- }
-
- var monitor = MonitorController.FindMonitor(monitors, args[1]);
-
- if (monitor == null)
- {
- if (jsonOutput)
- {
- var error = new ErrorResponse(false, $"Monitor '{args[1]}' not found");
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine($"[red]Error:[/] Monitor '{args[1]}' not found.");
- AnsiConsole.MarkupLine("Use [yellow]DDCSwitch list[/] to see available monitors.");
- }
-
- // Cleanup
- foreach (var m in monitors)
- {
- m.Dispose();
- }
-
- return 1;
- }
-
- // If we have a percentage value or need to validate raw value range, get the monitor's max value
- if (percentageValue.HasValue || (feature!.Code != InputSource.VcpInputSource && !percentageValue.HasValue))
- {
- if (monitor.TryGetVcpFeature(feature.Code, out uint currentValue, out uint maxValue, out int errorCode))
- {
- if (percentageValue.HasValue)
- {
- // Convert percentage to raw value
- setValue = FeatureResolver.ConvertPercentageToRaw(percentageValue.Value, maxValue);
- }
- else if (feature.Code != InputSource.VcpInputSource)
- {
- // Validate raw value is within supported range
- if (!FeatureResolver.IsValidRawVcpValue(setValue, maxValue))
- {
- string rangeError = VcpErrorHandler.CreateRangeValidationMessage(feature, setValue, maxValue);
-
- if (jsonOutput)
- {
- var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- var error = new ErrorResponse(false, rangeError, monitorRef);
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine($"[red]Error:[/] {rangeError}");
- }
-
- // Cleanup
- foreach (var m in monitors)
- {
- m.Dispose();
- }
-
- return 1;
- }
- }
- }
- else
- {
- string readError;
-
- if (VcpErrorHandler.IsTimeoutError(errorCode))
- {
- readError = VcpErrorHandler.CreateTimeoutMessage(monitor, feature, "read");
- }
- else if (VcpErrorHandler.IsUnsupportedFeatureError(errorCode))
- {
- readError = VcpErrorHandler.CreateUnsupportedFeatureMessage(monitor, feature);
- }
- else if (errorCode == 0x00000006) // ERROR_INVALID_HANDLE
- {
- readError = VcpErrorHandler.CreateCommunicationFailureMessage(monitor);
- }
- else
- {
- readError = $"Failed to read current {feature.Name} from monitor '{monitor.Name}' to validate range. {VcpErrorHandler.CreateReadFailureMessage(monitor, feature)}";
- }
-
- if (jsonOutput)
- {
- var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- var error = new ErrorResponse(false, readError, monitorRef);
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine($"[red]Error:[/] {readError}");
- }
-
- // Cleanup
- foreach (var m in monitors)
- {
- m.Dispose();
- }
-
- return 1;
- }
- }
-
- bool success = false;
- string? errorMsg = null;
-
- if (!jsonOutput)
- {
- string displayValue = percentageValue.HasValue ? $"{percentageValue}%" : setValue.ToString();
- AnsiConsole.Status()
- .Start($"Setting {monitor.Name} {feature.Name} to {displayValue}...", ctx =>
- {
- ctx.Spinner(Spinner.Known.Dots);
-
- if (!monitor.TrySetVcpFeature(feature!.Code, setValue, out int errorCode))
- {
- if (VcpErrorHandler.IsTimeoutError(errorCode))
- {
- errorMsg = VcpErrorHandler.CreateTimeoutMessage(monitor, feature, "write");
- }
- else if (VcpErrorHandler.IsUnsupportedFeatureError(errorCode))
- {
- errorMsg = VcpErrorHandler.CreateUnsupportedFeatureMessage(monitor, feature);
- }
- else if (errorCode == 0x00000006) // ERROR_INVALID_HANDLE
- {
- errorMsg = VcpErrorHandler.CreateCommunicationFailureMessage(monitor);
- }
- else
- {
- errorMsg = VcpErrorHandler.CreateWriteFailureMessage(monitor, feature, setValue);
- }
- }
- else
- {
- success = true;
- }
-
- if (success)
- {
- // Give the monitor a moment to apply the change
- Thread.Sleep(500);
- }
- });
- }
- else
- {
- if (!monitor.TrySetVcpFeature(feature!.Code, setValue, out int errorCode))
- {
- if (VcpErrorHandler.IsTimeoutError(errorCode))
- {
- errorMsg = VcpErrorHandler.CreateTimeoutMessage(monitor, feature, "write");
- }
- else if (VcpErrorHandler.IsUnsupportedFeatureError(errorCode))
- {
- errorMsg = VcpErrorHandler.CreateUnsupportedFeatureMessage(monitor, feature);
- }
- else if (errorCode == 0x00000006) // ERROR_INVALID_HANDLE
- {
- errorMsg = VcpErrorHandler.CreateCommunicationFailureMessage(monitor);
- }
- else
- {
- errorMsg = VcpErrorHandler.CreateWriteFailureMessage(monitor, feature, setValue);
- }
- }
- else
- {
- success = true;
- Thread.Sleep(500);
- }
- }
-
- if (!success)
- {
- if (jsonOutput)
- {
- var monitorRef =
- new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- var error = new ErrorResponse(false, errorMsg!, monitorRef);
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine($"[red]Error:[/] {errorMsg}");
- }
-
- // Cleanup
- foreach (var m in monitors)
- {
- m.Dispose();
- }
-
- return 1;
- }
-
- if (jsonOutput)
- {
- var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
-
- // Use generic VCP response for all features
- var result = new SetVcpResponse(true, monitorRef, feature!.Name, setValue, percentageValue);
- Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.SetVcpResponse));
- }
- else
- {
- if (feature!.Code == InputSource.VcpInputSource)
- {
- // Display input with name resolution
- AnsiConsole.MarkupLine($"[green]✓[/] Successfully set {monitor.Name} {feature.Name} to {InputSource.GetName(setValue)}");
- }
- else if (percentageValue.HasValue)
- {
- // Display percentage for brightness/contrast
- AnsiConsole.MarkupLine($"[green]✓[/] Successfully set {monitor.Name} {feature.Name} to {percentageValue}%");
- }
- else
- {
- // Display raw value for unknown VCP codes
- AnsiConsole.MarkupLine($"[green]✓[/] Successfully set {monitor.Name} {feature.Name} to {setValue}");
- }
- }
-
- // Cleanup
- foreach (var m in monitors)
- {
- m.Dispose();
- }
-
- return 0;
- }
-
- private static int HandleVcpScan(List monitors, bool jsonOutput)
- {
- if (jsonOutput)
- {
- // JSON output for VCP scan
- var scanResults = new List();
-
- foreach (var monitor in monitors)
- {
- try
- {
- var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- var features = monitor.ScanVcpFeatures();
-
- // Convert to list and filter only supported features for cleaner output
- var supportedFeatures = features.Values
- .Where(f => f.IsSupported)
- .OrderBy(f => f.Code)
- .ToList();
-
- scanResults.Add(new VcpScanResponse(true, monitorRef, supportedFeatures));
- }
- catch (Exception ex)
- {
- var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- scanResults.Add(new VcpScanResponse(false, monitorRef, new List(), ex.Message));
- }
- }
-
- // Output all scan results
- foreach (var result in scanResults)
- {
- Console.WriteLine(JsonSerializer.Serialize(result, JsonContext.Default.VcpScanResponse));
- }
- }
- else
- {
- // Table output for VCP scan
- foreach (var monitor in monitors)
- {
- try
- {
- AnsiConsole.MarkupLine($"\n[bold blue]Monitor {monitor.Index}: {monitor.Name}[/] ({monitor.DeviceName})");
-
- Dictionary features = null!;
- AnsiConsole.Status()
- .Start($"Scanning VCP features for {monitor.Name}...", ctx =>
- {
- ctx.Spinner(Spinner.Known.Dots);
- features = monitor.ScanVcpFeatures();
- });
-
- var supportedFeatures = features.Values
- .Where(f => f.IsSupported)
- .OrderBy(f => f.Code)
- .ToList();
-
- if (supportedFeatures.Count == 0)
- {
- AnsiConsole.MarkupLine("[yellow] No supported VCP features found[/]");
- continue;
- }
-
- var table = new Table()
- .Border(TableBorder.Rounded)
- .AddColumn("VCP Code")
- .AddColumn("Feature Name")
- .AddColumn("Access Type")
- .AddColumn("Current Value")
- .AddColumn("Max Value")
- .AddColumn("Percentage");
-
- foreach (var feature in supportedFeatures)
- {
- string vcpCode = $"0x{feature.Code:X2}";
- string accessType = feature.Type switch
- {
- VcpFeatureType.ReadOnly => "[yellow]Read-Only[/]",
- VcpFeatureType.WriteOnly => "[red]Write-Only[/]",
- VcpFeatureType.ReadWrite => "[green]Read-Write[/]",
- _ => "[dim]Unknown[/]"
- };
-
- string currentValue = feature.CurrentValue.ToString();
- string maxValue = feature.MaxValue.ToString();
-
- // Calculate percentage for known percentage-based features
- string percentage = "N/A";
- if ((feature.Code == VcpFeature.Brightness.Code || feature.Code == VcpFeature.Contrast.Code) && feature.MaxValue > 0)
- {
- uint percentageValue = FeatureResolver.ConvertRawToPercentage(feature.CurrentValue, feature.MaxValue);
- percentage = $"{percentageValue}%";
- }
-
- table.AddRow(vcpCode, feature.Name, accessType, currentValue, maxValue, percentage);
- }
-
- AnsiConsole.Write(table);
- }
- catch (Exception ex)
- {
- AnsiConsole.MarkupLine($"[red]Error scanning monitor {monitor.Index} ({monitor.Name}): {ex.Message}[/]");
- }
- }
- }
-
- // Cleanup
- foreach (var monitor in monitors)
- {
- monitor.Dispose();
- }
-
- return 0;
- }
-
- private static int HandleVcpScanForMonitor(string monitorIdentifier, bool jsonOutput)
- {
- List monitors;
-
- if (!jsonOutput)
- {
- monitors = null!;
- AnsiConsole.Status()
- .Start("Enumerating monitors...", ctx =>
- {
- ctx.Spinner(Spinner.Known.Dots);
- monitors = MonitorController.EnumerateMonitors();
- });
- }
- else
- {
- monitors = MonitorController.EnumerateMonitors();
- }
-
- if (monitors.Count == 0)
- {
- if (jsonOutput)
- {
- var error = new ErrorResponse(false, "No DDC/CI capable monitors found");
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine("[red]Error:[/] No DDC/CI capable monitors found.");
- }
-
- return 1;
- }
-
- var monitor = MonitorController.FindMonitor(monitors, monitorIdentifier);
-
- if (monitor == null)
- {
- if (jsonOutput)
- {
- var error = new ErrorResponse(false, $"Monitor '{monitorIdentifier}' not found");
- Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
- }
- else
- {
- AnsiConsole.MarkupLine($"[red]Error:[/] Monitor '{monitorIdentifier}' not found.");
- AnsiConsole.MarkupLine("Use [yellow]DDCSwitch list[/] to see available monitors.");
- }
-
- // Cleanup
- foreach (var m in monitors)
- {
- m.Dispose();
- }
-
- return 1;
- }
-
- try
- {
- var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- Dictionary features;
-
- if (!jsonOutput)
- {
- features = null!;
- AnsiConsole.Status()
- .Start($"Scanning VCP features for {monitor.Name}...", ctx =>
- {
- ctx.Spinner(Spinner.Known.Dots);
- features = monitor.ScanVcpFeatures();
- });
- }
- else
- {
- features = monitor.ScanVcpFeatures();
- }
-
- // Filter only supported features for cleaner output
- var supportedFeatures = features.Values
- .Where(f => f.IsSupported)
- .OrderBy(f => f.Code)
- .ToList();
-
- if (jsonOutput)
- {
- // JSON output for VCP scan
- var scanResult = new VcpScanResponse(true, monitorRef, supportedFeatures);
- Console.WriteLine(JsonSerializer.Serialize(scanResult, JsonContext.Default.VcpScanResponse));
- }
- else
- {
- // Table output for VCP scan - consistent with verbose listing format
- AnsiConsole.MarkupLine($"[bold blue]Monitor {monitor.Index}: {monitor.Name}[/] ({monitor.DeviceName})");
-
- if (supportedFeatures.Count == 0)
- {
- AnsiConsole.MarkupLine("[yellow] No supported VCP features found[/]");
- }
- else
- {
- var table = new Table()
- .Border(TableBorder.Rounded)
- .AddColumn("VCP Code")
- .AddColumn("Feature Name")
- .AddColumn("Access Type")
- .AddColumn("Current Value")
- .AddColumn("Max Value")
- .AddColumn("Percentage");
-
- foreach (var feature in supportedFeatures)
- {
- string vcpCode = $"0x{feature.Code:X2}";
- string accessType = feature.Type switch
- {
- VcpFeatureType.ReadOnly => "[yellow]Read-Only[/]",
- VcpFeatureType.WriteOnly => "[red]Write-Only[/]",
- VcpFeatureType.ReadWrite => "[green]Read-Write[/]",
- _ => "[dim]Unknown[/]"
- };
-
- string currentValue = feature.CurrentValue.ToString();
- string maxValue = feature.MaxValue.ToString();
-
- // Calculate percentage for known percentage-based features
- string percentage = "N/A";
- if ((feature.Code == VcpFeature.Brightness.Code || feature.Code == VcpFeature.Contrast.Code) && feature.MaxValue > 0)
- {
- uint percentageValue = FeatureResolver.ConvertRawToPercentage(feature.CurrentValue, feature.MaxValue);
- percentage = $"{percentageValue}%";
- }
-
- table.AddRow(vcpCode, feature.Name, accessType, currentValue, maxValue, percentage);
- }
-
- AnsiConsole.Write(table);
- }
- }
- }
- catch (Exception ex)
- {
- if (jsonOutput)
- {
- var monitorRef = new MonitorReference(monitor.Index, monitor.Name, monitor.DeviceName, monitor.IsPrimary);
- var scanResult = new VcpScanResponse(false, monitorRef, new List(), ex.Message);
- Console.WriteLine(JsonSerializer.Serialize(scanResult, JsonContext.Default.VcpScanResponse));
- }
- else
- {
- AnsiConsole.MarkupLine($"[red]Error scanning monitor {monitor.Index} ({monitor.Name}): {ex.Message}[/]");
- }
-
- // Cleanup
- foreach (var m in monitors)
- {
- m.Dispose();
- }
-
- return 1;
- }
-
- // Cleanup
- foreach (var m in monitors)
- {
- m.Dispose();
- }
-
- return 0;
- }
-}
\ No newline at end of file
From ff0b80882651477c9fc56c77184d283130af4c4a Mon Sep 17 00:00:00 2001
From: Quick <577652+markdwags@users.noreply.github.com>
Date: Thu, 8 Jan 2026 08:17:29 -0600
Subject: [PATCH 06/10] enhance command handling and output formatting; update
scan and list commands for improved usability, add support for monitor name
queries, and refine console output styles
---
DDCSwitch/Commands/CommandRouter.cs | 4 +-
DDCSwitch/Commands/ConsoleOutputFormatter.cs | 26 ++++++-
DDCSwitch/Commands/GetCommand.cs | 25 +++++--
DDCSwitch/Commands/HelpCommand.cs | 72 ++++++++++++++++----
DDCSwitch/Commands/ListCommand.cs | 41 +++++------
DDCSwitch/Commands/SetCommand.cs | 20 +++++-
DDCSwitch/Commands/VcpScanCommand.cs | 67 +++++++++++-------
DDCSwitch/Properties/launchSettings.json | 4 ++
EXAMPLES.md | 30 +++++++-
README.md | 31 ++++++++-
10 files changed, 246 insertions(+), 74 deletions(-)
diff --git a/DDCSwitch/Commands/CommandRouter.cs b/DDCSwitch/Commands/CommandRouter.cs
index a376628..a77f613 100644
--- a/DDCSwitch/Commands/CommandRouter.cs
+++ b/DDCSwitch/Commands/CommandRouter.cs
@@ -21,8 +21,8 @@ public static int Route(string[] args)
filteredArgs = filteredArgs.Where(a => !a.Equals("--verbose", StringComparison.OrdinalIgnoreCase)).ToArray();
// Check for --scan flag
- bool scanOutput = filteredArgs.Contains("--scan", StringComparer.OrdinalIgnoreCase);
- filteredArgs = filteredArgs.Where(a => !a.Equals("--scan", StringComparison.OrdinalIgnoreCase)).ToArray();
+ bool scanOutput = filteredArgs.Contains("--all", StringComparer.OrdinalIgnoreCase);
+ filteredArgs = filteredArgs.Where(a => !a.Equals("--alll", StringComparison.OrdinalIgnoreCase)).ToArray();
if (filteredArgs.Length == 0)
{
diff --git a/DDCSwitch/Commands/ConsoleOutputFormatter.cs b/DDCSwitch/Commands/ConsoleOutputFormatter.cs
index f58ed3c..3a917f2 100644
--- a/DDCSwitch/Commands/ConsoleOutputFormatter.cs
+++ b/DDCSwitch/Commands/ConsoleOutputFormatter.cs
@@ -6,17 +6,37 @@ internal static class ConsoleOutputFormatter
{
public static void WriteError(string message)
{
- AnsiConsole.MarkupLine($"[red]Error:[/] {message}");
+ AnsiConsole.MarkupLine($"[bold red]X Error:[/] [red]{message}[/]");
}
public static void WriteInfo(string message)
{
- AnsiConsole.MarkupLine(message);
+ AnsiConsole.MarkupLine($"[cyan]i[/] {message}");
}
public static void WriteSuccess(string message)
{
- AnsiConsole.MarkupLine($"[green]✓[/] {message}");
+ AnsiConsole.MarkupLine($"[bold green]> Success:[/] [green]{message}[/]");
+ }
+
+ public static void WriteWarning(string message)
+ {
+ AnsiConsole.MarkupLine($"[bold yellow]! Warning:[/] [yellow]{message}[/]");
+ }
+
+ public static void WriteHeader(string text)
+ {
+ var rule = new Rule($"[bold cyan]{text}[/]")
+ {
+ Justification = Justify.Left
+ };
+ AnsiConsole.Write(rule);
+ }
+
+ public static void WriteMonitorInfo(string label, string value, bool highlight = false)
+ {
+ var color = highlight ? "yellow" : "cyan";
+ AnsiConsole.MarkupLine($" [bold {color}]{label}:[/] {value}");
}
}
diff --git a/DDCSwitch/Commands/GetCommand.cs b/DDCSwitch/Commands/GetCommand.cs
index 83718c9..4a65a45 100644
--- a/DDCSwitch/Commands/GetCommand.cs
+++ b/DDCSwitch/Commands/GetCommand.cs
@@ -141,6 +141,7 @@ private static int ReadFeatureValue(Monitor monitor, VcpFeature feature, bool js
.Start($"Reading {feature.Name} from {monitor.Name}...", ctx =>
{
ctx.Spinner(Spinner.Known.Dots);
+ ctx.SpinnerStyle(Style.Parse("cyan"));
success = monitor.TryGetVcpFeature(feature.Code, out current, out max, out errorCode);
});
}
@@ -205,23 +206,39 @@ private static void OutputFeatureValue(Monitor monitor, VcpFeature feature, uint
}
else
{
- AnsiConsole.MarkupLine($"[green]Monitor:[/] {monitor.Name} ({monitor.DeviceName})");
+ var panel = new Panel(
+ $"[bold cyan]Monitor:[/] {monitor.Name}\n" +
+ $"[dim]Device:[/] [dim]{monitor.DeviceName}[/]")
+ {
+ Header = new PanelHeader($"[bold green]>> Feature Value[/]", Justify.Left),
+ Border = BoxBorder.Rounded,
+ BorderStyle = new Style(Color.Cyan)
+ };
+ AnsiConsole.Write(panel);
if (feature.Code == InputSource.VcpInputSource)
{
// Display input with name resolution
- AnsiConsole.MarkupLine($"[green]Current {feature.Name}:[/] {InputSource.GetName(current)} (0x{current:X2})");
+ var inputName = InputSource.GetName(current);
+ AnsiConsole.MarkupLine($" [bold yellow]{feature.Name}:[/] [cyan]{inputName}[/] [dim](0x{current:X2})[/]");
}
else if (feature.SupportsPercentage)
{
// Display percentage for brightness/contrast
uint percentage = FeatureResolver.ConvertRawToPercentage(current, max);
- AnsiConsole.MarkupLine($"[green]Current {feature.Name}:[/] {percentage}% (raw: {current}/{max})");
+ var progressBar = new BarChart()
+ .Width(40)
+ .Label($"[bold yellow]{feature.Name}[/]")
+ .CenterLabel()
+ .AddItem("", percentage, Color.Green);
+
+ AnsiConsole.Write(progressBar);
+ AnsiConsole.MarkupLine($" [bold green]{percentage}%[/] [dim](raw: {current}/{max})[/]");
}
else
{
// Display raw values for unknown VCP codes
- AnsiConsole.MarkupLine($"[green]Current {feature.Name}:[/] {current} (max: {max})");
+ AnsiConsole.MarkupLine($" [bold yellow]{feature.Name}:[/] [green]{current}[/] [dim](max: {max})[/]");
}
}
}
diff --git a/DDCSwitch/Commands/HelpCommand.cs b/DDCSwitch/Commands/HelpCommand.cs
index 1c52dde..366deae 100644
--- a/DDCSwitch/Commands/HelpCommand.cs
+++ b/DDCSwitch/Commands/HelpCommand.cs
@@ -21,9 +21,9 @@ public static int ShowVersion(bool jsonOutput)
}
else
{
- AnsiConsole.Write(new FigletText("DDCSwitch").Color(Color.Blue));
- AnsiConsole.MarkupLine($"[bold]Version:[/] [green]{version}[/]");
- AnsiConsole.MarkupLine("[dim]Windows DDC/CI Monitor Input Switcher[/]");
+ AnsiConsole.Write(new FigletText("DDCSwitch").Color(Color.Cyan1));
+ AnsiConsole.MarkupLine($"[bold cyan]Version:[/] [green]{version}[/]");
+ AnsiConsole.MarkupLine("[dim italic]>> Windows DDC/CI Monitor Input Switcher[/]");
}
return 0;
@@ -33,19 +33,63 @@ public static int ShowUsage()
{
var version = GetVersion();
- AnsiConsole.Write(new FigletText("DDCSwitch").Color(Color.Blue));
- AnsiConsole.MarkupLine($"[dim]Windows DDC/CI Monitor Input Switcher v{version}[/]\n");
+ AnsiConsole.Write(new FigletText("DDCSwitch").Color(Color.Cyan1));
+ AnsiConsole.MarkupLine($"[bold white]{version}[/]");
+ AnsiConsole.MarkupLine($"[dim italic]>> A Windows command-line utility to control monitors using DDC/CI[/]\n");
- AnsiConsole.MarkupLine("[yellow]Commands:[/]");
- AnsiConsole.WriteLine(" list [--verbose] [--scan] - List all DDC/CI capable monitors");
- AnsiConsole.WriteLine(" get monitor [feature] - Get current value for a monitor feature or scan all features");
- AnsiConsole.MarkupLine(" set monitor feature value - Set value for a monitor feature");
- AnsiConsole.MarkupLine(" version - Display version information");
+ var commandsTable = new Table()
+ .Border(TableBorder.Rounded)
+ .BorderColor(Color.White)
+ .AddColumn(new TableColumn("[bold yellow]Command[/]").LeftAligned())
+ .AddColumn(new TableColumn("[bold yellow]Description[/]").LeftAligned());
+
+ commandsTable.AddRow(
+ "[cyan]list[/] [dim]or[/] [cyan]ls[/]",
+ "List all DDC/CI capable monitors with current input sources");
+ commandsTable.AddRow(
+ "[cyan]list --verbose[/]",
+ "Include brightness and contrast information");
+ commandsTable.AddRow(
+ "[cyan]list --scan[/]",
+ "Enumerate all supported VCP features for each monitor");
+ commandsTable.AddRow(
+ "[cyan]get[/] [green][/] [blue][[feature]][/]",
+ "Get current value for a monitor feature or get all features");
+ commandsTable.AddRow(
+ "[cyan]set[/] [green][/] [blue][/] [magenta][/]",
+ "Set value for a monitor feature");
+ commandsTable.AddRow(
+ "[cyan]version[/] [dim]or[/] [cyan]-v[/]",
+ "Display version information");
+ commandsTable.AddRow(
+ "[cyan]help[/] [dim]or[/] [cyan]-h[/]",
+ "Show this help message");
+
+ AnsiConsole.Write(commandsTable);
+
+ AnsiConsole.WriteLine();
- AnsiConsole.MarkupLine("\nSupported features: brightness, contrast, input, or VCP codes like 0x10");
- AnsiConsole.MarkupLine("Use [yellow]--json[/] flag for JSON output");
- AnsiConsole.MarkupLine("Use [yellow]--verbose[/] flag with list to include brightness and contrast");
- AnsiConsole.MarkupLine("Use [yellow]--scan[/] flag with list to enumerate all VCP codes");
+ var panel = new Panel(
+ "[bold yellow]Features:[/] brightness, contrast, input, or VCP codes like [cyan]0x10[/]\n" +
+ "[bold yellow]Flags:[/]\n" +
+ " [cyan]--json[/] Machine-readable JSON output for automation\n" +
+ " [cyan]--verbose[/] Include detailed information in list command\n" +
+ " [cyan]--all[/] Enumerate all VCP features in list command")
+ {
+ Header = new PanelHeader("[bold green]>> Quick Reference[/]", Justify.Left),
+ Border = BoxBorder.Rounded,
+ BorderStyle = new Style(Color.Green)
+ };
+
+ AnsiConsole.Write(panel);
+
+ AnsiConsole.WriteLine();
+ AnsiConsole.MarkupLine("[dim]Examples:[/]");
+ AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch list");
+ AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch get 0");
+ AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch get 0 brightness");
+ AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch set 0 input HDMI1");
+ AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch set 1 brightness 75%");
return 0;
}
diff --git a/DDCSwitch/Commands/ListCommand.cs b/DDCSwitch/Commands/ListCommand.cs
index 9d02c22..64f7114 100644
--- a/DDCSwitch/Commands/ListCommand.cs
+++ b/DDCSwitch/Commands/ListCommand.cs
@@ -16,6 +16,7 @@ public static int Execute(bool jsonOutput, bool verboseOutput, bool scanOutput)
.Start(scanOutput ? "Scanning VCP features..." : "Enumerating monitors...", ctx =>
{
ctx.Spinner(Spinner.Known.Dots);
+ ctx.SpinnerStyle(Style.Parse("cyan"));
monitors = MonitorController.EnumerateMonitors();
});
}
@@ -141,46 +142,48 @@ private static void OutputTableList(List monitors, bool verboseOutput)
{
var table = new Table()
.Border(TableBorder.Rounded)
- .AddColumn("Index")
- .AddColumn("Monitor Name")
- .AddColumn("Device")
- .AddColumn("Current Input");
+ .BorderColor(Color.White)
+ .AddColumn(new TableColumn("[bold yellow]Index[/]").Centered())
+ .AddColumn(new TableColumn("[bold yellow]Monitor Name[/]").LeftAligned())
+ .AddColumn(new TableColumn("[bold yellow]Device[/]").LeftAligned())
+ .AddColumn(new TableColumn("[bold yellow]Current Input[/]").LeftAligned());
// Add brightness and contrast columns if verbose mode is enabled
if (verboseOutput)
{
- table.AddColumn("Brightness");
- table.AddColumn("Contrast");
+ table.AddColumn(new TableColumn("[bold yellow]Brightness[/]").Centered());
+ table.AddColumn(new TableColumn("[bold yellow]Contrast[/]").Centered());
}
- table.AddColumn("Status");
+ table.AddColumn(new TableColumn("[bold yellow]Status[/]").Centered());
foreach (var monitor in monitors)
{
- string inputInfo = "N/A";
- string status = "[green]OK[/]";
- string brightnessInfo = "N/A";
- string contrastInfo = "N/A";
+ string inputInfo = "[dim]N/A[/]";
+ string status = "[green]+[/] [bold green]OK[/]";
+ string brightnessInfo = "[dim]N/A[/]";
+ string contrastInfo = "[dim]N/A[/]";
try
{
if (monitor.TryGetInputSource(out uint current, out _))
{
- inputInfo = $"{InputSource.GetName(current)} (0x{current:X2})";
+ var inputName = InputSource.GetName(current);
+ inputInfo = $"[cyan]{inputName}[/] [dim](0x{current:X2})[/]";
}
else
{
- status = "[yellow]No DDC/CI[/]";
+ status = "[yellow]~[/] [bold yellow]No DDC/CI[/]";
}
// Get brightness and contrast if verbose mode is enabled and monitor supports DDC/CI
- if (verboseOutput && status == "[green]OK[/]")
+ if (verboseOutput && status == "[green]+[/] [bold green]OK[/]")
{
// Try to get brightness (VCP 0x10)
if (monitor.TryGetVcpFeature(VcpFeature.Brightness.Code, out uint brightnessCurrent, out uint brightnessMax))
{
uint brightnessPercentage = FeatureResolver.ConvertRawToPercentage(brightnessCurrent, brightnessMax);
- brightnessInfo = $"{brightnessPercentage}%";
+ brightnessInfo = $"[green]{brightnessPercentage}%[/]";
}
else
{
@@ -191,7 +194,7 @@ private static void OutputTableList(List monitors, bool verboseOutput)
if (monitor.TryGetVcpFeature(VcpFeature.Contrast.Code, out uint contrastCurrent, out uint contrastMax))
{
uint contrastPercentage = FeatureResolver.ConvertRawToPercentage(contrastCurrent, contrastMax);
- contrastInfo = $"{contrastPercentage}%";
+ contrastInfo = $"[green]{contrastPercentage}%[/]";
}
else
{
@@ -206,7 +209,7 @@ private static void OutputTableList(List monitors, bool verboseOutput)
}
catch
{
- status = "[red]Error[/]";
+ status = "[red]X[/] [bold red]Error[/]";
if (verboseOutput)
{
brightnessInfo = "[dim]N/A[/]";
@@ -216,9 +219,9 @@ private static void OutputTableList(List monitors, bool verboseOutput)
var row = new List
{
- monitor.IsPrimary ? $"{monitor.Index} [yellow]*[/]" : monitor.Index.ToString(),
+ monitor.IsPrimary ? $"[bold cyan]{monitor.Index}[/] [yellow]*[/]" : $"[cyan]{monitor.Index}[/]",
monitor.Name,
- monitor.DeviceName,
+ $"[dim]{monitor.DeviceName}[/]",
inputInfo
};
diff --git a/DDCSwitch/Commands/SetCommand.cs b/DDCSwitch/Commands/SetCommand.cs
index bcbaafa..b78515c 100644
--- a/DDCSwitch/Commands/SetCommand.cs
+++ b/DDCSwitch/Commands/SetCommand.cs
@@ -285,6 +285,7 @@ private static int WriteFeatureValue(Monitor monitor, VcpFeature feature, uint s
.Start($"Setting {monitor.Name} {feature.Name} to {displayValue}...", ctx =>
{
ctx.Spinner(Spinner.Known.Dots);
+ ctx.SpinnerStyle(Style.Parse("cyan"));
if (!monitor.TrySetVcpFeature(feature.Code, setValue, out int errorCode))
{
@@ -367,21 +368,34 @@ private static void OutputSuccess(Monitor monitor, VcpFeature feature, uint setV
}
else
{
+ string displayValue;
if (feature.Code == InputSource.VcpInputSource)
{
// Display input with name resolution
- ConsoleOutputFormatter.WriteSuccess($"Successfully set {monitor.Name} {feature.Name} to {InputSource.GetName(setValue)}");
+ displayValue = $"[cyan]{InputSource.GetName(setValue)}[/]";
}
else if (percentageValue.HasValue)
{
// Display percentage for brightness/contrast
- ConsoleOutputFormatter.WriteSuccess($"Successfully set {monitor.Name} {feature.Name} to {percentageValue}%");
+ displayValue = $"[green]{percentageValue}%[/]";
}
else
{
// Display raw value for unknown VCP codes
- ConsoleOutputFormatter.WriteSuccess($"Successfully set {monitor.Name} {feature.Name} to {setValue}");
+ displayValue = $"[green]{setValue}[/]";
}
+
+ var successPanel = new Panel(
+ $"[bold cyan]Monitor:[/] {monitor.Name}\n" +
+ $"[bold yellow]Feature:[/] {feature.Name}\n" +
+ $"[bold green]New Value:[/] {displayValue}")
+ {
+ Header = new PanelHeader("[bold green]>> Successfully Applied[/]", Justify.Left),
+ Border = BoxBorder.Rounded,
+ BorderStyle = new Style(Color.Green)
+ };
+
+ AnsiConsole.Write(successPanel);
}
}
}
diff --git a/DDCSwitch/Commands/VcpScanCommand.cs b/DDCSwitch/Commands/VcpScanCommand.cs
index 101115e..5560fec 100644
--- a/DDCSwitch/Commands/VcpScanCommand.cs
+++ b/DDCSwitch/Commands/VcpScanCommand.cs
@@ -36,6 +36,7 @@ public static int ScanSingleMonitor(string monitorIdentifier, bool jsonOutput)
.Start("Enumerating monitors...", ctx =>
{
ctx.Spinner(Spinner.Known.Dots);
+ ctx.SpinnerStyle(Style.Parse("cyan"));
monitors = MonitorController.EnumerateMonitors();
});
}
@@ -97,6 +98,7 @@ public static int ScanSingleMonitor(string monitorIdentifier, bool jsonOutput)
.Start($"Scanning VCP features for {monitor.Name}...", ctx =>
{
ctx.Spinner(Spinner.Known.Dots);
+ ctx.SpinnerStyle(Style.Parse("cyan"));
features = monitor.ScanVcpFeatures();
});
}
@@ -186,13 +188,19 @@ private static void OutputTableScanAll(List monitors)
{
try
{
- AnsiConsole.MarkupLine($"\n[bold blue]Monitor {monitor.Index}: {monitor.Name}[/] ({monitor.DeviceName})");
+ var rule = new Rule($"[bold cyan]Monitor {monitor.Index}: {monitor.Name}[/] [dim]({monitor.DeviceName})[/]")
+ {
+ Justification = Justify.Left,
+ Style = new Style(Color.Cyan)
+ };
+ AnsiConsole.Write(rule);
Dictionary features = null!;
AnsiConsole.Status()
.Start($"Scanning VCP features for {monitor.Name}...", ctx =>
{
ctx.Spinner(Spinner.Known.Dots);
+ ctx.SpinnerStyle(Style.Parse("cyan"));
features = monitor.ScanVcpFeatures();
});
@@ -203,15 +211,19 @@ private static void OutputTableScanAll(List monitors)
if (supportedFeatures.Count == 0)
{
- AnsiConsole.MarkupLine("[yellow] No supported VCP features found[/]");
+ ConsoleOutputFormatter.WriteWarning("No supported VCP features found");
+ AnsiConsole.WriteLine();
continue;
}
+ AnsiConsole.MarkupLine($"[bold green]>> Found {supportedFeatures.Count} supported features[/]\n");
OutputFeatureTable(supportedFeatures);
+ AnsiConsole.WriteLine();
}
catch (Exception ex)
{
ConsoleOutputFormatter.WriteError($"Error scanning monitor {monitor.Index} ({monitor.Name}): {ex.Message}");
+ AnsiConsole.WriteLine();
}
}
}
@@ -224,11 +236,20 @@ private static void OutputJsonScanSingle(MonitorReference monitorRef, List supportedFeatures)
{
- AnsiConsole.MarkupLine($"[bold blue]Monitor {monitor.Index}: {monitor.Name}[/] ({monitor.DeviceName})");
+ var panel = new Panel(
+ $"[bold cyan]Monitor:[/] {monitor.Name}\n" +
+ $"[dim]Device:[/] [dim]{monitor.DeviceName}[/]\n" +
+ $"[bold yellow]Supported Features:[/] [green]{supportedFeatures.Count}[/]")
+ {
+ Header = new PanelHeader($"[bold cyan]>> VCP Feature Scan Results[/]", Justify.Left),
+ Border = BoxBorder.Rounded,
+ BorderStyle = new Style(Color.White)
+ };
+ AnsiConsole.Write(panel);
if (supportedFeatures.Count == 0)
{
- AnsiConsole.MarkupLine("[yellow] No supported VCP features found[/]");
+ ConsoleOutputFormatter.WriteWarning("No supported VCP features found");
}
else
{
@@ -240,36 +261,36 @@ private static void OutputFeatureTable(List supportedFeatures)
{
var table = new Table()
.Border(TableBorder.Rounded)
- .AddColumn("VCP Code")
- .AddColumn("Feature Name")
- .AddColumn("Access Type")
- .AddColumn("Current Value")
- .AddColumn("Max Value")
- .AddColumn("Percentage");
+ .BorderColor(Color.White)
+ .AddColumn(new TableColumn("[bold yellow]VCP Code[/]").Centered())
+ .AddColumn(new TableColumn("[bold yellow]Feature Name[/]").LeftAligned())
+ .AddColumn(new TableColumn("[bold yellow]Access[/]").Centered())
+ .AddColumn(new TableColumn("[bold yellow]Current[/]").RightAligned())
+ .AddColumn(new TableColumn("[bold yellow]Max[/]").RightAligned());
foreach (var feature in supportedFeatures)
{
- string vcpCode = $"0x{feature.Code:X2}";
+ string vcpCode = $"[cyan]0x{feature.Code:X2}[/]";
string accessType = feature.Type switch
{
- VcpFeatureType.ReadOnly => "[yellow]Read-Only[/]",
- VcpFeatureType.WriteOnly => "[red]Write-Only[/]",
- VcpFeatureType.ReadWrite => "[green]Read-Write[/]",
- _ => "[dim]Unknown[/]"
+ VcpFeatureType.ReadOnly => "[yellow]Read Only[/]",
+ VcpFeatureType.WriteOnly => "[red]Write Only[/]",
+ VcpFeatureType.ReadWrite => "[green]Read+Write[/]",
+ _ => "[dim]? ?[/]"
};
- string currentValue = feature.CurrentValue.ToString();
- string maxValue = feature.MaxValue.ToString();
+ string currentValue = $"[green]{feature.CurrentValue}[/]";
+ string maxValue = $"[cyan]{feature.MaxValue}[/]";
+
+ var name = feature.Name;
- // Calculate percentage for known percentage-based features
- string percentage = "N/A";
- if ((feature.Code == VcpFeature.Brightness.Code || feature.Code == VcpFeature.Contrast.Code) && feature.MaxValue > 0)
+ // If the feature name isn't known, make it dim
+ if (feature.Name.StartsWith("VCP_"))
{
- uint percentageValue = FeatureResolver.ConvertRawToPercentage(feature.CurrentValue, feature.MaxValue);
- percentage = $"{percentageValue}%";
+ name = $"[dim]{feature.Name}[/]";
}
- table.AddRow(vcpCode, feature.Name, accessType, currentValue, maxValue, percentage);
+ table.AddRow(vcpCode, name, accessType, currentValue, maxValue);
}
AnsiConsole.Write(table);
diff --git a/DDCSwitch/Properties/launchSettings.json b/DDCSwitch/Properties/launchSettings.json
index 7d604ca..a689a4b 100644
--- a/DDCSwitch/Properties/launchSettings.json
+++ b/DDCSwitch/Properties/launchSettings.json
@@ -23,6 +23,10 @@
"DDCSwitch (help)": {
"commandName": "Project",
"commandLineArgs": "help"
+ },
+ "DDCSwitch (error command)": {
+ "commandName": "Project",
+ "commandLineArgs": "gettt"
}
}
}
diff --git a/EXAMPLES.md b/EXAMPLES.md
index 24ae3ec..4314941 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -228,11 +228,19 @@ This shows current brightness and contrast levels for each monitor (displays "N/
# Get all VCP features for primary monitor (scans all supported features)
DDCSwitch get 0
-# Get specific features
+# Get all features by monitor name (partial matching supported)
+DDCSwitch get "VG270U P"
+DDCSwitch get "Generic PnP"
+
+# Get specific features by index
DDCSwitch get 0 input # Current input source
DDCSwitch get 0 brightness # Current brightness
DDCSwitch get 0 contrast # Current contrast
+# Get specific features by monitor name
+DDCSwitch get "Generic PnP" input
+DDCSwitch get "LG" brightness
+
# Get raw VCP value
DDCSwitch get 0 0x10 # Brightness (raw)
```
@@ -355,8 +363,14 @@ Write-Host "Work profile activated!" -ForegroundColor Green
### Discover VCP Features
```powershell
-# Use verbose listing to see all supported VCP features
-DDCSwitch list --verbose
+# Scan all VCP features for all monitors
+DDCSwitch list --all
+
+# Scan all VCP features for a specific monitor
+DDCSwitch get 0
+
+# Scan by monitor name
+DDCSwitch get "VG270U P"
```
### Common VCP Codes
@@ -1527,6 +1541,12 @@ Write-Host "Testing complete! Document which codes worked for your monitor." -Fo
# Get current input (if this works, DDC/CI is functional)
DDCSwitch get 0
+# Get by monitor name
+DDCSwitch get "VG270U"
+
+# List all monitors with all VCP values
+DDCSwitch list --all
+
# Try setting to current input (should succeed instantly)
DDCSwitch list # Note the current input
DDCSwitch set 0
@@ -1556,6 +1576,10 @@ DDCSwitch set "LG ULTRAGEAR" HDMI1
# Partial name matching works
DDCSwitch set "ULTRAGEAR" HDMI1
+
+# Get settings by monitor name
+DDCSwitch get "VG270U" brightness
+DDCSwitch get "Generic PnP" # Gets all VCP values for this monitor
```
## Integration with Other Tools
diff --git a/README.md b/README.md
index 6cd32e7..8d4590b 100644
--- a/README.md
+++ b/README.md
@@ -99,6 +99,14 @@ DDCSwitch get 0
This will scan and display all supported VCP features for monitor 0, showing their names, access types, current values, and maximum values.
+You can also use the monitor name instead of the index (partial name matching supported):
+
+```powershell
+# Get all settings by monitor name
+DDCSwitch get "VG270U P"
+DDCSwitch get "Generic PnP"
+```
+
Get a specific feature:
```powershell
@@ -110,6 +118,10 @@ DDCSwitch get 0 brightness
# Get contrast as percentage
DDCSwitch get 0 contrast
+
+# Works with monitor names too
+DDCSwitch get "VG270U P" brightness
+DDCSwitch get "Generic PnP" input
```
Output: `Monitor: Generic PnP Monitor` / `Brightness: 75% (120/160)`
@@ -152,13 +164,23 @@ DDCSwitch set 0 0x10 120
### VCP Feature Scanning
-Discover all supported VCP features on a monitor:
+Discover all supported VCP features on all monitors:
```powershell
-DDCSwitch list --verbose
+DDCSwitch list --all
```
-This scans all VCP codes (0x00-0xFF) and displays supported features with their current values, maximum values, and access types (read-only, write-only, read-write).
+This scans all VCP codes (0x00-0xFF) for every monitor and displays supported features with their current values, maximum values, and access types (read-only, write-only, read-write).
+
+To scan a specific monitor, use the `get` command:
+
+```powershell
+# Scan specific monitor by index
+DDCSwitch get 0
+
+# Scan specific monitor by name
+DDCSwitch get "VG270U"
+```
### VCP Feature Categories and Discovery
@@ -256,6 +278,9 @@ DDCSwitch list --category color
# Search for features by name
DDCSwitch get 0 bright # Matches "brightness"
+
+# Or by monitor name
+DDCSwitch get "VG270U" bright
```
**Desktop shortcut:**
From b66f1b2185e25ef49c83dbbfaa65d30990cd7327 Mon Sep 17 00:00:00 2001
From: Quick <577652+markdwags@users.noreply.github.com>
Date: Thu, 8 Jan 2026 12:30:14 -0600
Subject: [PATCH 07/10] refactor command handling; replace --all flag with 'get
all' command for improved clarity and usability, update documentation and
examples accordingly
---
DDCSwitch/Commands/CommandRouter.cs | 6 +--
DDCSwitch/Commands/GetCommand.cs | 61 +++++++++++++++++++++++++++++
DDCSwitch/Commands/HelpCommand.cs | 10 ++---
DDCSwitch/Commands/ListCommand.cs | 10 +----
EXAMPLES.md | 4 +-
README.md | 4 +-
6 files changed, 74 insertions(+), 21 deletions(-)
diff --git a/DDCSwitch/Commands/CommandRouter.cs b/DDCSwitch/Commands/CommandRouter.cs
index a77f613..ff61316 100644
--- a/DDCSwitch/Commands/CommandRouter.cs
+++ b/DDCSwitch/Commands/CommandRouter.cs
@@ -20,9 +20,7 @@ public static int Route(string[] args)
bool verboseOutput = filteredArgs.Contains("--verbose", StringComparer.OrdinalIgnoreCase);
filteredArgs = filteredArgs.Where(a => !a.Equals("--verbose", StringComparison.OrdinalIgnoreCase)).ToArray();
- // Check for --scan flag
- bool scanOutput = filteredArgs.Contains("--all", StringComparer.OrdinalIgnoreCase);
- filteredArgs = filteredArgs.Where(a => !a.Equals("--alll", StringComparison.OrdinalIgnoreCase)).ToArray();
+
if (filteredArgs.Length == 0)
{
@@ -36,7 +34,7 @@ public static int Route(string[] args)
{
return command switch
{
- "list" or "ls" => ListCommand.Execute(jsonOutput, verboseOutput, scanOutput),
+ "list" or "ls" => ListCommand.Execute(jsonOutput, verboseOutput),
"get" => GetCommand.Execute(filteredArgs, jsonOutput),
"set" => SetCommand.Execute(filteredArgs, jsonOutput),
"version" or "-v" or "--version" => HelpCommand.ShowVersion(jsonOutput),
diff --git a/DDCSwitch/Commands/GetCommand.cs b/DDCSwitch/Commands/GetCommand.cs
index 4a65a45..ff173eb 100644
--- a/DDCSwitch/Commands/GetCommand.cs
+++ b/DDCSwitch/Commands/GetCommand.cs
@@ -18,11 +18,34 @@ public static int Execute(string[] args, bool jsonOutput)
{
ConsoleOutputFormatter.WriteError("Monitor identifier required.");
AnsiConsole.WriteLine("Usage: DDCSwitch get [feature]");
+ AnsiConsole.WriteLine(" DDCSwitch get all");
}
return 1;
}
+ // Check if the monitor identifier is "all"
+ if (args[1].Equals("all", StringComparison.OrdinalIgnoreCase))
+ {
+ // "get all" should only scan all monitors, no specific feature
+ if (args.Length > 2)
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, "Feature specification not supported with 'get all'");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError("Feature specification not supported with 'get all'.");
+ AnsiConsole.WriteLine("Usage: DDCSwitch get all");
+ }
+ return 1;
+ }
+
+ return GetAllMonitors(jsonOutput);
+ }
+
// If no feature is specified, perform VCP scan
if (args.Length == 2)
{
@@ -61,6 +84,44 @@ public static int Execute(string[] args, bool jsonOutput)
return result;
}
+ private static int GetAllMonitors(bool jsonOutput)
+ {
+ List monitors;
+
+ if (!jsonOutput)
+ {
+ monitors = null!;
+ AnsiConsole.Status()
+ .Start("Enumerating monitors...", ctx =>
+ {
+ ctx.Spinner(Spinner.Known.Dots);
+ ctx.SpinnerStyle(Style.Parse("cyan"));
+ monitors = MonitorController.EnumerateMonitors();
+ });
+ }
+ else
+ {
+ monitors = MonitorController.EnumerateMonitors();
+ }
+
+ if (monitors.Count == 0)
+ {
+ if (jsonOutput)
+ {
+ var error = new ErrorResponse(false, "No DDC/CI capable monitors found");
+ Console.WriteLine(JsonSerializer.Serialize(error, JsonContext.Default.ErrorResponse));
+ }
+ else
+ {
+ ConsoleOutputFormatter.WriteError("No DDC/CI capable monitors found.");
+ }
+
+ return 1;
+ }
+
+ return VcpScanCommand.ScanAllMonitors(monitors, jsonOutput);
+ }
+
private static int HandleInvalidFeature(string featureInput, bool jsonOutput)
{
string errorMessage;
diff --git a/DDCSwitch/Commands/HelpCommand.cs b/DDCSwitch/Commands/HelpCommand.cs
index 366deae..9c35f55 100644
--- a/DDCSwitch/Commands/HelpCommand.cs
+++ b/DDCSwitch/Commands/HelpCommand.cs
@@ -34,7 +34,7 @@ public static int ShowUsage()
var version = GetVersion();
AnsiConsole.Write(new FigletText("DDCSwitch").Color(Color.Cyan1));
- AnsiConsole.MarkupLine($"[bold white]{version}[/]");
+ AnsiConsole.MarkupLine($"[bold white]v{version}[/]");
AnsiConsole.MarkupLine($"[dim italic]>> A Windows command-line utility to control monitors using DDC/CI[/]\n");
var commandsTable = new Table()
@@ -50,8 +50,8 @@ public static int ShowUsage()
"[cyan]list --verbose[/]",
"Include brightness and contrast information");
commandsTable.AddRow(
- "[cyan]list --scan[/]",
- "Enumerate all supported VCP features for each monitor");
+ "[cyan]get all[/]",
+ "Enumerate all supported VCP features for all monitors");
commandsTable.AddRow(
"[cyan]get[/] [green][/] [blue][[feature]][/]",
"Get current value for a monitor feature or get all features");
@@ -73,8 +73,7 @@ public static int ShowUsage()
"[bold yellow]Features:[/] brightness, contrast, input, or VCP codes like [cyan]0x10[/]\n" +
"[bold yellow]Flags:[/]\n" +
" [cyan]--json[/] Machine-readable JSON output for automation\n" +
- " [cyan]--verbose[/] Include detailed information in list command\n" +
- " [cyan]--all[/] Enumerate all VCP features in list command")
+ " [cyan]--verbose[/] Include detailed information in list command\n\n")
{
Header = new PanelHeader("[bold green]>> Quick Reference[/]", Justify.Left),
Border = BoxBorder.Rounded,
@@ -86,6 +85,7 @@ public static int ShowUsage()
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine("[dim]Examples:[/]");
AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch list");
+ AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch get all");
AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch get 0");
AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch get 0 brightness");
AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch set 0 input HDMI1");
diff --git a/DDCSwitch/Commands/ListCommand.cs b/DDCSwitch/Commands/ListCommand.cs
index 64f7114..87cb204 100644
--- a/DDCSwitch/Commands/ListCommand.cs
+++ b/DDCSwitch/Commands/ListCommand.cs
@@ -5,7 +5,7 @@ namespace DDCSwitch.Commands;
internal static class ListCommand
{
- public static int Execute(bool jsonOutput, bool verboseOutput, bool scanOutput)
+ public static int Execute(bool jsonOutput, bool verboseOutput)
{
List monitors;
@@ -13,7 +13,7 @@ public static int Execute(bool jsonOutput, bool verboseOutput, bool scanOutput)
{
monitors = null!;
AnsiConsole.Status()
- .Start(scanOutput ? "Scanning VCP features..." : "Enumerating monitors...", ctx =>
+ .Start("Enumerating monitors...", ctx =>
{
ctx.Spinner(Spinner.Known.Dots);
ctx.SpinnerStyle(Style.Parse("cyan"));
@@ -40,12 +40,6 @@ public static int Execute(bool jsonOutput, bool verboseOutput, bool scanOutput)
return 1;
}
- // If scan mode is enabled, perform VCP scanning for each monitor
- if (scanOutput)
- {
- return VcpScanCommand.ScanAllMonitors(monitors, jsonOutput);
- }
-
if (jsonOutput)
{
OutputJsonList(monitors, verboseOutput);
diff --git a/EXAMPLES.md b/EXAMPLES.md
index 4314941..11c6f03 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -364,7 +364,7 @@ Write-Host "Work profile activated!" -ForegroundColor Green
```powershell
# Scan all VCP features for all monitors
-DDCSwitch list --all
+DDCSwitch get all
# Scan all VCP features for a specific monitor
DDCSwitch get 0
@@ -1545,7 +1545,7 @@ DDCSwitch get 0
DDCSwitch get "VG270U"
# List all monitors with all VCP values
-DDCSwitch list --all
+DDCSwitch get all
# Try setting to current input (should succeed instantly)
DDCSwitch list # Note the current input
diff --git a/README.md b/README.md
index 8d4590b..5ccfedd 100644
--- a/README.md
+++ b/README.md
@@ -167,12 +167,12 @@ DDCSwitch set 0 0x10 120
Discover all supported VCP features on all monitors:
```powershell
-DDCSwitch list --all
+DDCSwitch get all
```
This scans all VCP codes (0x00-0xFF) for every monitor and displays supported features with their current values, maximum values, and access types (read-only, write-only, read-write).
-To scan a specific monitor, use the `get` command:
+To scan a specific monitor:
```powershell
# Scan specific monitor by index
From 68c06a8877b0d6da740174e83ead8a364336c453 Mon Sep 17 00:00:00 2001
From: Quick <577652+markdwags@users.noreply.github.com>
Date: Thu, 8 Jan 2026 12:45:57 -0600
Subject: [PATCH 08/10] normalize casing for 'ddcswitch' throughout the
codebase; update usage examples and error messages for consistency
---
CHANGELOG.md | 3 +-
DDCSwitch/Commands/GetCommand.cs | 8 +-
DDCSwitch/Commands/HelpCommand.cs | 22 +-
DDCSwitch/Commands/SetCommand.cs | 4 +-
DDCSwitch/Commands/VcpScanCommand.cs | 2 +-
DDCSwitch/DDCSwitch.csproj | 3 +-
DDCSwitch/Properties/launchSettings.json | 14 +-
DDCSwitch/VcpErrorHandler.cs | 8 +-
EXAMPLES.md | 666 +++++++++++------------
README.md | 98 ++--
build.cmd | 8 +-
11 files changed, 419 insertions(+), 417 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 60827e3..392330f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,6 @@
# Changelog
-All notable changes to DDCSwitch will be documented in this file.
+All notable changes to ddcswitch will be documented in this file.
## [1.0.2] - 2026-01-07
@@ -9,6 +9,7 @@ All notable changes to DDCSwitch will be documented in this file.
- Improved error handling for unsupported monitors
- Enhanced documentation with additional examples
- Optimized performance for input switching operations
+- Better clarity in CLI output messages
## [1.0.1] - 2026-01-07
diff --git a/DDCSwitch/Commands/GetCommand.cs b/DDCSwitch/Commands/GetCommand.cs
index ff173eb..7c6914d 100644
--- a/DDCSwitch/Commands/GetCommand.cs
+++ b/DDCSwitch/Commands/GetCommand.cs
@@ -17,8 +17,8 @@ public static int Execute(string[] args, bool jsonOutput)
else
{
ConsoleOutputFormatter.WriteError("Monitor identifier required.");
- AnsiConsole.WriteLine("Usage: DDCSwitch get [feature]");
- AnsiConsole.WriteLine(" DDCSwitch get all");
+ AnsiConsole.WriteLine("Usage: ddcswitch get [feature]");
+ AnsiConsole.WriteLine(" ddcswitch get all");
}
return 1;
@@ -38,7 +38,7 @@ public static int Execute(string[] args, bool jsonOutput)
else
{
ConsoleOutputFormatter.WriteError("Feature specification not supported with 'get all'.");
- AnsiConsole.WriteLine("Usage: DDCSwitch get all");
+ AnsiConsole.WriteLine("Usage: ddcswitch get all");
}
return 1;
}
@@ -177,7 +177,7 @@ private static int HandleMonitorNotFound(List monitors, string monitorI
else
{
ConsoleOutputFormatter.WriteError($"Monitor '{monitorId}' not found.");
- AnsiConsole.MarkupLine("Use [yellow]DDCSwitch list[/] to see available monitors.");
+ AnsiConsole.MarkupLine("Use [yellow]ddcswitch list[/] to see available monitors.");
}
// Cleanup
diff --git a/DDCSwitch/Commands/HelpCommand.cs b/DDCSwitch/Commands/HelpCommand.cs
index 9c35f55..54895b0 100644
--- a/DDCSwitch/Commands/HelpCommand.cs
+++ b/DDCSwitch/Commands/HelpCommand.cs
@@ -21,8 +21,8 @@ public static int ShowVersion(bool jsonOutput)
}
else
{
- AnsiConsole.Write(new FigletText("DDCSwitch").Color(Color.Cyan1));
- AnsiConsole.MarkupLine($"[bold cyan]Version:[/] [green]{version}[/]");
+ AnsiConsole.Write(new FigletText("ddcswitch").Color(Color.Cyan1));
+ AnsiConsole.MarkupLine($"[bold white] v{version}[/]");
AnsiConsole.MarkupLine("[dim italic]>> Windows DDC/CI Monitor Input Switcher[/]");
}
@@ -33,7 +33,7 @@ public static int ShowUsage()
{
var version = GetVersion();
- AnsiConsole.Write(new FigletText("DDCSwitch").Color(Color.Cyan1));
+ AnsiConsole.Write(new FigletText("ddcswitch").Color(Color.Cyan1));
AnsiConsole.MarkupLine($"[bold white]v{version}[/]");
AnsiConsole.MarkupLine($"[dim italic]>> A Windows command-line utility to control monitors using DDC/CI[/]\n");
@@ -73,23 +73,23 @@ public static int ShowUsage()
"[bold yellow]Features:[/] brightness, contrast, input, or VCP codes like [cyan]0x10[/]\n" +
"[bold yellow]Flags:[/]\n" +
" [cyan]--json[/] Machine-readable JSON output for automation\n" +
- " [cyan]--verbose[/] Include detailed information in list command\n\n")
+ " [cyan]--verbose[/] Include detailed information in list command")
{
Header = new PanelHeader("[bold green]>> Quick Reference[/]", Justify.Left),
Border = BoxBorder.Rounded,
- BorderStyle = new Style(Color.Green)
+ BorderStyle = new Style(Color.White)
};
AnsiConsole.Write(panel);
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine("[dim]Examples:[/]");
- AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch list");
- AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch get all");
- AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch get 0");
- AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch get 0 brightness");
- AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch set 0 input HDMI1");
- AnsiConsole.MarkupLine(" [grey]$[/] DDCSwitch set 1 brightness 75%");
+ AnsiConsole.MarkupLine(" [grey]$[/] ddcswitch list");
+ AnsiConsole.MarkupLine(" [grey]$[/] ddcswitch get all");
+ AnsiConsole.MarkupLine(" [grey]$[/] ddcswitch get 0");
+ AnsiConsole.MarkupLine(" [grey]$[/] ddcswitch get 0 brightness");
+ AnsiConsole.MarkupLine(" [grey]$[/] ddcswitch set 0 input HDMI1");
+ AnsiConsole.MarkupLine(" [grey]$[/] ddcswitch set 1 brightness 75%");
return 0;
}
diff --git a/DDCSwitch/Commands/SetCommand.cs b/DDCSwitch/Commands/SetCommand.cs
index b78515c..ce4b6c2 100644
--- a/DDCSwitch/Commands/SetCommand.cs
+++ b/DDCSwitch/Commands/SetCommand.cs
@@ -18,7 +18,7 @@ public static int Execute(string[] args, bool jsonOutput)
else
{
ConsoleOutputFormatter.WriteError("Monitor, feature, and value required.");
- AnsiConsole.MarkupLine("Usage: [yellow]DDCSwitch set [/]");
+ AnsiConsole.MarkupLine("Usage: [yellow]ddcswitch set [/]");
}
return 1;
@@ -183,7 +183,7 @@ private static int HandleMonitorNotFound(List monitors, string monitorI
else
{
ConsoleOutputFormatter.WriteError($"Monitor '{monitorId}' not found.");
- AnsiConsole.MarkupLine("Use [yellow]DDCSwitch list[/] to see available monitors.");
+ AnsiConsole.MarkupLine("Use [yellow]ddcswitch list[/] to see available monitors.");
}
// Cleanup
diff --git a/DDCSwitch/Commands/VcpScanCommand.cs b/DDCSwitch/Commands/VcpScanCommand.cs
index 5560fec..971465e 100644
--- a/DDCSwitch/Commands/VcpScanCommand.cs
+++ b/DDCSwitch/Commands/VcpScanCommand.cs
@@ -72,7 +72,7 @@ public static int ScanSingleMonitor(string monitorIdentifier, bool jsonOutput)
else
{
ConsoleOutputFormatter.WriteError($"Monitor '{monitorIdentifier}' not found.");
- AnsiConsole.MarkupLine("Use [yellow]DDCSwitch list[/] to see available monitors.");
+ AnsiConsole.MarkupLine("Use [yellow]ddcswitch list[/] to see available monitors.");
}
// Cleanup
diff --git a/DDCSwitch/DDCSwitch.csproj b/DDCSwitch/DDCSwitch.csproj
index 2b27bce..c9d1921 100644
--- a/DDCSwitch/DDCSwitch.csproj
+++ b/DDCSwitch/DDCSwitch.csproj
@@ -11,6 +11,7 @@
Speed
false
true
+ ddcswitch
@@ -33,7 +34,7 @@
$(Version)
$(Version)
-
+
diff --git a/DDCSwitch/Properties/launchSettings.json b/DDCSwitch/Properties/launchSettings.json
index a689a4b..6e77e58 100644
--- a/DDCSwitch/Properties/launchSettings.json
+++ b/DDCSwitch/Properties/launchSettings.json
@@ -1,30 +1,30 @@
{
"profiles": {
- "DDCSwitch (get 0)": {
+ "ddcswitch (get 0)": {
"commandName": "Project",
"commandLineArgs": "get 0"
},
- "DDCSwitch (list)": {
+ "ddcswitch (list)": {
"commandName": "Project",
"commandLineArgs": "list"
},
- "DDCSwitch (list --verbose)": {
+ "ddcswitch (list --verbose)": {
"commandName": "Project",
"commandLineArgs": "list --verbose"
},
- "DDCSwitch (set)": {
+ "ddcswitch (set)": {
"commandName": "Project",
"commandLineArgs": "set 0 input HDMI1"
},
- "DDCSwitch (version)": {
+ "ddcswitch (version)": {
"commandName": "Project",
"commandLineArgs": "version"
},
- "DDCSwitch (help)": {
+ "ddcswitch (help)": {
"commandName": "Project",
"commandLineArgs": "help"
},
- "DDCSwitch (error command)": {
+ "ddcswitch (error command)": {
"commandName": "Project",
"commandLineArgs": "gettt"
}
diff --git a/DDCSwitch/VcpErrorHandler.cs b/DDCSwitch/VcpErrorHandler.cs
index aac96a6..c210331 100644
--- a/DDCSwitch/VcpErrorHandler.cs
+++ b/DDCSwitch/VcpErrorHandler.cs
@@ -156,7 +156,7 @@ private static string GetReadFailureSuggestions(VcpFeature feature)
else
{
suggestions.Add($"VCP code 0x{feature.Code:X2} may not be supported by this monitor");
- suggestions.Add("Use 'DDCSwitch list --scan' to see all supported VCP codes");
+ suggestions.Add("Use 'ddcswitch get ' to see all supported VCP codes");
}
suggestions.Add("Check that the monitor is properly connected and powered on");
@@ -184,7 +184,7 @@ private static string GetWriteFailureSuggestions(VcpFeature feature, uint attemp
else
{
suggestions.Add($"VCP code 0x{feature.Code:X2} may not support write operations on this monitor");
- suggestions.Add("Use 'DDCSwitch list --scan' to check if this VCP code supports write operations");
+ suggestions.Add("Use 'ddcswitch get ' to see all supported VCP codes");
}
suggestions.Add("Try running as administrator if permission issues persist");
@@ -202,7 +202,7 @@ private static string GetFeatureAlternatives(VcpFeature feature)
0x10 => "Try using your monitor's physical buttons or on-screen display (OSD) to adjust brightness",
0x12 => "Try using your monitor's physical buttons or on-screen display (OSD) to adjust contrast",
0x60 => "Try using your monitor's physical input selection button or check if the monitor supports other input switching methods",
- _ => "Use 'DDCSwitch list --scan' to see all supported VCP codes for this monitor"
+ _ => "Use 'ddcswitch get ' to see all supported VCP codes"
};
}
@@ -253,7 +253,7 @@ private static string GetRangeValidationSuggestion(VcpFeature feature, uint maxV
return feature.Code switch
{
0x60 => "For input sources, use names like 'HDMI1', 'DP1', or hex codes like '0x11'",
- _ => $"Use 'DDCSwitch get {feature.Name}' to see the current value and valid range"
+ _ => $"Use 'ddcswitch get {feature.Name}' to see the current value and valid range"
};
}
}
\ No newline at end of file
diff --git a/EXAMPLES.md b/EXAMPLES.md
index 11c6f03..d013532 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -1,17 +1,17 @@
-# DDCSwitch Examples
+# ddcswitch Examples
-This document contains detailed examples and use cases for DDCSwitch, including input switching, brightness/contrast control, comprehensive VCP feature access, and automation.
+This document contains detailed examples and use cases for ddcswitch, including input switching, brightness/contrast control, comprehensive VCP feature access, and automation.
## Comprehensive VCP Feature Examples
-DDCSwitch now supports all MCCS (Monitor Control Command Set) standardized VCP features, organized by categories for easy discovery.
+ddcswitch now supports all MCCS (Monitor Control Command Set) standardized VCP features, organized by categories for easy discovery.
### VCP Feature Categories
List all available feature categories:
```powershell
-DDCSwitch list --categories
+ddcswitch list --categories
```
Output:
@@ -29,13 +29,13 @@ Available VCP Feature Categories:
```powershell
# Image adjustment features
-DDCSwitch list --category image
+ddcswitch list --category image
# Color control features
-DDCSwitch list --category color
+ddcswitch list --category color
# Audio features
-DDCSwitch list --category audio
+ddcswitch list --category audio
```
### Color Control Examples
@@ -44,26 +44,26 @@ Control RGB gains for color calibration:
```powershell
# Set individual RGB gains (percentage values)
-DDCSwitch set 0 red-gain 95%
-DDCSwitch set 0 green-gain 90%
-DDCSwitch set 0 blue-gain 85%
+ddcswitch set 0 red-gain 95%
+ddcswitch set 0 green-gain 90%
+ddcswitch set 0 blue-gain 85%
# Get current RGB values
-DDCSwitch get 0 red-gain
-DDCSwitch get 0 green-gain
-DDCSwitch get 0 blue-gain
+ddcswitch get 0 red-gain
+ddcswitch get 0 green-gain
+ddcswitch get 0 blue-gain
# Color temperature control (if supported)
-DDCSwitch set 0 color-temp-request 6500
-DDCSwitch get 0 color-temp-request
+ddcswitch set 0 color-temp-request 6500
+ddcswitch get 0 color-temp-request
# Gamma control
-DDCSwitch set 0 gamma 2.2
-DDCSwitch get 0 gamma
+ddcswitch set 0 gamma 2.2
+ddcswitch get 0 gamma
# Hue and saturation
-DDCSwitch set 0 hue 50%
-DDCSwitch set 0 saturation 80%
+ddcswitch set 0 hue 50%
+ddcswitch set 0 saturation 80%
```
### Audio Control Examples
@@ -72,19 +72,19 @@ Control monitor speakers (if supported):
```powershell
# Volume control (percentage)
-DDCSwitch set 0 volume 75%
-DDCSwitch get 0 volume
+ddcswitch set 0 volume 75%
+ddcswitch get 0 volume
# Mute/unmute
-DDCSwitch set 0 mute 1 # Mute
-DDCSwitch set 0 mute 0 # Unmute
+ddcswitch set 0 mute 1 # Mute
+ddcswitch set 0 mute 0 # Unmute
# Audio balance (if supported)
-DDCSwitch set 0 audio-balance 50% # Centered
+ddcswitch set 0 audio-balance 50% # Centered
# Treble and bass (if supported)
-DDCSwitch set 0 audio-treble 60%
-DDCSwitch set 0 audio-bass 70%
+ddcswitch set 0 audio-treble 60%
+ddcswitch set 0 audio-bass 70%
```
### Advanced Image Controls
@@ -93,40 +93,40 @@ Beyond basic brightness and contrast:
```powershell
# Sharpness control
-DDCSwitch set 0 sharpness 75%
-DDCSwitch get 0 sharpness
+ddcswitch set 0 sharpness 75%
+ddcswitch get 0 sharpness
# Backlight control (LED monitors)
-DDCSwitch set 0 backlight 80%
-DDCSwitch get 0 backlight
+ddcswitch set 0 backlight 80%
+ddcswitch get 0 backlight
# Image orientation (if supported)
-DDCSwitch set 0 image-orientation 0 # Normal
-DDCSwitch set 0 image-orientation 1 # 90° rotation
+ddcswitch set 0 image-orientation 0 # Normal
+ddcswitch set 0 image-orientation 1 # 90 rotation
# Image mode presets (if supported)
-DDCSwitch set 0 image-mode 1 # Standard
-DDCSwitch set 0 image-mode 2 # Movie
-DDCSwitch set 0 image-mode 3 # Game
+ddcswitch set 0 image-mode 1 # Standard
+ddcswitch set 0 image-mode 2 # Movie
+ddcswitch set 0 image-mode 3 # Game
```
### Factory Reset and Calibration
```powershell
# Restore all factory defaults
-DDCSwitch set 0 restore-defaults 1
+ddcswitch set 0 restore-defaults 1
# Restore specific defaults
-DDCSwitch set 0 restore-brightness-contrast 1
-DDCSwitch set 0 restore-color 1
-DDCSwitch set 0 restore-geometry 1
+ddcswitch set 0 restore-brightness-contrast 1
+ddcswitch set 0 restore-color 1
+ddcswitch set 0 restore-geometry 1
# Degauss (CRT monitors)
-DDCSwitch set 0 degauss 1
+ddcswitch set 0 degauss 1
# Auto calibration features (if supported)
-DDCSwitch set 0 auto-color-setup 1
-DDCSwitch set 0 auto-size-center 1
+ddcswitch set 0 auto-color-setup 1
+ddcswitch set 0 auto-size-center 1
```
### Complete Monitor Profile Examples
@@ -138,20 +138,20 @@ Create comprehensive monitor profiles using all available features:
Write-Host "Activating Advanced Gaming Profile..." -ForegroundColor Cyan
# Input and basic settings
-DDCSwitch set 0 input HDMI1
-DDCSwitch set 0 brightness 90%
-DDCSwitch set 0 contrast 85%
+ddcswitch set 0 input HDMI1
+ddcswitch set 0 brightness 90%
+ddcswitch set 0 contrast 85%
# Color optimization for gaming
-DDCSwitch set 0 red-gain 100%
-DDCSwitch set 0 green-gain 95%
-DDCSwitch set 0 blue-gain 90%
-DDCSwitch set 0 saturation 110% # Enhanced colors
-DDCSwitch set 0 sharpness 80% # Crisp details
+ddcswitch set 0 red-gain 100%
+ddcswitch set 0 green-gain 95%
+ddcswitch set 0 blue-gain 90%
+ddcswitch set 0 saturation 110% # Enhanced colors
+ddcswitch set 0 sharpness 80% # Crisp details
# Audio settings
-DDCSwitch set 0 volume 60%
-DDCSwitch set 0 mute 0
+ddcswitch set 0 volume 60%
+ddcswitch set 0 mute 0
Write-Host "Advanced Gaming Profile Activated!" -ForegroundColor Green
```
@@ -161,20 +161,20 @@ Write-Host "Advanced Gaming Profile Activated!" -ForegroundColor Green
Write-Host "Activating Advanced Work Profile..." -ForegroundColor Cyan
# Input and basic settings
-DDCSwitch set 0 input DP1
-DDCSwitch set 0 brightness 60%
-DDCSwitch set 0 contrast 75%
+ddcswitch set 0 input DP1
+ddcswitch set 0 brightness 60%
+ddcswitch set 0 contrast 75%
# Color optimization for text work
-DDCSwitch set 0 red-gain 85%
-DDCSwitch set 0 green-gain 90%
-DDCSwitch set 0 blue-gain 95%
-DDCSwitch set 0 saturation 70% # Reduced saturation for comfort
-DDCSwitch set 0 sharpness 60% # Softer for long reading
+ddcswitch set 0 red-gain 85%
+ddcswitch set 0 green-gain 90%
+ddcswitch set 0 blue-gain 95%
+ddcswitch set 0 saturation 70% # Reduced saturation for comfort
+ddcswitch set 0 sharpness 60% # Softer for long reading
# Audio settings
-DDCSwitch set 0 volume 40% # Lower for office environment
-DDCSwitch set 0 mute 0
+ddcswitch set 0 volume 40% # Lower for office environment
+ddcswitch set 0 mute 0
Write-Host "Advanced Work Profile Activated!" -ForegroundColor Green
```
@@ -184,20 +184,20 @@ Write-Host "Advanced Work Profile Activated!" -ForegroundColor Green
Write-Host "Activating Photo Editing Profile..." -ForegroundColor Cyan
# Input and basic settings
-DDCSwitch set 0 input DP1
-DDCSwitch set 0 brightness 70%
-DDCSwitch set 0 contrast 80%
+ddcswitch set 0 input DP1
+ddcswitch set 0 brightness 70%
+ddcswitch set 0 contrast 80%
# Accurate color reproduction
-DDCSwitch set 0 red-gain 90%
-DDCSwitch set 0 green-gain 90%
-DDCSwitch set 0 blue-gain 90%
-DDCSwitch set 0 saturation 100% # Natural saturation
-DDCSwitch set 0 gamma 2.2 # Standard gamma
-DDCSwitch set 0 color-temp-request 6500 # D65 standard
+ddcswitch set 0 red-gain 90%
+ddcswitch set 0 green-gain 90%
+ddcswitch set 0 blue-gain 90%
+ddcswitch set 0 saturation 100% # Natural saturation
+ddcswitch set 0 gamma 2.2 # Standard gamma
+ddcswitch set 0 color-temp-request 6500 # D65 standard
# Disable audio to avoid distractions
-DDCSwitch set 0 mute 1
+ddcswitch set 0 mute 1
Write-Host "Photo Editing Profile Activated!" -ForegroundColor Green
```
@@ -207,7 +207,7 @@ Write-Host "Photo Editing Profile Activated!" -ForegroundColor Green
### Check What Monitors Support DDC/CI
```powershell
-DDCSwitch list
+ddcswitch list
```
This will show all your monitors and indicate which ones support DDC/CI control. Monitors with "OK" status can be controlled.
@@ -217,7 +217,7 @@ This will show all your monitors and indicate which ones support DDC/CI control.
Get detailed information including brightness and contrast:
```powershell
-DDCSwitch list --verbose
+ddcswitch list --verbose
```
This shows current brightness and contrast levels for each monitor (displays "N/A" for unsupported features).
@@ -226,39 +226,39 @@ This shows current brightness and contrast levels for each monitor (displays "N/
```powershell
# Get all VCP features for primary monitor (scans all supported features)
-DDCSwitch get 0
+ddcswitch get 0
# Get all features by monitor name (partial matching supported)
-DDCSwitch get "VG270U P"
-DDCSwitch get "Generic PnP"
+ddcswitch get "VG270U P"
+ddcswitch get "Generic PnP"
# Get specific features by index
-DDCSwitch get 0 input # Current input source
-DDCSwitch get 0 brightness # Current brightness
-DDCSwitch get 0 contrast # Current contrast
+ddcswitch get 0 input # Current input source
+ddcswitch get 0 brightness # Current brightness
+ddcswitch get 0 contrast # Current contrast
# Get specific features by monitor name
-DDCSwitch get "Generic PnP" input
-DDCSwitch get "LG" brightness
+ddcswitch get "Generic PnP" input
+ddcswitch get "LG" brightness
# Get raw VCP value
-DDCSwitch get 0 0x10 # Brightness (raw)
+ddcswitch get 0 0x10 # Brightness (raw)
```
### Set Monitor Settings
```powershell
# Switch input
-DDCSwitch set 0 HDMI1
+ddcswitch set 0 HDMI1
# Set brightness to 75%
-DDCSwitch set 0 brightness 75%
+ddcswitch set 0 brightness 75%
# Set contrast to 80%
-DDCSwitch set 0 contrast 80%
+ddcswitch set 0 contrast 80%
# Set raw VCP value
-DDCSwitch set 0 0x10 120 # Brightness (raw value)
+ddcswitch set 0 0x10 120 # Brightness (raw value)
```
## Brightness and Contrast Control
@@ -267,12 +267,12 @@ DDCSwitch set 0 0x10 120 # Brightness (raw value)
```powershell
# Set brightness to specific percentage
-DDCSwitch set 0 brightness 50%
-DDCSwitch set 0 brightness 75%
-DDCSwitch set 0 brightness 100%
+ddcswitch set 0 brightness 50%
+ddcswitch set 0 brightness 75%
+ddcswitch set 0 brightness 100%
# Get current brightness
-DDCSwitch get 0 brightness
+ddcswitch get 0 brightness
# Output: Monitor: Generic PnP Monitor / Brightness: 75% (120/160)
```
@@ -280,12 +280,12 @@ DDCSwitch get 0 brightness
```powershell
# Set contrast to specific percentage
-DDCSwitch set 0 contrast 60%
-DDCSwitch set 0 contrast 85%
-DDCSwitch set 0 contrast 100%
+ddcswitch set 0 contrast 60%
+ddcswitch set 0 contrast 85%
+ddcswitch set 0 contrast 100%
# Get current contrast
-DDCSwitch get 0 contrast
+ddcswitch get 0 contrast
# Output: Monitor: Generic PnP Monitor / Contrast: 85% (136/160)
```
@@ -295,19 +295,19 @@ Create quick brightness presets:
```powershell
# brightness-low.ps1
-DDCSwitch set 0 brightness 25%
+ddcswitch set 0 brightness 25%
Write-Host "Brightness set to 25% (Low)" -ForegroundColor Green
# brightness-medium.ps1
-DDCSwitch set 0 brightness 50%
+ddcswitch set 0 brightness 50%
Write-Host "Brightness set to 50% (Medium)" -ForegroundColor Green
# brightness-high.ps1
-DDCSwitch set 0 brightness 75%
+ddcswitch set 0 brightness 75%
Write-Host "Brightness set to 75% (High)" -ForegroundColor Green
# brightness-max.ps1
-DDCSwitch set 0 brightness 100%
+ddcswitch set 0 brightness 100%
Write-Host "Brightness set to 100% (Maximum)" -ForegroundColor Green
```
@@ -321,19 +321,19 @@ $hour = (Get-Date).Hour
if ($hour -ge 6 -and $hour -lt 9) {
# Morning: Medium brightness
- DDCSwitch set 0 brightness 60%
+ ddcswitch set 0 brightness 60%
Write-Host "Morning brightness: 60%" -ForegroundColor Yellow
} elseif ($hour -ge 9 -and $hour -lt 18) {
# Daytime: High brightness
- DDCSwitch set 0 brightness 85%
+ ddcswitch set 0 brightness 85%
Write-Host "Daytime brightness: 85%" -ForegroundColor Green
} elseif ($hour -ge 18 -and $hour -lt 22) {
# Evening: Medium brightness
- DDCSwitch set 0 brightness 50%
+ ddcswitch set 0 brightness 50%
Write-Host "Evening brightness: 50%" -ForegroundColor Orange
} else {
# Night: Low brightness
- DDCSwitch set 0 brightness 25%
+ ddcswitch set 0 brightness 25%
Write-Host "Night brightness: 25%" -ForegroundColor Blue
}
```
@@ -345,16 +345,16 @@ Create different brightness/contrast profiles:
```powershell
# gaming-profile.ps1
Write-Host "Activating Gaming Profile..." -ForegroundColor Cyan
-DDCSwitch set 0 input HDMI1 # Switch to console
-DDCSwitch set 0 brightness 90% # High brightness for gaming
-DDCSwitch set 0 contrast 85% # High contrast for visibility
+ddcswitch set 0 input HDMI1 # Switch to console
+ddcswitch set 0 brightness 90% # High brightness for gaming
+ddcswitch set 0 contrast 85% # High contrast for visibility
Write-Host "Gaming profile activated!" -ForegroundColor Green
# work-profile.ps1
Write-Host "Activating Work Profile..." -ForegroundColor Cyan
-DDCSwitch set 0 input DP1 # Switch to PC
-DDCSwitch set 0 brightness 60% # Comfortable brightness for long work
-DDCSwitch set 0 contrast 75% # Balanced contrast for text
+ddcswitch set 0 input DP1 # Switch to PC
+ddcswitch set 0 brightness 60% # Comfortable brightness for long work
+ddcswitch set 0 contrast 75% # Balanced contrast for text
Write-Host "Work profile activated!" -ForegroundColor Green
```
@@ -364,33 +364,33 @@ Write-Host "Work profile activated!" -ForegroundColor Green
```powershell
# Scan all VCP features for all monitors
-DDCSwitch get all
+ddcswitch get all
# Scan all VCP features for a specific monitor
-DDCSwitch get 0
+ddcswitch get 0
# Scan by monitor name
-DDCSwitch get "VG270U P"
+ddcswitch get "VG270U P"
```
### Common VCP Codes
```powershell
# Brightness (VCP 0x10)
-DDCSwitch get 0 0x10
-DDCSwitch set 0 0x10 120
+ddcswitch get 0 0x10
+ddcswitch set 0 0x10 120
# Contrast (VCP 0x12)
-DDCSwitch get 0 0x12
-DDCSwitch set 0 0x12 140
+ddcswitch get 0 0x12
+ddcswitch set 0 0x12 140
# Input Source (VCP 0x60)
-DDCSwitch get 0 0x60
-DDCSwitch set 0 0x60 0x11 # HDMI1
+ddcswitch get 0 0x60
+ddcswitch set 0 0x60 0x11 # HDMI1
# Color Temperature (VCP 0x14) - if supported
-DDCSwitch get 0 0x14
-DDCSwitch set 0 0x14 6500 # 6500K
+ddcswitch get 0 0x14
+ddcswitch set 0 0x14 6500 # 6500K
```
### Test Unknown VCP Codes
@@ -404,19 +404,19 @@ $commonCodes = @(0x10, 0x12, 0x14, 0x16, 0x18, 0x1A, 0x20, 0x30, 0x60, 0x62, 0x6
foreach ($code in $commonCodes) {
$hexCode = "0x{0:X2}" -f $code
try {
- $result = DDCSwitch get 0 $hexCode 2>$null
+ $result = ddcswitch get 0 $hexCode 2>$null
if ($result -notmatch "error|failed") {
- Write-Host "✓ VCP $hexCode supported: $result" -ForegroundColor Green
+ Write-Host "? VCP $hexCode supported: $result" -ForegroundColor Green
}
} catch {
- Write-Host "✗ VCP $hexCode not supported" -ForegroundColor Red
+ Write-Host "? VCP $hexCode not supported" -ForegroundColor Red
}
}
```
## JSON Output and Automation
-All DDCSwitch commands support the `--json` flag for machine-readable output. This is perfect for scripting, automation, and integration with other tools.
+All ddcswitch commands support the `--json` flag for machine-readable output. This is perfect for scripting, automation, and integration with other tools.
### PowerShell JSON Examples
@@ -426,12 +426,12 @@ Check the current input and switch only if needed, then adjust brightness:
```powershell
# Check if monitor is on HDMI1, switch to DP1 if not, then set work brightness
-$result = DDCSwitch get 0 --json | ConvertFrom-Json
+$result = ddcswitch get 0 --json | ConvertFrom-Json
if ($result.success -and $result.currentInputCode -ne "0x11") {
Write-Host "Monitor is on $($result.currentInput), switching to HDMI1..."
- DDCSwitch set 0 HDMI1 --json | Out-Null
- DDCSwitch set 0 brightness 75% --json | Out-Null
+ ddcswitch set 0 HDMI1 --json | Out-Null
+ ddcswitch set 0 brightness 75% --json | Out-Null
Write-Host "Switched to HDMI1 and set brightness to 75%" -ForegroundColor Green
} else {
Write-Host "Monitor already on HDMI1"
@@ -442,7 +442,7 @@ if ($result.success -and $result.currentInputCode -ne "0x11") {
```powershell
# Switch all available monitors with full configuration
-$listResult = DDCSwitch list --json | ConvertFrom-Json
+$listResult = ddcswitch list --json | ConvertFrom-Json
if ($listResult.success) {
foreach ($monitor in $listResult.monitors) {
@@ -450,25 +450,25 @@ if ($listResult.success) {
Write-Host "Configuring $($monitor.name)..." -ForegroundColor Cyan
# Set input
- $setResult = DDCSwitch set $monitor.index HDMI1 --json | ConvertFrom-Json
+ $setResult = ddcswitch set $monitor.index HDMI1 --json | ConvertFrom-Json
if ($setResult.success) {
- Write-Host " ✓ Input: HDMI1" -ForegroundColor Green
+ Write-Host " ? Input: HDMI1" -ForegroundColor Green
}
# Set brightness
- $brightnessResult = DDCSwitch set $monitor.index brightness 75% --json | ConvertFrom-Json
+ $brightnessResult = ddcswitch set $monitor.index brightness 75% --json | ConvertFrom-Json
if ($brightnessResult.success) {
- Write-Host " ✓ Brightness: 75%" -ForegroundColor Green
+ Write-Host " ? Brightness: 75%" -ForegroundColor Green
} else {
- Write-Host " ✗ Brightness not supported" -ForegroundColor Yellow
+ Write-Host " ? Brightness not supported" -ForegroundColor Yellow
}
# Set contrast
- $contrastResult = DDCSwitch set $monitor.index contrast 80% --json | ConvertFrom-Json
+ $contrastResult = ddcswitch set $monitor.index contrast 80% --json | ConvertFrom-Json
if ($contrastResult.success) {
- Write-Host " ✓ Contrast: 80%" -ForegroundColor Green
+ Write-Host " ? Contrast: 80%" -ForegroundColor Green
} else {
- Write-Host " ✗ Contrast not supported" -ForegroundColor Yellow
+ Write-Host " ? Contrast not supported" -ForegroundColor Yellow
}
}
}
@@ -483,7 +483,7 @@ Create a comprehensive dashboard showing all monitor states:
```powershell
# monitor-dashboard.ps1
-$result = DDCSwitch list --verbose --json | ConvertFrom-Json
+$result = ddcswitch list --verbose --json | ConvertFrom-Json
if ($result.success) {
Write-Host "`n=== Monitor Status Dashboard ===" -ForegroundColor Cyan
@@ -518,7 +518,7 @@ if ($result.success) {
# smart-brightness-toggle.ps1
param([int]$MonitorIndex = 0)
-$result = DDCSwitch get $MonitorIndex brightness --json | ConvertFrom-Json
+$result = ddcswitch get $MonitorIndex brightness --json | ConvertFrom-Json
if ($result.success) {
$currentPercent = $result.percentageValue
@@ -531,10 +531,10 @@ if ($result.success) {
default { 25 }
}
- $switchResult = DDCSwitch set $MonitorIndex brightness "$newBrightness%" --json | ConvertFrom-Json
+ $switchResult = ddcswitch set $MonitorIndex brightness "$newBrightness%" --json | ConvertFrom-Json
if ($switchResult.success) {
- Write-Host "Brightness: $currentPercent% → $newBrightness%" -ForegroundColor Green
+ Write-Host "Brightness: $currentPercent% ? $newBrightness%" -ForegroundColor Green
}
} else {
Write-Host "Brightness control not supported on this monitor" -ForegroundColor Yellow
@@ -552,7 +552,7 @@ import json
import sys
def run_ddc(args):
- """Run DDCSwitch and return JSON result"""
+ """Run ddcswitch and return JSON result"""
result = subprocess.run(
['DDCSwitch'] + args + ['--json'],
capture_output=True,
@@ -577,25 +577,25 @@ def set_brightness(monitor_index, percentage):
"""Set monitor brightness"""
data = run_ddc(['set', str(monitor_index), 'brightness', f'{percentage}%'])
if data['success']:
- print(f"✓ Set brightness to {percentage}% on {data['monitor']['name']}")
+ print(f"? Set brightness to {percentage}% on {data['monitor']['name']}")
else:
- print(f"✗ Error: {data['error']}", file=sys.stderr)
+ print(f"? Error: {data['error']}", file=sys.stderr)
def set_contrast(monitor_index, percentage):
"""Set monitor contrast"""
data = run_ddc(['set', str(monitor_index), 'contrast', f'{percentage}%'])
if data['success']:
- print(f"✓ Set contrast to {percentage}% on {data['monitor']['name']}")
+ print(f"? Set contrast to {percentage}% on {data['monitor']['name']}")
else:
- print(f"✗ Error: {data['error']}", file=sys.stderr)
+ print(f"? Error: {data['error']}", file=sys.stderr)
def switch_input(monitor_index, input_name):
"""Switch monitor input"""
data = run_ddc(['set', str(monitor_index), input_name])
if data['success']:
- print(f"✓ Switched {data['monitor']['name']} to {data['newInput']}")
+ print(f"? Switched {data['monitor']['name']} to {data['newInput']}")
else:
- print(f"✗ Error: {data['error']}", file=sys.stderr)
+ print(f"? Error: {data['error']}", file=sys.stderr)
sys.exit(1)
# Example usage
@@ -626,7 +626,7 @@ def run_ddc(args):
def set_brightness_all(percentage):
"""Set brightness on all supported monitors"""
- print(f"🔆 Setting brightness to {percentage}%...")
+ print(f"?? Setting brightness to {percentage}%...")
data = run_ddc(['list'])
if data['success']:
@@ -634,9 +634,9 @@ def set_brightness_all(percentage):
if monitor['status'] == 'ok':
result = run_ddc(['set', str(monitor['index']), 'brightness', f'{percentage}%'])
if result['success']:
- print(f" ✓ {monitor['name']} → {percentage}%")
+ print(f" ? {monitor['name']} ? {percentage}%")
else:
- print(f" ✗ {monitor['name']} → Brightness not supported")
+ print(f" ? {monitor['name']} ? Brightness not supported")
def get_brightness_for_time():
"""Get appropriate brightness based on current time"""
@@ -653,7 +653,7 @@ def get_brightness_for_time():
def gaming_mode():
"""Switch to gaming setup with high brightness"""
- print("🎮 Activating gaming mode...")
+ print("?? Activating gaming mode...")
data = run_ddc(['list'])
if data['success']:
@@ -662,16 +662,16 @@ def gaming_mode():
# Switch to HDMI (console)
input_result = run_ddc(['set', str(monitor['index']), 'HDMI1'])
if input_result['success']:
- print(f" ✓ {monitor['name']} → HDMI1")
+ print(f" ? {monitor['name']} ? HDMI1")
# Set high brightness for gaming
brightness_result = run_ddc(['set', str(monitor['index']), 'brightness', '90%'])
if brightness_result['success']:
- print(f" ✓ {monitor['name']} → 90% brightness")
+ print(f" ? {monitor['name']} ? 90% brightness")
def work_mode():
"""Switch to work setup with comfortable brightness"""
- print("💼 Activating work mode...")
+ print("?? Activating work mode...")
data = run_ddc(['list'])
if data['success']:
@@ -680,12 +680,12 @@ def work_mode():
# Switch to DisplayPort (PC)
input_result = run_ddc(['set', str(monitor['index']), 'DP1'])
if input_result['success']:
- print(f" ✓ {monitor['name']} → DP1")
+ print(f" ? {monitor['name']} ? DP1")
# Set comfortable brightness for work
brightness_result = run_ddc(['set', str(monitor['index']), 'brightness', '60%'])
if brightness_result['success']:
- print(f" ✓ {monitor['name']} → 60% brightness")
+ print(f" ? {monitor['name']} ? 60% brightness")
# Auto-brightness based on time
if __name__ == '__main__':
@@ -701,9 +701,9 @@ if __name__ == '__main__':
// monitor-api.js
const { execSync } = require('child_process');
-class DDCSwitch {
+class ddcswitch {
static exec(args) {
- const output = execSync(`DDCSwitch ${args.join(' ')} --json`, {
+ const output = execSync(`ddcswitch ${args.join(' ')} --json`, {
encoding: 'utf-8'
});
return JSON.parse(output);
@@ -757,12 +757,12 @@ monitors.monitors.forEach(monitor => {
// Set brightness and contrast
const brightnessResult = DDCSwitch.setBrightness(0, 75);
if (brightnessResult.success) {
- console.log(`✓ Set brightness to 75%`);
+ console.log(`? Set brightness to 75%`);
}
const contrastResult = DDCSwitch.setContrast(0, 80);
if (contrastResult.success) {
- console.log(`✓ Set contrast to 80%`);
+ console.log(`? Set contrast to 80%`);
}
```
@@ -778,7 +778,7 @@ app.use(express.json());
function runDDC(args) {
try {
- const output = execSync(`DDCSwitch ${args.join(' ')} --json`, {
+ const output = execSync(`ddcswitch ${args.join(' ')} --json`, {
encoding: 'utf-8'
});
return JSON.parse(output);
@@ -860,7 +860,7 @@ app.post('/monitors/:id/profile', (req, res) => {
});
app.listen(3000, () => {
- console.log('DDCSwitch API running on http://localhost:3000');
+ console.log('ddcswitch API running on http://localhost:3000');
console.log('Endpoints:');
console.log(' GET /monitors?verbose=true');
console.log(' GET /monitors/:id/brightness');
@@ -878,30 +878,30 @@ REM complete-setup.bat - Set input, brightness, and contrast
echo Setting up monitor configuration...
REM Switch to HDMI1
-for /f "delims=" %%i in ('DDCSwitch set 0 HDMI1 --json') do set INPUT_RESULT=%%i
+for /f "delims=" %%i in ('ddcswitch set 0 HDMI1 --json') do set INPUT_RESULT=%%i
echo %INPUT_RESULT% | find "\"success\":true" >nul
if not errorlevel 1 (
- echo ✓ Switched to HDMI1
+ echo ? Switched to HDMI1
) else (
- echo ✗ Failed to switch input
+ echo ? Failed to switch input
)
REM Set brightness to 75%
-for /f "delims=" %%i in ('DDCSwitch set 0 brightness 75%% --json') do set BRIGHTNESS_RESULT=%%i
+for /f "delims=" %%i in ('ddcswitch set 0 brightness 75%% --json') do set BRIGHTNESS_RESULT=%%i
echo %BRIGHTNESS_RESULT% | find "\"success\":true" >nul
if not errorlevel 1 (
- echo ✓ Set brightness to 75%%
+ echo ? Set brightness to 75%%
) else (
- echo ✗ Brightness not supported or failed
+ echo ? Brightness not supported or failed
)
REM Set contrast to 80%
-for /f "delims=" %%i in ('DDCSwitch set 0 contrast 80%% --json') do set CONTRAST_RESULT=%%i
+for /f "delims=" %%i in ('ddcswitch set 0 contrast 80%% --json') do set CONTRAST_RESULT=%%i
echo %CONTRAST_RESULT% | find "\"success\":true" >nul
if not errorlevel 1 (
- echo ✓ Set contrast to 80%%
+ echo ? Set contrast to 80%%
) else (
- echo ✗ Contrast not supported or failed
+ echo ? Contrast not supported or failed
)
echo Monitor setup complete!
@@ -1008,25 +1008,25 @@ fn main() -> Result<(), Box> {
match set_brightness(monitor_index, 75) {
Ok(response) if response.success => {
- println!("✓ Set brightness to 75%");
+ println!("? Set brightness to 75%");
}
Ok(response) => {
- println!("✗ Failed to set brightness: {:?}", response.error);
+ println!("? Failed to set brightness: {:?}", response.error);
}
Err(e) => {
- println!("✗ Error setting brightness: {}", e);
+ println!("? Error setting brightness: {}", e);
}
}
match set_contrast(monitor_index, 80) {
Ok(response) if response.success => {
- println!("✓ Set contrast to 80%");
+ println!("? Set contrast to 80%");
}
Ok(response) => {
- println!("✗ Failed to set contrast: {:?}", response.error);
+ println!("? Failed to set contrast: {:?}", response.error);
}
Err(e) => {
- println!("✗ Error setting contrast: {}", e);
+ println!("? Error setting contrast: {}", e);
}
}
}
@@ -1049,12 +1049,12 @@ Switch all monitors to PC inputs with comfortable brightness:
```powershell
# work-setup.ps1
Write-Host "Switching to work setup..." -ForegroundColor Cyan
-DDCSwitch set 0 DP1
-DDCSwitch set 0 brightness 60%
-DDCSwitch set 0 contrast 75%
-DDCSwitch set 1 DP2
-DDCSwitch set 1 brightness 60%
-DDCSwitch set 1 contrast 75%
+ddcswitch set 0 DP1
+ddcswitch set 0 brightness 60%
+ddcswitch set 0 contrast 75%
+ddcswitch set 1 DP2
+ddcswitch set 1 brightness 60%
+ddcswitch set 1 contrast 75%
Write-Host "Work setup ready!" -ForegroundColor Green
```
@@ -1064,12 +1064,12 @@ Switch monitors to console inputs with high brightness:
```powershell
# gaming-setup.ps1
Write-Host "Switching to gaming setup..." -ForegroundColor Cyan
-DDCSwitch set 0 HDMI1 # Main monitor to PS5
-DDCSwitch set 0 brightness 90%
-DDCSwitch set 0 contrast 85%
-DDCSwitch set 1 HDMI2 # Secondary to Switch
-DDCSwitch set 1 brightness 85%
-DDCSwitch set 1 contrast 80%
+ddcswitch set 0 HDMI1 # Main monitor to PS5
+ddcswitch set 0 brightness 90%
+ddcswitch set 0 contrast 85%
+ddcswitch set 1 HDMI2 # Secondary to Switch
+ddcswitch set 1 brightness 85%
+ddcswitch set 1 contrast 80%
Write-Host "Gaming setup ready!" -ForegroundColor Green
```
@@ -1079,9 +1079,9 @@ Optimize for media consumption:
```powershell
# media-setup.ps1
Write-Host "Switching to media setup..." -ForegroundColor Cyan
-DDCSwitch set 0 HDMI1 # Media device
-DDCSwitch set 0 brightness 40% # Lower brightness for comfortable viewing
-DDCSwitch set 0 contrast 90% # High contrast for better blacks
+ddcswitch set 0 HDMI1 # Media device
+ddcswitch set 0 brightness 40% # Lower brightness for comfortable viewing
+ddcswitch set 0 contrast 90% # High contrast for better blacks
Write-Host "Media setup ready!" -ForegroundColor Green
```
@@ -1090,11 +1090,11 @@ Write-Host "Media setup ready!" -ForegroundColor Green
Create a comprehensive input switching and brightness control system:
```autohotkey
-; DDCSwitch AutoHotkey Script with VCP Support
-; Place DDCSwitch.exe in C:\Tools\ or update path below
+; ddcswitch AutoHotkey Script with VCP Support
+; Place ddcswitch.exe in C:\Tools\ or update path below
; Global variables
-DDCSwitchPath := "C:\Tools\DDCSwitch.exe"
+DDCSwitchPath := "C:\Tools\ddcswitch.exe"
; Function to run DDCSwitch
RunDDCSwitch(args) {
@@ -1106,19 +1106,19 @@ RunDDCSwitch(args) {
; Ctrl+Alt+1: Switch monitor 0 to HDMI1
^!1::
RunDDCSwitch("set 0 HDMI1")
- TrayTip, DDCSwitch, Switched to HDMI1, 1
+ TrayTip, ddcswitch, Switched to HDMI1, 1
return
; Ctrl+Alt+2: Switch monitor 0 to HDMI2
^!2::
RunDDCSwitch("set 0 HDMI2")
- TrayTip, DDCSwitch, Switched to HDMI2, 1
+ TrayTip, ddcswitch, Switched to HDMI2, 1
return
; Ctrl+Alt+D: Switch monitor 0 to DisplayPort
^!d::
RunDDCSwitch("set 0 DP1")
- TrayTip, DDCSwitch, Switched to DisplayPort, 1
+ TrayTip, ddcswitch, Switched to DisplayPort, 1
return
; Brightness control hotkeys
@@ -1126,39 +1126,39 @@ RunDDCSwitch(args) {
^!NumpadAdd::
^!=::
RunDDCSwitch("set 0 brightness +10%")
- TrayTip, DDCSwitch, Brightness increased, 1
+ TrayTip, ddcswitch, Brightness increased, 1
return
; Ctrl+Alt+Minus: Decrease brightness by 10%
^!NumpadSub::
^!-::
RunDDCSwitch("set 0 brightness -10%")
- TrayTip, DDCSwitch, Brightness decreased, 1
+ TrayTip, ddcswitch, Brightness decreased, 1
return
; Brightness presets
; Ctrl+Alt+F1: 25% brightness (night mode)
^!F1::
RunDDCSwitch("set 0 brightness 25%")
- TrayTip, DDCSwitch, Night Mode (25%), 1
+ TrayTip, ddcswitch, Night Mode (25%), 1
return
; Ctrl+Alt+F2: 50% brightness (comfortable)
^!F2::
RunDDCSwitch("set 0 brightness 50%")
- TrayTip, DDCSwitch, Comfortable (50%), 1
+ TrayTip, ddcswitch, Comfortable (50%), 1
return
; Ctrl+Alt+F3: 75% brightness (bright)
^!F3::
RunDDCSwitch("set 0 brightness 75%")
- TrayTip, DDCSwitch, Bright (75%), 1
+ TrayTip, ddcswitch, Bright (75%), 1
return
; Ctrl+Alt+F4: 100% brightness (maximum)
^!F4::
RunDDCSwitch("set 0 brightness 100%")
- TrayTip, DDCSwitch, Maximum (100%), 1
+ TrayTip, ddcswitch, Maximum (100%), 1
return
; Profile hotkeys
@@ -1171,7 +1171,7 @@ RunDDCSwitch(args) {
RunDDCSwitch("set 0 contrast 75%")
Sleep 500
RunDDCSwitch("set 1 DP2")
- TrayTip, DDCSwitch, Work Setup Activated, 1
+ TrayTip, ddcswitch, Work Setup Activated, 1
return
; Ctrl+Alt+G: Gaming setup (all monitors to console with high brightness)
@@ -1183,7 +1183,7 @@ RunDDCSwitch(args) {
RunDDCSwitch("set 0 contrast 85%")
Sleep 500
RunDDCSwitch("set 1 HDMI2")
- TrayTip, DDCSwitch, Gaming Setup Activated, 1
+ TrayTip, ddcswitch, Gaming Setup Activated, 1
return
; Ctrl+Alt+M: Media setup (HDMI with low brightness, high contrast)
@@ -1193,17 +1193,17 @@ RunDDCSwitch(args) {
RunDDCSwitch("set 0 brightness 40%")
Sleep 500
RunDDCSwitch("set 0 contrast 90%")
- TrayTip, DDCSwitch, Media Setup Activated, 1
+ TrayTip, ddcswitch, Media Setup Activated, 1
return
; Ctrl+Alt+L: List all monitors with verbose info
^!l::
- Run, cmd /k DDCSwitch.exe list --verbose
+ Run, cmd /k ddcswitch.exe list --verbose
return
; Ctrl+Alt+I: Show current monitor info
^!i::
- Run, cmd /k "DDCSwitch.exe get 0 && DDCSwitch.exe get 0 brightness && DDCSwitch.exe get 0 contrast && pause"
+ Run, cmd /k "ddcswitch.exe get 0 && ddcswitch.exe get 0 brightness && ddcswitch.exe get 0 contrast && pause"
return
```
@@ -1214,40 +1214,40 @@ If you use Elgato Stream Deck, create actions for complete monitor control:
**Button 1: PC Mode**
```
Title: PC Mode
-Command: C:\Tools\DDCSwitch.exe set 0 DP1
-Arguments: && C:\Tools\DDCSwitch.exe set 0 brightness 60%
+Command: C:\Tools\ddcswitch.exe set 0 DP1
+Arguments: && C:\Tools\ddcswitch.exe set 0 brightness 60%
```
**Button 2: Console Mode**
```
Title: Console Mode
-Command: C:\Tools\DDCSwitch.exe set 0 HDMI1
-Arguments: && C:\Tools\DDCSwitch.exe set 0 brightness 90%
+Command: C:\Tools\ddcswitch.exe set 0 HDMI1
+Arguments: && C:\Tools\ddcswitch.exe set 0 brightness 90%
```
**Button 3: Brightness Low**
```
-Title: 🔅 Low
-Command: C:\Tools\DDCSwitch.exe set 0 brightness 25%
+Title: ?? Low
+Command: C:\Tools\ddcswitch.exe set 0 brightness 25%
```
**Button 4: Brightness High**
```
-Title: 🔆 High
-Command: C:\Tools\DDCSwitch.exe set 0 brightness 85%
+Title: ?? High
+Command: C:\Tools\ddcswitch.exe set 0 brightness 85%
```
**Button 5: Monitor Info**
```
Title: Monitor Info
-Command: cmd /k C:\Tools\DDCSwitch.exe list --verbose
+Command: cmd /k C:\Tools\ddcswitch.exe list --verbose
```
**Button 6: Gaming Profile**
```
-Title: 🎮 Gaming
-Command: C:\Tools\DDCSwitch.exe set 0 HDMI1
-Arguments: && timeout /t 1 && C:\Tools\DDCSwitch.exe set 0 brightness 90% && C:\Tools\DDCSwitch.exe set 0 contrast 85%
+Title: ?? Gaming
+Command: C:\Tools\ddcswitch.exe set 0 HDMI1
+Arguments: && timeout /t 1 && C:\Tools\ddcswitch.exe set 0 brightness 90% && C:\Tools\ddcswitch.exe set 0 contrast 85%
```
### Task Scheduler Integration
@@ -1257,10 +1257,10 @@ Automatically switch inputs at specific times:
#### Morning: Switch to Work Setup (8 AM)
1. Open Task Scheduler
-2. Create Basic Task → "Morning Work Setup"
+2. Create Basic Task ? "Morning Work Setup"
3. Trigger: Daily at 8:00 AM
4. Action: Start a program
- - Program: `C:\Tools\DDCSwitch.exe`
+ - Program: `C:\Tools\ddcswitch.exe`
- Arguments: `set 0 DP1`
#### Evening: Switch to Gaming Setup (6 PM)
@@ -1272,45 +1272,45 @@ Same steps, but with trigger at 6:00 PM and arguments: `set 0 HDMI1`
Add to your PowerShell profile (`$PROFILE`):
```powershell
-# DDCSwitch aliases for complete monitor control
-function ddc-list { DDCSwitch list --verbose }
+# ddcswitch aliases for complete monitor control
+function ddc-list { ddcswitch list --verbose }
function ddc-work {
- DDCSwitch set 0 DP1
- DDCSwitch set 0 brightness 60%
- DDCSwitch set 0 contrast 75%
- DDCSwitch set 1 DP2
- Write-Host "✓ Work setup activated" -ForegroundColor Green
+ ddcswitch set 0 DP1
+ ddcswitch set 0 brightness 60%
+ ddcswitch set 0 contrast 75%
+ ddcswitch set 1 DP2
+ Write-Host "? Work setup activated" -ForegroundColor Green
}
function ddc-game {
- DDCSwitch set 0 HDMI1
- DDCSwitch set 0 brightness 90%
- DDCSwitch set 0 contrast 85%
- DDCSwitch set 1 HDMI2
- Write-Host "✓ Gaming setup activated" -ForegroundColor Green
+ ddcswitch set 0 HDMI1
+ ddcswitch set 0 brightness 90%
+ ddcswitch set 0 contrast 85%
+ ddcswitch set 1 HDMI2
+ Write-Host "? Gaming setup activated" -ForegroundColor Green
}
function ddc-media {
- DDCSwitch set 0 HDMI1
- DDCSwitch set 0 brightness 40%
- DDCSwitch set 0 contrast 90%
- Write-Host "✓ Media setup activated" -ForegroundColor Green
+ ddcswitch set 0 HDMI1
+ ddcswitch set 0 brightness 40%
+ ddcswitch set 0 contrast 90%
+ Write-Host "? Media setup activated" -ForegroundColor Green
}
-function ddc-hdmi { DDCSwitch set 0 HDMI1 }
-function ddc-dp { DDCSwitch set 0 DP1 }
-function ddc-bright([int]$level) { DDCSwitch set 0 brightness "$level%" }
-function ddc-contrast([int]$level) { DDCSwitch set 0 contrast "$level%" }
+function ddc-hdmi { ddcswitch set 0 HDMI1 }
+function ddc-dp { ddcswitch set 0 DP1 }
+function ddc-bright([int]$level) { ddcswitch set 0 brightness "$level%" }
+function ddc-contrast([int]$level) { ddcswitch set 0 contrast "$level%" }
# Brightness shortcuts
-function ddc-dim { DDCSwitch set 0 brightness 25% }
-function ddc-normal { DDCSwitch set 0 brightness 60% }
-function ddc-bright { DDCSwitch set 0 brightness 85% }
-function ddc-max { DDCSwitch set 0 brightness 100% }
+function ddc-dim { ddcswitch set 0 brightness 25% }
+function ddc-normal { ddcswitch set 0 brightness 60% }
+function ddc-bright { ddcswitch set 0 brightness 85% }
+function ddc-max { ddcswitch set 0 brightness 100% }
# Then use: ddc-work, ddc-game, ddc-media, ddc-bright 75, ddc-list
```
### Complete Monitor Control
-Use DDCSwitch as a comprehensive monitor management solution:
+Use ddcswitch as a comprehensive monitor management solution:
```powershell
# complete-monitor-control.ps1
@@ -1327,28 +1327,28 @@ function Apply-Profile {
switch ($ProfileName.ToLower()) {
"work" {
- DDCSwitch set $MonitorIndex DP1
- DDCSwitch set $MonitorIndex brightness 60%
- DDCSwitch set $MonitorIndex contrast 75%
- Write-Host "✓ Applied work profile" -ForegroundColor Green
+ ddcswitch set $MonitorIndex DP1
+ ddcswitch set $MonitorIndex brightness 60%
+ ddcswitch set $MonitorIndex contrast 75%
+ Write-Host "? Applied work profile" -ForegroundColor Green
}
"gaming" {
- DDCSwitch set $MonitorIndex HDMI1
- DDCSwitch set $MonitorIndex brightness 90%
- DDCSwitch set $MonitorIndex contrast 85%
- Write-Host "✓ Applied gaming profile" -ForegroundColor Green
+ ddcswitch set $MonitorIndex HDMI1
+ ddcswitch set $MonitorIndex brightness 90%
+ ddcswitch set $MonitorIndex contrast 85%
+ Write-Host "? Applied gaming profile" -ForegroundColor Green
}
"media" {
- DDCSwitch set $MonitorIndex HDMI1
- DDCSwitch set $MonitorIndex brightness 40%
- DDCSwitch set $MonitorIndex contrast 90%
- Write-Host "✓ Applied media profile" -ForegroundColor Green
+ ddcswitch set $MonitorIndex HDMI1
+ ddcswitch set $MonitorIndex brightness 40%
+ ddcswitch set $MonitorIndex contrast 90%
+ Write-Host "? Applied media profile" -ForegroundColor Green
}
"custom" {
- if ($Input) { DDCSwitch set $MonitorIndex $Input }
- if ($Brightness) { DDCSwitch set $MonitorIndex brightness "$Brightness%" }
- if ($Contrast) { DDCSwitch set $MonitorIndex contrast "$Contrast%" }
- Write-Host "✓ Applied custom settings" -ForegroundColor Green
+ if ($Input) { ddcswitch set $MonitorIndex $Input }
+ if ($Brightness) { ddcswitch set $MonitorIndex brightness "$Brightness%" }
+ if ($Contrast) { ddcswitch set $MonitorIndex contrast "$Contrast%" }
+ Write-Host "? Applied custom settings" -ForegroundColor Green
}
}
}
@@ -1375,39 +1375,39 @@ Write-Host "=" * 50
# Test brightness support
Write-Host "`nTesting Brightness (VCP 0x10)..." -ForegroundColor Yellow
try {
- $brightness = DDCSwitch get $monitor brightness 2>$null
+ $brightness = ddcswitch get $monitor brightness 2>$null
if ($brightness -match "Brightness:") {
- Write-Host "✓ Brightness supported: $brightness" -ForegroundColor Green
+ Write-Host "? Brightness supported: $brightness" -ForegroundColor Green
# Test setting brightness
- DDCSwitch set $monitor brightness 50% | Out-Null
+ ddcswitch set $monitor brightness 50% | Out-Null
Start-Sleep -Seconds 1
- $newBrightness = DDCSwitch get $monitor brightness
- Write-Host "✓ Brightness control works: $newBrightness" -ForegroundColor Green
+ $newBrightness = ddcswitch get $monitor brightness
+ Write-Host "? Brightness control works: $newBrightness" -ForegroundColor Green
} else {
- Write-Host "✗ Brightness not supported" -ForegroundColor Red
+ Write-Host "? Brightness not supported" -ForegroundColor Red
}
} catch {
- Write-Host "✗ Brightness not supported" -ForegroundColor Red
+ Write-Host "? Brightness not supported" -ForegroundColor Red
}
# Test contrast support
Write-Host "`nTesting Contrast (VCP 0x12)..." -ForegroundColor Yellow
try {
- $contrast = DDCSwitch get $monitor contrast 2>$null
+ $contrast = ddcswitch get $monitor contrast 2>$null
if ($contrast -match "Contrast:") {
- Write-Host "✓ Contrast supported: $contrast" -ForegroundColor Green
+ Write-Host "? Contrast supported: $contrast" -ForegroundColor Green
# Test setting contrast
- DDCSwitch set $monitor contrast 75% | Out-Null
+ ddcswitch set $monitor contrast 75% | Out-Null
Start-Sleep -Seconds 1
- $newContrast = DDCSwitch get $monitor contrast
- Write-Host "✓ Contrast control works: $newContrast" -ForegroundColor Green
+ $newContrast = ddcswitch get $monitor contrast
+ Write-Host "? Contrast control works: $newContrast" -ForegroundColor Green
} else {
- Write-Host "✗ Contrast not supported" -ForegroundColor Red
+ Write-Host "? Contrast not supported" -ForegroundColor Red
}
} catch {
- Write-Host "✗ Contrast not supported" -ForegroundColor Red
+ Write-Host "? Contrast not supported" -ForegroundColor Red
}
# Test raw VCP codes
@@ -1428,14 +1428,14 @@ $vcpCodes = @{
foreach ($code in $vcpCodes.Keys) {
try {
- $result = DDCSwitch get $monitor $code 2>$null
+ $result = ddcswitch get $monitor $code 2>$null
if ($result -and $result -notmatch "error|failed|not supported") {
- Write-Host "✓ VCP $code ($($vcpCodes[$code])): $result" -ForegroundColor Green
+ Write-Host "? VCP $code ($($vcpCodes[$code])): $result" -ForegroundColor Green
} else {
- Write-Host "✗ VCP $code ($($vcpCodes[$code])): Not supported" -ForegroundColor Gray
+ Write-Host "? VCP $code ($($vcpCodes[$code])): Not supported" -ForegroundColor Gray
}
} catch {
- Write-Host "✗ VCP $code ($($vcpCodes[$code])): Not supported" -ForegroundColor Gray
+ Write-Host "? VCP $code ($($vcpCodes[$code])): Not supported" -ForegroundColor Gray
}
}
@@ -1461,9 +1461,9 @@ foreach ($brightness in $brightnessLevels) {
foreach ($contrast in $contrastLevels) {
Write-Host "Setting: Brightness $brightness%, Contrast $contrast%" -ForegroundColor Yellow
- DDCSwitch set $monitor brightness "$brightness%" | Out-Null
+ ddcswitch set $monitor brightness "$brightness%" | Out-Null
Start-Sleep -Milliseconds 500
- DDCSwitch set $monitor contrast "$contrast%" | Out-Null
+ ddcswitch set $monitor contrast "$contrast%" | Out-Null
Write-Host "How does this look? (Press Enter to continue, 'q' to quit, 's' to save this setting)" -ForegroundColor Green
$input = Read-Host
@@ -1473,7 +1473,7 @@ foreach ($brightness in $brightnessLevels) {
break
} elseif ($input -eq 's') {
Write-Host "Saved setting: Brightness $brightness%, Contrast $contrast%" -ForegroundColor Cyan
- Write-Host "Command to reproduce: DDCSwitch set $monitor brightness $brightness%; DDCSwitch set $monitor contrast $contrast%" -ForegroundColor White
+ Write-Host "Command to reproduce: ddcswitch set $monitor brightness $brightness%; ddcswitch set $monitor contrast $contrast%" -ForegroundColor White
Read-Host "Press Enter to continue or Ctrl+C to stop"
}
}
@@ -1483,7 +1483,7 @@ foreach ($brightness in $brightnessLevels) {
### Finding Non-Standard VCP Codes
-Some monitors use non-standard DDC/CI codes. If DDCSwitch shows the wrong current input or switching doesn't work, you can find the correct codes:
+Some monitors use non-standard DDC/CI codes. If ddcswitch shows the wrong current input or switching doesn't work, you can find the correct codes:
#### Method 1: Use ControlMyMonitor by NirSoft
@@ -1499,15 +1499,15 @@ Some monitors use non-standard DDC/CI codes. If DDCSwitch shows the wrong curren
**Example:**
```
VCP Code 60 (Input Source):
-- HDMI1 physical input → Shows value 17 (0x11) ✓ Standard
-- HDMI2 physical input → Shows value 18 (0x12) ✓ Standard
-- DisplayPort physical input → Shows value 15 (0x0F) ✓ Standard
-- DisplayPort physical input → Shows value 27 (0x1B) ✗ Non-standard!
+- HDMI1 physical input ? Shows value 17 (0x11) ? Standard
+- HDMI2 physical input ? Shows value 18 (0x12) ? Standard
+- DisplayPort physical input ? Shows value 15 (0x0F) ? Standard
+- DisplayPort physical input ? Shows value 27 (0x1B) ? Non-standard!
```
Once you know the correct codes, use them with DDCSwitch:
```powershell
-DDCSwitch set 0 0x1B # Use the actual code your monitor responds to
+ddcswitch set 0 0x1B # Use the actual code your monitor responds to
```
#### Method 2: Trial and Error
@@ -1525,7 +1525,7 @@ $codes = @(0x01, 0x02, 0x03, 0x04, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x1
foreach ($code in $codes) {
$hexCode = "0x{0:X2}" -f $code
Write-Host "Testing $hexCode..." -ForegroundColor Green
- DDCSwitch set 0 $hexCode
+ ddcswitch set 0 $hexCode
Start-Sleep -Seconds 3
}
@@ -1539,17 +1539,17 @@ Write-Host "Testing complete! Document which codes worked for your monitor." -Fo
```powershell
# Get current input (if this works, DDC/CI is functional)
-DDCSwitch get 0
+ddcswitch get 0
# Get by monitor name
-DDCSwitch get "VG270U"
+ddcswitch get "VG270U"
# List all monitors with all VCP values
-DDCSwitch get all
+ddcswitch get all
# Try setting to current input (should succeed instantly)
-DDCSwitch list # Note the current input
-DDCSwitch set 0
+ddcswitch list # Note the current input
+ddcswitch set 0
```
### Dealing with Slow Monitors
@@ -1558,9 +1558,9 @@ Some monitors are slow to respond to DDC/CI commands. Add delays:
```powershell
# slow-switch.ps1
-DDCSwitch set 0 HDMI1
+ddcswitch set 0 HDMI1
Start-Sleep -Seconds 2 # Wait for monitor to switch
-DDCSwitch set 1 HDMI2
+ddcswitch set 1 HDMI2
Start-Sleep -Seconds 2
Write-Host "Done" -ForegroundColor Green
```
@@ -1571,25 +1571,25 @@ Useful if monitor order changes:
```powershell
# Find monitor with "LG" in the name and switch it
-DDCSwitch list # Note the exact name
-DDCSwitch set "LG ULTRAGEAR" HDMI1
+ddcswitch list # Note the exact name
+ddcswitch set "LG ULTRAGEAR" HDMI1
# Partial name matching works
-DDCSwitch set "ULTRAGEAR" HDMI1
+ddcswitch set "ULTRAGEAR" HDMI1
# Get settings by monitor name
-DDCSwitch get "VG270U" brightness
-DDCSwitch get "Generic PnP" # Gets all VCP values for this monitor
+ddcswitch get "VG270U" brightness
+ddcswitch get "Generic PnP" # Gets all VCP values for this monitor
```
## Integration with Other Tools
### PowerToys Run
-Add DDCSwitch to your PATH, then use PowerToys Run (Alt+Space):
+Add ddcswitch to your PATH, then use PowerToys Run (Alt+Space):
```
-> DDCSwitch set 0 HDMI1
+> ddcswitch set 0 HDMI1
```
### Windows Run Dialog
@@ -1597,7 +1597,7 @@ Add DDCSwitch to your PATH, then use PowerToys Run (Alt+Space):
Press Win+R and type:
```
-DDCSwitch set 0 DP1
+ddcswitch set 0 DP1
```
### Batch File Shortcuts
@@ -1607,21 +1607,21 @@ Create `.bat` files on your desktop:
**switch-to-hdmi.bat:**
```batch
@echo off
-"C:\Tools\DDCSwitch.exe" set 0 HDMI1
+"C:\Tools\ddcswitch.exe" set 0 HDMI1
```
**switch-to-dp.bat:**
```batch
@echo off
-"C:\Tools\DDCSwitch.exe" set 0 DP1
+"C:\Tools\ddcswitch.exe" set 0 DP1
```
Make them double-clickable for quick access!
## Tips and Tricks
-1. **Add to PATH**: Add DDCSwitch.exe location to your Windows PATH for easier access
-2. **Create shortcuts**: Right-click DDCSwitch.exe → Send to → Desktop (create shortcut), then edit properties to add arguments
+1. **Add to PATH**: Add ddcswitch.exe location to your Windows PATH for easier access
+2. **Create shortcuts**: Right-click ddcswitch.exe ? Send to ? Desktop (create shortcut), then edit properties to add arguments
3. **Use monitoring**: Combine with other tools to detect when certain apps launch and switch inputs automatically
4. **Test first**: Always test with `list` and `get` before creating automation scripts
5. **Admin rights**: Some monitors require running as Administrator - right-click and "Run as administrator"
@@ -1632,12 +1632,12 @@ Make them double-clickable for quick access!
```powershell
# toggle-brightness.ps1 - Toggle between low/medium/high brightness
-$current = DDCSwitch get 0 brightness --json | ConvertFrom-Json
+$current = ddcswitch get 0 brightness --json | ConvertFrom-Json
if ($current.success) {
$currentPercent = $current.percentageValue
- # Cycle through 25% → 50% → 75% → 100% → 25%
+ # Cycle through 25% ? 50% ? 75% ? 100% ? 25%
$newBrightness = switch ($currentPercent) {
{$_ -le 25} { 50 }
{$_ -le 50} { 75 }
@@ -1645,8 +1645,8 @@ if ($current.success) {
default { 25 }
}
- DDCSwitch set 0 brightness "$newBrightness%"
- Write-Host "Brightness: $currentPercent% → $newBrightness%" -ForegroundColor Green
+ ddcswitch set 0 brightness "$newBrightness%"
+ Write-Host "Brightness: $currentPercent% ? $newBrightness%" -ForegroundColor Green
} else {
Write-Host "Brightness control not supported" -ForegroundColor Red
}
@@ -1659,8 +1659,8 @@ if ($current.success) {
param([string]$Profile = "work")
# Get current settings
-$inputResult = DDCSwitch get 0 --json | ConvertFrom-Json
-$brightnessResult = DDCSwitch get 0 brightness --json | ConvertFrom-Json
+$inputResult = ddcswitch get 0 --json | ConvertFrom-Json
+$brightnessResult = ddcswitch get 0 brightness --json | ConvertFrom-Json
if (-not $inputResult.success) {
Write-Error "Monitor not accessible"
@@ -1683,17 +1683,17 @@ $targetProfile = $profiles[$Profile]
# Apply settings only if different
if ($inputResult.currentInputCode -ne $targetProfile.input) {
- DDCSwitch set 0 $targetProfile.input
- Write-Host "✓ Input: $($targetProfile.input)" -ForegroundColor Green
+ ddcswitch set 0 $targetProfile.input
+ Write-Host "? Input: $($targetProfile.input)" -ForegroundColor Green
}
if ($brightnessResult.success -and $brightnessResult.percentageValue -ne $targetProfile.brightness) {
- DDCSwitch set 0 brightness "$($targetProfile.brightness)%"
- Write-Host "✓ Brightness: $($targetProfile.brightness)%" -ForegroundColor Green
+ ddcswitch set 0 brightness "$($targetProfile.brightness)%"
+ Write-Host "? Brightness: $($targetProfile.brightness)%" -ForegroundColor Green
}
-DDCSwitch set 0 contrast "$($targetProfile.contrast)%"
-Write-Host "✓ Profile '$Profile' applied" -ForegroundColor Cyan
+ddcswitch set 0 contrast "$($targetProfile.contrast)%"
+Write-Host "? Profile '$Profile' applied" -ForegroundColor Cyan
```
### Pattern 3: Sync All Monitors
@@ -1706,7 +1706,7 @@ param(
[int]$Contrast = 80
)
-$result = DDCSwitch list --json | ConvertFrom-Json
+$result = ddcswitch list --json | ConvertFrom-Json
if (-not $result.success) {
Write-Error $result.error
@@ -1719,27 +1719,27 @@ foreach ($monitor in $okMonitors) {
Write-Host "Configuring monitor $($monitor.index) ($($monitor.name))..." -ForegroundColor Cyan
# Set input
- $inputResult = DDCSwitch set $monitor.index $Input --json | ConvertFrom-Json
+ $inputResult = ddcswitch set $monitor.index $Input --json | ConvertFrom-Json
if ($inputResult.success) {
- Write-Host " ✓ Input: $Input" -ForegroundColor Green
+ Write-Host " ? Input: $Input" -ForegroundColor Green
} else {
- Write-Host " ✗ Input failed: $($inputResult.error)" -ForegroundColor Red
+ Write-Host " ? Input failed: $($inputResult.error)" -ForegroundColor Red
}
# Set brightness
- $brightnessResult = DDCSwitch set $monitor.index brightness "$Brightness%" --json | ConvertFrom-Json
+ $brightnessResult = ddcswitch set $monitor.index brightness "$Brightness%" --json | ConvertFrom-Json
if ($brightnessResult.success) {
- Write-Host " ✓ Brightness: $Brightness%" -ForegroundColor Green
+ Write-Host " ? Brightness: $Brightness%" -ForegroundColor Green
} else {
- Write-Host " ✗ Brightness not supported" -ForegroundColor Yellow
+ Write-Host " ? Brightness not supported" -ForegroundColor Yellow
}
# Set contrast
- $contrastResult = DDCSwitch set $monitor.index contrast "$Contrast%" --json | ConvertFrom-Json
+ $contrastResult = ddcswitch set $monitor.index contrast "$Contrast%" --json | ConvertFrom-Json
if ($contrastResult.success) {
- Write-Host " ✓ Contrast: $Contrast%" -ForegroundColor Green
+ Write-Host " ? Contrast: $Contrast%" -ForegroundColor Green
} else {
- Write-Host " ✗ Contrast not supported" -ForegroundColor Yellow
+ Write-Host " ? Contrast not supported" -ForegroundColor Yellow
}
Start-Sleep -Milliseconds 500 # Prevent DDC/CI overload
diff --git a/README.md b/README.md
index 5ccfedd..a6cc656 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# DDCSwitch
+# ddcswitch
[](https://github.com/markdwags/DDCSwitch/releases)
[](https://github.com/markdwags/DDCSwitch/blob/main/LICENSE)
@@ -28,7 +28,7 @@ A Windows command-line utility to control monitor settings via DDC/CI (Display D
### Pre-built Binary
-Download the latest release from the [Releases](../../releases) page and extract `DDCSwitch.exe` to a folder in your PATH.
+Download the latest release from the [Releases](../../releases) page and extract `ddcswitch.exe` to a folder in your PATH.
### Build from Source
@@ -47,7 +47,7 @@ dotnet publish -c Release
The project is pre-configured with NativeAOT (`true`), which produces a ~3-5 MB native executable with instant startup and no .NET runtime dependency.
-Executable location: `DDCSwitch/bin/Release/net10.0/win-x64/publish/DDCSwitch.exe`
+Executable location: `DDCSwitch/bin/Release/net10.0/win-x64/publish/ddcswitch.exe`
## Usage
@@ -56,7 +56,7 @@ Executable location: `DDCSwitch/bin/Release/net10.0/win-x64/publish/DDCSwitch.ex
Display all DDC/CI capable monitors with their current input sources:
```powershell
-DDCSwitch list
+ddcswitch list
```
Example output:
@@ -74,7 +74,7 @@ Example output:
Add `--verbose` to include brightness and contrast information:
```powershell
-DDCSwitch list --verbose
+ddcswitch list --verbose
```
Example output:
@@ -94,7 +94,7 @@ Add `--json` for machine-readable output (see [EXAMPLES.md](EXAMPLES.md) for aut
Get all VCP features for a specific monitor:
```powershell
-DDCSwitch get 0
+ddcswitch get 0
```
This will scan and display all supported VCP features for monitor 0, showing their names, access types, current values, and maximum values.
@@ -103,25 +103,25 @@ You can also use the monitor name instead of the index (partial name matching su
```powershell
# Get all settings by monitor name
-DDCSwitch get "VG270U P"
-DDCSwitch get "Generic PnP"
+ddcswitch get "VG270U P"
+ddcswitch get "Generic PnP"
```
Get a specific feature:
```powershell
# Get current input source
-DDCSwitch get 0 input
+ddcswitch get 0 input
# Get brightness as percentage
-DDCSwitch get 0 brightness
+ddcswitch get 0 brightness
# Get contrast as percentage
-DDCSwitch get 0 contrast
+ddcswitch get 0 contrast
# Works with monitor names too
-DDCSwitch get "VG270U P" brightness
-DDCSwitch get "Generic PnP" input
+ddcswitch get "VG270U P" brightness
+ddcswitch get "Generic PnP" input
```
Output: `Monitor: Generic PnP Monitor` / `Brightness: 75% (120/160)`
@@ -132,20 +132,20 @@ Switch a monitor to a different input:
```powershell
# By monitor index
-DDCSwitch set 0 HDMI1
+ddcswitch set 0 HDMI1
# By monitor name (partial match)
-DDCSwitch set "LG ULTRAGEAR" HDMI2
+ddcswitch set "LG ULTRAGEAR" HDMI2
```
Set brightness or contrast with percentage values:
```powershell
# Set brightness to 75%
-DDCSwitch set 0 brightness 75%
+ddcswitch set 0 brightness 75%
# Set contrast to 80%
-DDCSwitch set 0 contrast 80%
+ddcswitch set 0 contrast 80%
```
Output: `✓ Successfully set brightness to 75% (120/160)`
@@ -156,10 +156,10 @@ For advanced users, access any VCP feature by code:
```powershell
# Get raw VCP value (e.g., VCP code 0x10 for brightness)
-DDCSwitch get 0 0x10
+ddcswitch get 0 0x10
# Set raw VCP value
-DDCSwitch set 0 0x10 120
+ddcswitch set 0 0x10 120
```
### VCP Feature Scanning
@@ -167,7 +167,7 @@ DDCSwitch set 0 0x10 120
Discover all supported VCP features on all monitors:
```powershell
-DDCSwitch get all
+ddcswitch get all
```
This scans all VCP codes (0x00-0xFF) for every monitor and displays supported features with their current values, maximum values, and access types (read-only, write-only, read-write).
@@ -176,10 +176,10 @@ To scan a specific monitor:
```powershell
# Scan specific monitor by index
-DDCSwitch get 0
+ddcswitch get 0
# Scan specific monitor by name
-DDCSwitch get "VG270U"
+ddcswitch get "VG270U"
```
### VCP Feature Categories and Discovery
@@ -188,12 +188,12 @@ Discover and browse VCP features by category:
```powershell
# List all available categories
-DDCSwitch list --categories
+ddcswitch list --categories
# List features in a specific category
-DDCSwitch list --category image
-DDCSwitch list --category color
-DDCSwitch list --category audio
+ddcswitch list --category image
+ddcswitch list --category color
+ddcswitch list --category audio
```
Example output:
@@ -248,49 +248,49 @@ Color Control Features:
**Switch multiple monitors:**
```powershell
-DDCSwitch set 0 HDMI1
-DDCSwitch set 1 DP1
+ddcswitch set 0 HDMI1
+ddcswitch set 1 DP1
```
**Control comprehensive VCP features:**
```powershell
-DDCSwitch set 0 brightness 75%
-DDCSwitch set 0 contrast 80%
-DDCSwitch get 0 brightness
+ddcswitch set 0 brightness 75%
+ddcswitch set 0 contrast 80%
+ddcswitch get 0 brightness
# Color controls
-DDCSwitch set 0 red-gain 90%
-DDCSwitch set 0 green-gain 85%
-DDCSwitch set 0 blue-gain 95%
+ddcswitch set 0 red-gain 90%
+ddcswitch set 0 green-gain 85%
+ddcswitch set 0 blue-gain 95%
# Audio controls (if supported)
-DDCSwitch set 0 volume 50%
-DDCSwitch set 0 mute 1
+ddcswitch set 0 volume 50%
+ddcswitch set 0 mute 1
```
**VCP feature discovery:**
```powershell
# List all available VCP feature categories
-DDCSwitch list --categories
+ddcswitch list --categories
# List features in a specific category
-DDCSwitch list --category color
+ddcswitch list --category color
# Search for features by name
-DDCSwitch get 0 bright # Matches "brightness"
+ddcswitch get 0 bright # Matches "brightness"
# Or by monitor name
-DDCSwitch get "VG270U" bright
+ddcswitch get "VG270U" bright
```
**Desktop shortcut:**
-Create a shortcut with target: `C:\Path\To\DDCSwitch.exe set 0 brightness 50%`
+Create a shortcut with target: `C:\Path\To\ddcswitch.exe set 0 brightness 50%`
**AutoHotkey:**
```autohotkey
-^!h::Run, DDCSwitch.exe set 0 HDMI1 ; Ctrl+Alt+H for HDMI1
-^!d::Run, DDCSwitch.exe set 0 DP1 ; Ctrl+Alt+D for DisplayPort
-^!b::Run, DDCSwitch.exe set 0 brightness 75% ; Ctrl+Alt+B for 75% brightness
+^!h::Run, ddcswitch.exe set 0 HDMI1 ; Ctrl+Alt+H for HDMI1
+^!d::Run, ddcswitch.exe set 0 DP1 ; Ctrl+Alt+D for DisplayPort
+^!b::Run, ddcswitch.exe set 0 brightness 75% ; Ctrl+Alt+B for 75% brightness
```
### JSON Output for Automation
@@ -299,20 +299,20 @@ All commands support `--json` for machine-readable output:
```powershell
# PowerShell: Conditional switching
-$result = DDCSwitch get 0 --json | ConvertFrom-Json
+$result = ddcswitch get 0 --json | ConvertFrom-Json
if ($result.currentInputCode -ne "0x11") {
- DDCSwitch set 0 HDMI1
+ ddcswitch set 0 HDMI1
}
```
```python
# Python: Switch all monitors
import subprocess, json
-data = json.loads(subprocess.run(['DDCSwitch', 'list', '--json'],
+data = json.loads(subprocess.run(['ddcswitch', 'list', '--json'],
capture_output=True, text=True).stdout)
for m in data['monitors']:
if m['status'] == 'ok':
- subprocess.run(['DDCSwitch', 'set', str(m['index']), 'HDMI1'])
+ subprocess.run(['ddcswitch', 'set', str(m['index']), 'HDMI1'])
```
📚 **See [EXAMPLES.md](EXAMPLES.md) for comprehensive automation examples** including Stream Deck, Task Scheduler, Python, Node.js, Rust, and more.
@@ -345,7 +345,7 @@ If you need to verify DDC/CI values or troubleshoot monitor-specific issues, try
## Technical Details
-DDCSwitch uses the Windows DXVA2 API to communicate with monitors via DDC/CI protocol. It reads/writes VCP (Virtual Control Panel) features following the MCCS specification.
+ddcswitch uses the Windows DXVA2 API to communicate with monitors via DDC/CI protocol. It reads/writes VCP (Virtual Control Panel) features following the MCCS specification.
**Common VCP Codes:**
- `0x10` Brightness, `0x12` Contrast, `0x60` Input Source
diff --git a/build.cmd b/build.cmd
index 0122b57..2b507cc 100644
--- a/build.cmd
+++ b/build.cmd
@@ -2,7 +2,7 @@
setlocal enabledelayedexpansion
echo ========================================
-echo Building DDCSwitch with NativeAOT
+echo Building ddcswitch with NativeAOT
echo ========================================
echo.
@@ -30,7 +30,7 @@ if not exist "dist" mkdir "dist"
REM Copy the NativeAOT executable
echo Copying executable to dist folder...
-copy /Y "DDCSwitch\bin\Release\net10.0\win-x64\publish\DDCSwitch.exe" "dist\DDCSwitch.exe"
+copy /Y "DDCSwitch\bin\Release\net10.0\win-x64\publish\ddcswitch.exe" "dist\ddcswitch.exe"
if errorlevel 1 (
echo ERROR: Failed to copy executable
exit /b 1
@@ -39,11 +39,11 @@ if errorlevel 1 (
echo.
echo ========================================
echo Build completed successfully!
-echo Output: dist\DDCSwitch.exe
+echo Output: dist\ddcswitch.exe
echo ========================================
REM Display file size
-for %%A in ("dist\DDCSwitch.exe") do (
+for %%A in ("dist\ddcswitch.exe") do (
set size=%%~zA
set /a sizeMB=!size! / 1048576
echo File size: !sizeMB! MB
From dc319085c95d0bc4ef678c4ed9ad80a712a8dab1 Mon Sep 17 00:00:00 2001
From: Quick <577652+markdwags@users.noreply.github.com>
Date: Thu, 8 Jan 2026 14:11:08 -0600
Subject: [PATCH 09/10] normalize casing for 'ddcswitch' in CI/CD
configuration; update paths and filenames for consistency
---
.github/workflows/ci-cd.yml | 16 ++++++++--------
1 file changed, 8 insertions(+), 8 deletions(-)
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index 289b72d..a131780 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -38,7 +38,7 @@ jobs:
- name: Verify executable exists
shell: pwsh
run: |
- $exePath = "DDCSwitch/bin/Release/net10.0/win-x64/publish/DDCSwitch.exe"
+ $exePath = "DDCSwitch/bin/Release/net10.0/win-x64/publish/ddcswitch.exe"
if (Test-Path $exePath) {
$size = (Get-Item $exePath).Length / 1MB
Write-Host "✓ Build successful! Executable size: $([math]::Round($size, 2)) MB"
@@ -52,7 +52,7 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: DDCSwitch-build-${{ github.sha }}
- path: DDCSwitch/bin/Release/net10.0/win-x64/publish/DDCSwitch.exe
+ path: DDCSwitch/bin/Release/net10.0/win-x64/publish/ddcswitch.exe
retention-days: 7
release:
@@ -92,7 +92,7 @@ jobs:
shell: pwsh
run: |
New-Item -ItemType Directory -Force -Path release
- Copy-Item artifact/DDCSwitch.exe release/
+ Copy-Item artifact/ddcswitch.exe release/
Copy-Item README.md release/
Copy-Item LICENSE release/
Copy-Item EXAMPLES.md release/
@@ -102,7 +102,7 @@ jobs:
shell: pwsh
run: |
$version = "${{ steps.get_version.outputs.version }}"
- Compress-Archive -Path release/* -DestinationPath "DDCSwitch-$version-win-x64.zip"
+ Compress-Archive -Path release/* -DestinationPath "ddcswitch-$version-win-x64.zip"
- name: Generate release notes
id: release_notes
@@ -150,20 +150,20 @@ jobs:
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.get_version.outputs.tag }}
- name: DDCSwitch ${{ steps.get_version.outputs.version }}
+ name: ddcswitch ${{ steps.get_version.outputs.version }}
body: ${{ steps.release_notes.outputs.notes }}
draft: false
prerelease: false
files: |
- DDCSwitch-${{ steps.get_version.outputs.version }}-win-x64.zip
- release/DDCSwitch.exe
+ ddcswitch-${{ steps.get_version.outputs.version }}-win-x64.zip
+ release/ddcswitch.exe
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
- name: DDCSwitch-${{ steps.get_version.outputs.version }}-win-x64
+ name: ddcswitch-${{ steps.get_version.outputs.version }}-win-x64
path: release/
retention-days: 30
From e8e41427949d506bf7abb2dcc06eadfbbef6d357 Mon Sep 17 00:00:00 2001
From: Quick <577652+markdwags@users.noreply.github.com>
Date: Thu, 8 Jan 2026 14:47:09 -0600
Subject: [PATCH 10/10] Add Chocolatey package installation and uninstallation
scripts; update CI/CD configuration for package creation and checksum
calculation
---
.github/workflows/ci-cd.yml | 120 +++++++++++++++++++++++
DDCSwitch/Commands/CommandRouter.cs | 2 +-
chocolatey/ddcswitch.nuspec | 48 +++++++++
chocolatey/tools/CHECKSUM | 1 +
chocolatey/tools/VERIFICATION.txt | 22 +++++
chocolatey/tools/chocolateyinstall.ps1 | 28 ++++++
chocolatey/tools/chocolateyuninstall.ps1 | 10 ++
7 files changed, 230 insertions(+), 1 deletion(-)
create mode 100644 chocolatey/ddcswitch.nuspec
create mode 100644 chocolatey/tools/CHECKSUM
create mode 100644 chocolatey/tools/VERIFICATION.txt
create mode 100644 chocolatey/tools/chocolateyinstall.ps1
create mode 100644 chocolatey/tools/chocolateyuninstall.ps1
diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml
index a131780..dcce49a 100644
--- a/.github/workflows/ci-cd.yml
+++ b/.github/workflows/ci-cd.yml
@@ -167,3 +167,123 @@ jobs:
path: release/
retention-days: 30
+ chocolatey-package:
+ needs: release
+ runs-on: windows-latest
+ if: github.event_name == 'push' && github.ref == 'refs/heads/main'
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Get version from changelog
+ id: get_version
+ shell: pwsh
+ run: |
+ $changelog = Get-Content CHANGELOG.md -Raw
+ if ($changelog -match '\[(\d+\.\d+\.\d+)\]') {
+ $version = $matches[1]
+ echo "version=$version" >> $env:GITHUB_OUTPUT
+ echo "Found version: $version"
+ } else {
+ echo "Error: Could not find version in CHANGELOG.md"
+ exit 1
+ }
+
+ - name: Calculate checksum
+ id: checksum
+ shell: pwsh
+ run: |
+ $version = "${{ steps.get_version.outputs.version }}"
+ $url = "https://github.com/markdwags/ddcswitch/releases/download/v$version/ddcswitch-$version-win-x64.zip"
+ $tempFile = "$env:TEMP\ddcswitch-$version.zip"
+
+ Write-Host "Downloading ZIP from: $url"
+ Start-Sleep -Seconds 2 # Give GitHub a moment to make the release available
+
+ # Retry logic for download
+ $maxRetries = 5
+ $retryCount = 0
+ $downloaded = $false
+
+ while (-not $downloaded -and $retryCount -lt $maxRetries) {
+ try {
+ Invoke-WebRequest -Uri $url -OutFile $tempFile -ErrorAction Stop
+ $downloaded = $true
+ } catch {
+ $retryCount++
+ Write-Host "Download attempt $retryCount failed. Retrying in 10 seconds..."
+ Start-Sleep -Seconds 10
+ }
+ }
+
+ if (-not $downloaded) {
+ Write-Host "Failed to download after $maxRetries attempts"
+ exit 1
+ }
+
+ $hash = Get-FileHash $tempFile -Algorithm SHA256
+ $checksum = $hash.Hash
+ echo "checksum=$checksum" >> $env:GITHUB_OUTPUT
+ Write-Host "SHA256 Checksum: $checksum"
+ Remove-Item $tempFile
+
+ - name: Update Chocolatey files with version and checksum
+ shell: pwsh
+ run: |
+ $version = "${{ steps.get_version.outputs.version }}"
+ $checksum = "${{ steps.checksum.outputs.checksum }}"
+
+ # Update version in nuspec (Chocolatey will pass to scripts via $env:chocolateyPackageVersion)
+ (Get-Content chocolatey\ddcswitch.nuspec -Raw) -replace '__VERSION__', $version | Set-Content chocolatey\ddcswitch.nuspec -NoNewline
+
+ # Create CHECKSUM file for install script to read
+ Set-Content chocolatey\tools\CHECKSUM $checksum -NoNewline
+
+ # Update VERIFICATION.txt for moderators
+ (Get-Content chocolatey\tools\VERIFICATION.txt -Raw) -replace '__VERSION__', $version -replace '__CHECKSUM__', $checksum | Set-Content chocolatey\tools\VERIFICATION.txt -NoNewline
+
+ Write-Host "✓ Updated Chocolatey files"
+ Write-Host " Version: $version (in nuspec, passed via env to scripts)"
+ Write-Host " Checksum: $checksum (in CHECKSUM file)"
+
+ - name: Install Chocolatey
+ shell: pwsh
+ run: |
+ if (-not (Get-Command choco -ErrorAction SilentlyContinue)) {
+ Write-Host "Installing Chocolatey..."
+ Set-ExecutionPolicy Bypass -Scope Process -Force
+ [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
+ iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
+ } else {
+ Write-Host "Chocolatey is already installed"
+ }
+
+ - name: Create Chocolatey package
+ shell: pwsh
+ run: |
+ cd chocolatey
+ choco pack
+ $version = "${{ steps.get_version.outputs.version }}"
+ if (Test-Path "ddcswitch.$version.nupkg") {
+ Write-Host "✓ Successfully created ddcswitch.$version.nupkg"
+ } else {
+ Write-Host "✗ Failed to create package"
+ exit 1
+ }
+
+ - name: Upload Chocolatey package
+ uses: actions/upload-artifact@v4
+ with:
+ name: chocolatey-package-${{ steps.get_version.outputs.version }}
+ path: chocolatey/*.nupkg
+ retention-days: 90
+
+ - name: Upload to GitHub Release
+ uses: softprops/action-gh-release@v2
+ with:
+ tag_name: v${{ steps.get_version.outputs.version }}
+ files: chocolatey/*.nupkg
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
diff --git a/DDCSwitch/Commands/CommandRouter.cs b/DDCSwitch/Commands/CommandRouter.cs
index ff61316..e9761c3 100644
--- a/DDCSwitch/Commands/CommandRouter.cs
+++ b/DDCSwitch/Commands/CommandRouter.cs
@@ -68,7 +68,7 @@ private static int InvalidCommand(string command, bool jsonOutput)
else
{
ConsoleOutputFormatter.WriteError($"Unknown command: {command}");
- ConsoleOutputFormatter.WriteInfo("Run DDCSwitch help for usage information.");
+ ConsoleOutputFormatter.WriteInfo("Run ddcswitch help for usage information.");
}
return 1;
diff --git a/chocolatey/ddcswitch.nuspec b/chocolatey/ddcswitch.nuspec
new file mode 100644
index 0000000..83e762a
--- /dev/null
+++ b/chocolatey/ddcswitch.nuspec
@@ -0,0 +1,48 @@
+
+
+
+ ddcswitch
+ __VERSION__
+ https://github.com/markdwags/DDCSwitch
+ markdwags
+ DDCSwitch
+ markdwags
+ https://github.com/markdwags/DDCSwitch
+ https://cdn.jsdelivr.net/gh/markdwags/DDCSwitch@main/icon.png
+ https://github.com/markdwags/DDCSwitch/blob/main/LICENSE
+ false
+ https://github.com/markdwags/DDCSwitch
+ https://github.com/markdwags/DDCSwitch#readme
+ https://github.com/markdwags/DDCSwitch/issues
+ ddcswitch ddc-ci monitor display cli input-switching brightness contrast vcp-features admin
+ Control monitor settings via DDC/CI from the command line
+
+ https://github.com/markdwags/DDCSwitch/blob/main/CHANGELOG.md
+
+
+
+
+
diff --git a/chocolatey/tools/CHECKSUM b/chocolatey/tools/CHECKSUM
new file mode 100644
index 0000000..5f28270
--- /dev/null
+++ b/chocolatey/tools/CHECKSUM
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/chocolatey/tools/VERIFICATION.txt b/chocolatey/tools/VERIFICATION.txt
new file mode 100644
index 0000000..c7fd512
--- /dev/null
+++ b/chocolatey/tools/VERIFICATION.txt
@@ -0,0 +1,22 @@
+VERIFICATION
+
+Verification is intended to assist the Chocolatey moderators and community
+in verifying that this package's contents are trustworthy.
+
+Package can be verified like this:
+
+1. Download the following:
+
+ x64: https://github.com/markdwags/DDCSwitch/releases/download/v__VERSION__/ddcswitch-__VERSION__-win-x64.zip
+
+2. You can use one of the following methods to obtain the SHA256 checksum:
+ - Use powershell function 'Get-FileHash'
+ - Use Chocolatey utility 'checksum.exe'
+
+ checksum64: __CHECKSUM__
+
+The binary is downloaded directly from the official GitHub releases.
+
+Project source: https://github.com/markdwags/ddcswitch
+Releases: https://github.com/markdwags/ddcswitch/releases
+
diff --git a/chocolatey/tools/chocolateyinstall.ps1 b/chocolatey/tools/chocolateyinstall.ps1
new file mode 100644
index 0000000..4ed9379
--- /dev/null
+++ b/chocolatey/tools/chocolateyinstall.ps1
@@ -0,0 +1,28 @@
+$ErrorActionPreference = 'Stop'
+
+$packageName = 'ddcswitch'
+$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
+
+# Version is automatically provided by Chocolatey from the nuspec
+$version = $env:chocolateyPackageVersion
+$url64 = "https://github.com/markdwags/ddcswitch/releases/download/v$version/ddcswitch-$version-win-x64.zip"
+
+# Checksum is stored in a separate file created during package build
+$checksumFile = Join-Path $toolsDir "CHECKSUM"
+$checksum64 = Get-Content $checksumFile -Raw
+$checksum64 = $checksum64.Trim()
+$checksumType64 = 'sha256'
+
+$packageArgs = @{
+ packageName = $packageName
+ unzipLocation = $toolsDir
+ url64bit = $url64
+ checksum64 = $checksum64
+ checksumType64 = $checksumType64
+}
+
+Install-ChocolateyZipPackage @packageArgs
+
+# The ZIP contains the executable at the root
+# Chocolatey will automatically create a shim for ddcswitch.exe
+
diff --git a/chocolatey/tools/chocolateyuninstall.ps1 b/chocolatey/tools/chocolateyuninstall.ps1
new file mode 100644
index 0000000..9abf76a
--- /dev/null
+++ b/chocolatey/tools/chocolateyuninstall.ps1
@@ -0,0 +1,10 @@
+$ErrorActionPreference = 'Stop'
+
+$packageName = 'ddcswitch'
+$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
+
+# Remove the executable (Chocolatey handles shim removal automatically)
+Remove-Item "$toolsDir\ddcswitch.exe" -ErrorAction SilentlyContinue -Force
+
+Write-Host "$packageName has been uninstalled successfully."
+