#!/usr/bin/env bash # spmenu_desktop # This script provides functions for handling .desktop entries # # See LICENSE file for copyright and license details. check_desktop() { [ ! -d "$CONFDIR/spmenu/run" ] && mkdir -p "$CONFDIR/spmenu/run" } print_desktop_help() { if [ "$DMENU_COMPAT" != "true" ]; then COL='\033[0;31m' RUNLAUNCHER_EX_ARGS="--lines 20 --columns 1 --normal --sgr1 $HELP_COLOR --hide-cursor --hide-caps --no-allow-typing --no-color-items --hide-prompt --hide-powerline --hide-input --hide-right-arrow --hide-left-arrow --hide-mode --hide-match-count" read -ra rl_ex <<< "${RUNLAUNCHER_EX_ARGS}" fi cat << EOF | $RUNLAUNCHER "${rl_help[@]}" "${rl_ex[@]}" > /dev/null Start typing in keywords to list out entries. Press Enter to select an entry. The selected entry will be run through a shell. To set spmenu options, you modify \$RUNLAUNCHER_ARGS in the config. See 'spmenu --help' for a list of valid arguments to add to the variable. To configure spmenu itself, you may copy ${DESTDIR}${PREFIX}/share/spmenu.conf to ~/.config/spmenu/spmenu.conf. By default, spmenu_run will cache entries for speed reasons. You can find these entries in ~/.config/spmenu/run/cache. If you make changes to .desktop files (not new entries, modified old entries), you need to clear the cache for the changes to appear. Simply delete the directory to do this. - Type in '?' to show this help screen at any time. $(printf "%b" "$COL")Note: This may also be displayed if you deleted your spmenu configuration directory. EOF } print_desktop_list() { # should we use cached files? if [ -f "$TITLEFILE" ] && [ -f "$ICONFILE" ] && [ -f "$EXECFILE" ] && [ -f "$FILEFILE" ] && [ -f "$DESCFILE" ] && [ -f "$DISPLAYFILE" ] && [ -f "$ONLYFILE" ] && [ -f "$COMMENTFILE" ]; then cfiles=true else cfiles=false fi read -ra uniq_args <<< "${UNIQ_ARGS}" it_title=() it_desc=() it_icon=() it_exec=() it_file=() it_term=() it_display=() it_comment=() it_only=() # autorefreshing if [ "$AUTOREFRESH" = "true" ] && [ "$1" != "noc" ]; then entry_c="$(find -L "${desktopdir[@]}" -type f -name '*.desktop' 2>/dev/null | grep -c "")" [ -f "$FILEFILE" ] && cached_c="$(grep -c "" < "$FILEFILE")" || cached_c="0" printf "%s: %d\n%s: %d\n" "Cached" "$cached_c" "Entries" "$entry_c" >> "$LOGFILE" [ "$cfiles" = "true" ] && [ "$entry_c" != "$cached_c" ] && cfiles=false [ "$entry_c" = "0" ] && return fi # print data from entries if [ "$cfiles" = "false" ]; then command -v desktop_pre_caching_func > /dev/null && desktop_pre_caching_func printf "Writing cache files because none exist.\nTitle file: '%s'\nDescription file: '%s'\nIcon file: '%s'\nExec file: '%s'\nFile file: '%s'\nTerm file: '%s'\nDisplay file: '%s'\nOnly file: '%s'\nComment file: '%s'\n" "$TITLEFILE" "$DESCFILE" "$ICONFILE" "$EXECFILE" "$FILEFILE" "$TERMFILE" "$DISPLAYFILE" "$ONLYFILE" "$COMMENTFILE" >> "$LOGFILE" [ "$PRINT_LOGS_STDERR" = "true" ] && printf "spmenu_run: Updating .desktop entries.\n" >> /dev/stderr icons="$(find "${icondir[@]}" -type f 2>/dev/null)" [ -z "$entry" ] && entry="$(find -L "${desktopdir[@]}" -type f -name '*.desktop' 2>/dev/null)" [ -z "$entry_c" ] && entry_c="$(printf "%s\n" "$entry" | grep -c "")" rm -f "$TITLEFILE" "$ICONFILE" "$DESCFILE" "$EXECFILE" "$FILEFILE" "$TERMFILE" "$DISPLAYFILE" "$ONLYFILE" "$COMMENTFILE" for ((i=1; i <= "$entry_c"; i++)); do command -v desktop_file_caching_func > /dev/null && desktop_file_caching_func "$cur_file" cur_file="$(printf "%s" "$entry" | sed "${i}q;d")" [ ! -f "$cur_file" ] && printf "No desktop entries found." && return icon_name="$(grep "Icon=" "$cur_file" | head -n 1 | sed "s/Icon=//g")" file_=$(sed '/\[Desktop Action/q' "$cur_file") it_title[i]="$(awk -F'=' '/^Name=/ && !/Generic/ {gsub("Name=", ""); print; exit}' <<< "$file_")" it_desc[i]="$(awk -F'=' '/GenericName=/ {gsub("GenericName=", ""); print}' <<< "$file_")" it_comment[i]="$(awk -F'=' '/Comment=/ {gsub("Comment=", ""); print}' <<< "$file_")" it_icon[i]="$(grep -F "/${icon_name}." <<< "$icons" | head -n 1)" it_exec[i]="$(awk -F'=' '!/TryExec/ && /Exec=/ {gsub("Exec=", ""); gsub("%[UuFf]", ""); print; exit}' <<< "$file_")" it_file[i]="$cur_file" it_term[i]="false" it_only[i]="false" it_display[i]="true" if [ "$PREFERRED_LANGUAGE" != "generic" ]; then t_title="$(sed '/\[Desktop Action/q' "$cur_file" | grep "Name\[$PREFERRED_LANGUAGE\]=" | grep -v Generic | head -n 1 | sed "s/Name\[$PREFERRED_LANGUAGE\]=//g")" t_desc="$(sed '/\[Desktop Action/q' "$cur_file" | grep "GenericName\[$PREFERRED_LANGUAGE\]=" | sed "s/GenericName\[$PREFERRED_LANGUAGE\]=//g")" t_comment="$(sed '/\[Desktop Action/q' "$cur_file" | grep "Comment\[$PREFERRED_LANGUAGE\]=" | sed "s/Comment\[$PREFERRED_LANGUAGE\]=//g")" [ -n "$t_title" ] && it_title[i]="$t_title" [ -n "$t_desc" ] && it_desc[i]="$t_desc" [ -n "$t_comment" ] && it_comment[i]="$t_comment" fi grep -q "Terminal=true" "$cur_file" && it_term[i]="true" grep -qE "NoDisplay=true|Hidden=true" "$cur_file" && it_display[i]="false" grep -q "OnlyShowIn=" "$cur_file" && it_only[i]="true" # write files printf "%s\n" "${it_title[i]}" >> "$TITLEFILE" printf "%s\n" "${it_icon[i]}" >> "$ICONFILE" printf "%s\n" "${it_desc[i]}" >> "$DESCFILE" printf "%s\n" "${it_exec[i]}" >> "$EXECFILE" printf "%s\n" "${it_file[i]}" >> "$FILEFILE" printf "%s\n" "${it_term[i]}" >> "$TERMFILE" printf "%s\n" "${it_display[i]}" >> "$DISPLAYFILE" printf "%s\n" "${it_comment[i]}" >> "$COMMENTFILE" printf "%s\n" "${it_only[i]}" >> "$ONLYFILE" # log it all printf "%d. Title - %s\n" "${i}" "${it_title[i]}" >> "$LOGFILE" printf "%d. Description - %s\n" "${i}" "${it_desc[i]}" >> "$LOGFILE" printf "%d. Executable - %s\n" "${i}" "${it_exec[i]}" >> "$LOGFILE" printf "%d. Icon - %s\n" "${i}" "${it_icon[i]}" >> "$LOGFILE" printf "%d. File - %s\n" "${i}" "${it_file[i]}" >> "$LOGFILE" printf "%d. Term - %s\n" "${i}" "${it_term[i]}" >> "$LOGFILE" printf "%d. Display - %s\n" "${i}" "${it_display[i]}" >> "$LOGFILE" printf "%d. Only - %s\n" "${i}" "${it_only[i]}" >> "$LOGFILE" printf "%d. Comment - %s\n" "${i}" "${it_comment[i]}" >> "$LOGFILE" done command -v desktop_post_caching_func > /dev/null && desktop_post_caching_func print_desktop_list "noc" else # we have entries, let's populate the arrays command -v desktop_pre_func > /dev/null && desktop_pre_func mapfile -t it_title < "$TITLEFILE" mapfile -t it_icon < "$ICONFILE" mapfile -t it_exec < "$EXECFILE" mapfile -t it_file < "$FILEFILE" mapfile -t it_desc < "$DESCFILE" mapfile -t it_display < "$DISPLAYFILE" mapfile -t it_only < "$ONLYFILE" mapfile -t it_comment < "$COMMENTFILE" d_print_array | \ sort -k 2 "${sort_args[@]}" | \ uniq "${uniq_args[@]}" | \ grep -vE "$HIDDEN_ENTRY_KEYWORDS" | \ grep -E "$ENTRY_KEYWORDS" fi } d_print_array() { command -v print_array > /dev/null && print_array && return printf "SORT_ARGS: %s\n" "${sort_args[*]}" >> "$LOGFILE" printf "UNIQ_ARGS: %s\n" "${uniq_args[*]}" >> "$LOGFILE" for i in "${!it_title[@]}"; do [ "$RESPECT_ONLYSHOWIN" != "false" ] && [ "${it_only[i]}" != "false" ] && continue [ "$RESPECT_NODISPLAY" != "false" ] && [ "${it_display[i]}" != "true" ] && continue if [ -f "${it_icon[i]}" ] && [ -n "${it_title[i]}" ] && [ -n "${it_exec[i]}" ] && [ "$IMAGE" != "false" ]; then printf "%s\t%s" "img://${it_icon[i]}" "${it_title[i]}" elif [ -n "${it_title[i]}" ] && [ -n "${it_exec[i]}" ]; then printf "%s" "${it_title[i]}" else continue fi if [ -n "${it_desc[i]}" ] && [ "$DISPLAY_DESCRIPTION" = "true" ]; then [ "$DMENU_COMPAT" != "true" ] && COL='\033[0;31m' printf -- "$DESCRIPTION_SEPARATOR%b%s" "${COL}" "${it_desc[i]}" fi if [ -n "${it_comment[i]}" ] && [ "$DISPLAY_COMMENT" = "true" ]; then [ "$DMENU_COMPAT" != "true" ] && COL='\033[0;32m' printf -- "$COMMENT_SEPARATOR%b%s" "${COL}" "${it_comment[i]}" fi printf "\n" done } exec_program() { [ ! -f "$TITLEFILE" ] || [ ! -f "$EXECFILE" ] || [ ! -f "$TERMFILE" ] && exit 1 mapfile -t it_title < "$TITLEFILE" mapfile -t it_exec < "$EXECFILE" mapfile -t it_term < "$TERMFILE" # set exec [ -z "${it_exec[1]}" ] && printf "Executable array is empty.\n" >> "$LOGFILE" for i in "${!it_title[@]}"; do if [ "${it_title[i]}" = "$menusel" ]; then exec="${it_exec[i]}" term="${it_term[i]}" printf "Executable %s is: '%s'\n" "$i" "${it_exec[i]}" >> "$LOGFILE" printf "Executable %s term status: '%s'\n" "$i" "${it_term[i]}" >> "$LOGFILE" break; else printf "Executable %s is: '%s'\n" "$i" "${it_exec[i]}" >> "$LOGFILE" printf "Executable %s term status: '%s'\n" "$i" "${it_term[i]}" >> "$LOGFILE" fi done # finally run the program if [ -n "$exec" ] && [ "$term" != "true" ]; then ${SHELL:-/bin/sh} -c "$exec" elif [ -n "$exec" ] && [ "$term" = "true" ]; then ${SHELL:-/bin/sh} -c "${TERMINAL} $exec" else printf "No executable found. Try clearing cache." >> "$LOGFILE" fi } print_desktop_menu() { check_desktop HIDDEN_ENTRY_KEYWORDS="${HIDDEN_ENTRY_KEYWORDS:-NULL_ENTRY}" print_desktop_list "" | $RUNLAUNCHER "${rl_desktop[@]}" > /tmp/spmenu_out while read -r menusel; do [ "$menusel" = "?" ] && print_desktop_help && print_desktop_menu command -v desktop_post_func > /dev/null && desktop_post_func "$menusel" [ -z "$menusel" ] && return 1 \ || printf "User input: %s\n" "$menusel" >> "$LOGFILE" # this must now be the title only menusel="$(printf "%s" "$menusel" | sed "s/ -.*//")" exec_program "$@" [ "$MULTISELECT" != "true" ] && break done < /tmp/spmenu_out; rm -f /tmp/spmenu_out } prep_desktop() { printf "spmenu_run log (%s)\nFunction: Desktop\n" "$(date "+%D %T")" > "$LOGFILE" } clear_cache() { rm -f "${TITLEFILE}" "${FILEFILE}" "${EXECFILE}" "${ICONFILE}" "${DESCFILE}" "${TERMFILE}" "${DISPLAYFILE}" "${ONLYFILE}" "${COMMENTFILE}" }