#!/bin/sh # AUTOSHART: autostart *.desktop files # Copyright (C) 2022 Case Duckworth # Licensed under the Good Choices License. See COPYING for details. # # Commentary: # # An autostart script. Called "autoshart" because I literally sharted it out, # and have /not/ tested it at all. So, caveat emptor, I guess. # # Freedesktop autostart specification: # https://specifications.freedesktop.org/autostart-spec/latest/ # Freedesktop desktop entry specification: # https://specifications.freedesktop.org/desktop-entry-spec/latest/ # # Prior art: # - dex -- https://github.com/jceb/dex # # Code: usage() { cat <&2 "Unknown option: -$opt" usage 1 ;; esac done shift "$((OPTIND - 1))" auto_search } ## Library functions auto_kill() { xargs kill <"$PID_FILE" } auto_start() { ## auto_start FILE eprint "$1" # When the .desktop file has the Hidden key set to true, the .desktop # file MUST be ignored. When multiple .desktop files with the same name # exists in multiple directories then only the Hidden key in the most # important .desktop file must be considered: If it is set to true all # .desktop files with the same name in the other directories MUST be # ignored as well. hidden="$(get Hidden "$1")" test "$hidden" = true && return "$(die 1 "Stopped: Hidden: $1")" # This specification defines 3 types of desktop entries: Application # (type 1), Link (type 2) and Directory (type 3). To allow the addition # of new types in the future, implementations should ignore desktop # entries with an unknown type. (REQUIRED) type="$(get Type "$1")" test "$type" = Application || return "$(die 2 "Error: Bad desktop type: ${type:-[none]}")" # Specific name of the application, for example "Mozilla". (REQUIRED) name="$(get Name "$1" | head -n1)" test -n "$name" || return "$(die 3 "Error: No application name")" # Program to execute, possibly with arguments. See the Exec key for # details on how this key works. The Exec key is required if # DBusActivatable is not set to true. Even if DBusActivatable is true, # Exec should be specified for compatibility with implementations that # do not understand DBusActivatable. (Makes no sense to be omitted in an # autostart context --- acdw) exec="$(get Exec "$1")" test -n "$exec" || return "$(die 4 "Error: No Exec key: $1")" # The OnlyShowIn entry may contain a list of strings identifying # the desktop environments that MUST autostart this application, all # other desktop environments MUST NOT autostart this application. The # NotShowIn entry may contain a list of strings identifying the desktop # environments that MUST NOT autostart this application, all other # desktop environments MUST autostart this application. Only one of # these keys, either OnlyShowIn or NotShowIn, may appear in a single # .desktop file. # ### The OnlyShowIn, NotShowIn keys are specified thus: # # A list of strings identifying the desktop environments that should # display/not display a given desktop entry. # # By default, a desktop file should be shown, unless an OnlyShowIn key # is present, in which case, the default is for the file not to be # shown. # # If $XDG_CURRENT_DESKTOP is set then it contains a colon-separated list # of strings. In order, each string is considered. If a matching entry # is found in OnlyShowIn then the desktop file is shown. If an entry is # found in NotShowIn then the desktop file is not shown. If none of the # strings match then the default action is taken (as above). # # $XDG_CURRENT_DESKTOP should have been set by the login manager, # according to the value of the DesktopNames found in the session # file. The entry in the session file has multiple values separated in # the usual way: with a semicolon. # # The same desktop name may not appear in both OnlyShowIn and NotShowIn # of a group. # ### I think this "functionality" is dubious at best and pretty ### ... silly. --- acdw onlyshowin="$(get OnlyShowIn "$1")" notshowin="$(get NotShowIn "$1")" if test -n "$onlyshowin" && test -n "$notshowin"; then return "$(die 5 "Error: Incompatible keys: OnlyShowIn, NotShowIn: $1")" elif test -n "$onlyshowin"; then start=false else start=true fi # This is so cursed quit="$( echo "$XDG_CURRENT_DESKTOP" | tr : \\n | while read -r desktop; do case "$onlyshowin;$notshowin" in *$desktop*) if test -n "$onlyshowin"; then break elif test -n "$notshowin"; then echo true die 6 "Stopped: NotShowIn=$notshowin" fi ;; *) if ! $start; then echo true die 6 "Stopped: OnlyShowIn=$onlyshowin" fi ;; esac done echo false )" if $quit; then return 6 fi # A .desktop file with a non-empty TryExec field MUST NOT be autostarted # if the value of the TryExec key does NOT match with an installed # executable program. The value of the TryExec field may either be an # absolute path or the name of an executable without any path # components. If the name of an executable is specified without any path # components then the $PATH environment is searched to find a matching # executable program. tryexec="$(get TryExec "$1")" if test -n "$tryexec"; then command -v "$tryexec" | grep -v ^alias | grep -q / || return "$(die 7 "Error: TryExec command not found: $tryexec")" fi # If all these tests pass, we're ready to go # XXX: probably won't do quoting properly eprint "> $exec" if $RUN; then $exec & print $! >>"$PID_FILE" fi : } auto_search() { ## auto_search # The Autostart Directories are $XDG_CONFIG_DIRS/autostart as defined # in accordance with the "Referencing this specification" section in # the "desktop base directory specification". # # If the same filename is located under multiple Autostart Directories # only the file under the most important directory should be used. # # Example: If $XDG_CONFIG_HOME is not set the Autostart Directory in the # user's home directory is ~/.config/autostart/ # # Example: If $XDG_CONFIG_DIRS is not set the system wide Autostart # Directory is /etc/xdg/autostart/ # # Example: If $XDG_CONFIG_HOME and $XDG_CONFIG_DIRS are not set and the # two files /etc/xdg/autostart/foo.desktop and # ~/.config/autostart/foo.desktop exist then only the file # ~/.config/autostart/foo.desktop will be used because # ~/.config/autostart/ is more important than /etc/xdg/autostart/ echo "${XDG_CONFIG_HOME:-$HOME/.config}:${XDG_CONFIG_DIRS:-/etc/xdg}" | tr : \\n | sed 's,$,/autostart,' | while read -r dir; do if test -d "$dir"; then for desktop in "$dir"/*.desktop; do if ! grep -q "$desktop" "$FOUND_FILE"; then auto_start "$desktop" && # `echo' here is # important; it /needs/ # to be output to the # $FOUND_FILE echo "$desktop" >>"$FOUND_FILE" else eprint "Already executed: $desktop" fi done fi done } ## Convenience functions get() { # get KEY FILE grep "^$1" "${2:-}" 2>/dev/null | cut -d= -f2- } print() { $QUIET || printf '%s\n' "$*" } eprint() { print "$@" >&2 } die() { errcode="$1" shift eprint "! $*" echo "$errcode" exit "$errcode" } ## Entry point test "$DEBUG" && set -x main "$@"