Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
106 changes: 72 additions & 34 deletions bin/omarchy-brightness-display
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

no_osd=0
monitor=""
drm_path="${OMARCHY_DRM_PATH:-/sys/class/drm}"

# omarchy-brightness-display-ddc's "this connector answers no DDC display",
# as opposed to a transient read failure worth retrying.
no_ddc_display=3

while (( $# > 0 )); do
case "$1" in
Expand Down Expand Up @@ -35,6 +40,29 @@ monitor_is_internal() {
[[ $monitor =~ ^(eDP|LVDS|DSI)- ]]
}

# All-in-ones wire their built-in panel to a DisplayPort connector, so its
# output name carries no hint that it is internal and the panel gets routed to
# DDC/CI, which a built-in panel does not answer. The kernel backlight is the
# right target there -- but only on a machine with no eDP/LVDS/DSI connector to
# own that backlight instead, or a laptop's DDC-less external monitor would move
# the laptop's own panel. Read the connectors rather than counting Hyprland
# outputs: Hyprland invents a FALLBACK output when every monitor is disabled.
machine_has_internal_connector() {
local status

for status in "$drm_path"/card*-*/status; do
[[ -e $status ]] || continue
[[ $status =~ -(eDP|LVDS|DSI)-[^/]+/status$ ]] || continue
[[ $(<"$status") == "connected" ]] && return 0
done

return 1
}

backlight_owns_named_monitor() {
! machine_has_internal_connector && omarchy-hw-display >/dev/null 2>&1
}

use_apple_display() {
omarchy-hyprland-monitor-focused-apple "$monitor"
}
Expand All @@ -49,7 +77,9 @@ if (( $# == 0 )); then
exit
elif use_ddc_display; then
omarchy-brightness-display-ddc "$monitor"
exit
ddc_status=$?
(( ddc_status == 0 )) && exit 0
(( ddc_status == no_ddc_display )) && backlight_owns_named_monitor || exit 1
fi

device="$(omarchy-hw-display)" || exit 1
Expand Down Expand Up @@ -82,41 +112,49 @@ if use_apple_display; then
else
omarchy-brightness-display-apple "$step"
fi
elif use_ddc_display; then
brightness="$(omarchy-brightness-display-ddc "$monitor" "$step")" || exit 1
(( no_osd )) || omarchy-osd -i brightness -p "$brightness"
else
# Current device highlighted
device="$(omarchy-hw-display)" || exit 1
exit
fi

# Current brightness percentage
current=$(backlight_brightness "$device") || exit 1

# Apply non-uniform step size: 1% steps if at or below 5%, otherwise set an
# absolute target percentage to avoid raw backlight rounding causing uneven OSD steps.
if [[ $step == "+5%" ]]; then
if (( current < 5 )); then
(( target = current + 1 ))
else
(( target = current + 5 ))
fi

(( target > 100 )) && target=100
step="$target%"
elif [[ $step == "5%-" ]]; then
if (( current <= 5 )); then
(( target = current - 1 ))
else
(( target = current - 5 ))
fi

(( target < 1 )) && target=1
step="$target%"
if use_ddc_display; then
brightness="$(omarchy-brightness-display-ddc "$monitor" "$step")"
ddc_status=$?
if (( ddc_status == 0 )); then
(( no_osd )) || omarchy-osd -i brightness -p "$brightness"
exit 0
fi
(( ddc_status == no_ddc_display )) && backlight_owns_named_monitor || exit 1
fi

# Set brightness of the display device.
brightnessctl -d "$device" set "$step" >/dev/null
# Current device highlighted
device="$(omarchy-hw-display)" || exit 1

# Show the new brightness in OSD
(( no_osd )) || omarchy-osd -i brightness -p "$(backlight_brightness "$device")"
# Current brightness percentage
current=$(backlight_brightness "$device") || exit 1

# Apply non-uniform step size: 1% steps if at or below 5%, otherwise set an
# absolute target percentage to avoid raw backlight rounding causing uneven OSD steps.
if [[ $step == "+5%" ]]; then
if (( current < 5 )); then
(( target = current + 1 ))
else
(( target = current + 5 ))
fi

(( target > 100 )) && target=100
step="$target%"
elif [[ $step == "5%-" ]]; then
if (( current <= 5 )); then
(( target = current - 1 ))
else
(( target = current - 5 ))
fi

(( target < 1 )) && target=1
step="$target%"
fi

# Set brightness of the display device.
brightnessctl -d "$device" set "$step" >/dev/null

# Show the new brightness in OSD
(( no_osd )) || omarchy-osd -i brightness -p "$(backlight_brightness "$device")"
23 changes: 16 additions & 7 deletions bin/omarchy-brightness-display-ddc
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ step="${2:-}"

[[ -n $monitor ]] || exit 1

# Distinguishes "this connector answers no DDC display" from a transient read
# failure, so a caller can route the connector somewhere else rather than retry
# a bus that is never going to appear.
no_ddc_display=3

cache_dir="${XDG_RUNTIME_DIR:-/tmp}/omarchy-brightness-display-ddc"
cache_name="${monitor//[^[:alnum:]_.-]/_}"
cache_file="$cache_dir/$cache_name.bus"
Expand Down Expand Up @@ -51,7 +56,7 @@ find_bus() {
if [[ $bus == "unavailable" ]]; then
now=$(date +%s)
if [[ $cached_value =~ ^[0-9]+$ ]] && (( now - cached_value < unavailable_cache_seconds )); then
return 1
return "$no_ddc_display"
fi
bus=""
rm -f "$cache_file"
Expand All @@ -61,7 +66,7 @@ find_bus() {
bus="$(detect_bus)" || return 1
if [[ ! $bus =~ ^[0-9]+$ ]]; then
cache_unavailable
return 1
return "$no_ddc_display"
fi
mkdir -p "$cache_dir" 2>/dev/null || true
printf '%s\n' "$bus" >"$cache_file" 2>/dev/null || true
Expand Down Expand Up @@ -89,7 +94,7 @@ read_brightness() {
local now=0
local values=""

bus="$(find_bus)" || return 1
bus="$(find_bus)" || return $?
values="$(read_vcp "$bus")" || {
rm -f "$cache_file"
return 1
Expand All @@ -108,7 +113,8 @@ percent=""
range_cached_at=""

if [[ -z $step ]]; then
read -r bus current maximum < <(read_brightness) || exit 1
brightness="$(read_brightness)" || exit $?
read -r bus current maximum <<<"$brightness"
[[ -n ${bus:-} && -n ${current:-} && -n ${maximum:-} ]] || exit 1
(( percent = (current * 100 + maximum / 2) / maximum ))
printf '%s\n' "$percent"
Expand All @@ -130,10 +136,12 @@ if [[ $step =~ ^([0-9]+)%$ ]]; then
fi

if (( ! range_cache_fresh )); then
read -r bus current maximum < <(read_brightness) || exit 1
brightness="$(read_brightness)" || exit $?
read -r bus current maximum <<<"$brightness"
fi
elif [[ $step =~ ^\+([0-9]+)%$ ]]; then
read -r bus current maximum < <(read_brightness) || exit 1
brightness="$(read_brightness)" || exit $?
read -r bus current maximum <<<"$brightness"
[[ -n ${bus:-} && -n ${current:-} && -n ${maximum:-} ]] || exit 1
(( percent = (current * 100 + maximum / 2) / maximum ))
amount="${BASH_REMATCH[1]}"
Expand All @@ -143,7 +151,8 @@ elif [[ $step =~ ^\+([0-9]+)%$ ]]; then
(( target = percent + amount ))
fi
elif [[ $step =~ ^([0-9]+)%-$ ]]; then
read -r bus current maximum < <(read_brightness) || exit 1
brightness="$(read_brightness)" || exit $?
read -r bus current maximum <<<"$brightness"
[[ -n ${bus:-} && -n ${current:-} && -n ${maximum:-} ]] || exit 1
(( percent = (current * 100 + maximum / 2) / maximum ))
amount="${BASH_REMATCH[1]}"
Expand Down
70 changes: 69 additions & 1 deletion test/shell.d/brightness-display-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,27 @@ SH

chmod +x "$mock_bin"/*

# Machine shape, as the kernel presents it. A laptop owns its backlight through
# an eDP connector; an all-in-one wires its built-in panel to DisplayPort and has
# no internal-named connector at all. The all-in-one gets both topologies, alone
# and with a second display attached, so each case runs against the DRM set it
# actually describes.
drm_laptop="$test_tmp/drm-laptop"
drm_aio="$test_tmp/drm-aio"
drm_aio_docked="$test_tmp/drm-aio-docked"
mkdir -p "$drm_laptop/card1-eDP-1" "$drm_laptop/card1-DP-2" \
"$drm_aio/card1-DP-1" "$drm_aio/card1-DP-2" \
"$drm_aio_docked/card1-DP-1" "$drm_aio_docked/card1-DP-2"
printf 'connected\n' >"$drm_laptop/card1-eDP-1/status"
printf 'connected\n' >"$drm_laptop/card1-DP-2/status"
printf 'connected\n' >"$drm_aio/card1-DP-1/status"
printf 'disconnected\n' >"$drm_aio/card1-DP-2/status"
printf 'connected\n' >"$drm_aio_docked/card1-DP-1/status"
printf 'connected\n' >"$drm_aio_docked/card1-DP-2/status"

run_brightness() {
CALL_LOG="$call_log" XDG_RUNTIME_DIR="$runtime_dir" PATH="$mock_bin:$ROOT/bin:$PATH" \
CALL_LOG="$call_log" XDG_RUNTIME_DIR="$runtime_dir" OMARCHY_DRM_PATH="${DRM_PATH:-$drm_laptop}" \
PATH="$mock_bin:$ROOT/bin:$PATH" \
"$ROOT/bin/omarchy-brightness-display" "$@"
}

Expand Down Expand Up @@ -144,3 +163,52 @@ if PATH="$mock_bin:$PATH" "$ROOT/bin/omarchy-hyprland-monitor-focused-apple"; th
fail "focused non-Apple display is not detected as Apple"
fi
pass "named Apple display is detected independently of focus"

# An all-in-one's built-in panel is named DP-1 and answers no DDC display, and
# the machine has no eDP/LVDS/DSI connector that could own the kernel backlight
# instead -- so the backlight is the panel behind that connector.
rm -rf "$runtime_dir/omarchy-brightness-display-ddc"
brightness=$(DRM_PATH="$drm_aio" DDC_CONNECTOR=DP-9 run_brightness --monitor DP-1) ||
fail "all-in-one panel falls back to the kernel backlight" "the command exited nonzero"
[[ $brightness == "40" ]] || fail "all-in-one panel falls back to the kernel backlight" "actual: $brightness"
pass "a DisplayPort panel with no DDC display uses the kernel backlight"

rm -rf "$runtime_dir/omarchy-brightness-display-ddc"
DRM_PATH="$drm_aio" DDC_CONNECTOR=DP-9 run_brightness --no-osd --monitor DP-1 60%
grep -F 'brightnessctl -d mock_backlight set 60%' "$call_log" >/dev/null ||
fail "all-in-one panel sets brightness through the kernel backlight"
pass "a DisplayPort panel with no DDC display is adjusted through the kernel backlight"

# The same all-in-one with a real external monitor attached and connected: that
# one answers DDC, so it keeps using it. Only the connector without a DDC display
# falls back.
rm -rf "$runtime_dir/omarchy-brightness-display-ddc"
brightness=$(DRM_PATH="$drm_aio_docked" DDC_CONNECTOR=DP-2 run_brightness --monitor DP-2) ||
fail "an external monitor on an all-in-one still uses DDC" "the command exited nonzero"
[[ $brightness == "50" ]] || fail "an external monitor on an all-in-one still uses DDC" "actual: $brightness"
pass "an external monitor that answers DDC still uses DDC"

# The built-in panel of that same docked all-in-one still has to reach the
# backlight. A second connected display is not an internal panel, so it cannot
# be what the backlight belongs to.
rm -rf "$runtime_dir/omarchy-brightness-display-ddc"
brightness=$(DRM_PATH="$drm_aio_docked" DDC_CONNECTOR=DP-2 run_brightness --monitor DP-1) ||
fail "a docked all-in-one still drives its own panel" "the command exited nonzero"
[[ $brightness == "40" ]] || fail "a docked all-in-one still drives its own panel" "actual: $brightness"
pass "an all-in-one panel keeps the kernel backlight with a second display connected"

# A laptop's DDC-less external monitor must keep failing. Falling back would
# move the laptop's own panel while the user is asking for the other one.
rm -rf "$runtime_dir/omarchy-brightness-display-ddc"
if DRM_PATH="$drm_laptop" DDC_CONNECTOR=DP-9 run_brightness --monitor DP-2 >/dev/null 2>&1; then
fail "a laptop's DDC-less external monitor does not move the internal backlight"
fi
pass "an external monitor on a machine with an internal panel does not fall back"

# A transient DDC read failure is not "no display on this connector", so it must
# not fall back either -- the monitor is there and will answer on the next try.
rm -rf "$runtime_dir/omarchy-brightness-display-ddc"
if DRM_PATH="$drm_aio" DDC_READ_FAIL=1 run_brightness --monitor DP-1 >/dev/null 2>&1; then
fail "a transient DDC read failure does not fall back to the backlight"
fi
pass "a transient DDC read failure is not treated as a missing DDC display"