wren-linux / bin / wren-autostart
#!/bin/bash
# wren-autostart: written by Wren Linux parts/autostart.sh
# What opens when you log in, and on which workspace. It is all in one file,
# ~/.config/wren/autostart: a line for each program, its workspace (1 to 10) and
# then the command, as you would type it. Change the file, or use this:
# wren-autostart edit a small screen to change it (the Wren menu:
# Settings > Startup programs)
# wren-autostart list what opens where
# wren-autostart add 3 firefox open firefox on workspace 3 (a program already
# listed moves there)
# wren-autostart remove firefox not any more
# sway runs it at login (wren-autostart, with no word). It starts each program on
# its workspace, then shows the workspace of the first one listed. A change counts
# from your next login.
set -uo pipefail
FILE=$HOME/.config/wren/autostart
die() { printf 'wren-autostart: %s\n' "$*" >&2; exit 1; }
workspace_ok() { [[ $1 =~ ^([1-9]|10)$ ]]; }
command_ok() { [ -n "$1" ] && [[ $1 != *[[:cntrl:]]* ]] && [[ $1 != \#* ]]; }
# entries: the file's good lines: LINE-NUMBER, WORKSPACE, COMMAND, tab-separated
entries() {
local n=0 line ws cmd
[ -f "$FILE" ] || return 0
while IFS= read -r line || [ -n "$line" ]; do
n=$((n + 1))
[[ $line =~ ^[[:space:]]*([0-9]+)[[:space:]]+(.*[^[:space:]])[[:space:]]*$ ]] || continue
# (the checks below use a pattern too, which would replace these)
ws=${BASH_REMATCH[1]} cmd=${BASH_REMATCH[2]}
if workspace_ok "$ws" && command_ok "$cmd"; then
printf '%s\t%s\t%s\n' "$n" "$ws" "$cmd"
fi
done <"$FILE"
}
# not_understood: the lines that are neither blank, a comment nor good, to say so
not_understood() {
local n=0 line good
[ -f "$FILE" ] || return 0
good=$(entries | cut -f1)
while IFS= read -r line || [ -n "$line" ]; do
n=$((n + 1))
[[ $line =~ ^[[:space:]]*(#.*)?$ ]] && continue
grep -qx "$n" <<<"$good" || printf '%s\n' "${line//[[:cntrl:]]/?}"
done <"$FILE"
}
list() {
local _n ws cmd any=false bad
while IFS=$'\t' read -r _n ws cmd; do
any=true
printf ' workspace %-2s %s\n' "$ws" "$cmd"
done < <(entries)
$any || echo " Nothing opens by itself."
bad=$(not_understood)
if [ -n "$bad" ]; then
echo " Not understood, so skipped (a workspace from 1 to 10, then the command):"
printf '%s\n' "$bad" | sed 's/^/ /'
fi
}
# drop COMMAND: its lines out of the file; the rest stays as it is
drop() {
local n=0 line tmp cmd=$1
tmp=$(mktemp)
while IFS= read -r line || [ -n "$line" ]; do
n=$((n + 1))
if [ "$(entries | awk -F'\t' -v n="$n" '$1 == n { print $3 }')" = "$cmd" ]; then continue; fi
printf '%s\n' "$line"
done <"$FILE" >"$tmp"
cat "$tmp" >"$FILE"
rm -f "$tmp"
}
add() {
local ws=${1:-}
[ $# -ge 2 ] || die "usage: wren-autostart add WORKSPACE COMMAND"
shift
local cmd="$*"
workspace_ok "$ws" || die "a workspace is a number from 1 to 10, not \"$ws\"."
command_ok "$cmd" || die "that isn't a command."
mkdir -p "${FILE%/*}"
touch "$FILE"
drop "$cmd"
printf '%s %s\n' "$ws" "$cmd" >>"$FILE"
# (it is added all the same: it may be installed later)
command -v "${cmd%% *}" >/dev/null || printf 'wren-autostart: note: there is no program called %s here yet.\n' "${cmd%% *}" >&2
}
remove() {
[ $# -ge 1 ] || die "usage: wren-autostart remove COMMAND"
[ -f "$FILE" ] || return 0
drop "$*"
}
# start: each program on its workspace. sway remembers which workspace a program
# was started on, also one that takes a while to open its window, so it opens
# there whatever you are looking at by then. Started through --run, so that sway
# sees only a line number: nothing in a command can be read as a sway command.
start() {
local n ws _cmd first='' one=false
# the one-workspace extra: no keys to reach another, so all open on the one
[ ! -f "$HOME/.config/sway/config.d/one-workspace.conf" ] || one=true
while IFS=$'\t' read -r n ws _cmd; do
if $one; then ws=1; fi
[ -n "$first" ] || first=$ws
swaymsg -q "workspace number $ws"
# shellcheck disable=SC2088 # a literal ~: the shell sway starts expands it
swaymsg -q exec "~/.local/bin/wren-autostart --run $n"
done < <(entries)
[ -z "$first" ] || swaymsg -q "workspace number $first"
}
# run LINE-NUMBER: the command on that line
run() {
local cmd
[[ ${1:-} =~ ^[0-9]{1,6}$ ]] || die "--run takes a line number."
cmd=$(entries | awk -F'\t' -v n="$1" '$1 == n { print $3 }')
[ -n "$cmd" ] || exit 0
exec sh -c "$cmd"
}
edit() {
local input a b c cmds=()
while :; do
echo
echo "What opens when you log in:"
mapfile -t cmds < <(entries | cut -f3)
if [ ${#cmds[@]} -gt 0 ]; then
local i=0 ws
while IFS= read -r ws; do
printf ' %d) workspace %-2s %s\n' "$((i + 1))" "$ws" "${cmds[i]}"
i=$((i + 1))
done < <(entries | cut -f2)
else
echo " Nothing opens by itself."
fi
echo
echo " add 3 firefox open firefox on workspace 3"
echo " move 1 4 line 1 to workspace 4"
echo " remove 1 line 1 no longer opens"
echo " Enter: done. It counts from your next login. (The file: ${FILE/#$HOME/\~})"
IFS= read -r -p "> " input || return 0
read -r a b c <<<"$input"
case ${a:-} in
"" | q | quit | done) return 0 ;;
add) if [ -n "${b:-}" ] && [ -n "${c:-}" ]; then (add "$b" "$c"); else echo "For example: add 3 firefox"; fi ;;
move | remove)
if ! [[ ${b:-} =~ ^[0-9]{1,3}$ ]] || [ "$b" -lt 1 ] || [ "$b" -gt ${#cmds[@]} ]; then
echo "Which line? A number from the list."
elif [ "$a" = remove ]; then
remove "${cmds[b - 1]}"
elif workspace_ok "${c:-}"; then
add "$c" "${cmds[b - 1]}"
else
echo "To which workspace? For example: move 1 4"
fi ;;
*) echo "Type add, move or remove, as shown; Enter when you are done." ;;
esac
done
}
case ${1:-} in
"") start ;;
--run) run "${2:-}" ;;
list) list ;;
add) shift; add "$@" ;;
remove) shift; remove "$@" ;;
edit) edit ;;
-h | --help) sed -n '2,13p' "$0" | sed 's/^# \{0,1\}//' ;;
*) die "usage: wren-autostart [list | edit | add WORKSPACE COMMAND | remove COMMAND]" ;;
esac