initial codebase

This commit is contained in:
DrMaxNix 2024-05-01 19:48:46 +02:00
parent ccfc21a116
commit 94478aba82
8 changed files with 507 additions and 0 deletions

1
VERSION Normal file
View File

@ -0,0 +1 @@
1.0.0

36
lib/color Normal file
View File

@ -0,0 +1,36 @@
#!/bin/bash
## EMPTY DEFAULTS ##
color_bold=""
color_underline=""
color_standout=""
color_normal=""
color_black=""
color_red=""
color_green=""
color_yellow=""
color_blue=""
color_magenta=""
color_cyan=""
color_white=""
## ONLY IF PRINTING TO A TERMINAL ##
if test -t 1; then
# check count of supported colors
color_count=$(tput colors)
if [[ ! -z "$color_count" && "$color_count" -ge 8 ]]; then
color_bold="$(tput bold)"
color_underline="$(tput smul)"
color_standout="$(tput smso)"
color_normal="$(tput sgr0)"
color_black="$(tput setaf 0)"
color_red="$(tput setaf 1)"
color_green="$(tput setaf 2)"
color_yellow="$(tput setaf 3)"
color_blue="$(tput setaf 4)"
color_magenta="$(tput setaf 5)"
color_cyan="$(tput setaf 6)"
color_white="$(tput setaf 7)"
fi
fi

34
lib/ini Normal file
View File

@ -0,0 +1,34 @@
#!/bin/bash
ini_parse(){
local filename="$1"
sections="$(gawk '{ if ($1 ~ /^\[/) section=tolower(gensub(/\[(.+)\]/,"\\1",1,$1)); configuration[section]=1 } END {for (key in configuration) { print key} }' $filename)"
for section in $sections; do
array_name="ini_${section}"
declare -g -A ${array_name}
done
eval $(gawk -F= '{
if($1 ~ /^\[/)
section=tolower(gensub(/\[(.+)\]/,"\\1",1,$1))
else if($1 !~ /^$/ && $1 !~ /^;/){
gsub(/^[ \t]+|[ \t]+$/, "", $1);
gsub(/[\[\]]/, "", $1);
gsub(/^[ \t]+|[ \t]+$/, "", $2);
if(configuration[section][$1] == "")
configuration[section][$1]=$2
else
configuration[section][$1]=configuration[section][$1]" "$2
}
}
END {
for(section in configuration)
for(key in configuration[section]){
section_name = section
gsub("-", "_", section_name)
print "ini_" section_name "[\""key"\"]=\""configuration[section][key]"\";"
}
}' ${filename}
)
}

40
lib/log Normal file
View File

@ -0,0 +1,40 @@
#!/bin/bash
#
# Log a line with debug level.
#
log_debug(){
echo -ne "${color_green}[DEBUG]${color_normal} " 1>&2
echo "$1" 1>&2
}
#
# Log a line with info level.
#
log_info(){
echo -ne "${color_blue}[INFO ]${color_normal} " 1>&2
echo "$1" 1>&2
}
#
# Log a line with warning level.
#
log_warn(){
echo -ne "${color_yellow}[WARN ]${color_normal} " 1>&2
echo "$1" 1>&2
}
#
# Log a line with error level and exit.
#
log_error(){
echo -ne "${color_red}[ERROR]${color_normal} " 1>&2
echo "$1" 1>&2
exit 1
}

23
lib/toolcheck Normal file
View File

@ -0,0 +1,23 @@
#!/bin/bash
#
# Make sure all required tools are installed.
#
workplace_toolcheck(){
## BINARIES IN PATH ##
bin_tool_list=("grep xeventbind")
for b in $bin_tool_list; do
if [[ ! -f $(which "$b") ]]; then
log_error "Missing tool '$b'"
fi
done
## DPKG PACKAGES ##
dpkg_tool_list=("gawk sed findutils procps usbutils libglib2.0-bin")
for d in $dpkg_tool_list; do
if ! dpkg -s $d 2>&1 | grep -q "Status: install ok installed"; then
log_error "Missing tool '$d'"
fi
done
}

351
workplace Executable file
View File

