Files
dotfiles/dotfiles/lib/functions/mpg341cx_input

286 lines
6.8 KiB
Bash
Executable File

#!/usr/bin/env -S zsh -f
function mpg341cx_input_usage {
cat <<'EOF'
Usage:
mpg341cx_input [toggle|dp|usb-c|hdmi1|hdmi2]
Switches the current DDC/CI monitor input. On the MSI MPG341CX OLED:
dp -> DisplayPort
usb-c -> USB-C / Type-C
toggle -> DisplayPort <-> USB-C
Environment:
MPG341CX_INPUT_DDC_DISPLAY DDC display number, default: 1
MPG341CX_INPUT_BACKEND auto, ddcutil, ddcctl, or m1ddc; default: auto
MPG341CX_INPUT_DARWIN_DISPLAY_NAME
macOS display name used to infer USB-C state
EOF
}
function mpg341cx_input_notify {
local message="$1"
if command -v notify-send >/dev/null 2>&1; then
notify-send "MPG341CX input" "$message"
elif command -v osascript >/dev/null 2>&1; then
osascript - "$message" <<'EOF' >/dev/null 2>&1
on run argv
display notification (item 1 of argv) with title "MPG341CX input"
end run
EOF
fi
}
function mpg341cx_input_backend {
local requested="${MPG341CX_INPUT_BACKEND:-auto}"
local candidate
local -a candidates
MPG341CX_INPUT_SELECTED_BACKEND=""
if [[ "$requested" != auto ]]; then
if command -v "$requested" >/dev/null 2>&1; then
MPG341CX_INPUT_SELECTED_BACKEND="$requested"
return 0
fi
echo "mpg341cx_input: requested backend '$requested' is not available on PATH" >&2
return 1
fi
if [[ "$OSTYPE" == darwin* ]]; then
candidates=(m1ddc ddcctl ddcutil)
else
candidates=(ddcutil ddcctl)
fi
for candidate in "${candidates[@]}"; do
if command -v "$candidate" >/dev/null 2>&1; then
MPG341CX_INPUT_SELECTED_BACKEND="$candidate"
return 0
fi
done
echo "mpg341cx_input: no supported DDC command is available on PATH" >&2
return 1
}
function mpg341cx_input_current_ddcutil {
local display="$1"
local output_file
output_file="${TMPDIR:-/tmp}/mpg341cx-input-current-$$-$RANDOM"
ddcutil --display "$display" getvcp 60 --terse 2>/dev/null \
| grep -Eo 'x[0-9a-fA-F]+' \
| tail -1 > "$output_file"
read -r MPG341CX_INPUT_CURRENT < "$output_file"
rm -f "$output_file"
}
function mpg341cx_input_current_ddcctl {
local display="$1"
local current
local output_file
output_file="${TMPDIR:-/tmp}/mpg341cx-input-current-$$-$RANDOM"
ddcctl -d "$display" -i '?' 2>/dev/null \
| sed -nE 's/.*current: ([0-9]+).*/\1/p' \
| tail -1 > "$output_file"
read -r current < "$output_file"
rm -f "$output_file"
if [[ -n "$current" ]]; then
printf -v MPG341CX_INPUT_CURRENT 'x%02x' "$current"
fi
}
function mpg341cx_input_current_m1ddc {
local display="$1"
local current
local output_file parsed_file
output_file="${TMPDIR:-/tmp}/mpg341cx-input-current-$$-$RANDOM"
parsed_file="${TMPDIR:-/tmp}/mpg341cx-input-parsed-$$-$RANDOM"
m1ddc display "$display" get input > "$output_file" 2>/dev/null
sed -nE 's/.*[^0-9]([0-9]+)$/\1/p; /^[0-9]+$/p' "$output_file" | tail -1 > "$parsed_file"
read -r current < "$parsed_file"
rm -f "$output_file" "$parsed_file"
if [[ -n "$current" ]]; then
printf -v MPG341CX_INPUT_CURRENT 'x%02x' "$current"
fi
}
function mpg341cx_input_current {
local backend="$1"
local display="$2"
MPG341CX_INPUT_CURRENT=""
case "$backend" in
ddcutil)
mpg341cx_input_current_ddcutil "$display"
;;
ddcctl)
mpg341cx_input_current_ddcctl "$display"
;;
m1ddc)
mpg341cx_input_current_m1ddc "$display"
;;
esac
}
function mpg341cx_input_current_darwin_active {
local display_name="${MPG341CX_INPUT_DARWIN_DISPLAY_NAME:-MPG341CX OLED}"
MPG341CX_INPUT_CURRENT_TARGET=""
if [[ "$OSTYPE" != darwin* ]] || ! command -v system_profiler >/dev/null 2>&1; then
return 1
fi
if system_profiler SPDisplaysDataType 2>/dev/null | awk -v display_name="$display_name" '
{
line = $0
trimmed = line
gsub(/^[[:space:]]+|[[:space:]]+$/, "", trimmed)
if (trimmed == display_name ":") {
in_display = 1
next
}
if (in_display && line ~ /^ [^[:space:]].*:[[:space:]]*$/) {
exit
}
if (in_display && trimmed == "Online: Yes") {
found = 1
exit
}
}
END {
exit(found ? 0 : 1)
}
'; then
MPG341CX_INPUT_CURRENT_TARGET="usb-c"
return 0
fi
return 1
}
function mpg341cx_input_current_target {
local backend="$1"
local current="${2:l}"
MPG341CX_INPUT_CURRENT_TARGET=""
case "$backend:$current" in
m1ddc:x0f|m1ddc:0x0f)
MPG341CX_INPUT_CURRENT_TARGET="dp"
;;
m1ddc:x10|m1ddc:0x10)
MPG341CX_INPUT_CURRENT_TARGET="usb-c"
;;
ddcutil:x0f|ddcutil:0x0f|ddcctl:x0f|ddcctl:0x0f)
MPG341CX_INPUT_CURRENT_TARGET="usb-c"
;;
ddcutil:x10|ddcutil:0x10|ddcctl:x10|ddcctl:0x10)
MPG341CX_INPUT_CURRENT_TARGET="dp"
;;
esac
}
function mpg341cx_input {
local target="${1:-toggle}"
local display="${MPG341CX_INPUT_DDC_DISPLAY:-1}"
local backend value_hex value_dec value_m1ddc label current current_target
if [[ "$target" == "-h" || "$target" == "--help" ]]; then
mpg341cx_input_usage
return 0
fi
if ! mpg341cx_input_backend; then
mpg341cx_input_notify "No DDC command is available"
return 1
fi
backend="$MPG341CX_INPUT_SELECTED_BACKEND"
case "${target:l}" in
toggle)
mpg341cx_input_current "$backend" "$display"
current="$MPG341CX_INPUT_CURRENT"
mpg341cx_input_current_target "$backend" "$current"
current_target="$MPG341CX_INPUT_CURRENT_TARGET"
if [[ -z "$current_target" ]]; then
mpg341cx_input_current_darwin_active
current_target="$MPG341CX_INPUT_CURRENT_TARGET"
fi
case "${current_target:l}" in
dp)
target="usb-c"
;;
usb-c)
target="dp"
;;
*)
echo "mpg341cx_input: current input is '${current:-unknown}', defaulting to DisplayPort" >&2
target="dp"
;;
esac
;;
esac
case "${target:l}" in
dp|displayport|display-port)
value_hex="0x10"
value_dec="16"
value_m1ddc="15"
label="DisplayPort"
target="dp"
;;
usb-c|usbc|type-c|typec)
value_hex="0x0f"
value_dec="15"
value_m1ddc="16"
label="USB-C"
target="usb-c"
;;
hdmi1|hdmi-1)
value_hex="0x11"
value_dec="17"
value_m1ddc="17"
label="HDMI 1"
target="hdmi1"
;;
hdmi2|hdmi-2)
value_hex="0x12"
value_dec="18"
value_m1ddc="18"
label="HDMI 2"
target="hdmi2"
;;
*)
mpg341cx_input_usage >&2
return 2
;;
esac
mpg341cx_input_notify "Switching to $label"
case "$backend" in
ddcutil)
ddcutil --display "$display" --noverify setvcp 60 "$value_hex" || return $?
;;
ddcctl)
ddcctl -d "$display" -i "$value_dec" || return $?
;;
m1ddc)
m1ddc display "$display" set input "$value_m1ddc" || return $?
;;
esac
}
mpg341cx_input "$@"