Skip to content

Conversation

@harisonw
Copy link

@harisonw harisonw commented Jan 30, 2026

Upgrade the project to target .NET Framework 4.8, update NuGet dependencies, and enhance project configuration. Introduce a profile toggle hotkey feature with UI components and state persistence.

Half 'Vibe Coded' as C#/.net is not a stack I've used before - code seems fairly sensible to my uneducated glance though and the feature is working really well from my testing on Nvidia cards

Edit: While it works well, my main use case for this was for Arc Raiders to toggle gamma settings, however it appears that Arc has some kind of control against the Windows GDI32 API vs the Nvidia one which makes it worse. Using the Nvidia control panel it works just fine.

harisonw and others added 8 commits January 30, 2026 19:24
Upgraded project to target .NET Framework 4.8, updated NuGet packages (CommonServiceLocator, Costura.Fody, Fody), and refreshed .gitignore. Enhanced FodyWeavers.xsd with new options for runtime and unmanaged assemblies. Regenerated designer files with updated tooling. Improved project configuration for modern .NET and Visual Studio compatibility.
Co-authored-by: harisonw <87620631+harisonw@users.noreply.github.com>
Co-authored-by: harisonw <87620631+harisonw@users.noreply.github.com>
Co-authored-by: harisonw <87620631+harisonw@users.noreply.github.com>
Co-authored-by: harisonw <87620631+harisonw@users.noreply.github.com>
Co-authored-by: harisonw <87620631+harisonw@users.noreply.github.com>
Clears focus from the hotkey textbox after setting a profile toggle hotkey to improve user experience. Also adds the System namespace to IVibranceProxy.cs for completeness.
Copilot AI review requested due to automatic review settings January 30, 2026 20:02
@harisonw
Copy link
Author

@juv - cant assign to you

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request upgrades the project from .NET Framework 4.0 to 4.8 and introduces a profile toggle hotkey feature that allows users to temporarily enable/disable profile settings via a global hotkey.

Changes:

  • Upgraded target framework from .NET 4.0 to .NET 4.8 and updated NuGet dependencies (Costura.Fody 4.1.0 → 6.0.0, Fody 6.3.0 → 6.9.3, CommonServiceLocator 1.3 → 2.0.7)
  • Added profile toggle hotkey feature with UI controls, state persistence, and integration into both NVIDIA and AMD vibrance proxy implementations
  • Updated project configuration files and auto-generated designer files to reflect the framework upgrade

Reviewed changes

Copilot reviewed 11 out of 15 changed files in this pull request and generated 48 comments.

Show a summary per file
File Description
vibrance.GUI/vibrance.GUI.csproj Updated target framework to v4.8, upgraded NuGet package references, and modified MSBuild tooling version
vibrance.GUI/packages.config Updated package versions to be compatible with .NET 4.8
vibrance.GUI/App.config Changed supported runtime to .NET Framework 4.8
vibrance.GUI/common/VibranceGUI.cs Added profile toggle hotkey registration, WndProc override, hotkey parsing, and toggle logic with P/Invoke declarations
vibrance.GUI/common/VibranceGUI.Designer.cs Added UI controls for profile toggle feature (checkbox, label, textbox)
vibrance.GUI/common/SettingsController.cs Extended settings persistence to include profile toggle configuration (enabled state, hotkey string, toggle state)
vibrance.GUI/common/ISettingsController.cs Updated interface signatures to include profile toggle parameters
vibrance.GUI/common/IVibranceProxy.cs Added interface methods for profile toggle feature management
vibrance.GUI/common/Definitions.cs Added profile toggle state fields to VibranceInfo struct
vibrance.GUI/NVIDIA/NvidiaDynamicVibranceProxy.cs Implemented profile toggle methods and integrated toggle state check into OnWinEventHook
vibrance.GUI/AMD/AmdDynamicVibranceProxy.cs Implemented profile toggle methods and integrated toggle state check into OnWinEventHook
vibrance.GUI/FodyWeavers.xsd Updated schema to reflect Costura.Fody 6.0.0 capabilities
vibrance.GUI/Properties/Settings.Designer.cs Auto-generated file updated for new framework version
vibrance.GUI/Properties/Resources.Designer.cs Auto-generated file updated for new framework version
.gitignore Added .vs/ directory to gitignore (Visual Studio directory)
Files not reviewed (3)
  • vibrance.GUI/Properties/Resources.Designer.cs: Language not supported
  • vibrance.GUI/Properties/Settings.Designer.cs: Language not supported
  • vibrance.GUI/common/VibranceGUI.Designer.cs: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +528 to +564
