commit c7004b2ad7c8d87dffd01a923de1f3235ef0387a
Author: Giygas <mvk@girlpoison.org>
Date: Sun, 2 Jun 2024 01:53:47 +0200
Initial commit
Diffstat:
A | README | | | 78 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | dnavigate | | | 76 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | examples/README | | | 1 | + |
A | examples/abducate | | | 47 | +++++++++++++++++++++++++++++++++++++++++++++++ |
A | examples/abduco | | | 40 | ++++++++++++++++++++++++++++++++++++++++ |
5 files changed, 242 insertions(+), 0 deletions(-)
diff --git a/README b/README
@@ -0,0 +1,78 @@
+Intro
+----------
+dnavigate is a wrapper for dmenu designed to accomodate the easy creation of menu trees. It does not depend on any patches, though noinput is recommended. The author enjoys mousesupport, too.
+
+dnavigate renders a menu based on a given tree and branch. A tree corresponds to a shell script located in DNAVIGATE_DIR ('$HOME/dnavigate.d' by default), which is executed with branch as its sole argument. The selected branch defines several variables, which dnavigate uses to construct a menu and execute a command corresponding to the selected option.
+
+An option may run the custom child command, which recursively calls dnavigate on the same tree, with a new branch. The dmenu prompt is updated accordingly, inheriting the prompt of the branch that spawned the child. Canceling out of a child will re-execute dnavigate again for the parent branch.
+
+Installation
+----------
+Not really, no.
+
+Usage
+----------
+dnavigate [-p parents] tree [branch]
+
+OPTIONS
+ -p parents
+ Specify a space-delimited list of parent branches, from oldest to newest.
+ dnavigate will build a prompt from every specified branch, and execute
+ itself with the most recent parent if the user cancels out of dmenu.
+
+TREES AND BRANCHES
+ When invoked, dnavigate executes a shell script in DNAVIGATE_DIR whose name
+ corresponds to tree, passing branch as its only argument. If no branch was
+ specified on the command line, dnavigate will assume 'main' as the branch.
+
+ The tree script initialises a number of shell variables that define menu items
+ and options. These variables are LEAVES, ACTIONS, PROMPT, OPTIONS, LINES and
+ INPUT.
+
+ LEAVES defines a newline-delimited list of menu items to be displayed by dmenu.
+
+ ACTIONS defines a newline-delimited list of commands, corresponding one-to-one
+ with the items listed in LEAVES. The two must have the same number of items.
+ When the user selects one of the items in LEAVES, the corresponding command
+ is executed. If INPUT is set, this variable will be ignored.
+
+ (Optional) PROMPT defines the dmenu prompt. It may be left empty. You must
+ set this variable to allow dnavigate to build up a prompt from a stack of
+ parents; manually specifying dmenu -p breaks this behaviour.
+
+ (Optional) INPUT defines a command that dnavigate will pass the given input
+ to. If set, LEAVES will still be drawn but ACTIONS will be ignored.
+
+ (Optional) LINES defines the number of rows drawn by dmenu -l.
+ dnavigate will automatically pad the LEAVES and ACTIONS strings with extra
+ newlines and no-ops if necessary.
+
+ (Optional) OPTIONS defines any further options for dmenu.
+
+CHILD, RUN, BACK & CANCEL
+ dnavigate provides three special functions that can be included in the
+ ACTIONS list: run, child and back
+
+ By default, dnavigate will block until the selected command has finished
+ executing. The run command will execute its arguments as a background
+ process and dnavigate will exit promptly.
+
+ The child command executes dnavigate -p with the same tree, passing its
+ only argument along as the new branch, replicating the behaviour of a
+ submenu.
+
+ The back command executes dnavigate on the direct parent. If no parents
+ are left, dnavigate will run the cancel function defined in the tree file,
+ if it exists. The back command is executed automatically if the user cancels
+ out of dmenu.
+
+ENVIRONMENT VARIABLES
+ DNAVIGATE_DIR
+ Defines the directory that dnavigate searches for trees
+ ('$HOME/.dnavigate.d' by default)
+
+EXAMPLES
+ The examples/ directory contains several example trees that provide a
+ starting-off point for creating your own. Among these are dmenu
+ interfaces for abduco & abducate session management.
+
diff --git a/dnavigate b/dnavigate
@@ -0,0 +1,76 @@
+#!/bin/sh
+PROGNAME="$(basename $0)"
+FOREST="${DNAVIGATE_DIR=$HOME/.$PROGNAME.d}"
+
+err() { echo "$PROGNAME: "$* >&2; }
+usage() { err "usage: $0 [-p parent stack] menu [dmenu options]"; }
+child() { $0 -p "$PARENTS $BRANCH" $TREE $1; }
+run() { $SHELL -c "$*" & }
+
+buildprompt() {
+ for B in $*; do
+ source "$FOREST/$TREE" $B
+ PPROMPT="$PPROMPT${PROMPT+"$PROMPT => "}"
+ unset PROMPT
+ done
+ unset LEAVES ACTIONS INPUT OPTIONS LINES RETURN
+}
+
+runparent() {
+ CENN="$(awk '{print $NF}' <<<"$PARENTS")"
+ GRANDCENNS="$(awk '{$NF=""; print}' <<<"$PARENTS")"
+ $0 -p "$GRANDCENNS" $TREE $CENN
+}
+
+back() {
+ if [ "$PARENTS" ]; then runparent
+ else
+ typeset -f cancel >&- && cancel
+ exit 0
+ fi
+}
+
+while getopts "p:" opt; do
+ case "${opt}" in
+ p)
+ PARENTS="${OPTARG}"
+ shift 2;;
+ *)
+ usage
+ exit 1;;
+ esac
+done
+
+TREE=$1;
+BRANCH=$2; [ $BRANCH ] && shift
+if [ ! "$TREE" ]; then usage; exit 1; fi
+buildprompt "$PARENTS"
+
+source $FOREST/$TREE "${BRANCH:=main}" || exit 1
+
+NLEAVES=$(wc -l <<<"$LEAVES")
+NACTIONS=$(wc -l <<<"$ACTIONS")
+if [ ! "$INPUT" ]; then
+ [ $NLEAVES -ne $NACTIONS ] && err "leaf and action count mismatch for '$TREE/$BRANCH'" && exit 1
+fi
+
+if [ $NLEAVES -lt ${LINES=$NLEAVES} ]; then
+ LEAVES=$(awk -v l=$LINES '1;END{for(;NR<l;NR++)print" "}' <<<"$LEAVES")
+ ACTIONS=$(awk -v l=$LINES '1;END{for(;NR<l;NR++)print"back"}' <<<"$ACTIONS")
+fi
+
+SELECTED="$(echo -n "$LEAVES" | dmenu -p "$PPROMPT$PROMPT" -l $LINES $OPTIONS)"
+if [ ! "$SELECTED" ]; then back; exit; fi
+
+if [ "$INPUT" ]; then
+ $INPUT $SELECTED
+else
+ LINE="$(awk -F. -v s="$SELECTED" '$0==s{print NR;exit}' <<<"$LEAVES")"
+ ACTION="$(awk -v l="$LINE" 'NR==l' <<<"$ACTIONS")"
+
+
+ export PARENTSEL="$SELECTED"
+ $ACTION
+fi
+
+if [ "$RETURN" ]; then back; exit; fi
diff --git a/examples/README b/examples/README
@@ -0,0 +1 @@
+Example trees. Put in your .dnavigate.d directory and execute via dnavigate.
diff --git a/examples/abducate b/examples/abducate
@@ -0,0 +1,47 @@
+#!/bin/bash
+OPTIONS="-noi"
+TERMEXEC="urxvt -e"
+
+SERVICES=\
+"picom picom
+sxhkd sxhkd
+mousejail xpointerbarrier 0 0 50 0"
+
+case $1 in
+main)
+ unset LEAVES ACTIONS
+ PROMPT="$(abducate list | sed 1q)"
+ SESSIONS="$(abducate list | sed -n '2,$s/\t/ /gp')"
+ while read -r S; do
+ read LABEL _ <<<"$S"
+ RUNNING=$(awk -v label=$LABEL '$NF==label{print}' <<<"$SESSIONS")
+ if [ "$RUNNING" ]; then
+ LEAVES="$LEAVES$RUNNING
+"
+ else
+ LEAVES="$LEAVES ... ................... ..... $LABEL
+"
+ fi
+ done <<<"$SERVICES"
+
+ LEAVES=$(awk '$0' <<<"$LEAVES")
+ ACTIONS=$(awk '$0{print "child manage"}' <<<"$LEAVES");;
+manage)
+ SESSION=$(awk '$0=$NF' <<<"$PARENTSEL")
+ while read -r S; do
+ read LABEL COMMAND <<<"$S"
+ [ "$LABEL" = "$SESSION" ] && break
+ done <<<"$SERVICES"
+
+ PROMPT="Manage service: $SESSION"
+ LEAVES=\
+"on
+off
+toggle
+attach"
+ ACTIONS=\
+"abducate -nl $LABEL start $COMMAND
+abducate -nl $LABEL stop
+abducate -nl $LABEL toggle $COMMAND
+$TERMEXEC abducate -l $LABEL attach";;
+esac
diff --git a/examples/abduco b/examples/abduco
@@ -0,0 +1,40 @@
+#!/bin/bash
+# A simple abduco session management menu
+OPTIONS="-noi"
+TERMEXEC="urxvt -e"
+
+# Suggestions for sessions
+SUGGESTIONS=""
+
+case $1 in
+main)
+ PROMPT="$(abduco | sed 1q)"
+ LEAVES="+ new session..."
+ ACTIONS="child new"
+
+ SESSIONS=$(abduco | sed -n '2,$s/\t/ /gp' | awk 'NF{print}')
+ if [ "$SESSIONS" ]; then
+ LEAVES="$SESSIONS
+$LEAVES"
+ ACTIONS="$(awk '{print "child manage"}' <<<"$SESSIONS")
+$ACTIONS"
+ fi;;
+manage)
+ PARENTSEL="$(sed 's/^[*+] //' <<<"$PARENTSEL" | tr -s "[:blank:]" " ")"
+ SESSION=$(awk '$1=$2=$3=$4="";1' <<<"$PARENTSEL" | sed 's/^ *//')
+ PID=$(awk '{print $4}' <<<"$PARENTSEL")
+ PROMPT="Manage session: $SESSION"
+ LEAVES="attach
+kill"
+ ACTIONS="run $TERMEXEC abduco -a '$SESSION'
+kill $PID";;
+new)
+ unset OPTIONS
+ LEAVES="$SUGGESTIONS"
+ INPUT="create"
+ PROMPT="new session: ";;
+esac
+
+create() {
+ $TERMEXEC abduco -c "${*:=$SHELL}.$$" $* &
+}