5 Commits

Author SHA1 Message Date
89c922c265 v1.1.0 - Fix shortcut icons and update publisher
All checks were successful
Build / build (push) Successful in 9h0m7s
Build and Release / build (push) Successful in 8h0m12s
- Add explicit icon to all shortcuts (Start menu, desktop, startup)
- Include ico file in installer for shortcut icons
- Set installer icon
- Update publisher to MarketAlly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 02:13:30 -05:00
38f5aa325c Add portable zip to release workflow
All checks were successful
Build / build (push) Successful in 9h0m8s
Build and Release / build (push) Successful in 9h0m13s
- Create MonitorControl-Portable-{version}.zip alongside installer
- Updated release notes to mention both options
- Portable version useful for users with antivirus false positives

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 02:06:32 -05:00
ac53fbf80e Fix missing input source in dropdown and update metadata
All checks were successful
Build / build (push) Successful in 9h0m28s
Build and Release / build (push) Successful in 8h0m32s
- Add current input to options if monitor doesn't report it in possible values
- Never hide the currently active input port in config filtering
- Make GetInputSourceName public for reuse
- Update company to MarketAlly, author to David H. Friedel Jr
- Add application icon to exe

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-10 02:03:11 -05:00
139be6f779 Add debug logging for dropdown selection and power unsupported state
All checks were successful
Build / build (push) Successful in 8h0m11s
Build and Release / build (push) Successful in 8h0m12s
- Added detailed logging in CreateInputRow to help diagnose dropdown
  selection issues after config label changes
- Power button now shows "Power Unsupported" and is disabled for
  monitors that don't support DDC/CI power control

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 12:24:55 -05:00
dfec8c07b5 Show 'Unsupported' in config dialog for monitors without input options
All checks were successful
Build / build (push) Successful in 8h0m10s
Build and Release / build (push) Successful in 9h0m14s
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 12:14:28 -05:00
7 changed files with 101 additions and 34 deletions

View File

@@ -82,6 +82,17 @@ jobs:
echo Installer directory contents:
dir installer
- name: Create Portable Zip
shell: powershell
run: |
$version = "${{ gitea.ref_name }}".TrimStart("v")
$buildDir = "DellMonitorControl\bin\Release\net9.0-windows"
$zipName = "MonitorControl-Portable-$version.zip"
Write-Host "Creating portable zip: $zipName"
Compress-Archive -Path "$buildDir\*" -DestinationPath $zipName -Force
Write-Host "Zip created successfully"
- name: Create Release
shell: powershell
env:
@@ -104,7 +115,7 @@ jobs:
$body = @{
tag_name = $tag
name = "Monitor Control $tag"
body = "## Monitor Control $tag`n`n### Installation`nDownload and run the installer.`n`n### Features`n- System tray monitor control`n- Brightness/Contrast adjustment`n- Input source switching`n- Quick-switch toolbar`n`n### Requirements`n- Windows 10/11`n- .NET 9.0 Runtime"
body = "## Monitor Control $tag`n`n### Installation`n- **Installer**: Download and run the setup exe`n- **Portable**: Download the zip, extract anywhere, and run DellMonitorControl.exe`n`n### Features`n- System tray monitor control`n- Brightness/Contrast adjustment`n- Input source switching`n- Quick-switch toolbar`n`n### Requirements`n- Windows 10/11`n- .NET 9.0 Runtime"
} | ConvertTo-Json
$release = Invoke-RestMethod -Uri "$baseUrl/repos/$repo/releases" -Method Post -Headers @{"Authorization"="token $env:GITEA_TOKEN"; "Content-Type"="application/json"} -Body $body
@@ -113,10 +124,15 @@ jobs:
}
# Upload installer
$filePath = "C:\build\app\installer\MonitorControl-Setup-$version.exe"
$fileName = "MonitorControl-Setup-$version.exe"
Write-Host "Uploading $fileName..."
$installerPath = "C:\build\app\installer\MonitorControl-Setup-$version.exe"
$installerName = "MonitorControl-Setup-$version.exe"
Write-Host "Uploading $installerName..."
curl.exe -X POST -H "Authorization: token $env:GITEA_TOKEN" -F "attachment=@$installerPath" "$baseUrl/repos/$repo/releases/$releaseId/assets?name=$installerName"
curl.exe -X POST -H "Authorization: token $env:GITEA_TOKEN" -F "attachment=@$filePath" "$baseUrl/repos/$repo/releases/$releaseId/assets?name=$fileName"
# Upload portable zip
$zipPath = "MonitorControl-Portable-$version.zip"
$zipName = "MonitorControl-Portable-$version.zip"
Write-Host "Uploading $zipName..."
curl.exe -X POST -H "Authorization: token $env:GITEA_TOKEN" -F "attachment=@$zipPath" "$baseUrl/repos/$repo/releases/$releaseId/assets?name=$zipName"
Write-Host "Installer uploaded successfully"
Write-Host "All assets uploaded successfully"

View File

@@ -34,6 +34,22 @@ public partial class ConfigWindow : Window
spPorts.Children.Clear();
_portRows.Clear();
// Show unsupported message if no ports available
if (_availablePorts == null || _availablePorts.Count == 0)
{
spPorts.Children.Add(new TextBlock
{
Text = "Unsupported",
Foreground = Brushes.Gray,
FontSize = 16,
FontStyle = FontStyles.Italic,
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Margin = new Thickness(0, 40, 0, 40)
});
return;
}
foreach (var port in _availablePorts)
{
var existingPortConfig = config.Ports.FirstOrDefault(p => p.VcpValue == port.Value);

View File

@@ -8,14 +8,16 @@
<RootNamespace>DellMonitorControl</RootNamespace>
<Product>ControlMyMonitorManagement</Product>
<UseWPF>true</UseWPF>
<Company>Dang</Company>
<Copyright>Copyright © DangWang $([System.DateTime]::Now.ToString(yyyy))</Copyright>
<Company>MarketAlly</Company>
<Authors>David H. Friedel Jr</Authors>
<Copyright>Copyright © MarketAlly $([System.DateTime]::Now.ToString(yyyy))</Copyright>
<ApplicationIcon>MonitorIcon.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup>
<Version>1.0.15</Version>
<AssemblyVersion>1.0.15.0</AssemblyVersion>
<FileVersion>1.0.15.0</FileVersion>
<Version>1.1.0</Version>
<AssemblyVersion>1.1.0.0</AssemblyVersion>
<FileVersion>1.1.0.0</FileVersion>
</PropertyGroup>
<ItemGroup>

View File

@@ -181,6 +181,15 @@ public partial class MainWindow : Window
var inputOptions = await CMMCommand.GetInputSourceOptions(m.SerialNumber);
DebugLogger.Log($" Input options count: {inputOptions.Count}");
// Some monitors don't report current input in their possible values list
// Add it if missing so user can see what's currently selected
if (inputSource.HasValue && !inputOptions.Any(o => o.Value == inputSource.Value))
{
var currentInputName = CMMCommand.GetInputSourceName(inputSource.Value);
inputOptions.Insert(0, new InputSourceOption(inputSource.Value, currentInputName));
DebugLogger.Log($" Added missing current input: {inputSource.Value} ({currentInputName})");
}
DebugLogger.Log($" Getting power status...");
var powerStatus = await CMMCommand.GetMonPowerStatus(m.SerialNumber) ?? "Unknown";
DebugLogger.Log($" Power status: {powerStatus}");
@@ -188,7 +197,8 @@ public partial class MainWindow : Window
_loadedMonitors.Add((m, inputOptions));
// Apply config to filter hidden ports and use custom labels
var filteredOptions = MonitorConfigManager.ApplyConfigToOptions(m.SerialNumber, inputOptions);
// Pass currentInput so we never hide the currently active port
var filteredOptions = MonitorConfigManager.ApplyConfigToOptions(m.SerialNumber, inputOptions, inputSource);
DebugLogger.Log($" Filtered options count: {filteredOptions.Count}");
// Monitor name header with Config button
@@ -371,6 +381,10 @@ public partial class MainWindow : Window
private StackPanel CreateInputRow(int? currentInput, List<InputSourceOption> options, string serialNumber)
{
DebugLogger.Log($" CreateInputRow: currentInput={currentInput}, optionCount={options.Count}");
foreach (var opt in options)
DebugLogger.Log($" Option: Value={opt.Value}, Name={opt.Name}");
var row = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 3, 0, 3) };
row.Children.Add(new TextBlock { Text = "Input", Foreground = Brushes.LightGray, Width = 70, FontSize = 12, VerticalAlignment = VerticalAlignment.Center });
@@ -390,9 +404,14 @@ public partial class MainWindow : Window
if (currentInput.HasValue)
{
var index = options.FindIndex(o => o.Value == currentInput.Value);
DebugLogger.Log($" Selection: Looking for Value={currentInput.Value}, found at index={index}");
if (index >= 0)
combo.SelectedIndex = index;
}
else
{
DebugLogger.Log($" Selection: currentInput is null, no selection");
}
// Add event handler AFTER setting the initial selection
combo.SelectionChanged += async (s, e) =>
@@ -410,26 +429,33 @@ public partial class MainWindow : Window
var row = new StackPanel { Orientation = Orientation.Horizontal, Margin = new Thickness(0, 3, 0, 3) };
row.Children.Add(new TextBlock { Text = "Power", Foreground = Brushes.LightGray, Width = 70, FontSize = 12, VerticalAlignment = VerticalAlignment.Center });
var isUnsupported = string.IsNullOrEmpty(status) || status == "Unknown";
var btn = new Button
{
Content = status,
Content = isUnsupported ? "Power Unsupported" : status,
Width = 170,
Tag = serialNumber,
Style = (Style)FindResource("DarkButton")
Style = (Style)FindResource("DarkButton"),
IsEnabled = !isUnsupported
};
btn.Click += async (s, e) =>
if (!isUnsupported)
{
if (s is Button b && b.Tag is string sn)
btn.Click += async (s, e) =>
{
var current = await CMMCommand.GetMonPowerStatus(sn);
if (current == "Sleep" || current == "PowerOff")
await CMMCommand.PowerOn(sn);
else
await CMMCommand.Sleep(sn);
await Task.Delay(1000);
b.Content = await CMMCommand.GetMonPowerStatus(sn);
}
};
if (s is Button b && b.Tag is string sn)
{
var current = await CMMCommand.GetMonPowerStatus(sn);
if (current == "Sleep" || current == "PowerOff")
await CMMCommand.PowerOn(sn);
else
await CMMCommand.Sleep(sn);
await Task.Delay(1000);
b.Content = await CMMCommand.GetMonPowerStatus(sn);
}
};
}
row.Children.Add(btn);
return row;

