ddcutil Wrapper

I wrote a small wrapper over ddcutil to be able to use the brightness up/down keys to change the currently focused monitor brightness (even with external ones). This way you can manage them independently.

This wrapper is very unstable and hacky, the worst of the worst. You have been warned.

If you port all this into a single service without intermediate files (as it should be), please ping me. It should be a C++ (or other) program using swayipc lib that subscribes to the event and places a hook that updates an internal state struct with the screens globally. Then it can create… sockets? So users can interact with it, increasing and decreasing brightness. Something like bulb service and then bulb increase and bulb decrease, maybe with the ability of tweaking the amount.

Put this in /usr/bin/bulb

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/bin/bash

# Get the model of the currently focused display
outputs=$(swaymsg -t get_outputs)
model=$(echo ${outputs} | jq -r '.[] | select(.focused) | .model')
name=$(echo ${outputs} | jq -r '.[] | select(.focused) | .name')

echo "${model}"
exec 100>/tmp/ddcutil.lock || (echo "failed to lock"; exit -1)
flock -x -w 10 100

if [[ "$name" == "eDP"* ]];then
  if [[ "$1" == "+" ]]; then
      echo "Increase"
      light -T 1.1
  else
      echo "Decrease"
      light -T 0.9
  fi
  flock -u 100
  exit 0
fi

# Cached as this is slow
i2c_bus=$(awk -v model="$model" '
    /Invalid display/ {skip=1} 
    /Display/ {skip=0} 
    /Model:/ {if (!skip) current_model=$2}
    /I2C bus:/ {if (!skip) i2c_bus=$3}
    current_model == model {print i2c_bus; exit}
' ~/.cache/ddcutil-script/state | cut -d '-' -f 2)

# Output the result
if [[ -n "$i2c_bus" ]]; then
  if [[ "$1" == "+" ]]; then
    echo "Increase ${i2_cbus}"
    ddcutil setvcp -b ${i2c_bus} 10 + 5
  else
    echo "Decrease ${i2_cbus}"
    ddcutil setvcp -b ${i2c_bus} 10 - 5
  fi
   echo "${i2_cbus}"
fi
  echo "${i2_cbus}"

flock -u 100

Now, we use ~/.cache/ddcutil-script/state to cache the ddcutil detect command, as its very slow and should only be run once per monitor state change.

Create the following systemd service in ~/.config/systemd/user/ddcutil-watcher.service:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
[Unit]
Description=ddcutil watcher cache
Wants=graphical.target
Required=graphical.target

[Service]
Type=simple
ExecStart=%h/.local/bin/ddcutil-watcher
Restart=always

[Install]
WantedBy=default.target multi-user.target

Then create ~/.local/bin/ddcutil-watcher:

1
2
3
4
5
6
mkdir -p ~/.cache/ddcutil-script
ddcutil detect 2>/dev/null > ~/.cache/ddcutil-script/state
swaymsg -t subscribe '["output"]' | while read -r event; do
    echo "Screen state changed, update ic2bus..."
    ddcutil detect 2>/dev/null > ~/.cache/ddcutil-script/state
done

Now in your sway config, you can put:

1
2
bindsym --locked XF86MonBrightnessUp exec bulb +
bindsym --locked XF86MonBrightnessDown exec bulb -