-
Notifications
You must be signed in to change notification settings - Fork 52
Upgrade to .NET 4.8 and add profile toggle hotkey feature #153
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: feature/add-color-settings
Are you sure you want to change the base?
Upgrade to .NET 4.8 and add profile toggle hotkey feature #153
Conversation
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.
|
@juv - cant assign to you |
There was a problem hiding this 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.
| 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; |
Copilot
AI
Jan 30, 2026
There was a problem hiding this comment.
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.
| } | ||
| else | ||
| { | ||
| _amdAdapter.SetSaturationOnAllDisplays(_vibranceInfo.userVibranceSettingDefault); |
Copilot
AI
Jan 30, 2026
There was a problem hiding this comment.
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.
| _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); | |
| } |
| return; | ||
| } | ||
|
|
||
| _isProfileToggleHotkeyRegistered = RegisterHotKey(this.Handle, ProfileToggleHotkeyId, modifiers, key); |
Copilot
AI
Jan 30, 2026
There was a problem hiding this comment.
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.
| _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); | |
| } |
| public void SetProfileToggleEnabled(bool profileToggleEnabled) | ||
| { | ||
| _vibranceInfo.isProfileToggleEnabled = profileToggleEnabled; | ||
| _vibranceInfo.isProfileToggleOn = true; |
Copilot
AI
Jan 30, 2026
There was a problem hiding this comment.
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.
| _vibranceInfo.isProfileToggleOn = true; | |
| if (!profileToggleEnabled) | |
| { | |
| _vibranceInfo.isProfileToggleOn = true; | |
| } |
| public void SetProfileToggleEnabled(bool profileToggleEnabled) | ||
| { | ||
| _vibranceInfo.isProfileToggleEnabled = profileToggleEnabled; | ||
| _vibranceInfo.isProfileToggleOn = true; |
Copilot
AI
Jan 30, 2026
There was a problem hiding this comment.
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.
| _vibranceInfo.isProfileToggleOn = true; | |
| if (!profileToggleEnabled) | |
| { | |
| _vibranceInfo.isProfileToggleOn = true; | |
| } |
| _vibranceInfo.displayHandles.ForEach(handle => setDVCLevel(handle, _vibranceInfo.userVibranceSettingDefault)); | ||
| } | ||
|
|
||
| if (_vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == true) |
Copilot
AI
Jan 30, 2026
There was a problem hiding this comment.
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'.
|
|
||
| //test if color settings change is needed | ||
| if (_vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == false && | ||
| if (shouldApplyProfileSettings && _vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == false && |
Copilot
AI
Jan 30, 2026
There was a problem hiding this comment.
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'.
| if (_vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == false && | ||
| DeviceGammaRampHelper.IsGammaRampEqualToWindowsValues(_vibranceInfo, applicationSetting) == false) |
Copilot
AI
Jan 30, 2026
There was a problem hiding this comment.
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'.
| if (_vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == false && | |
| DeviceGammaRampHelper.IsGammaRampEqualToWindowsValues(_vibranceInfo, applicationSetting) == false) | |
| if (!_vibranceInfo.neverChangeColorSettings && !_vibranceInfo.isColorSettingApplied && | |
| !DeviceGammaRampHelper.IsGammaRampEqualToWindowsValues(_vibranceInfo, applicationSetting)) |
| setDVCLevel(_vibranceInfo.defaultHandle, applicationSetting.IngameLevel); | ||
| } | ||
|
|
||
| if (_vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == false && |
Copilot
AI
Jan 30, 2026
There was a problem hiding this comment.
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'.
|
|
||
| //test if color settings change is needed | ||
| if (_vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == false && | ||
| if (shouldApplyProfileSettings && _vibranceInfo.neverChangeColorSettings == false && _vibranceInfo.isColorSettingApplied == false && |
Copilot
AI
Jan 30, 2026
There was a problem hiding this comment.
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'.
|
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.
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.
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. |
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.