View File

@@ -1,7 +1,8 @@
#define MyAppName "Monitor Control"
#define MyAppVersion "1.0.0"
#define MyAppPublisher "Dang"
#define MyAppVersion "1.1.0"
#define MyAppPublisher "MarketAlly"
#define MyAppExeName "DellMonitorControl.exe"
#define MyAppIcon "MonitorIcon.ico"
#define SourcePath "bin\Release\net9.0-windows"
[Setup]
@@ -19,6 +20,7 @@ Compression=lzma
SolidCompression=yes
WizardStyle=modern
PrivilegesRequired=admin
SetupIconFile={#MyAppIcon}
UninstallDisplayIcon={app}\{#MyAppExeName}
[Languages]
@@ -30,12 +32,13 @@ Name: "startupicon"; Description: "Start with Windows"; GroupDescription: "Start
[Files]
Source: "bin\Release\net9.0-windows\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "{#MyAppIcon}"; DestDir: "{app}"; Flags: ignoreversion
[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\{#MyAppIcon}"
Name: "{group}\Uninstall {#MyAppName}"; Filename: "{uninstallexe}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
Name: "{userstartup}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: startupicon
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\{#MyAppIcon}"; Tasks: desktopicon
Name: "{userstartup}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; IconFilename: "{app}\{#MyAppIcon}"; Tasks: startupicon
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent

View File

@@ -86,7 +86,8 @@ public static class MonitorConfigManager
public static List<InputSourceOption> ApplyConfigToOptions(
string serialNumber,
List<InputSourceOption> options)
List<InputSourceOption> options,
int? currentInput = null)
{
var monitorConfig = GetMonitorConfig(serialNumber);
@@ -101,8 +102,11 @@ public static class MonitorConfigManager
if (portConfig != null)
{
// Respect hidden setting - don't show hidden ports
if (portConfig.IsHidden)
// Never hide the currently active input - user needs to see what's selected
bool isCurrentInput = currentInput.HasValue && option.Value == currentInput.Value;
// Respect hidden setting - don't show hidden ports (unless it's the current input)
if (portConfig.IsHidden && !isCurrentInput)
continue;
if (!string.IsNullOrWhiteSpace(portConfig.CustomLabel))

View File

@@ -130,7 +130,7 @@ public static class CMMCommand
return options;
}
private static string GetInputSourceName(int vcpValue)
public static string GetInputSourceName(int vcpValue)
{
return vcpValue switch
{