Adding rofi-waypass to Niri config.

This commit is contained in:
Derek Taylor
2026-01-17 06:37:03 -06:00
parent 369a705f4d
commit 563947d5d6
3 changed files with 500 additions and 2 deletions

View File

@@ -392,6 +392,7 @@ binds {
Mod+Alt+Minus hotkey-overlay-title="Lock screen: swaylock" { spawn "swaylock"; } Mod+Alt+Minus hotkey-overlay-title="Lock screen: swaylock" { spawn "swaylock"; }
Mod+E hotkey-overlay-title="Open emacs" { spawn "emacsclient" "-c" "-a" "emacs"; } Mod+E hotkey-overlay-title="Open emacs" { spawn "emacsclient" "-c" "-a" "emacs"; }
Mod+W hotkey-overlay-title="Open browser: brave" { spawn "brave"; } Mod+W hotkey-overlay-title="Open browser: brave" { spawn "brave"; }
Mod+Shift+P hotkey-overlay-title="Password menu" { spawn "rofi" "-show" "waypass"; }
Mod+Shift+Q hotkey-overlay-title="Quit out of niri" { quit; } Mod+Shift+Q hotkey-overlay-title="Quit out of niri" { quit; }
// Use spawn-sh to run a shell command. Do this if you need pipes, multiple commands, etc. // Use spawn-sh to run a shell command. Do this if you need pipes, multiple commands, etc.
@@ -597,5 +598,5 @@ binds {
// Powers off the monitors. To turn them back on, do any input like // Powers off the monitors. To turn them back on, do any input like
// moving the mouse or pressing any other key. // moving the mouse or pressing any other key.
Mod+Shift+P { power-off-monitors; } Ctrl+Alt+P { power-off-monitors; }
} }

View File

@@ -1,5 +1,5 @@
configuration { configuration {
modi: "window,drun,ssh,combi"; modi: "window,drun,ssh,combi,waypass";
font: "hack 10"; font: "hack 10";
combi-modi: "window,drun,ssh"; combi-modi: "window,drun,ssh";
} }

497
.config/rofi/scripts/waypass.bash Executable file
View File

