Toggle screen HDR on or off automatically with this script

Got a fancy nice HDR monitor?

Love that KDE plasma desktop helps utilise HDR?

Tired of manually toggling HDR on or off depending on what you’re doing?

Can’t stand editing a bunch of custom launch options or PRE/POST scripts for each game/application each time the screen setup changes? (i.e. laptop docking stations or KVM switches)

And yeah… having your ICC profile erased each time an HDR title is launched is another annoyance we solve here. (Optional)

Fret no more. Here’s a lil helper-script to automate this. It utilises kscreen-doctor and makes implementing it a lot easier in Steam, Lutris and/or other launchers/wrappers. One place to edit/change instead of the hundreds!

This script can also be used as a simple command in the CLI if one so desires. It only requires the plasma-desktop environment to be active. (Because it uses kscreen-doctor. I know of no other commands that independently can change hdr and wide gamut settings outside of plasma)

How it works:

Usage:
hdr-toggle [enable|disable|toggle|help] [output] [ICC profile]

KDE plasma desktop HDR toggler.
Utilises kscreen-doctor.
To be used with launch wrapper scripts or command lines such as Steam, Lutris and others.

ICC profile is only applied when HDR mode is disabled.
Omit all options to simply toggle HDR on or off depending on the current status. It will then use the internal defaults. These defaults can be changed by editing this script.

Steam:
Add this to the games command line: 'hdr-toggle enable; %command%; hdr-toggle disable'
This will enable HDR, launch the game, and once the game quits, disable HDR.

Lutris:
Right-click and choose 'Configure'. Switch to tab 'System options'.
In the field 'Pre-launch script', add 'hdr-toggle enable'. In the field 'Post-exit script, add 'hdr-toggle disable'

You can also add hdr-toggle as a command in your wrapper scripts. See 'Usage' above.

Tip: Edit /home/sysghost/.bin/hdr-toggle and change the variables at the top to customize the defaults to your setup.
See 'kscreen-doctor -o' to list available outputs and their capabilities.

To disable this script from toggling anything, a global system variable can be used: 'export DISABLE_HDR_TOGGLING=true'.
Unset the variable or set it to false to resume normal operation.

'nuf babbling. Here’s the script. Name it “hdr-toggle” and make it executable. Save it under ~/.bin/, or your preferred user executable path. then run ‘hdr-toggle help’ to see how it works. It depends on kscreen-doctor to properly operate.

#/bin/bash
DEFAULT_OUTPUT="DP-1"
DEFAULT_ICC_PROFILE=""



#################################################################
# END OF USER VARIABLES. Do not edit variables below this line. #
#################################################################
TOGGLE="${1:-toggle}"
OUTPUT="${2:-$DEFAULT_OUTPUT}"

