Important
This repository is a proof of concept that will be improved over time, e.g. proper CLI and distro packaging are WIP. Contributions are welcome, as I'm not primarily a C/C++ developer 🙂
NVIDIA GPUs have a nice feature called Digital Vibrance that increases the colors saturation of the display. The option is readily available on nvidia-settings in Linux, but is too coupled with libxnvctrl
. Therefore, it's "exclusive" to the X11 display server and unavailable on Wayland; but I paid for them pixels to glow :^)
An interesting observation is that the setting persists after modifying it on X11 and then switching to Wayland. I theorized [1] [2] it was possible to call some shared library or interface to configure it directly in their driver, independently of the display server. And indeed, it is possible!
This repository uses nvidia-modeset
and nvkms
headers found at NVIDIA/open-gpu-kernel-modules to make ioctl calls in the /dev/nvidia-modeset
device for configuring display attributes. These headers are synced with the proprietary releases, should work fine if you're on any of nvidia-dkms
, nvidia-open
or nvidia
.
Note: A future (and intended) way to will be through NVML, as evident by some nvidia-settings comments.
Grab the latest Prebuilt Release, download from your Package Manager or Build it Yourself to get the binary. Remember to run chmod +x nvibrant*
to mark the file as executable!
Note: You might need to set nvidia_drm.modeset=1
kernel parameter, but I think it's enabled by default on recent drivers.
Inputs: Vibrance Levels are numbers from -1024
to 1023
that determines the intensity of the effect. Zero being the default for all displays at boot, -1024
grayscale, and 1023
max saturation (200%).
The values are passed as arguments to nvibrant
, and the order must match the physical outputs of the output ports in your GPU (not the index of the video server). For example, I have two monitors on DisplayPort and HDMI in an RTX 3060, and want to set the vibrance to 512
and 1023
respectively:
$ ./nvibrant 512 1023
Driver version: (570.133.07)
GPU 0:
• (0, HDMI) • Set Vibrance ( 512) • Success
• (1, DP ) • Set Vibrance ( 1023) • Success
• (2, DP ) • Set Vibrance ( 0) • None
• (3, DP ) • Set Vibrance ( 0) • None
• (4, DP ) • Set Vibrance ( 0) • None
• (5, DP ) • Set Vibrance ( 0) • None
• (6, DP ) • Set Vibrance ( 0) • None
If a value is not passed for the Nth physical output, nvibrant will default to zero. When no argument is passed, it will effectively clear the vibrance for all outputs. None
means the output is disconnected.
$ ./nvibrant 0 0 0 1023
Driver version: (570.133.07)
GPU 0:
• (0, HDMI) • Set Vibrance ( 0) • None
• (1, DP ) • Set Vibrance ( 0) • None
• (2, DP ) • Set Vibrance ( 0) • None
• (3, DP ) • Set Vibrance ( 1023) • Success
• (4, DP ) • Set Vibrance ( 0) • None
If you have multiple displays on multiple GPUs, it should work too:
$ ./nvibrant 0 100 512 1023
Driver version: (570.133.07)
GPU 0:
• (0, HDMI) • Set Vibrance ( 0) • Success
• (1, DP ) • Set Vibrance ( 100) • Success
GPU 1:
• (0, HDMI) • Set Vibrance ( 512) • Success
• (1, DP ) • Set Vibrance ( 1023) • Success
-
If you get a "Driver version mismatch" or
ioctl
errors, maybe try rebooting (if you haven't) since the last driver update. Otherwise, you can force the version withNVIDIA_DRIVER_VERSION=x.y.z
. It must match what/dev/nvidia-modeset
expects and is currently loaded in ther kernel. -
It's possible that nVibrant may fail on future or older drivers due to differences between the internal structs and enums in the latest
nvkms
headers. Please report any issues you encounter!
❤️ Consider supporting my work, this took 16 hours to figure out and implement :)
Pre-requisites: Have git, uv and GCC Compiler installed, then run:
git clone https://github.com/Tremeschin/nVibrant && cd nVibrant
git submodule update --init --recursive
uv run nvibrant-build
You should have the executable located at release/nvibrant*
Integrating this work directly in libvibrant would be the ideal solution, although matching the nvidia driver version could be annoying for a generalized solution. Feel free to base off this code for an upstream solution and PR, in the meantime, here's some local improvements that could be made:
- Make an actual CLI interface with
--help
,--version
, etc. - Package the binary in many linux distros (help needed!)
- Save the current values to restore it later.
- I am probably not doing safe-C code or mallocs right :^)
Contributions are welcome if you are more C/C++ savy than me! 🙂