mirror of
https://gitlab.com/dabruh/dotfiles.git
synced 2024-12-26 03:48:12 +01:00
Compare commits
2 commits
a71430f12e
...
f7292d6958
Author | SHA1 | Date | |
---|---|---|---|
|
f7292d6958 | ||
|
90b7fc35d2 |
5 changed files with 529 additions and 335 deletions
78
.config/dabruh/xmonconf.yaml
Normal file
78
.config/dabruh/xmonconf.yaml
Normal file
|
@ -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"
|
|
@ -1,81 +1,76 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
|
||||||
# This script is used to apply monitor profiles based on the connected monitors and USB devices.
|
# 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
|
||||||
|
|
||||||
_monitors=() # Cache for the xrandr output
|
# Function to check if USB devices exist
|
||||||
|
check_usb_devices() {
|
||||||
|
local config_index=$1
|
||||||
|
local config_file=$2
|
||||||
|
|
||||||
# Get the list of connected and disconnected monitors.
|
local required_usb_device_list_count
|
||||||
# Includes information about the primary monitor
|
required_usb_device_list_count=$(yq e ".displayConfig[$config_index].requiredUsbDevices | length" "$config_file")
|
||||||
# E.g:
|
|
||||||
# eDP-1,connected,primary,1920x1080+0+0,enabled
|
|
||||||
# HDMI-1,disconnected,1920x1080+1920+0,enabled
|
|
||||||
# DP-1,disconnected,disabled
|
|
||||||
_get_monitors() {
|
|
||||||
local monitors enabled_regex="[[:digit:]]+x[[:digit:]]+\+[[:digit:]]+\+[[:digit:]]+" # Regex for enabled monitors
|
|
||||||
|
|
||||||
# Use caching to avoid multiple calls to xrandr
|
for i in $(seq 0 $((required_usb_device_list_count - 1))); do
|
||||||
if [ ${#_monitors[@]} -eq 0 ]; then
|
local required_usb_devices
|
||||||
# Get the list of connected and disconnected monitors
|
required_usb_devices=$(yq e -o=j -I=0 ".displayConfig[$config_index].requiredUsbDevices[$i]" "$config_file")
|
||||||
monitors=$(xrandr | grep -Eo "^.* ((dis|)connected)( primary|) ($enabled_regex|)" | sed 's/ /,/g' | sed 's/,$//g')
|
echo "Required USB devices: $required_usb_devices"
|
||||||
|
|
||||||
for monitor in $monitors; do
|
local required_usb_device_group_list required_usb_device_group_list_count
|
||||||
# Set "enabled" if it matches the enabled regex and remove the matching string, else set "disabled"
|
required_usb_device_group_list=$(echo "$required_usb_devices" | yq e -o=j -I=0 '.group')
|
||||||
if [[ "$monitor" =~ $enabled_regex ]]; then
|
required_usb_device_group_list_count=$(echo "$required_usb_device_group_list" | yq e -o=j -I=0 '. | length')
|
||||||
monitor="$monitor,enabled"
|
|
||||||
else
|
|
||||||
monitor="$monitor,disabled"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Add to the list of monitors
|
local require match
|
||||||
_monitors+=("$monitor")
|
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
|
done
|
||||||
|
|
||||||
# _monitors="$monitors"
|
# Call the function with the array of USB devices
|
||||||
fi
|
if usb_devices_exist "$require" "$match" "${usb_devices[@]}" >/dev/null 2>&1; then
|
||||||
|
return 0
|
||||||
for monitor in "${_monitors[@]}"; do
|
fi
|
||||||
echo "$monitor"
|
|
||||||
done
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# Function that gets all monitors and takes an optional amount of arguments to filter the list.
|
# Function to check if monitors exist and return the group index for the first group that exists
|
||||||
# Filters are applied as an OR operation.
|
check_monitors() {
|
||||||
# E.g:
|
local config_index=$1
|
||||||
# get_monitors connected primary
|
local config_file=$2
|
||||||
# get_monitors connected primary eDP-1
|
|
||||||
get_monitors() {
|
|
||||||
local monitors
|
|
||||||
monitors=$(_get_monitors)
|
|
||||||
|
|
||||||
# If no filters are set, return all monitors
|
local required_output_list_count
|
||||||
if [ "$#" -eq 0 ]; then
|
required_output_list_count=$(yq e ".displayConfig[$config_index].requiredOutputs | length" "$config_file")
|
||||||
echo "$monitors"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
for monitor in $monitors; do
|
for i in $(seq 0 $((required_output_list_count - 1))); do
|
||||||
for filter in "$@"; do
|
local required_outputs
|
||||||
for part in ${monitor//,/ }; do
|
required_outputs=$(yq e -o=j -I=0 ".displayConfig[$config_index].requiredOutputs[$i]" "$config_file")
|
||||||
if [ "$part" = "$filter" ]; then
|
|
||||||
echo "$monitor"
|
local required_output_group_list required_output_group_list_count
|
||||||
continue 3 # Continue to the next monitor
|
required_output_group_list=$(echo "$required_outputs" | yq e -o=j -I=0 '.group')
|
||||||
fi
|
required_output_group_list_count=$(echo "$required_output_group_list" | yq e -o=j -I=0 '. | length')
|
||||||
done
|
|
||||||
|
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
|
done
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# monitor_exist checks if the specified monitor is connected to the system
|
# Call the function with the array of monitors
|
||||||
# $1: The monitor name to search for
|
if monitors_exist "${monitors[@]}" >/dev/null; then
|
||||||
monitor_exist() {
|
echo "$i" # Return the group index
|
||||||
local monitor=${1:?Monitor not set}
|
|
||||||
local connected_monitors
|
|
||||||
connected_monitors=$(get_monitors connected)
|
|
||||||
|
|
||||||
for connected_monitor in $connected_monitors; do
|
|
||||||
if [ "$(cut -d',' -f1 <<<"$connected_monitor")" = "$monitor" ]; then
|
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
@ -83,249 +78,56 @@ monitor_exist() {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
# monitors_exist checks if the specified monitors are connected to the system
|
# Function to apply the display settings
|
||||||
# $1..$n: The monitor names to search for
|
apply_display_settings() {
|
||||||
monitors_exist() {
|
local config_index=$1
|
||||||
local monitor matches=0
|
local group_index=$2
|
||||||
|
local config_file=$3
|
||||||
|
|
||||||
for monitor in "$@"; do
|
local outputs
|
||||||
if monitor_exist "$monitor"; then
|
outputs=$(yq e -o=j -I=0 ".displayConfig[$config_index].outputs" "$config_file")
|
||||||
echo "$monitor"
|
|
||||||
matches=$((matches + 1))
|
output_group=$(yq e -o=j -I=0 ".displayConfig[$config_index].requiredOutputs[$group_index].group" "$config_file")
|
||||||
fi
|
|
||||||
|
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
|
done
|
||||||
|
|
||||||
if [ "$matches" -ne "$#" ]; then
|
echo "Running xrandr ${args[*]}"
|
||||||
echo "Amount of devices does not match the amount of arguments" 1>&2
|
xrandr "${args[@]}"
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Disable all monitors that are not in the list of connected monitors
|
# Function to parse and apply display configuration
|
||||||
disable_disconnected_monitors() {
|
apply_display_config() {
|
||||||
local connected_monitors
|
local config_index=$1
|
||||||
connected_monitors=$(get_monitors disconnected)
|
local config_file=$2
|
||||||
|
|
||||||
echo "Disabling disconnected monitors"
|
check_usb_devices "$config_index" "$config_file" || return 1
|
||||||
|
group_index=$(check_monitors "$config_index" "$config_file") || return 2
|
||||||
for monitor in $connected_monitors; do
|
apply_display_settings "$config_index" "$group_index" "$config_file"
|
||||||
echo "Disabling monitor $(cut -d',' -f1 <<<"$monitor")"
|
|
||||||
xrandr --output "$(cut -d',' -f1 <<<"$monitor")" --off
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get the list of USB devices using lsusb, outputs for example "06cb:00f0 Synaptics, Inc."
|
|
||||||
get_usb_devices() {
|
|
||||||
lsusb | grep -Eo ' ID .*' | cut -d' ' -f3-
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get the list of USB device names using lsusb, outputs for example "Synaptics, Inc."
|
|
||||||
get_usb_device_names() {
|
|
||||||
get_usb_devices | cut -d' ' -f2-
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get the list of USB devices that match the name
|
|
||||||
get_usb_devices_by_name() {
|
|
||||||
local name=${1:?Name not set}
|
|
||||||
get_usb_device_names | while read -r device; do
|
|
||||||
if [ "$device" = "$name" ]; then
|
|
||||||
echo "$device"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get the list of USB devices that match the pattern
|
|
||||||
get_usb_devices_by_pattern() {
|
|
||||||
local pattern=${1:?Pattern not set}
|
|
||||||
get_usb_device_names | grep -E "$pattern"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Returns the list of USB devices that match the criteria
|
|
||||||
# $1: Required, either "all" or "any"
|
|
||||||
# $2: Mode, either "exact" or "pattern"
|
|
||||||
# $3..$n: The name or pattern to search for
|
|
||||||
usb_devices_exist() {
|
|
||||||
local required=${1:?Required not set}
|
|
||||||
local mode=${2:?Mode not set}
|
|
||||||
shift 2
|
|
||||||
local devices matches=0
|
|
||||||
|
|
||||||
# Check lenght of arguments
|
|
||||||
if [ "$#" -eq 0 ]; then
|
|
||||||
echo "No patterns set" 1>&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if required and mode args are valid
|
|
||||||
if ! [[ "$required" =~ ^(all|any)$ ]]; then
|
|
||||||
echo "Invalid 'required' argument: $required" 1>&2
|
|
||||||
return 1
|
|
||||||
elif ! [[ "$mode" =~ ^(exact|pattern)$ ]]; then
|
|
||||||
echo "Invalid 'mode' argument: $mode" 1>&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Process each pattern
|
|
||||||
for pattern in "$@"; do
|
|
||||||
if [ -z "$pattern" ]; then
|
|
||||||
echo "Got empty pattern" 1>&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$mode" = "exact" ]; then
|
|
||||||
devices="$(get_usb_devices_by_name "$pattern")"
|
|
||||||
elif [ "$mode" = "pattern" ]; then
|
|
||||||
devices="$(get_usb_devices_by_pattern "$pattern")"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if at least one device was found
|
|
||||||
if [ -n "$devices" ]; then
|
|
||||||
matches=$((matches + 1))
|
|
||||||
echo "$devices"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Check if all patterns had a match
|
|
||||||
if [ "$required" = "all" ]; then
|
|
||||||
if [ "$matches" -eq "$#" ]; then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
echo "Amount of devices does not match the amount of patterns" 1>&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
# Check if at least one pattern had a match
|
|
||||||
elif [ "$required" = "any" ]; then
|
|
||||||
if [ "$matches" -ge "0" ]; then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
echo "Amount of devices does not match the amount of patterns" 1>&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get the list of empty i3 workspaces
|
|
||||||
get_empty_i3_workspaces() {
|
|
||||||
i3-msg -t get_tree | jq -r '
|
|
||||||
recurse(.nodes[]?) | # Recursively descend into "nodes" arrays
|
|
||||||
select(.type == "workspace") | # Select elements where "type" is "workspace"
|
|
||||||
select(.name | test("^[^_].*")) | # Select elements where "name" does not start with "_"
|
|
||||||
select(.nodes | length == 0) | # Further select those where "nodes" array is empty
|
|
||||||
.name # Output the "name" of these workspaces
|
|
||||||
'
|
|
||||||
}
|
|
||||||
|
|
||||||
i3_workspace_is_empty() {
|
|
||||||
local workspace_name=${1:?Workspace name not set}
|
|
||||||
local empty_workspace
|
|
||||||
empty_workspace=$(get_empty_i3_workspaces)
|
|
||||||
|
|
||||||
for workspace in $empty_workspace; do
|
|
||||||
if [ "$workspace" = "$workspace_name" ]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get a comma separated list of i3 workspace names and the output they are on
|
|
||||||
get_i3_workspaces() {
|
|
||||||
i3-msg -t get_workspaces | jq -r '.[] | .name + "," + .output'
|
|
||||||
}
|
|
||||||
|
|
||||||
get_i3_workspace_prop() {
|
|
||||||
local workspace_name=${1:?Workspace name not set}
|
|
||||||
local prop=${2:?Property not set}
|
|
||||||
i3-msg -t get_workspaces | jq -r ".[] | select(.name == \"$workspace_name\") | .$prop"
|
|
||||||
}
|
|
||||||
|
|
||||||
i3_workspace_exists() {
|
|
||||||
local workspace_name=${1:?Workspace name not set}
|
|
||||||
local workspaces name
|
|
||||||
|
|
||||||
if [[ -n "$(get_i3_workspace_prop "$workspace_name" name)" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# This function selects a workspace by name and moves it to the desired monitor.
|
|
||||||
# If the workspace name is an integer, it will allow incrementing the workspace name by the given number
|
|
||||||
move_i3_workspace() {
|
|
||||||
local workspace_name=${1:?Workspace name not set}
|
|
||||||
local monitor_name=${2:?Monitor name not set}
|
|
||||||
local increment=${3:-0}
|
|
||||||
local new_workspace_name="$workspace_name"
|
|
||||||
|
|
||||||
if ! i3_workspace_exists "$workspace_name"; then
|
|
||||||
echo "Workspace $workspace_name not found" 1>&2
|
|
||||||
return 1
|
|
||||||
elif ! monitor_exist "$monitor_name"; then
|
|
||||||
echo "Monitor $monitor_name not found" 1>&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Ensure the new output is not the same as the current output
|
|
||||||
if [[ "$(get_i3_workspace_prop "$workspace_name" output)" == "$monitor_name" ]]; then
|
|
||||||
echo "Workspace $workspace_name is already on monitor $monitor_name" 1>&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Increment the workspace name if it is an integer
|
|
||||||
if [[ "$workspace_name" =~ ^[0-9]+$ ]]; then
|
|
||||||
new_workspace_name=$((workspace_name + increment))
|
|
||||||
elif [ "$increment" -ne 0 ]; then
|
|
||||||
echo "Workspace name is not an integer. Unable to increment/decrement." 1>&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if the new workspace name already exists and is not the same as the current workspace name
|
|
||||||
if i3_workspace_exists "$new_workspace_name"; then
|
|
||||||
if [[ "$new_workspace_name" != "$workspace_name" ]]; then
|
|
||||||
echo "Workspace $new_workspace_name already exists" 1>&2
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Moving workspace $workspace_name to monitor $monitor_name as $new_workspace_name"
|
|
||||||
|
|
||||||
i3-msg "workspace $workspace_name" 1>/dev/null # Select the workspace
|
|
||||||
i3-msg "rename workspace $workspace_name to $new_workspace_name" 1>/dev/null
|
|
||||||
i3-msg "move workspace to output $monitor_name" 1>/dev/null
|
|
||||||
}
|
|
||||||
|
|
||||||
# This function moves all workspaces that are not on the given monitor to the given monitor
|
|
||||||
move_nonexistent_i3_workspaces() {
|
|
||||||
local monitor_name=${1:?Monitor name not set}
|
|
||||||
local increment=${2:-0}
|
|
||||||
local workspaces
|
|
||||||
workspaces=$(get_i3_workspaces)
|
|
||||||
|
|
||||||
echo "Moving workspaces to monitor $monitor_name"
|
|
||||||
|
|
||||||
for workspace in $workspaces; do
|
|
||||||
local workspace_name
|
|
||||||
local workspace_output
|
|
||||||
workspace_name=$(echo "$workspace" | cut -d',' -f1)
|
|
||||||
workspace_output=$(echo "$workspace" | cut -d',' -f2)
|
|
||||||
|
|
||||||
if i3_workspace_is_empty "$workspace_name"; then
|
|
||||||
echo "Skipping empty workspace $workspace_name"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! monitor_exist "$workspace_output"; then
|
|
||||||
move_i3_workspace "$workspace_name" "$monitor_name" "$increment"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
local primary_monitor monitor_count monitors=() profile_found=false exists
|
local config_file=$1
|
||||||
|
local primary_monitor monitor_count profile_found=false
|
||||||
primary_monitor=$(get_monitors primary | cut -d',' -f1)
|
primary_monitor=$(get_monitors primary | cut -d',' -f1)
|
||||||
monitor_count=$(get_monitors connected | wc -l)
|
monitor_count=$(get_monitors connected | wc -l)
|
||||||
echo "Monitors connected: $monitor_count"
|
echo "Monitors connected: $monitor_count"
|
||||||
|
@ -334,49 +136,20 @@ main() {
|
||||||
echo "Applying laptop-only profile"
|
echo "Applying laptop-only profile"
|
||||||
xrandr --auto
|
xrandr --auto
|
||||||
profile_found=true
|
profile_found=true
|
||||||
elif usb_devices_exist all exact "HP, Inc HP USB-C Universal Dock" >/dev/null 2>&1; then
|
else
|
||||||
monitors=(
|
local config_count
|
||||||
"eDP-1,DVI-I-1-1,DVI-I-2-2"
|
config_count=$(yq e '.displayConfig | length' "$config_file")
|
||||||
"eDP-1,DVI-I-2-2,DVI-I-3-3"
|
|
||||||
)
|
|
||||||
|
|
||||||
for monitor in "${monitors[@]}"; do
|
for i in $(seq 0 $((config_count - 1))); do
|
||||||
IFS=',' read -r -a monitor_array <<<"$monitor"
|
name=$(yq e ".displayConfig[$i].name" "$config_file")
|
||||||
if monitors_exist "${monitor_array[@]}" >/dev/null; then
|
echo "Trying profile #$i '$name'"
|
||||||
echo "Applying work profile for ${monitor_array[*]}"
|
if apply_display_config "$i" "$config_file"; then
|
||||||
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
|
|
||||||
profile_found=true
|
profile_found=true
|
||||||
break
|
break
|
||||||
|
else
|
||||||
|
echo "Profile #$i '$name' failed with exit code $?" >&2
|
||||||
fi
|
fi
|
||||||
done
|
echo
|
||||||
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
|
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
@ -390,7 +163,12 @@ main() {
|
||||||
i3-msg restart >/dev/null
|
i3-msg restart >/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
# Run if invoked directly
|
|
||||||
if [ "$0" = "${BASH_SOURCE[0]}" ]; then
|
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
|
fi
|
||||||
|
|
137
.local/share/dabruh/libs/bash/i3.sh
Normal file
137
.local/share/dabruh/libs/bash/i3.sh
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This script contains utility functions related to i3 workspaces.
|
||||||
|
|
||||||
|
# Ensure required functions exist
|
||||||
|
if ! command -v monitor_exist >/dev/null 2>&1; then
|
||||||
|
echo "monitor_exist function is required but not found. Please source x.sh before i3.sh." 1>&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the list of empty i3 workspaces.
|
||||||
|
# Usage: get_empty_i3_workspaces
|
||||||
|
get_empty_i3_workspaces() {
|
||||||
|
set -euo pipefail
|
||||||
|
i3-msg -t get_tree | jq -r '
|
||||||
|
recurse(.nodes[]?) |
|
||||||
|
select(.type == "workspace") |
|
||||||
|
select(.name | test("^[^_].*")) |
|
||||||
|
select(.nodes | length == 0) |
|
||||||
|
.name
|
||||||
|
'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if the i3 workspace is empty.
|
||||||
|
# Usage: i3_workspace_is_empty <workspace_name>
|
||||||
|
i3_workspace_is_empty() {
|
||||||
|
set -euo pipefail
|
||||||
|
local workspace_name=${1:?Workspace name not set}
|
||||||
|
local empty_workspace
|
||||||
|
empty_workspace=$(get_empty_i3_workspaces)
|
||||||
|
|
||||||
|
for workspace in $empty_workspace; do
|
||||||
|
if [ "$workspace" = "$workspace_name" ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get a comma separated list of i3 workspace names and the output they are on.
|
||||||
|
# Usage: get_i3_workspaces
|
||||||
|
get_i3_workspaces() {
|
||||||
|
set -euo pipefail
|
||||||
|
i3-msg -t get_workspaces | jq -r '.[] | .name + "," + .output'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get a property of an i3 workspace.
|
||||||
|
# Usage: get_i3_workspace_prop <workspace_name> <property>
|
||||||
|
get_i3_workspace_prop() {
|
||||||
|
set -euo pipefail
|
||||||
|
local workspace_name=${1:?Workspace name not set}
|
||||||
|
local prop=${2:?Property not set}
|
||||||
|
i3-msg -t get_workspaces | jq -r ".[] | select(.name == \"$workspace_name\") | .$prop"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if an i3 workspace exists.
|
||||||
|
# Usage: i3_workspace_exists <workspace_name>
|
||||||
|
i3_workspace_exists() {
|
||||||
|
set -euo pipefail
|
||||||
|
local workspace_name=${1:?Workspace name not set}
|
||||||
|
if [[ -n "$(get_i3_workspace_prop "$workspace_name" name)" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Move an i3 workspace to a different monitor.
|
||||||
|
# Usage: move_i3_workspace <workspace_name> <monitor_name> [increment]
|
||||||
|
move_i3_workspace() {
|
||||||
|
set -euo pipefail
|
||||||
|
local workspace_name=${1:?Workspace name not set}
|
||||||
|
local monitor_name=${2:?Monitor name not set}
|
||||||
|
local increment=${3:-0}
|
||||||
|
local new_workspace_name="$workspace_name"
|
||||||
|
|
||||||
|
if ! i3_workspace_exists "$workspace_name"; then
|
||||||
|
echo "Workspace $workspace_name not found" 1>&2
|
||||||
|
return 1
|
||||||
|
elif ! monitor_exist "$monitor_name"; then
|
||||||
|
echo "Monitor $monitor_name not found" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$(get_i3_workspace_prop "$workspace_name" output)" == "$monitor_name" ]]; then
|
||||||
|
echo "Workspace $workspace_name is already on monitor $monitor_name" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$workspace_name" =~ ^[0-9]+$ ]]; then
|
||||||
|
new_workspace_name=$((workspace_name + increment))
|
||||||
|
elif [ "$increment" -ne 0 ]; then
|
||||||
|
echo "Workspace name is not an integer. Unable to increment/decrement." 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if i3_workspace_exists "$new_workspace_name"; then
|
||||||
|
if [[ "$new_workspace_name" != "$workspace_name" ]]; then
|
||||||
|
echo "Workspace $new_workspace_name already exists" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Moving workspace $workspace_name to monitor $monitor_name as $new_workspace_name"
|
||||||
|
|
||||||
|
i3-msg "workspace $workspace_name" 1>/dev/null
|
||||||
|
i3-msg "rename workspace $workspace_name to $new_workspace_name" 1>/dev/null
|
||||||
|
i3-msg "move workspace to output $monitor_name" 1>/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Move all workspaces to a specified monitor.
|
||||||
|
# Usage: move_nonexistent_i3_workspaces <monitor_name> [increment]
|
||||||
|
move_nonexistent_i3_workspaces() {
|
||||||
|
set -euo pipefail
|
||||||
|
local monitor_name=${1:?Monitor name not set}
|
||||||
|
local increment=${2:-0}
|
||||||
|
local workspaces
|
||||||
|
workspaces=$(get_i3_workspaces)
|
||||||
|
|
||||||
|
echo "Moving workspaces to monitor $monitor_name"
|
||||||
|
|
||||||
|
for workspace in $workspaces; do
|
||||||
|
local workspace_name
|
||||||
|
local workspace_output
|
||||||
|
workspace_name=$(echo "$workspace" | cut -d',' -f1)
|
||||||
|
workspace_output=$(echo "$workspace" | cut -d',' -f2)
|
||||||
|
|
||||||
|
if i3_workspace_is_empty "$workspace_name"; then
|
||||||
|
echo "Skipping empty workspace $workspace_name"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! monitor_exist "$workspace_output"; then
|
||||||
|
move_i3_workspace "$workspace_name" "$monitor_name" "$increment"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
94
.local/share/dabruh/libs/bash/usb.sh
Normal file
94
.local/share/dabruh/libs/bash/usb.sh
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This script contains utility functions related to USB devices.
|
||||||
|
|
||||||
|
# Get the list of USB devices.
|
||||||
|
# Usage: get_usb_devices
|
||||||
|
get_usb_devices() {
|
||||||
|
set -euo pipefail
|
||||||
|
lsusb | grep -Eo ' ID .*' | cut -d' ' -f3-
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the list of USB device names.
|
||||||
|
# Usage: get_usb_device_names
|
||||||
|
get_usb_device_names() {
|
||||||
|
set -euo pipefail
|
||||||
|
get_usb_devices | cut -d' ' -f2-
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the list of USB devices that match the name.
|
||||||
|
# Usage: get_usb_devices_by_name <name>
|
||||||
|
get_usb_devices_by_name() {
|
||||||
|
set -euo pipefail
|
||||||
|
local name=${1:?Name not set}
|
||||||
|
get_usb_device_names | while read -r device; do
|
||||||
|
if [ "$device" = "$name" ]; then
|
||||||
|
echo "$device"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the list of USB devices that match the pattern.
|
||||||
|
# Usage: get_usb_devices_by_pattern <pattern>
|
||||||
|
get_usb_devices_by_pattern() {
|
||||||
|
set -euo pipefail
|
||||||
|
local pattern=${1:?Pattern not set}
|
||||||
|
get_usb_device_names | grep -E "$pattern"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Returns the list of USB devices that match the criteria.
|
||||||
|
# Usage: usb_devices_exist <all|any> <exact|pattern> <pattern1> [pattern2] ...
|
||||||
|
usb_devices_exist() {
|
||||||
|
set -euo pipefail
|
||||||
|
local required=${1:?Required not set}
|
||||||
|
local mode=${2:?Mode not set}
|
||||||
|
shift 2
|
||||||
|
local devices matches=0
|
||||||
|
|
||||||
|
if [ "$#" -eq 0 ]; then
|
||||||
|
echo "No patterns set" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! [[ "$required" =~ ^(all|any)$ ]]; then
|
||||||
|
echo "Invalid 'required' argument: $required" 1>&2
|
||||||
|
return 1
|
||||||
|
elif ! [[ "$mode" =~ ^(exact|pattern)$ ]]; then
|
||||||
|
echo "Invalid 'mode' argument: $mode" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for pattern in "$@"; do
|
||||||
|
if [ -z "$pattern" ]; then
|
||||||
|
echo "Got empty pattern" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$mode" = "exact" ]; then
|
||||||
|
devices="$(get_usb_devices_by_name "$pattern")"
|
||||||
|
elif [ "$mode" = "pattern" ]; then
|
||||||
|
devices="$(get_usb_devices_by_pattern "$pattern")"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -n "$devices" ]; then
|
||||||
|
matches=$((matches + 1))
|
||||||
|
echo "$devices"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$required" = "all" ]; then
|
||||||
|
if [ "$matches" -eq "$#" ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "Amount of devices does not match the amount of patterns" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
elif [ "$required" = "any" ]; then
|
||||||
|
if [ "$matches" -ge "0" ]; then
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo "Amount of devices does not match the amount of patterns" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
107
.local/share/dabruh/libs/bash/x.sh
Normal file
107
.local/share/dabruh/libs/bash/x.sh
Normal file
|
@ -0,0 +1,107 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This script contains utility functions related to X monitors.
|
||||||
|
|
||||||
|
_monitors=() # Cache for the xrandr output
|
||||||
|
|
||||||
|
# Get the list of connected and disconnected monitors.
|
||||||
|
# Usage: _get_monitors
|
||||||
|
_get_monitors() {
|
||||||
|
set -euo pipefail
|
||||||
|
local monitors enabled_regex="[[:digit:]]+x[[:digit:]]+\+[[:digit:]]+\+[[:digit:]]+" # Regex for enabled monitors
|
||||||
|
|
||||||
|
if [ ${#_monitors[@]} -eq 0 ]; then
|
||||||
|
monitors=$(xrandr | grep -Eo "^.* ((dis|)connected)( primary|) ($enabled_regex|)" | sed 's/ /,/g' | sed 's/,$//g')
|
||||||
|
for monitor in $monitors; do
|
||||||
|
if [[ "$monitor" =~ $enabled_regex ]]; then
|
||||||
|
monitor="$monitor,enabled"
|
||||||
|
else
|
||||||
|
monitor="$monitor,disabled"
|
||||||
|
fi
|
||||||
|
_monitors+=("$monitor")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
for monitor in "${_monitors[@]}"; do
|
||||||
|
echo "$monitor"
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Function to get all monitors with optional filters.
|
||||||
|
# Usage: get_monitors [filter1] [filter2] ...
|
||||||
|
get_monitors() {
|
||||||
|
set -euo pipefail
|
||||||
|
local monitors
|
||||||
|
monitors=$(_get_monitors)
|
||||||
|
|
||||||
|
if [ "$#" -eq 0 ]; then
|
||||||
|
echo "$monitors"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for monitor in $monitors; do
|
||||||
|
for filter in "$@"; do
|
||||||
|
for part in ${monitor//,/ }; do
|
||||||
|
if [ "$part" = "$filter" ]; then
|
||||||
|
echo "$monitor"
|
||||||
|
continue 3 # Continue to the next monitor
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if the specified monitor is connected.
|
||||||
|
# Usage: monitor_exist <monitor_name>
|
||||||
|
monitor_exist() {
|
||||||
|
set -euo pipefail
|
||||||
|
local monitor=${1:?Monitor not set}
|
||||||
|
local connected_monitors
|
||||||
|
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
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Check if specified monitors are connected.
|
||||||
|
# Usage: monitors_exist <monitor1> [monitor2] ...
|
||||||
|
monitors_exist() {
|
||||||
|
set -euo pipefail
|
||||||
|
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))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$matches" -ne "$#" ]; then
|
||||||
|
echo "Amount of devices does not match the amount of arguments ($#: $*)" 1>&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Disable all monitors that are not connected.
|
||||||
|
# Usage: disable_disconnected_monitors
|
||||||
|
disable_disconnected_monitors() {
|
||||||
|
set -euo pipefail
|
||||||
|
local connected_monitors
|
||||||
|
connected_monitors=$(get_monitors disconnected)
|
||||||
|
|
||||||
|
echo "Disabling disconnected monitors"
|
||||||
|
|
||||||
|
for monitor in $connected_monitors; do
|
||||||
|
echo "Disabling monitor $(cut -d',' -f1 <<<"$monitor")"
|
||||||
|
xrandr --output "$(cut -d',' -f1 <<<"$monitor")" --off
|
||||||
|
done
|
||||||
|
}
|
Loading…
Reference in a new issue