# Variable juggling to make sure the default ICC profile is only applied to the default output in cases where multiple monitors are present.
declare ICCPROF_${DEFAULT_OUTPUT//-/_}="${DEFAULT_ICC_PROFILE}"
ICCPROF=ICCPROF_${OUTPUT//-/_}

# Check on a few things before actually toggling HDR.
if [[ ! $DESKTOP_SESSION == "plasma" ]] || [[ $DISABLE_HDR_TOGGLING == "true" ]]; then echo "Plasma desktop not active or DISABLE_HDR_TOGGLING has been set to true. Bailing."; exit 0; fi
OUTPUT_HDR_STATE=$( kscreen-doctor -o | grep "$OUTPUT" -A16 | grep "HDR" | cut -c 21- ) # Read the current HDR state of the chosen output and strip off some ansi escape stuff.
if [[ $OUTPUT_HDR_STATE == "incapable" ]]; then echo "Output $OUTPUT cannot toggle HDR. Quitting..."; exit 1; fi
if [[ $OUTPUT_HDR_STATE == "" ]]; then echo "Output $OUTPUT not found. See 'kscreen-doctor -o'. Quitting..."; exit 1; fi

# Due to a bug with kscreen-doctor, we need a delayed screen update. 
# 'kscreen-doctor' waits indefinitely for the screen to update. Any kind of redraw. It won't continue until that has happened.
# For this to work something needs to be launched before 'kscreen-doctor', but do something on screen after 'kscreen-doctor' has been launched.
# 'sleep' and 'notify-send' do this job perfectly fine, while also notifying the user. Hitting two birds with one stone.

function hdr_disable() {
  echo "${OUTPUT}: Toggling HDR off"
  if [[ -v 3 ]]; then 
    ICCPROF_=${3}
  else
    ICCPROF_=${!ICCPROF}
  fi
  sleep .5 && notify-send "${OUTPUT}: HDR disabled" & # kscreen-doctor wait-bug circumventer.
  nohup kscreen-doctor output.$OUTPUT.hdr.disable output.$OUTPUT.wcg.disable output.$OUTPUT.iccprofile."${ICCPROF_}" >/dev/null 2>&1 & 
}

function hdr_enable() {
  echo "${OUTPUT}: Toggling HDR on"
  sleep .5 && notify-send "${OUTPUT}: HDR enabled" &  # kscreen-doctor wait-bug circumventer.
  nohup kscreen-doctor output.$OUTPUT.hdr.enable output.$OUTPUT.wcg.enable >/dev/null 2>&1 & 
}

case $TOGGLE in
  toggle)
    case $OUTPUT_HDR_STATE in
      enabled)
        hdr_disable
        exit 0
        ;;
      disabled)
        hdr_enable
        exit 0
        ;;
      *)
        echo "OUTPUT_HDR_STATE: $OUTPUT_HDR_STATE - Unexpected value. Bailing... "
        exit 2
        ;;
    esac
    ;;
  enable)
    hdr_enable
    exit 0
    ;;
  disable)
    hdr_disable
    exit 0
    ;;
  help|h|-h|--help)
    echo -e "Usage:\n$(basename $0) [enable|disable|toggle|help] [output] [ICC profile]\n\nKDE plasma desktop HDR toggler.\nUtilises kscreen-doctor.\nTo be used with launch wrapper scripts or command lines such as steam, lutris and others.\n\nICC profile is only applied when HDR mode is disabled.\nOmit all options to simply toggle HDR on or off depending on the current status. It will then use the internal defaults. These defaults can be changed by editing this script.\n\nSteam:\nAdd this to the games command line: 'hdr-toggle enable; %command%; hdr-toggle disable'\nThis will enable HDR, launch the game, and once the game quits, disable HDR.\n\nLutris:\nRight-click and choose 'Configure'. Switch to tab 'System options'.\nIn field 'Pre-launch script', add 'hdr-toggle enable'. In the field 'Post-exit script, add 'hdr-toggle disable'\n\nYou can also add $(basename $0) as a command in your wrapper-scripts. See 'Usage' above.\n\nTip: Edit $0 and change the variables at the top to customize the defaults to your setup.\nSee 'kscreen-doctor -o' to list available outputs and their capabilities.\n\nTo disable this script from toggling anything, a global system variable can be used: 'export DISABLE_HDR_TOGGLING=true'.\nUset the variable or set it to false to resume normal operation."
    ;;
  *)
    echo "Unknown command: $1."
    exit 2
    ;;
esac

exit 0

Constructive criticism, suggestions and additions are welcome.
I’m no expert in these things so if you have a better way to do this I’m all ears!

7 Likes

Thank you very much for this script, it is working wonderfully!

I’m using it for a Steam game. Since I also have to pass some additional arguments, while I’m fairly new to all of this, I had to tinker a bit to make it work as expected. At first the game didn’t recognise the additional arguments I specified. Then I did some trial and error and learned about argument order and the meaning of the semicolon. My final (working) start option looks like this:

hdr-toggle enable; WINEDLLOVERRIDES="dsound,dxgi=n,b" PROTON_ENABLE_WAYLAND=1 PROTON_ENABLE_HDR=1 %command%; hdr-toggle disable

One of the latest KDE Plasma updates broke this script for me under CachyOS. If you find yourself with it no longer toggling HDR, try this slightly modified version instead:

#/bin/bash
DEFAULT_OUTPUT="DP-1"
DEFAULT_ICC_PROFILE=""



#################################################################
# END OF USER VARIABLES. Do not edit variables below this line. #
#################################################################
TOGGLE="${1:-toggle}"
OUTPUT="${2:-$DEFAULT_OUTPUT}"

