dnavigate

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.girlpoison.org/dnavigate
Log | Files | Refs | README

commit c7004b2ad7c8d87dffd01a923de1f3235ef0387a
Author: Giygas <mvk@girlpoison.org>
Date:   Sun,  2 Jun 2024 01:53:47 +0200

Initial commit

Diffstat:
AREADME | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Adnavigate | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aexamples/README | 1+
Aexamples/abducate | 47+++++++++++++++++++++++++++++++++++++++++++++++
Aexamples/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}.$$" $* & +}