@ -0,0 +1,351 @@
#!/bin/bash
set -euo pipefail
## INIT ##
# get script base directory
SCRIPT_PATH=$(readlink -f "${BASH_SOURCE[0]}")
SCRIPT_DIR=$(dirname -- "$SCRIPT_PATH")
# store arguments globally
ARG_LIST=("$@")
# get version number
VERSION=$(cat ${SCRIPT_DIR}/VERSION | xargs)
# location of workplace config file
CONFIG_PATH="${HOME}/.config/workplace.ini"
# load libraries
source "${SCRIPT_DIR}/lib/color"
source "${SCRIPT_DIR}/lib/log"
source "${SCRIPT_DIR}/lib/toolcheck"
source "${SCRIPT_DIR}/lib/ini"
#
# Explain usage of script.
#
workplace_help(){
echo "WorkPlace v${VERSION}"
echo
echo "Usage: $0 <command>"
echo
echo "Commands:"
echo " set <name> - Set workplace"
echo " update - Automatically determine current workplace setup"
echo " updatedaemon - Run as daemon and call update when setup changes are detected"
echo
echo " help - Display this help message and exit"
echo " version - Display version information and exit"
}
#
# Display version information.
#
workplace_version(){
echo "WorkPlace v${VERSION}"
echo "(c) 2024 DrMaxNix"
}
#
# Run commands `laptop` and `dock`.
#
workplace_set(){
## FIND CONFIG SECTION ##
# make sure param is present
if [[ ! ${1:-""} ]]; then
log_error "Missing workplace name; See '$0 help' for usage information"
fi
# try to find section
workplace_section=""
for n in $(workplace_available_name_list); do
if [[ "$n" == "$1" ]]; then
workplace_name="$n"
fi
done
if [[ ! ${workplace_name:-""} ]]; then
log_error "Invalid workplace name '$1'; Check config file"
fi
## RUN SET COMMANDS ##
local -n workplace_section_name="ini_$workplace_name"
for k in "${!workplace_section_name[@]}"; do
# make sure this is a `set` command
if [[ ! "$k" =~ ^set\. ]]; then
continue
fi
# extract command and parameter
command=$(echo "$k" | grep -oPe '(?<=^set.).*')
param="${workplace_section_name[$k]}"
# run command
workplace_setcommand_run "$command" "$param"
done
}
#
# Run command `update`.
#
workplace_update(){
## DETECT SETUP ##
# connected usb devices
usb_id_list=$(workplace_usb_id_list)
## FILTER ##
workplace_list_prio_0=""
workplace_list_prio_1=""
for n in $(workplace_available_name_list); do
local -n workplace_section_name="ini_$n"
# default
trigger_default="${workplace_section_name[trigger.default]:-""}"
if [[ "$trigger_default" == "true" ]]; then
workplace_list_prio_0="${workplace_list_prio_0} $n"
fi
# usb-id
trigger_usbid="${workplace_section_name[trigger.usb-id]:-""}"
if [[ "$trigger_usbid" ]]; then
for i in $usb_id_list; do
if [[ "$i" == "$trigger_usbid" ]]; then
workplace_list_prio_1="${workplace_list_prio_1} $n"
break
fi
done
fi
done
workplace_list_prio_0="$(echo $workplace_list_prio_0 | xargs)"
workplace_list_prio_1="$(echo $workplace_list_prio_1 | xargs)"
## SELECT BEST ONE ##
workplace=""
# by prio
if [[ "$workplace_list_prio_1" ]]; then
workplace="$workplace_list_prio_1"
elif [[ "$workplace_list_prio_0" ]]; then
workplace="$workplace_list_prio_0"
fi
# make sure we have found one
if [[ ! "$workplace" ]]; then
log_warn "No matching workplace trigger found"
return
fi
## SET NEW WORKPLACE ##
log_info "Switching to workplace '$workplace'"
workplace_set "$workplace"
}
#
# Run command `updatedaemon`.
#
workplace_updatedaemon(){
## INIT ##
# prepare forking
trap "kill -s SIGTERM -- -$$" EXIT
# update once
kill -s SIGUSR1 $$
## LISTEN FOR SCREEN RESOLUTION CHANGE ##
xeventbind resolution /usr/bin/echo |
while read -r; do
kill -s SIGUSR1 $$
done &
## LISTEN FOR LOGIN ##
gdbus monitor -y -d org.freedesktop.login1 |
grep --line-buffered "LockedHint" |
while read -r; do
kill -s SIGUSR1 $$
done &
## KEEP RUNNING ##
while true; do
workplace_handle_change
sleep 0.5s
done
}
#
# HELPER: (Re-)load config.
#
workplace_config_load(){
# clear used variables
for v in $(compgen -A variable | grep -Pe '^ini_'); do
unset $v
done
# parse ini file
ini_parse "$CONFIG_PATH"
}
#
# HELPER: Get defined workplace names.
#
workplace_available_name_list(){
for v in $(compgen -A variable | grep -oPe '(?<=^ini_).*'); do
echo $v
done
}
#
# HELPER: Run a `set` command.
#
workplace_setcommand_run(){
case ${1:-""} in
## CORE ##
# volume
"volume")
if [[ "$2" -le 0 ]]; then
amixer set 'Master',0 "$2%" off > /dev/null || true
else
amixer set 'Master',0 "$2%" on > /dev/null || true
fi
;;
# powerprofile
"powerprofile")
powerprofilesctl set "$2" || true
;;
# text scaling factor
"text-scaling-factor")
dconf write /org/gnome/desktop/interface/text-scaling-factor "$2" || true
;;
## DASH-TO-DOCK EXTENSION ##
# dock position (includes fix for buggy window size)
"dash-to-dock.position")
if [[ "$2" == "LEFT" ]]; then
dconf write /org/gnome/shell/extensions/dash-to-dock/dock-position "'BOTTOM'" || true
else
dconf write /org/gnome/shell/extensions/dash-to-dock/dock-position "'LEFT'" || true
fi
sleep 0.1s
dconf write /org/gnome/shell/extensions/dash-to-dock/dock-position "'$2'" || true
;;
## ERROR HANDLING ##
*)
log_error "Invalid setcommand '$1'"
exit 1
;;
esac
}
#
# HELPER: Get list of connected usb device ids.
#
workplace_usb_id_list(){
lsusb_id_regex='^Bus [0-9]+ Device [0-9]+: ID ([0-9a-f]{4}:[0-9a-f]{4}) .*$'
lsusb_out=$"$(lsusb)"
while IFS= read -r line; do
if [[ "$line" =~ $lsusb_id_regex ]]; then
echo $line | sed -E "s/$lsusb_id_regex/\1/g"
fi
done <<< "$lsusb_out"
}
#
# HELPER: Handle detected setup change
#
trap "workplace_change_flag=1" SIGUSR1
workplace_handle_change(){
## CHECK FLAG ##
# ignore if not set
if [[ ! ${workplace_change_flag:-""} -gt 0 ]]; then
return
fi
# unset flag
workplace_change_flag=0
## DO UPDATE ##
# reload config
workplace_config_load
# run update function
workplace_update
# sleep to prevent spamming
sleep 1s
}
## MAIN ##
case ${1:-""} in
set)
workplace_toolcheck
workplace_config_load
workplace_set "$2"
exit 0
;;
update)
workplace_toolcheck
workplace_config_load
workplace_update
exit 0
;;
updatedaemon)
workplace_toolcheck
workplace_updatedaemon
exit 0
;;
help)
workplace_help
exit 0
;;
version)
workplace_version
exit 0
;;
"")
log_error "No command given; See '$0 help' for usage information"
exit 1
;;
*)
log_error "Invalid command '$1'; See '$0 help' for usage information"
exit 1
;;
esac

5
workplace.desktop Normal file
View File

@ -0,0 +1,5 @@
[Desktop Entry]
Name=WorkPlace Update Daemon
Type=Application
Exec=~/.local/bin/workplace updatedaemon
Terminal=false

17
workplace.template.ini Normal file
View File

@ -0,0 +1,17 @@
[laptop]
trigger.default = true
set.dash-to-dock.position = BOTTOM
set.text-scaling-factor = 1.3700000000000001
set.powerprofile = power-saver
set.volume = 0
[dock]
trigger.usb-id = 1234:5678
set.dash-to-dock.position = LEFT
set.text-scaling-factor = 1.0
set.powerprofile = balanced
set.volume = 90