# Variable juggling to make sure the default ICC profile is only applied to the default output in cases where multiple monitors are present.
declare ICCPROF_${DEFAULT_OUTPUT//-/_}="${DEFAULT_ICC_PROFILE}"
ICCPROF=ICCPROF_${OUTPUT//-/_}

# Check on a few things before actually toggling HDR.
if [[ ! $DESKTOP_SESSION == "/usr/share/wayland-sessions/plasma.desktop" ]] || [[ $DISABLE_HDR_TOGGLING == "true" ]]; then echo "Plasma desktop not active or DISABLE_HDR_TOGGLING has been set to true. Bailing."; exit 0; fi
OUTPUT_HDR_STATE=$( kscreen-doctor -o | grep "$OUTPUT" -A16 | grep "HDR" | cut -c 21- ) # Read the current HDR state of the chosen output and strip off some ansi escape stuff.
if [[ $OUTPUT_HDR_STATE == "incapable" ]]; then echo "Output $OUTPUT cannot toggle HDR. Quitting..."; exit 1; fi
if [[ $OUTPUT_HDR_STATE == "" ]]; then echo "Output $OUTPUT not found. See 'kscreen-doctor -o'. Quitting..."; exit 1; fi

# Due to a bug with kscreen-doctor, we need a delayed screen update.
# 'kscreen-doctor' waits indefinitely for the screen to update. Any kind of redraw. It won't continue until that has happened.
# For this to work something needs to be launched before 'kscreen-doctor', but do something on screen after 'kscreen-doctor' has been launched.
# 'sleep' and 'notify-send' do this job perfectly fine, while also notifying the user. Hitting two birds with one stone.

function hdr_disable() {
  echo "${OUTPUT}: Toggling HDR off"
  if [[ -v 3 ]]; then
    ICCPROF_=${3}
  else
    ICCPROF_=${!ICCPROF}
  fi
  sleep .5 && notify-send "${OUTPUT}: HDR disabled" & # kscreen-doctor wait-bug circumventer.
  nohup kscreen-doctor output.$OUTPUT.hdr.disable output.$OUTPUT.wcg.disable output.$OUTPUT.iccprofile."${ICCPROF_}" >/dev/null 2>&1 &
}

function hdr_enable() {
  echo "${OUTPUT}: Toggling HDR on"
  sleep .5 && notify-send "${OUTPUT}: HDR enabled" &  # kscreen-doctor wait-bug circumventer.
  nohup kscreen-doctor output.$OUTPUT.hdr.enable output.$OUTPUT.wcg.enable >/dev/null 2>&1 &
}

case $TOGGLE in
  toggle)
    case $OUTPUT_HDR_STATE in
      enabled)
        hdr_disable
        exit 0
        ;;
      disabled)
        hdr_enable
        exit 0
        ;;
      *)
        echo "OUTPUT_HDR_STATE: $OUTPUT_HDR_STATE - Unexpected value. Bailing... "
        exit 2
        ;;
    esac
    ;;
  enable)
    hdr_enable
    exit 0
    ;;
  disable)
    hdr_disable
    exit 0
    ;;
  help|h|-h|--help)
    echo -e "Usage:\n$(basename $0) [enable|disable|toggle|help] [output] [ICC profile]\n\nKDE plasma desktop HDR toggler.\nUtilises kscreen-doctor.\nTo be used with launch wrapper scripts or command lines such as steam, lutris and others.\n\nICC profile is only applied when HDR mode is disabled.\nOmit all options to simply toggle HDR on or off depending on the current status. It will then use the internal defaults. These defaults can be changed by editing this script.\n\nSteam:\nAdd this to the games command line: 'hdr-toggle enable; %command%; hdr-toggle disable'\nThis will enable HDR, launch the game, and once the game quits, disable HDR.\n\nLutris:\nRight-click and choose 'Configure'. Switch to tab 'System options'.\nIn field 'Pre-launch script', add 'hdr-toggle enable'. In the field 'Post-exit script, add 'hdr-toggle disable'\n\nYou can also add $(basename $0) as a command in your wrapper-scripts. See 'Usage' above.\n\nTip: Edit $0 and change the variables at the top to customize the defaults to your setup.\nSee 'kscreen-doctor -o' to list available outputs and their capabilities.\n\nTo disable this script from toggling anything, a global system variable can be used: 'export DISABLE_HDR_TOGGLING=true'.\nUset the variable or set it to false to resume normal operation."
    ;;
  *)
    echo "Unknown command: $1."
    exit 2
    ;;
esac

exit 0

Hi,

thank you very much for the script. This was exactly what I was looking for. However I wasn’t satisfied how to run it and the script could be improved. So I rewrote it more or less and uploaded it under GPLv2 or later here:

codeberg org/cryptomilk/hdr-toggle
(I’m not allowed to create links)

@SysGhost I hope you’re fine with the license.

Happy gaming!