private bool TryParseHotkey(string hotkey, out uint modifiers, out uint key)
{
modifiers = 0;
key = 0;
if (string.IsNullOrWhiteSpace(hotkey))
{
return false;
}

string[] parts = hotkey.Split(new[] { '+' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string part in parts)
{
string normalized = part.Trim();
if (string.Equals(normalized, "Ctrl", StringComparison.OrdinalIgnoreCase) ||
string.Equals(normalized, "Control", StringComparison.OrdinalIgnoreCase))
{
modifiers |= HotkeyModControl;
continue;
}
if (string.Equals(normalized, "Alt", StringComparison.OrdinalIgnoreCase))
{
modifiers |= HotkeyModAlt;
continue;
}
if (string.Equals(normalized, "Shift", StringComparison.OrdinalIgnoreCase))
{
modifiers |= HotkeyModShift;
continue;
}

if (Enum.TryParse(normalized, true, out Keys parsedKey))
{
key = (uint)parsedKey;
}
}

return key != 0;
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The TryParseHotkey method doesn't properly handle invalid hotkey strings where multiple non-modifier keys appear. If a hotkey string contains multiple keys like "Ctrl+A+B", the method will accept the last valid key ("B") and silently ignore the first one. This could lead to unexpected behavior where the parsed hotkey doesn't match what the user sees in the textbox. Consider validating that only one non-modifier key exists in the string.

Copilot uses AI. Check for mistakes.
}
else
{
_amdAdapter.SetSaturationOnAllDisplays(_vibranceInfo.userVibranceSettingDefault);
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When toggling off the profile, the AMD implementation always calls SetSaturationOnAllDisplays regardless of the affectPrimaryMonitorOnly setting. This is inconsistent with the NVIDIA implementation which respects this setting. The code should check affectPrimaryMonitorOnly and call SetSaturationOnDisplay for the specific screen when that setting is enabled, similar to how the NVIDIA version handles it in lines 414-424 of NvidiaDynamicVibranceProxy.cs.

Suggested change
_amdAdapter.SetSaturationOnAllDisplays(_vibranceInfo.userVibranceSettingDefault);
if (_vibranceInfo.affectPrimaryMonitorOnly && _gameScreen != null && _gameScreen.DeviceName.Equals(screen.DeviceName))
{
_amdAdapter.SetSaturationOnDisplay(_vibranceInfo.userVibranceSettingDefault, _gameScreen.DeviceName);
}
else
{
_amdAdapter.SetSaturationOnAllDisplays(_vibranceInfo.userVibranceSettingDefault);
}

Copilot uses AI. Check for mistakes.
return;
}

_isProfileToggleHotkeyRegistered = RegisterHotKey(this.Handle, ProfileToggleHotkeyId, modifiers, key);
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RegisterHotKey function's return value is stored but never checked for failure. If hotkey registration fails (e.g., because another application is already using that hotkey combination), the user won't receive any feedback. Consider logging a warning or showing a notification when hotkey registration fails.

Suggested change
_isProfileToggleHotkeyRegistered = RegisterHotKey(this.Handle, ProfileToggleHotkeyId, modifiers, key);
bool registrationSucceeded = RegisterHotKey(this.Handle, ProfileToggleHotkeyId, modifiers, key);
_isProfileToggleHotkeyRegistered = registrationSucceeded;
if (!registrationSucceeded)
{
MessageBox.Show(
"Failed to register the profile toggle hotkey. It may already be in use by another application.",
"Hotkey Registration Failed",
MessageBoxButtons.OK,
MessageBoxIcon.Warning);
}

Copilot uses AI. Check for mistakes.
public void SetProfileToggleEnabled(bool profileToggleEnabled)
{
_vibranceInfo.isProfileToggleEnabled = profileToggleEnabled;
_vibranceInfo.isProfileToggleOn = true;
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SetProfileToggleEnabled unconditionally resets isProfileToggleOn to true. This could override the user's saved toggle state when disabling and re-enabling the feature, or when changing settings. The state should only be reset when the feature is being disabled, not when it's being enabled.

Suggested change
_vibranceInfo.isProfileToggleOn = true;
if (!profileToggleEnabled)
{
_vibranceInfo.isProfileToggleOn = true;
}

Copilot uses AI. Check for mistakes.
public void SetProfileToggleEnabled(bool profileToggleEnabled)
{
_vibranceInfo.isProfileToggleEnabled = profileToggleEnabled;
_vibranceInfo.isProfileToggleOn = true;
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SetProfileToggleEnabled unconditionally resets isProfileToggleOn to true. This could override the user's saved toggle state when disabling and re-enabling the feature, or when changing settings. The state should only be reset when the feature is being disabled, not when it's being enabled.

Suggested change
_vibranceInfo.isProfileToggleOn = true;
if (!profileToggleEnabled)
{
_vibranceInfo.isProfileToggleOn = true;
}

Copilot uses AI. Check for mistakes.
_vibranceInfo.displayHandles.ForEach(handle => setDVCLevel(handle, _vibranceInfo.userVibranceSettingDefault));
}

if (_vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == true)
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression 'A == true' can be simplified to 'A'.

Copilot uses AI. Check for mistakes.

//test if color settings change is needed
if (_vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == false &&
if (shouldApplyProfileSettings && _vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == false &&
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression 'A == false' can be simplified to '!A'.

Copilot uses AI. Check for mistakes.
Comment on lines +405 to +406
if (_vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == false &&
DeviceGammaRampHelper.IsGammaRampEqualToWindowsValues(_vibranceInfo, applicationSetting) == false)
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression 'A == false' can be simplified to '!A'.

Suggested change
if (_vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == false &&
DeviceGammaRampHelper.IsGammaRampEqualToWindowsValues(_vibranceInfo, applicationSetting) == false)
if (!_vibranceInfo.neverChangeColorSettings && !_vibranceInfo.isColorSettingApplied &&
!DeviceGammaRampHelper.IsGammaRampEqualToWindowsValues(_vibranceInfo, applicationSetting))

Copilot uses AI. Check for mistakes.
setDVCLevel(_vibranceInfo.defaultHandle, applicationSetting.IngameLevel);
}

if (_vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == false &&
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression 'A == false' can be simplified to '!A'.

Copilot uses AI. Check for mistakes.

//test if color settings change is needed
if (_vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == false &&
if (shouldApplyProfileSettings && _vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == false &&
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The expression 'A == false' can be simplified to '!A'.

Copilot uses AI. Check for mistakes.
@juv
Copy link
Owner

juv commented Feb 1, 2026

Hi @harisonw, thanks for your effort! Can you help me to understand the improvement that comes from the toggle hotkey feature? When do you use this hotkey in-game?

I can recall it was requested a few times in the past but I never really got the point. And I actually hesitated to implement it. One of the main purpose of vibranceGUI v2 is that the in-game/desktop settings should be applied automatically by listening to the Windows event hooks if the focussed application changes.

While it works well, my main use case for this was for Arc Raiders to toggle gamma settings, however it appears that Arc has some kind of control against the Windows GDI32 API vs the Nvidia one which makes it worse. Using the Nvidia control panel it works just fine.

Yes, this is a big downside of how the color settings control is implemented in #140. Using the SetDeviceGammaRamp function is just hacky. This is the primary reason why my branch is still open too. Another reason is that none of the users gave feedback - probably because it took too long to be implemented and people moved on. It's fine. No hard feelings at all.
But even without the feedback of the users, I faced multiple issues when trying this feature with a few friends:

  1. Some applications - just like you mention - override the color settings and lead to unexpected behaviour for the user. They think vibranceGUI will set a certain color setting but at the same time, the application overrides those settings and reverts them. A lot of people had problems with the main functionality of vibranceGUI already, despite them working rather reliably due to direct API access of the GPU. I can see how this color setting feature will create a lot of frustration and a flood of requests on my twitter, reddit, etc. That other applications can override the gamma ramp at any point in time is to be expected as per Microsoft documentation.
  2. Some monitors override the color settings without user feedback
  3. Behaviour for HDR modes are undefined as per documentation
  4. Apparently Windows can override the gamma ramp for "system events" (wake/sleep, UAC popup)
  5. Other color calibration tools like F.lux "compete" again the color settings of vibranceGUI
  6. We found some more issues but I can't recall all of them. Anyways, the problems are diverse: https://www.perplexity.ai/search/are-there-known-occasions-of-m-JIBVIfWDSemLHf2PcE3cfg#0

In conclusion: this implementation w/ SetDeviceGammaRamp sucks.

It's a bummer that NVIDIA does not offer an API for setting the same value that you can change in the Nvidia Control Panel. According to some threads on the NVIDIA developer forums, the Nvidia Control Panel itself also uses the Windows APIs under the hood but I could never verify that myself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants