258 lines
8.1 KiB
Bash
Executable file
258 lines
8.1 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
|
|
# set -euo pipefail
|
|
|
|
print_help() {
|
|
echo ""
|
|
echo "Usage: ndrop [OPTIONS] [COMMAND]"
|
|
echo ""
|
|
echo "Command:"
|
|
echo " The usual command you would run to start the desired program"
|
|
echo ""
|
|
echo "Options:"
|
|
echo " -c, --class"
|
|
echo " Set classname of the program to be run. Use this if the"
|
|
echo " classname is different from the name of the [COMMAND]"
|
|
echo " and ndrop has no hardcoded replacement."
|
|
echo ""
|
|
echo " -F, --focus"
|
|
echo " Changes the default behaviour: focus the specified program's"
|
|
echo " window and switch to its present workspace if necessary."
|
|
echo " Do not hide it, if it's already on the current workspace."
|
|
echo ""
|
|
echo " -H, --help"
|
|
echo " Print this help message"
|
|
echo ""
|
|
echo " -i, --insensitive"
|
|
echo " Case insensitive partial matching of class names."
|
|
echo " You can try this if a running program is not recognized and"
|
|
echo " a new instance is launched instead."
|
|
echo ""
|
|
echo " -o, --online"
|
|
echo " Delay initial launch for up to 20 seconds"
|
|
echo " until internet connectivity is established."
|
|
echo ""
|
|
echo " -v, --verbose"
|
|
echo " Show notifications regarding the matching process."
|
|
echo " Use this to figure out why running programs are not matched."
|
|
echo ""
|
|
echo " -V, --version"
|
|
echo " Print version"
|
|
echo ""
|
|
echo "Multiple instances:"
|
|
echo ""
|
|
echo "Multiple instances of the same program can be run concurrently, if"
|
|
echo "different class names are assigned to each instance. Presently there is"
|
|
echo "support for the following flags in the [COMMAND] string:"
|
|
echo ""
|
|
echo " -a ('foot' terminal emulator)"
|
|
echo " --class (all other programs)"
|
|
echo ""
|
|
echo "See man page for more information"
|
|
}
|
|
|
|
print_version() {
|
|
echo "ndrop version: 0.1.0-dev"
|
|
}
|
|
|
|
notify() {
|
|
notify-send "$@"
|
|
echo "$@"
|
|
}
|
|
|
|
notify_low() {
|
|
notify-send -u low "$@"
|
|
echo "$@"
|
|
}
|
|
|
|
partial_match() {
|
|
CLASS=$(niri msg --json windows | jq -r ".[] | select((.app_id |match(\"$1\";\"i\"))) | .app_id" | head -1)
|
|
}
|
|
|
|
wait_online() {
|
|
wait_online=0
|
|
while [[ $wait_online -lt 200 ]]; do
|
|
ping -qc 1 github.com && break
|
|
sleep 0.1
|
|
$wait_online++
|
|
done
|
|
}
|
|
|
|
active_workspace() {
|
|
active_workspace_name=$(niri msg --json workspaces | jq -r ".[] | select(.is_focused==true) | .name")
|
|
if [[ "$active_workspace_name" != "null" ]]; then
|
|
echo "$active_workspace_name"
|
|
else
|
|
niri msg --json workspaces | jq -r ".[] | select(.is_focused==true) | .id"
|
|
fi
|
|
}
|
|
|
|
ndrop_flags() {
|
|
while true; do
|
|
case "$1" in
|
|
-c | --class)
|
|
shift
|
|
CLASS_OVERRIDE="$1"
|
|
;;
|
|
-F | --focus)
|
|
FOCUS=true
|
|
;;
|
|
-H | --help)
|
|
print_help
|
|
exit
|
|
;;
|
|
-i | --insensitive)
|
|
INSENSITIVE=true
|
|
;;
|
|
-o | --online)
|
|
ONLINE=true
|
|
;;
|
|
-v | --verbose)
|
|
VERBOSE=true
|
|
;;
|
|
-V | --version)
|
|
print_version
|
|
exit
|
|
;;
|
|
*) break ;;
|
|
esac
|
|
shift
|
|
done
|
|
}
|
|
|
|
FOCUS=false
|
|
INSENSITIVE=false
|
|
ONLINE=false
|
|
VERBOSE=false
|
|
NDROP_FLAGS=()
|
|
|
|
while true; do
|
|
case "$1" in
|
|
"")
|
|
notify "ndrop: Missing Argument" "Run 'ndrop -h' for more information"
|
|
print_help
|
|
exit 1
|
|
;;
|
|
-c | --class)
|
|
NDROP_FLAGS+=("$1" "$2")
|
|
shift
|
|
;;
|
|
-*)
|
|
NDROP_FLAGS+=("$1")
|
|
;;
|
|
*) break ;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
# shellcheck disable=SC2128 # erroneous warning
|
|
if [[ -n $NDROP_FLAGS ]]; then
|
|
# shellcheck disable=SC2046 # avoids having to run 'eval set -- "$NDROP_FLAGS"' to remove leading whitespace
|
|
ndrop_flags $(getopt -u --options c:FHiovV --longoptions class:,focus,help,insensitive,online,verbose,version, -n ndrop -- "${NDROP_FLAGS[@]}")
|
|
fi
|
|
|
|
CLASS="$1"
|
|
COMMANDLINE="${*:1}"
|
|
ACTIVE_WORKSPACE=$(active_workspace)
|
|
ACTIVE_WORKSPACE_ID=$(niri msg --json workspaces | jq -r ".[] | select(.is_focused==true) | .id")
|
|
# ACTIVE_OUTPUT=$(niri msg --json focused-output | jq -r ".name")
|
|
# NDROP_OUTPUT=$(niri msg --json workspaces | jq -r ".[] | select(.name==\"ndrop\") | .output")
|
|
|
|
case "$1" in
|
|
epiphany)
|
|
CLASS="org.gnome.Epiphany"
|
|
;;
|
|
brave)
|
|
CLASS="brave-browser"
|
|
;;
|
|
foot)
|
|
OPT=$(getopt --options a: --longoptions app-id: -n ndrop -- "$@")
|
|
;;
|
|
godot4)
|
|
partial_match "org.godotengine."
|
|
;;
|
|
logseq)
|
|
CLASS="Logseq"
|
|
;;
|
|
telegram-desktop)
|
|
CLASS="org.telegram.desktop"
|
|
;;
|
|
tor-browser)
|
|
CLASS="Tor Browser"
|
|
;;
|
|
*)
|
|
OPT=$(getopt --options "" --longoptions class: -n ndrop -- "$@")
|
|
;;
|
|
esac
|
|
|
|
if $VERBOSE && [[ $1 != "$CLASS" ]]; then notify_low "ndrop: Using '$CLASS' as hardcoded classname of '$1' for matching"; fi
|
|
|
|
if [[ -n $OPT ]]; then
|
|
eval set -- "$OPT" # remove leading whitespace
|
|
case "$1" in
|
|
-a | --class)
|
|
CLASS="$2"
|
|
if $VERBOSE; then notify_low "ndrop: Extracted '$CLASS' from [COMMAND] for matching"; fi
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
if $INSENSITIVE && [[ -n $(niri msg --json windows | jq -r ".[] | select((.app_id |test(\"$CLASS\";\"i\")))") ]]; then
|
|
if $VERBOSE; then notify_low "ndrop: --insensitive -> Insensitive (partial) match of class '$CLASS' successful"; fi
|
|
CLASS=$(niri msg --json windows | jq -r ".[] | select((.app_id |match(\"$CLASS\";\"i\"))) | .app_id" | head -1) || notify "ndrop: Error assigning case insensitive (partial) match to CLASS"
|
|
if $VERBOSE; then notify_low "ndrop: --insensitive -> Using class '$CLASS' after insensitive (partial) matching"; fi
|
|
fi
|
|
|
|
if [[ -n $CLASS_OVERRIDE ]]; then
|
|
if $VERBOSE; then notify_low "ndrop: --class -> Using given class '$CLASS_OVERRIDE' instead of '$CLASS' for matching"; fi
|
|
CLASS="$CLASS_OVERRIDE"
|
|
fi
|
|
|
|
WINDOW_ID=$(niri msg --json windows | jq -r ".[] | select(.app_id==\"$CLASS\") | .id")
|
|
FOCUSED=$(niri msg --json windows | jq -r ".[] | select(.app_id==\"$CLASS\") | .is_focused")
|
|
if [[ -n ${WINDOW_ID} ]]; then
|
|
if [[ "${ACTIVE_WORKSPACE}" == "ndrop" ]]; then
|
|
if ${VERBOSE}; then
|
|
notify_low "ndrop: CLASS: ${CLASS}, ACTIVE_WORKSPACE_ID: ${ACTIVE_WORKSPACE_ID}, ACTIVE_WORKSPACE: ${ACTIVE_WORKSPACE}, WINDOW_ID: ${WINDOW_ID}"
|
|
notify_low "ndrop: Matched we are on ws 'ndrop'; focus previous ws"
|
|
fi
|
|
niri msg action focus-workspace-previous
|
|
exit 0
|
|
else
|
|
if [[ $FOCUSED == true ]]; then
|
|
if $VERBOSE; then
|
|
notify_low "ndrop: CLASS: ${CLASS}, ACTIVE_WORKSPACE_ID: ${ACTIVE_WORKSPACE_ID}, ACTIVE_WORKSPACE: ${ACTIVE_WORKSPACE}, WINDOW_ID: ${WINDOW_ID}"
|
|
notify_low "ndrop: Matched focused target on active ws '${ACTIVE_WORKSPACE}'; move target to 'ndrop'"
|
|
fi
|
|
niri msg action move-window-to-workspace --window-id "${WINDOW_ID}" "ndrop"
|
|
niri msg action focus-workspace-previous
|
|
# For good multi-monitor support, we need something like this in niri.
|
|
# if [[ "$ACTIVE_OUTPUT" != "$NDROP_OUTPUT" ]]; then
|
|
# niri msg action focus-monitor "${ACTIVE_OUTPUT}"
|
|
# fi
|
|
niri msg action focus-workspace "${ACTIVE_WORKSPACE}"
|
|
exit 0
|
|
else
|
|
if [[ $VERBOSE == true ]]; then
|
|
notify_low "ndrop: CLASS: ${CLASS}, ACTIVE_WORKSPACE_ID: ${ACTIVE_WORKSPACE_ID}, ACTIVE_WORKSPACE: ${ACTIVE_WORKSPACE}, WINDOW_ID: ${WINDOW_ID}"
|
|
# notify_low "ndrop: Matched unfocused target on active ws '${ACTIVE_WORKSPACE}'; focu'"
|
|
fi
|
|
niri msg action move-window-to-workspace --window-id "${WINDOW_ID}" "${ACTIVE_WORKSPACE}"
|
|
niri msg action focus-window --id "${WINDOW_ID}"
|
|
niri msg action set-window-width 90% && niri msg action set-window-height 70%
|
|
exit 0
|
|
fi
|
|
fi
|
|
else
|
|
if $ONLINE; then wait_online; fi
|
|
# 'foot' always throws an error when its window is closed. Thus we disable the notification.
|
|
if [[ $COMMAND == "foot" ]]; then
|
|
# shellcheck disable=SC2086 # when quoting COMMANDLINE the execution of the command fails
|
|
$COMMANDLINE
|
|
else
|
|
# shellcheck disable=SC2086 # when quoting COMMANDLINE the execution of the command fails
|
|
$COMMANDLINE || notify "ndrop: Error executing given command" "$COMMANDLINE"
|
|
niri msg action set-window-width 90% && niri msg action set-window-height 70%
|
|
fi
|
|
if $VERBOSE; then notify_low "ndrop: No running program matches class '$CLASS'." "Currently active classes are '$(niri msg --json windows | jq -r '.[] | .app_id' | sort | tr '\n' ' ')'. Executed '$COMMANDLINE' in case it was not running already."; fi
|
|
fi
|