Great script, big thanks for putting it together. Another use case you might accommodate is running it manually as a one-off switch that just detects whether HDR is currently on, then flips it on/off accordingly and closes. Maybe when run with a “–manual” flag or something. Its nice having the option to switch on demand, especially for mixed video. A lot of us have muscle memory from manually toggling HDR in Windows, so I go to KDE Settings > Keyboard > Shortcuts > Add New Command and set it to run with the same Meta+Alt+B combo. My quick n dirty solution was to point it to a second copy of the script where I replaced the “Main logic” section with:

HDR_STATUS=$(kscreen-doctor --outputs | \
    grep -i "$OUTPUT" -A 20 | \
    grep -i 'hdr:' | \
    sed 's/\x1b\[[0-9;]*m//g' | \
    awk -F: '{gsub(/^[ \t]+|[ \t]+$/, "", $2); print tolower($2)}')

if [[ "$HDR_STATUS" == "enabled" ]]; then
  hdr_disable
  exit 0
elif [[ "$HDR_STATUS" == "disabled" ]]; then
  hdr_enable
  exit 0
else
  exit 1
fi

A minor snag is KDE Settings gripes if you try to run a script without an extension (like .sh). Also heads up that whenever I edited a launch command in Settings it would silently break, so if you need to change a shortcut I’d just delete it and Add New again.

I’ve implemented hat at codeberg. org/cryptomilk/hdr-tooggle

I’m just not allowed to insert links into posts …

Usage:
  hdr-toggle [OPTIONS] [COMMAND [ARGS...]]

KDE Plasma desktop HDR toggler. Uses kscreen-doctor to enable or disable HDR
on the specified display output.

Wrapper mode — HDR is enabled before the command runs and disabled when it
exits (including on Ctrl+C or SIGTERM):
  hdr-toggle [OPTIONS] COMMAND [ARGS...]

Examples:
  WINEDLLOVERRIDES="dsound,dxgi=n,b" PROTON_ENABLE_WAYLAND=1 PROTON_ENABLE_HDR=1 hdr-toggle %command%
  PROTON_ENABLE_WAYLAND=1 PROTON_ENABLE_HDR=1 hdr-toggle taskset 0x00ff00ff %command%

Standalone mode:
  hdr-toggle --enable             Enable HDR and exit
  hdr-toggle --disable            Disable HDR and exit

Options:
  --output <name>   Target a specific output (default: auto-detect HDR-capable output)
  --help, -h        Show this help text

Tips:
  - Create ~/.config/hdr-toggle.conf to set defaults and export environment
    variables inherited by the wrapped command, e.g.:
      DEFAULT_OUTPUT="HDMI-1"        # optional; auto-detected if omitted
      DEFAULT_ICC_PROFILE="/path/to/profile.icc"
      export PROTON_ENABLE_WAYLAND=1
      export PROTON_ENABLE_HDR=1
      export WINEDLLOVERRIDES="dsound,dxgi=n,b"
    This lets the Steam launch option be simply: hdr-toggle %command%
    Run 'kscreen-doctor --outputs' to list available outputs.
  - The ICC profile (if set) is re-applied when HDR is disabled, restoring
    accurate colour management for desktop use.
  - Requires a KDE Wayland session (XDG_SESSION_TYPE=wayland and
    XDG_CURRENT_DESKTOP=KDE).
  - Set DISABLE_HDR_TOGGLING=true in your environment to suppress all toggling
    without removing hdr-toggle from your launch options.
  - Set HDR_TOGGLE_DEBUG=1 to write a debug log to /tmp/hdr-toggle.log.

What flag would I use if I wanted to call your script with a single shortcut that can switch HDR on and off? In the OG they use -toggle, but I’m not sure how I’d do it manually in yours without making separate shortcuts to --enable and --disable.

Ah, somehow I didn’t get it. I’ve added a --switch option which should do what you want.

Awesome - works perfectly! Might also be worth mentioning in your guide that it’s really easy to set up a hotkey combo. In the Shortcuts tab of KDE settings just click +Add New > Command_or_Script, and in the Command field enter:
./hdr-toggle --switch
Since the script should already be in $PATH that’s all it needs. After the rule is generated there’s a button right under it to capture any key combo you want.

My script has a “toggle” command as well.

hdr-toggle --toggle will change the current state, no matter what state it is in. I have that set to a shortcut key so I can turn HDR on or off with a simple press of a button.

But kscreen-doctor has become a bit of a mess as of late.
It is hard to keep track of what works and what doesn’t. It’s still quite buggy and I’ve encountered a bunch of new bugs in kscreen-doctor, which I’m currently dealing with.