diff --git a/.config/dabruh/xmonconf.yaml b/.config/dabruh/xmonconf.yaml new file mode 100644 index 0000000..46aa955 --- /dev/null +++ b/.config/dabruh/xmonconf.yaml @@ -0,0 +1,78 @@ +displayConfig: + - name: "Work" + requiredUsbDevices: # Group of USB devices that are required to be connected to the system. All devices in a group must be connected for the display config to be applied + - require: all + match: exact + group: + - "HP, Inc HP USB-C Universal Dock" + + requiredOutputs: # Group of outputs that are required to be connected to the system. All outputs in a group must be connected for the display config to be applied + - group: + - "eDP-1" + - "DVI-I-1-1" + - "DVI-I-2-2" + - group: + - "eDP-1" + - "DVI-I-2-2" + - "DVI-I-3-3" + outputs: # The order of the outputs is important as it is linked to requiredOutputs + - mode: "1920x1200" + rotate: "normal" + pos: "1200x1450" + primary: true + - mode: "1920x1200" + rotate: "left" + pos: "0x0" + - mode: "1920x1200" + rotate: "normal" + pos: "1200x250" + + - name: "Home, Private Laptop" + requiredUsbDevices: + - require: all + match: exact + group: + - "Lenovo ThinkPad USB-C Dock Audio" + requiredOutputs: + - group: + - "eDP-1" + - "DP-1-0.3" + - "DP-1-0.1" + outputs: + - mode: "2560x1440" + rotate: "normal" + pos: "0x480" + primary: true + - mode: "3440x1440" + rotate: "normal" + pos: "2560x480" + - mode: "1920x1200" + rotate: "right" + pos: "6000x0" + + - name: "Home, Work Laptop" + requiredUsbDevices: + - require: all + match: exact + group: + - "Lenovo ThinkPad USB-C Dock Audio" + requiredOutputs: + - group: + - "DP-3-3" + - "eDP-1" + - "DP-3-1" + - group: + - "DP-1-3" + - "eDP-1" + - "DP-1-1" + outputs: + - mode: "3440x1440" + rotate: "normal" + pos: "0x480" + - mode: "1920x1200" + rotate: "normal" + pos: "3440x720" + primary: true + - mode: "1920x1200" + rotate: "right" + pos: "5360x0" diff --git a/.local/bin/xmonconf b/.local/bin/xmonconf index 750b1c4..9d50d51 100755 --- a/.local/bin/xmonconf +++ b/.local/bin/xmonconf @@ -3,15 +3,131 @@ # This script is used to apply monitor profiles based on the connected monitors and USB devices. -set -euo pipefail - # Source the utility functions source ~/.local/share/dabruh/libs/bash/x.sh source ~/.local/share/dabruh/libs/bash/i3.sh source ~/.local/share/dabruh/libs/bash/usb.sh +# Function to check if USB devices exist +check_usb_devices() { + local config_index=$1 + local config_file=$2 + + local required_usb_device_list_count + required_usb_device_list_count=$(yq e ".displayConfig[$config_index].requiredUsbDevices | length" "$config_file") + + for i in $(seq 0 $((required_usb_device_list_count - 1))); do + local required_usb_devices + required_usb_devices=$(yq e -o=j -I=0 ".displayConfig[$config_index].requiredUsbDevices[$i]" "$config_file") + echo "Required USB devices: $required_usb_devices" + + local required_usb_device_group_list required_usb_device_group_list_count + required_usb_device_group_list=$(echo "$required_usb_devices" | yq e -o=j -I=0 '.group') + required_usb_device_group_list_count=$(echo "$required_usb_device_group_list" | yq e -o=j -I=0 '. | length') + + local require match + require=$(echo "$required_usb_devices" | jq -r '.require') + match=$(echo "$required_usb_devices" | jq -r '.match') + + local usb_devices=() + for j in $(seq 0 $((required_usb_device_group_list_count - 1))); do + local item + item=$(echo "$required_usb_device_group_list" | jq -r ".[$j]") + usb_devices+=("$item") + done + + # Call the function with the array of USB devices + if usb_devices_exist "$require" "$match" "${usb_devices[@]}" >/dev/null 2>&1; then + return 0 + fi + done + + return 1 +} + +# Function to check if monitors exist and return the group index for the first group that exists +check_monitors() { + local config_index=$1 + local config_file=$2 + + local required_output_list_count + required_output_list_count=$(yq e ".displayConfig[$config_index].requiredOutputs | length" "$config_file") + + for i in $(seq 0 $((required_output_list_count - 1))); do + local required_outputs + required_outputs=$(yq e -o=j -I=0 ".displayConfig[$config_index].requiredOutputs[$i]" "$config_file") + + local required_output_group_list required_output_group_list_count + required_output_group_list=$(echo "$required_outputs" | yq e -o=j -I=0 '.group') + required_output_group_list_count=$(echo "$required_output_group_list" | yq e -o=j -I=0 '. | length') + + local monitors=() + for j in $(seq 0 $((required_output_group_list_count - 1))); do + local item + item=$(echo "$required_output_group_list" | jq -r ".[$j]") + monitors+=("$item") + done + + # Call the function with the array of monitors + if monitors_exist "${monitors[@]}" >/dev/null; then + echo "$i" # Return the group index + return 0 + fi + done + + return 1 +} + +# Function to apply the display settings +apply_display_settings() { + local config_index=$1 + local group_index=$2 + local config_file=$3 + + local outputs + outputs=$(yq e -o=j -I=0 ".displayConfig[$config_index].outputs" "$config_file") + + output_group=$(yq e -o=j -I=0 ".displayConfig[$config_index].requiredOutputs[$group_index].group" "$config_file") + + local output_list_count + output_list_count=$(echo "$outputs" | yq e -o=j -I=0 '. | length') + + local args + args=() + + for o in $(seq 0 $((output_list_count - 1))); do + local output_name + output_name=$(echo "$output_group" | jq -r ".[$o]") + + local mode rotate pos primary + mode=$(echo "$outputs" | jq -r ".[$o].mode") + rotate=$(echo "$outputs" | jq -r ".[$o].rotate") + pos=$(echo "$outputs" | jq -r ".[$o].pos") + primary=$(echo "$outputs" | jq -r ".[$o].primary // false") + + echo "Setting $output_name to $mode, $rotate, $pos, primary=$primary" + + args+=("--output" "$output_name" "--mode" "$mode" "--rotate" "$rotate" "--pos" "$pos") + [ "$primary" = "true" ] && args+=("--primary") + done + + echo "Running xrandr ${args[*]}" + xrandr "${args[@]}" +} + +# Function to parse and apply display configuration +apply_display_config() { + local config_index=$1 + local config_file=$2 + + check_usb_devices "$config_index" "$config_file" || return 1 + group_index=$(check_monitors "$config_index" "$config_file") || return 2 + apply_display_settings "$config_index" "$group_index" "$config_file" +} + main() { - local primary_monitor monitor_count monitors=() profile_found=false + local config_file=$1 + local primary_monitor monitor_count profile_found=false primary_monitor=$(get_monitors primary | cut -d',' -f1) monitor_count=$(get_monitors connected | wc -l) echo "Monitors connected: $monitor_count" @@ -20,49 +136,20 @@ main() { echo "Applying laptop-only profile" xrandr --auto profile_found=true - elif usb_devices_exist all exact "HP, Inc HP USB-C Universal Dock" >/dev/null 2>&1; then - monitors=( - "eDP-1,DVI-I-1-1,DVI-I-2-2" - "eDP-1,DVI-I-2-2,DVI-I-3-3" - ) + else + local config_count + config_count=$(yq e '.displayConfig | length' "$config_file") - for monitor in "${monitors[@]}"; do - IFS=',' read -r -a monitor_array <<<"$monitor" - if monitors_exist "${monitor_array[@]}" >/dev/null; then - echo "Applying work profile for ${monitor_array[*]}" - xrandr \ - --output "${monitor_array[0]}" --mode 1920x1200 --rotate normal --pos 1200x1474 --primary \ - --output "${monitor_array[1]}" --mode 1920x1200 --rotate left --pos 0x0 \ - --output "${monitor_array[2]}" --mode 1920x1200 --rotate normal --pos 1200x274 + for i in $(seq 0 $((config_count - 1))); do + name=$(yq e ".displayConfig[$i].name" "$config_file") + echo "Trying profile #$i '$name'" + if apply_display_config "$i" "$config_file"; then profile_found=true break + else + echo "Profile #$i '$name' failed with exit code $?" >&2 fi - done - elif usb_devices_exist all exact "Lenovo ThinkPad USB-C Dock Audio" >/dev/null 2>&1; then - monitor_array=(eDP-1 DP-1-0.3 DP-1-0.1) - if monitors_exist "${monitor_array[@]}" >/dev/null; then - echo "Applying home profile for ${monitor_array[*]}" - xrandr \ - --output "${monitor_array[0]}" --mode 2560x1440 --rotate normal --pos 0x480 --primary \ - --output "${monitor_array[1]}" --mode 3440x1440 --rotate normal --pos 2560x480 \ - --output "${monitor_array[2]}" --mode 1920x1200 --rotate right --pos 6000x0 - profile_found=true - fi - - monitors=( - "DP-3-3,eDP-1,DP-3-1" - "DP-1-3,eDP-1,DP-1-1" - ) - - for monitor in "${monitors[@]}"; do - IFS=',' read -r -a monitor_array <<<"$monitor" - if monitors_exist "${monitor_array[@]}" >/dev/null; then - xrandr \ - --output "${monitor_array[0]}" --mode 3440x1440 --pos 0x480 --rotate normal \ - --output "${monitor_array[1]}" --mode 1920x1200 --pos 3440x720 --rotate normal --primary \ - --output "${monitor_array[2]}" --mode 1920x1200 --pos 5360x0 --rotate right - profile_found=true - fi + echo done fi @@ -76,7 +163,12 @@ main() { i3-msg restart >/dev/null } -# Run if invoked directly if [ "$0" = "${BASH_SOURCE[0]}" ]; then - main + set -euo pipefail + config_file="${1:-$HOME/.config/dabruh/xmonconf.yaml}" + if ! [ -f "$config_file" ]; then + echo "Config file not found: $config_file" + exit 1 + fi + main "$config_file" fi diff --git a/.local/share/dabruh/libs/bash/x.sh b/.local/share/dabruh/libs/bash/x.sh index b732844..1e9048d 100644 --- a/.local/share/dabruh/libs/bash/x.sh +++ b/.local/share/dabruh/libs/bash/x.sh @@ -60,6 +60,7 @@ monitor_exist() { connected_monitors=$(get_monitors connected) for connected_monitor in $connected_monitors; do + echo "Processing monitor $connected_monitor" 1>&2 if [ "$(cut -d',' -f1 <<<"$connected_monitor")" = "$monitor" ]; then return 0 fi @@ -75,6 +76,7 @@ monitors_exist() { local monitor matches=0 for monitor in "$@"; do + echo "Checking if monitor $monitor exists" 1>&2 if monitor_exist "$monitor"; then echo "$monitor" matches=$((matches + 1))