@@ -0,0 +1,497 @@
#!/usr/bin/env bash
# Copyright (C) 2024 Blyte Scholar
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, version 3.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#########################################################################
# Usage: #
# Place this script in your rofi scripts directory #
# (i.e. copy it to ~/.config/rofi/scripts/waypass) #
# and make it executable. Then invoke it with the following command: #
# #
# rofi -show waypass #
# #
# The custom keybindings 1 and 2 are used to cycle between user/pass #
# and copying/autotyping. These default to Alt+1 and Alt+2 but can be #
# set during the rofi invocation with the -kb-custom-1 and #
# -kb-custom-2 switches. #
# #
# For example, to set Alt-a to toggle auto-typing and Alt-p to cycle #
# between user/pass/both: #
# rofi -show waypass -kb-custom-1 "Alt+p" -kb-custom-2 "Alt+a" #
# #
# #
# Keybindings: #
# kb-custom-1: cycle b/w username, password, and both #
# kb-custom-2: toggle between copy to clipboard and auto-typing #
#########################################################################
#########################################################################
# USER Configuration Variables #
# #
# # Notification #
# The icon can be the name of an icon or the full path to one. #
# #
# # Prompt #
# The rofi prompt will be formed from $pre$content$post where $content #
# is determined by which data is being acted on #
# (username/password/both). In the case of both, the separator is #
# added between them. #
#########################################################################
SEND_NOTIFICATIONS=1
NOTIFICATION_ICON="dialog-password"
NOTIFICATION_APP_NAME="Password Store"
CLIPBOARD_PROMPT_PRE="Copy "
CLIPBOARD_PROMPT_POST=" to clipboard"
USERNAME_PROMPT="username"
PASSWORD_PROMPT="password"
AUTOTYPE_PROMPT_PRE="Autotype "
AUTOTYPE_PROMPT_POST=""
MULTIPLE_SEP_PROMPT=" and "
## Autotype Configuration
WAIT_BEFORE_TYPING=0.2
## Password Store Configuration
PASSWORD_STORE_DIR="${PASSWORD_STORE_DIR:-$HOME/.password-store}"
PASSWORD_STORE_EXTENSIONS_DIR="${PASSWORD_STORE_EXTENSIONS_DIR:-$PASSWORD_STORE_DIR/.extensions}"
PASSWORD_STORE_SYSTEM_EXTENSIONS_DIR="/usr/lib/password-store/extensions"
PASSWORD_STORE_CLIP_TIME="${PASSWORD_STORE_CLIP_TIME:-5}"
PASS_EXTRA_ARGS=""
PASS="pass $PASS_EXTRA_ARGS"
DEBUG_MODE=0
#########################################################################
# Global Constants #
#########################################################################
## ROFI_RETV states
_ROFI_INIT=0
_ROFI_SELECTED=1
_ROFI_CUSTOM=2
# These signal custom hotkeys
_AUTOTYPE=10
_CYCLE_MODE=11
## Values signifying active mode
_MODE_PASSWORD=0
_MODE_USERNAME=1
_MODE_USERPASS=2
_MODE_MAX=3
_AUTOTYPE_ENABLED=1
#########################################################################
# Password Store Extensions #
# The optional `pass attr` extension is required to check if a username #
# if present in a multiline file. #
#########################################################################
_pass_attr=0
if [ -e "$PASSWORD_STORE_SYSTEM_EXTENSIONS_DIR/attr.bash" ]
then
_pass_attr=1
elif [ -e "$PASSWORD_STORE_EXTENSIONS_DIR/attr.bash" ] &&
[ "$PASSWORD_STORE_ENABLE_EXTENSIONS" = true ]
then
_pass_attr=1
fi
#########################################################################
# Start of main script #
#########################################################################
main() {
shopt -s nullglob globstar
check_dependencies
pass_file="$1"
# Defaults to copying password to clipboard.
_mode=$_MODE_PASSWORD
_autotype=0
_window_state=""
# Hotkeys are used to cycle between modes.
rofi_set_option use-hot-keys true
# Typing in a custom entry should not be supported.
rofi_set_option no-custom true
# Restore saved data.
#
# rofi runs the script multiple times so this needed to persist
# variables.
eval "$ROFI_DATA"
# Contains the return code of the previous rofi run. Used to
# signify state of rofi/what action was taken including custom
# hotkeys.
case "$ROFI_RETV" in
"$_ROFI_SELECTED")
# A password has been selected.
rofi_act_on_file "$pass_file" || die "Error reading from '$pass_file'"
exit 0
;;
"$_CYCLE_MODE")
# Cycle between available modes.
((_mode = (_mode + 1) % _MODE_MAX))
;;
"$_AUTOTYPE")
# Toggle between copying and autotyping.
((_autotype ^= 1))
if [ $_autotype -eq 1 ]
then
lock_window > /dev/null
else
restore_state > /dev/null
fi
;;
esac
# Store variables for next iteration.
rofi_store_data
# Update the rofi prompt based on the current mode.
rofi_update_prompt
# Display all available password files in rofi.
rofi_display_password_files
}
#########################################################################
# Utility Functions #
#########################################################################
die() {
printf "%s\n" "$*" 1>&2
rofi_set_option prompt Error
rofi_set_option message "$*"
exit 1
}
debug() {
[ $DEBUG_MODE -eq 1 ] && printf "%s\n" "$*" 1>&2
}
check_dependencies() {
for bin in pass notify-send wtype rofi
do
command -v "$bin" >/dev/null || die "No executable '$bin' found in PATH."
done
}
#########################################################################
# Rofi-specific functions #
#########################################################################
rofi_set_option() {
printf '\0%s\x1f%s\n' "$1" "$2"
}
rofi_store_data() {
save_state
rofi_set_option data "_autotype=$_autotype; _mode=$_mode; _window_state=$_window_state"
debug "Data stored: '$ROFI_DATA'"
}
rofi_update_prompt() {
# Use the prompt for copying.
if [ $_autotype -eq 0 ]; then
if [ $_mode -eq $_MODE_USERNAME ]; then
copy_prompt="$USERNAME_PROMPT"
else
# When username and password are selected, copy password.
copy_prompt="$PASSWORD_PROMPT"
fi
prompt="$CLIPBOARD_PROMPT_PRE$copy_prompt$CLIPBOARD_PROMPT_POST"
else
# Use the prompt for autotyping.
prompt="$AUTOTYPE_PROMPT_PRE"
if [ $_mode -gt $_MODE_PASSWORD ]
then
prompt="$prompt$USERNAME_PROMPT"
[ $_mode -eq $_MODE_USERPASS ] && prompt="$prompt$MULTIPLE_SEP_PROMPT"
fi
if [ $_mode -eq $_MODE_PASSWORD ] || [ $_mode -eq $_MODE_USERPASS ]
then
prompt="$prompt$PASSWORD_PROMPT"
fi
prompt="$prompt$AUTOTYPE_PROMPT_POST"
fi
rofi_set_option prompt "$prompt"
}
rofi_display_password_files() {
# Requires `shopt -s nullglob globstar`.
pass_files=( "$PASSWORD_STORE_DIR"/**/*.gpg )
pass_files=( "${pass_files[@]#"$PASSWORD_STORE_DIR"/}" )
pass_files=( "${pass_files[@]%.gpg}" )
printf '%s\n' "${pass_files[@]}"
}
rofi_act_on_file() {
pass_file="$1"
# Type entry out.
if [ $_autotype -eq $_AUTOTYPE_ENABLED ]; then
autotype "$1"
else
# Copy entry to clipboard.
if [ $_mode -eq $_MODE_USERNAME ]; then
echo "Getting username" 1>&2
pass_get_user "$pass_file" | CLIPBOARD_STATE=sensitive wl-copy
else
$PASS show -c "$pass_file" >/dev/null
fi
coproc send_countdown_notification
fi
}
#########################################################################
# Auto-type functions #
#########################################################################
# rofi runs this before exiting, so put it in a coprocess and sleep
# before typing.
_wtype() {
coproc (
sleep $WAIT_BEFORE_TYPING
restore_state
wtype "$1" >/dev/null 2>&1
if [ -n "$2" ]; then
wtype -k Tab
sleep $WAIT_BEFORE_TYPING
wtype -d 50 "$2" >/dev/null 2>&1
fi
)
}
autotype() {
pass_file="$1"
username="$(pass_get_user "$pass_file")"
password="$(pass_get_pass "$pass_file")"
case $_mode in
$_MODE_PASSWORD)
_wtype "$password"
;;
$_MODE_PASSWORD)
_wtype "$username"
;;
$_MODE_USERPASS)
_wtype "$username" "$password"
;;
esac
}
#########################################################################
# Password Store definitions. #
#########################################################################
pass_get_pass() {
$PASS show "$1" | head -n 1
}
pass_get_user() {
pass_file="$1"
pass_parent="$(dirname "$pass_file")"
# Requires the 'pass attr' extension
if [ "$_pass_attr" -eq 1 ]
then
pass_username="$($PASS attr "$pass_file" user)"
fi
# If the 'User' attribute exists in the password file, use that.
if [ -n "$pass_username" ]
then
debug "Found username in '$pass_file'"
printf '%s\n' "$pass_username"
# Check for storing usernames as separate file in same directory.
elif [ -e "$PASSWORD_STORE_DIR/$pass_parent/user.gpg" ]
then
debug "Using contents of '$PASSWORD_STORE_DIR/$pass_parent/user.gpg' as username."
pass_get_pass "$pass_parent/user"
elif [ -e "$PASSWORD_STORE_DIR/$pass_parent/username.gpg" ]
then
debug "Using contents of '$PASSWORD_STORE_DIR/$pass_parent/username.gpg' as username."
pass_get_pass "$pass_parent/username"
elif [ -e "$PASSWORD_STORE_DIR/$pass_parent/User.gpg" ]
then
debug "Using contents of '$PASSWORD_STORE_DIR/$pass_parent/User.gpg' as username."
pass_get_pass "$pass_parent/user"
elif [ -e "$PASSWORD_STORE_DIR/$pass_parent/Username.gpg" ]
then
debug "Using contents of '$PASSWORD_STORE_DIR/$pass_parent/Username.gpg' as username."
pass_get_pass "$pass_parent/Username"
# As last resort, use the name of the file. Has form 'USER@HOSTNAME:PORT'
# with but the hostname and port as optional.
else
debug "Extracting username from basename of '$pass_file'"
basename "$pass_file" | cut -d "@" -f 1
fi
}
#########################################################################
# Generic window manager function definitions #
# #
# Can be extended to support more window managers. #
#########################################################################
# Save any state information that the window manager might need.
save_state() {
case "$XDG_CURRENT_DESKTOP" in
"sway")
sway_save_state
;;
esac
}
# Restore any settings changed by this script to their original
# values.
restore_state() {
case "$XDG_CURRENT_DESKTOP" in
"sway")
sway_restore_state
;;
esac
}
# Implement some form of locking to prevent auto-typing sensitive
# information into the wrong window.
lock_window() {
case "$XDG_CURRENT_DESKTOP" in
"sway")
sway_lock_window
;;
esac
}
#########################################################################
# Sway-specific function definitions #
#########################################################################
sway_save_state() {
# Save information about the currently focused window only at
# first run.
if [ -z "$_window_state" ]
then
# This ugly bastard makes a shell eval'able string containing
# the needed window information as `_var1=val1;_var2=val2`
_sway_window_state="$(swaymsg -r -t get_tree | jq -r -c \
'..|try select(.focused == true) |
"_window_id=\(.id);_window_border=\(.border);_window_border_width=\(.current_border_width);"')"
# Pass on the state. This will be placed in `ROFI_DATA`.
_window_state="'$_sway_window_state'"
else
# Seems hacky, but it works.
_window_state="'$_window_state'"
fi
}
sway_restore_focus() {
swaymsg "[con_id=$_window_id]" focus
}
sway_restore_state() {
eval "$_window_state"
sway_restore_focus
swaymsg 'urgent disable'
# I can't find a way to get this value from swaymsg, so assume yes.
swaymsg 'focus_follows_mouse' yes
# Return the border to it's original settings.
swaymsg "border $_window_border $_window_border_width"
}
sway_lock_window() {
sway_restore_focus
swaymsg 'focus_follows_mouse no'
swaymsg 'urgent enable'
# It would be nice to also change the color. Maybe urgency?
swaymsg 'border pixel 8'
}
notify() {
[ $SEND_NOTIFICATIONS -eq 1 ] || return
notify-send --app-name="$NOTIFICATION_APP_NAME" \
--icon="$NOTIFICATION_ICON" "$@"
}
send_countdown_notification() {
[ $SEND_NOTIFICATIONS -eq 1 ] || return
_countdown_pct=100
_countdown_sleep=0.5
# Update once every half second.
_steps=$(( PASSWORD_STORE_CLIP_TIME * 2 ))
_step_size=$(echo "$_countdown_pct / $_steps" | bc -l)
# Just to store the notification id.
_notify_id="$(notify -p "...")"
_time_left=$PASSWORD_STORE_CLIP_TIME
while [ $_steps -gt 0 ]
do
[ $((_steps % 2)) -eq 1 ] && ((_time_left -= 1))
notify -h "int:value:$_countdown_pct" \
-r "$_notify_id" \
"$NOTIFICATION_APP_NAME" \
"Clipboard will be cleared after $_time_left seconds..."
sleep $_countdown_sleep
_countdown_pct="$(echo "$_countdown_pct - $_step_size" | bc -l)"
((_steps -= 1))
done
# A final notification lasting 3 seconds.
notify -r "$_notify_id" \
-t 3000 \
"Clipboard entry cleared."
}
main "$@"