#!/bin/bash
#
# EndeavourOS bash scripts "library". Functions using 'yad'.
#
# - ALL FUNCTIONS HERE START WITH "eos_yad_" AND ARE EXPORTED.
# - NO GLOBAL VARIABLES ARE USED NOR PROVIDED.
#
# User MUST declare the following exports in the *using* bash code:
#
# export -f eos_yad
# export -f eos_yad_terminal
# export -f eos_yad_check_internet_connection
# export -f eos_yad_GetArgVal
# export -f eos_yad_RunCmdTermBash
# export -f eos_yad_problem
# export -f eos_yad_DIE
# export -f eos_yad_WARN
#
# export -f eos_yad__detectDE
# export -f eos_yad_GetDesktopName
# export -f eos_GetArch
# export -f eos_select_browser
# export -f eos_yad_nothing_todo
#
# The two last functions above do not use yad.
#
# shellcheck disable=SC1090      # Don't show warnings on sourceing via a variable

source /etc/eos-color.conf
source /etc/eos-script-lib-yad.conf      # for EOS_ROOTER and other configs

export EOS_WICON=/usr/share/endeavouros/EndeavourOS-icon.png
export EOS_YAD_STARTER_CMD="/usr/bin/yad --window-icon=$EOS_WICON"

# Avoid hyphens in function names because of POSIX standard, use underscores instead.

eos_yad() { GDK_BACKEND=x11 $EOS_YAD_STARTER_CMD "$@"; }

translations_dir=/usr/share/endeavouros/scripts  # needed in translations.bash
source $translations_dir/translations.bash || {
    echo "$translations_dir/translations.bash not found!" >&2
    exit 1
}

CanRunYad() {
    [ -x /bin/yad ]                   || return 1    # fail, yad not installed
    [ -d /usr/include/webkitgtk-4.1 ] || return 2    # fail, webkit2gtk-4.1 not installed
    return 0                                         # OK
}

yad_missing_check() {
    if [ "${EOS_YAD_MISSING_NOTIFICATION^^}" = "YES" ] ; then
        if ! which yad &>/dev/null ; then
            local app="$1"
            local timeout="$2"
            [ "$timeout" ] || timeout=10000
            local msg="Some operations require yad installed."
            eos_notification "$ICO_INFO" normal $timeout "$app" "Notification from $app" "$msg"
        fi
    fi
}

eos_assert_deps() {      # params: prog deps
    local -r prog="$1"
    shift
    local failcount=0
    local dep
    for dep in "$@" ; do
        expac %n "$dep" &>/dev/null || {
           echo "==> $prog requires package '$dep'" >&2
           ((failcount++))
        }
    done
    return $failcount
}

eos_icon_path() {
    # on success, echo full icon path and return 0
    # on failure, return 1

    local -r type="$1"
    local -r name="$2"
    local icon family

    case "$type" in
        actions|animations|apps|categories|devices|emblems|emotes|filesystems|intl|mimetypes|places|status|stock) ;;
        *) return 1 ;;
    esac
    case "$name" in
        "") return 1 ;;
    esac

    # use Qogir as default, then try other icons

    for family in Qogir "${EOS_ICON_SETS_PREFERENCE[@]}"             # may check Qogir twice...
    do
        family=${family##*/}
        # echo "$FUNCNAME: family = $family" >&2
        icon="/usr/share/icons/$family/scalable/$type/$name.svg"     # find only scalable icons
        if [ -e "$icon" ] ; then
            echo "$icon"
            return 0
        fi
    done
    return 1
}

AssignIconVariables_in_eos_bash_shared() {
    export ICO_INFO_WEB=$(eos_icon_path apps internet-web-browser)

    if false && [ -x /bin/xdg-mime ] ; then
        local default_app="$(xdg-mime query default text/html)"        # 'xdg-mime query default' may issue a warning on KDE if run with elevated privileges
        case "$default_app" in
            chromium.desktop)
                export ICO_INFO_WEB=$(eos_icon_path apps chromium) ;;
            firefox.desktop)
                export ICO_INFO_WEB=$(eos_icon_path apps firefox) ;;
            firefoxdeveloperedition.desktop)
                export ICO_INFO_WEB=$(eos_icon_path apps firefox-developer-edition) ;;
            opera.desktop)
                export ICO_INFO_WEB=$(eos_icon_path apps opera) ;;
            vivaldi-stable.desktop)
                export ICO_INFO_WEB=$(eos_icon_path apps vivaldi) ;;
            *)
                export ICO_INFO_WEB=$(eos_icon_path apps browser) ;;
        esac
    fi

    case "$CurrentDesktop" in
        kde) export ICO_CALAMARES=/usr/share/icons/breeze-dark/apps/48/calamares.svg ;;
        *)   export ICO_CALAMARES=$(eos_icon_path apps calamares) ;;
    esac

    export ICO_NEWS=$(eos_icon_path apps newsflash)
    export ICO_CHANGES=$(eos_icon_path mimetypes text-x-changelog)
    export ICO_HELP=$(eos_icon_path apps system-help)
    export ICO_LOG=$(eos_icon_path apps utilities-log-viewer)
    export ICO_INSTALL=$(eos_icon_path apps system-software-install)
    export ICO_UPDATE=$(eos_icon_path apps system-software-update)
    export ICO_SYSTOOLS=$(eos_icon_path apps applications-utilities)
    export ICO_QUESTION=$(eos_icon_path apps system-help)
    export ICO_TIPS=$(eos_icon_path apps user-info)
    export ICO_INFO=$(eos_icon_path apps dialog-information)
    export ICO_ERROR=$(eos_icon_path apps system-error)
    export ICO_PREFERENCES_SYSTEM=$(eos_icon_path apps preferences-system)
    export ICO_NOTIFICATION=$(eos_icon_path apps preferences-system-notifications)
    export ICO_NOTIFICATION_DISABLE=$(eos_icon_path apps preferences-desktop-notifications)
    export ICO_SHARE=$(eos_icon_path apps share)
    export ICO_SAVE=$(eos_icon_path actions document-save)
    export ICO_MIRROR_RANK=$(eos_icon_path apps preferences-system-performance)

    export ICO_EDIT_CONFIG=$(eos_icon_path apps dconf-editor)
    export ICO_SCHEDULE=$(eos_icon_path apps preferences-system-time)
    export ICO_RESET=$(eos_icon_path apps system-restart)
    export ICO_OPERATION_TEST=$(eos_icon_path apps system-run)
    export ICO_CONNECTION=$(eos_icon_path apps org.gnome.Connections)

    export ICO_BLUETOOTH=$(eos_icon_path devices bluetooth)
    export ICO_DISK=$(eos_icon_path devices drive-harddisk)

    export ICO_NO_CONNECTION=/usr/share/icons/Qogir/24/panel/nm-no-connection.svg
    export ICO_WARNING=/usr/share/icons/Qogir/32/status/dialog-warning.svg
}




# Icons for various use cases like
#   --field=...
#   --image=...
#   --button=...

if false ; then
export WELCOME_ICON_SYSTEM_LOGS=search                               # face-worried
export WELCOME_ICON_LEAVE=stop                                       # face-crying
export WELCOME_ICON_HONEST=face-angel

export WELCOME_ICON_INSTALL_TAB=system-software-install              # $(eos_IconGrasp system-software-install)
export WELCOME_ICON_PARTITION_MANAGER=gparted                        # $(eos_IconGrasp gparted)
export WELCOME_ICON_BLUETOOTH=bluetooth                              # $(eos_IconGrasp bluetooth)
export WELCOME_ICON_TOGGLE_r8169_DRIVER=system-software-update       # $(eos_IconGrasp system-software-update)

export WELCOME_ICON_CHANGE_RESOLUTION=preferences-desktop-display
export WELCOME_ICON_CHANGE_DISPLAY_MANAGER=preferences-desktop-display
export WELCOME_ICON_INSTALL_OFFICIAL=calamares
export WELCOME_ICON_INSTALL_COMMUNITY=calamares
export WELCOME_ICON_INSTALL_INFO=$WELCOME_ICON_INSTALL_TAB
export WELCOME_ICON_MIRROR_UPDATE=software-update-available                 # or applications-internet ?
export WELCOME_ICON_BLUETOOTH_INFO=$WELCOME_ICON_BLUETOOTH
export WELCOME_ICON_LATEST_RELEASE=user-info                                # or info ?
export WELCOME_ICON_INSTALLATION_TIPS=user-info
export WELCOME_ICON_SYSTEM_RESCUE=user-info
export WELCOME_ICON_SYSTEM_LOGS_HOWTO=user-info
export WELCOME_ICON_WEBSITE=user-info                                       # or info ?
export WELCOME_ICON_WIKI=user-info
export WELCOME_ICON_NEWS=user-info
export WELCOME_ICON_FORUM=user-info
export WELCOME_ICON_DONATE=user-info
export WELCOME_ICON_ABOUT_WELCOME=user-info
export WELCOME_ICON_SYSTEM_UPDATE=system-software-update
export WELCOME_ICON_PACCACHE_SRV=applications-system
export WELCOME_ICON_UPDATE_NOTIFIER_CONF=preferences-system
export WELCOME_ICON_WALLPAPER_SET_DEFAULT=preferences-desktop-wallpaper
export WELCOME_ICON_WALLPAPER_SET=preferences-desktop-wallpaper
export WELCOME_ICON_WALLPAPER_DOWNLOAD=preferences-desktop-wallpaper
export WELCOME_ICON_XFCE_THEME_VANILLA=preferences-desktop-theme
export WELCOME_ICON_XFCE_THEME_EOS=preferences-desktop-theme
export WELCOME_ICON_INITIAL_TAB=preferences-system
export WELCOME_ICON_EOS_GENERIC=endeavouros-icon
export WELCOME_ICON_TIPS=user-info
export WELCOME_ICON_ARM=system-run
export WELCOME_ICON_RUNINTERMINAL=preferences-system
export WELCOME_ICON_QUESTION=dialog-question
export WELCOME_ICON_INFO=dialog-information
export WELCOME_ICON_PREFERENCES=preferences-system                          # was: system-preferences
export WELCOME_ICON_OWN_COMMANDS=applications-other
export WELCOME_ICON_HELP=help-about
export WELCOME_ICON_ERROR=dialog-error
export WELCOME_ICON_WARNING=dialog-warning
export WELCOME_ICON_APPDEV=applications-development
export WELCOME_ICON_CHANGELOG=applications-development
export WELCOME_ICON_HELP_CONTENTS=help-contents
export WELCOME_ICON_NOTIFICATIONS=notifications
export WELCOME_ICON_NOTIFICATIONS_DISABLED=notifications-disabled
fi

eos_GetArch() {
    local arch; arch="$(/usr/bin/uname -m)"
    case "$arch" in
        armv7* | aarch64) arch=armv7h ;;
    esac
    printf "%s" "$arch"
}

EosSudoEditor() {
    local editor ed1

    for editor in "${EOS_SUDO_EDITORS[@]}" rnano nano vim "emacs -nw" "$SUDO_EDITOR" "$VISUAL" "$EDITOR" ; do
        ed1="${editor%% *}"
        if /usr/bin/which "$ed1" >& /dev/null ; then
            case "$ed1" in
                mousepad | /bin/mousepad | /usr/bin/mousepad) editor="/usr/bin/dbus-launch $editor" ;;
            esac
            echo "$editor"
            return 0
        fi
    done
    return 1
    # DIE "suitable editor not found"
}

eos_GetProgName() { /usr/bin/basename "$0" ; }

eos_select_browser() {
    # Select an existing browser.
    # User may export a variable _WELCOME_BROWSER to use the preferred browser.
    # Additionally, variable _WELCOME_BROWSER_OPTIONS may include browser options.

    if [ -n "$_WELCOME_BROWSER" ] ; then
        if [ -n "$_WELCOME_BROWSER_OPTIONS" ] ; then
            echo "$_WELCOME_BROWSER $_WELCOME_BROWSER_OPTIONS"
        else
            echo "$_WELCOME_BROWSER"
        fi
        return
    fi

    # If _WELCOME_BROWSER is not exported, use the user configurable list of browsers
    # in file /etc/eos-script-lib-yad.conf

    local old_list="xdg-open exo-open kde-open firefox chromium vivaldi-stable opera"   # for backwards compatibility
    local browser

    for browser in "${EOS_PREFERRED_BROWSERS[@]}" $old_list
    do
        if (/usr/bin/which "$browser" >& /dev/null) ; then
            echo "$browser"
            return
        fi
    done
    eos_yad_WARN "${FUNCNAME[0]}: cannot find a browser"
}

eos_workaround_fix_critical_urgency() {
    # libnotify 0.8.4-1 workaround:
    # For notification's critical urgency,
    # value 0 for --expire-time does not work, any other value works (?)
    # or don't use --expire-time at all.

    [ "$urgency" = critical ] || cmd+=(--expire-time="$expiretime")
}

eos_notification() {   # sending notification to the current user
    local icon="$1"
    local urgency="$2"        # low, normal, critical
    local expiretime="$3"
    local appname="$4"
    local summary="$5"
    local body="$6"
    local action="$7"

    [ "$urgency" ] || urgency=normal

    local cmd=(
        notify-send
        --icon="$icon"
        --urgency="$urgency"
        --app-name="$appname"
        "$summary"
	"$body"
    )
    eos_workaround_fix_critical_urgency

    if [ -n "$action" ] ; then
        cmd+=(--action="$action")
    fi
    "${cmd[@]}"
}

eos_notification_all() {   # sending notification to all users
    eos_GetUsers() { /usr/bin/users | sed 's| |\n|g' | sort -u ; }

    local icon="$1"
    local urgency="$2"        # low, normal, critical
    local expiretime="$3"     # milliseconds
    local appname="$4"
    local summary="$5"        # title
    local body="$6"           # msg
    local color="$7"          # message color (for TTY): RED, YELLOW, or "" (empty or no value means plain text)

    [ -n "$urgency" ] || urgency=normal

    # [ "$body_to_tty" = "yes" ] && echo "==> INFO: $body" >&2     # meant to print to TTY

    if [ "$XDG_SESSION_TYPE" = "tty" ] || [ -z "$DISPLAY" ] || [ "$TERM" = "linux" ] || [[ $(tty) =~ /dev/tty ]] ; then
        local -r C_RESET="$RESET"
        local C_ON=""

        # bold colors
        case "$color" in
            YELLOW)  C_ON="$YELLOW" ;;
            RED)     C_ON="$RED" ;;
        esac

        printf "%s==> %s%s\n" "$C_ON" "$body" "$C_RESET" >&2
        return
    fi

    local users="$(eos_GetUsers)"
    local user userid
    local cmd=()

    for user in $users ; do
        userid=$(/usr/bin/id -u "$user")
        cmd=(
            DISPLAY=:0
            DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$userid/bus
            notify-send
            --icon="$icon"
            --urgency="$urgency"
            --app-name="$appname"
            "'$summary'"
	    "'$body'"
        )
        eos_workaround_fix_critical_urgency

        /usr/bin/su "$user" -c "${cmd[*]}"
    done
}

eos_yad_terminal() {
    local conf=/etc/eos-script-lib-yad.conf
    if [ -r $conf ] ; then
        source $conf
        if [ -n "$EOS_YAD_TERMINAL" ] ; then
            if [ -x /usr/bin/"${EOS_YAD_TERMINAL%% *}" ] || [ -x "${EOS_YAD_TERMINAL%% *}" ] ; then  # allow options in $EOS_YAD_TERMINAL"
                echo "$EOS_YAD_TERMINAL"
                return
            fi
            echo "Sorry, terminal program '$EOS_YAD_TERMINAL' is not available. Please check your configuration file $conf." | \
                eos_yad --text-info --title=Warning --height=100 --width=500 --wrap --button=yad-ok:0
        fi
    fi

    # Show a terminal that is capable of supporting option -e properly. Empty if not found.
    # Requires: yad
    #
    # These terminal programs are known not to work with this program:
    #     - putty
    # The following terminals are known to work:
    local suitable_terminals=(
        xfce4-terminal
        konsole
        gnome-terminal
        mate-terminal
        lxterminal
        deepin-terminal
        terminator
        qterminal
        alacritty
        tilix
        termite
        xterm
        kitty
        terminology
        sakura
        ghostty
        foot           # wayland
    )
    local xx

    for xx in "${suitable_terminals[@]}" ; do
        if [ -x "/usr/bin/${xx%% *}" ] ; then      # allow options for the "suitable terminals"
            echo "/usr/bin/$xx"
            return 0
        fi
    done

    printf "%s\n    %s\n%s" \
           "Sorry, none of the terminal programs:" \
           "${suitable_terminals[*]}" \
           "is installed. Some features may not work as expected." \
        | eos_yad --text-info \
                  --title="Warning" --height=200 --width=700 --wrap --button=yad-ok:0
    return 1
}

eos_yad_check_internet_connection() {
    local verbose="$1"          # yes|verbose = show dialog, otherwise don't show dialog
    local waitrounds="$2"       # try max $waitrounds times for a connection, test once per $onewait
    local onewait="$3"          # time to wait in one waiting round
    local caller="$4"           # who is calling this function (often empty)
    local ix
    local title="Warning"
    local checker="ping -c 1 8.8.8.8"

    case "$EOS_CONNECTION_CHECKER" in
        curl)
            # checker="curl --silent --connect-timeout 8 https://8.8.8.8"
            checker=eos-connection-checker
            ;;
    esac

    test -z "$verbose"    && verbose=no
    test -z "$waitrounds" && waitrounds=5
    test -z "$onewait"    && onewait=1s

    if [ -n "$caller" ] ; then
        title+=" from $caller"
    fi

    for ((ix=0; ix<waitrounds; ix++)) ; do
        $checker >/dev/null && return 0   # is connected
        sleep $onewait
    done

    $checker >/dev/null || {
        case "$verbose" in
            [yY]*|1|true|TRUE|True|on|enable*|verbose)
                if [ 0 -eq 1 ] ; then
                    echo "No internet connection!" | \
                        eos_yad --text-info --title="$title" \
                                --height=100 --width=300 --justify=center --wrap \
                                --button=yad-quit:1 \
                                --button=" Continue anyway!go-next!Don't stop me now":0
                else
                    eos_notification "$ICO_WARNING" normal 60000 "$caller" "$title" "No internet connection!"
                fi
                ;;
            *)
                return 1
                ;;
        esac
    }
}

eos_yad_GetArgVal() { echo "${1#*=}" ; }

eos_yad_RunCmdTermBashOpt() {  # like eos_yad_RunCmdTermBash, but supports certain options
    local cmd=""
    local prompt=""
    local termopts=""
    local waitopt=""
    local opts
    local retval

    # Handle options for variables cmd ... waitopt.
    opts="$(getopt -o=p:t:w:E --longoptions prompt:,term:,wait:,no-enter-wait --name "${FUNCNAME[0]}" -- "$@")"
    retval=$?
    [ $retval -eq 0 ] || return 1
    eval set -- "$opts"
    while true ; do
        case "$1" in
            --prompt | -p)
                prompt="$2" ; shift ;;
            --term | -t)
                termopts="$2" ; shift ;;        # e.g. --term="-T 'my title' --geometry=100x200"
            --wait | -w)
                waitopt="$2" ; shift ;;         # either --wait="--no-enter-wait" or --wait="sleep=30" (30 (seconds) can be something else too)
            --no-enter-wait | -E)
                waitopt="--no-enter-wait" ;;    # alternative to --wait
            --)
                shift; break ;;
        esac
        shift
    done
    cmd="$*"
    [ -n "$cmd" ] || { echo "${FUNCNAME[0]}: warning: required command is missing." >&2; return 1; }

    # Get the available/configured terminal.
    local term; term="$(eos_yad_terminal)"
    test -n "$term" || return 1

    mkdir -p "$HOME/.cache"
    local tmpfile; tmpfile=$(mktemp "$HOME/.cache/${FUNCNAME[0]}.XXXXX")

    trap "sleep 0.2; rm -f '$tmpfile'" EXIT

    echo "#!/bin/bash"                                   >> "$tmpfile"
    test -n "$prompt" && echo "echo $prompt"             >> "$tmpfile"
    echo "$cmd"                                          >> "$tmpfile"
    echo "echo"                                          >> "$tmpfile"
    case "$term" in
        /usr/bin/deepin-terminal | deepin-terminal) ;;
        *)
            _init_translations
            case "$waitopt" in
                --no-enter-wait | no-enter-wait) ;;
                sleep=*)
                    local waittime="${waitopt#*=}"
                    echo "eos-sleep-counter $waittime"                      >> "$tmpfile"
                    ;;
                *)
                    echo "read -p '$(ltr updt_press_enter): '"              >> "$tmpfile"
                    ;;
            esac
            ;;
    esac
    echo "sleep 0.5"         >> "$tmpfile"
    echo "rm -f $tmpfile"    >> "$tmpfile"                    # this may cause issues in very special cases!

    chmod +x "$tmpfile"

    # Try make sure terminal $term does not return to the caller immediately
    # but waits until the commands from $tmpfile are executed.
    case "$term" in
        /usr/bin/gnome-terminal | gnome-terminal)
            $term $termopts --wait -- "$tmpfile" 2>/dev/null ;;
        /usr/bin/xfce4-terminal | xfce4-terminal)
            $term --disable-server $termopts -e "$tmpfile" 2>/dev/null ;;
        *)
            $term $termopts -e "$tmpfile" 2>/dev/null ;;
    esac

    # The following DOES NOT WORK with plain gnome-terminal:
    #echo "Deleting '$tmpfile'."
    #rm -f "$tmpfile"
}

eos_yad_RunCmdTermBash() {
    local cmd="$1"
    local prompt="$2"
    local termopts="$3"
    local waitopt="$4"

    eos_yad_RunCmdTermBashOpt --prompt="$prompt" --term="$termopts" --wait="$waitopt" "$cmd"
}

eos_yad_problem() {
    local title="$1"
    shift
    eos_yad --text-info --title="$title" --wrap --image=$ICO_ERROR \
            --width=700 --height=500 --button=yad-quit:0 "$@"
    # removed --tail
}

eos_yad_DIE() {
    # This function is only for small messages.
    # The local Usage can be used only if it is
    #  - defined before sourcing this file
    #  - exported

    local msg="$1"
    local title="Error"
    shift
    while true ; do
        echo "$msg"
        # run Usage function if available (usually is not ...)
        test -n "$(declare -F | grep -w '^declare -f Usage$')" && Usage
        break
    done | eos_yad_problem "$title" "$@"
    exit 1
}

eos_yad_WARN() {
    local msg="$1"
    local title="Warning"
    shift
    echo "$msg" | eos_yad_problem "$title" --image=$ICO_WARNING "$@"
}

# Function detectDE is copied from: https://cgit.freedesktop.org/xdg/xdg-utils/tree/scripts/xdg-utils-common.in

#--------------------------------------
# Checks for known desktop environments.
# Returning the value, two alternative ways:
#   a. Set variable named DE to the name of the desktop environment, in lowercase.
#   b. User given variable (as the first parameter) gets the value.
#      With this mode user can give option --toupper (as a second parameter) to convert the value to uppercase.
#      (Also option --tolower is supported.)

eos_yad__detectDE()
{
    local variable_from_arg=no               # use variable DE by default
    local toupper=no                         # do not convert to uppercase by default

    case "$1" in
        "")
            local _desktop_=""               # will assign variable named DE if the desktop is found
            ;;
        *)                                   # will assign the user given variable name in "$1" with the DE value, if found
            local -n _desktop_="$1"          # _desktop_ refers to the user given variable
            variable_from_arg=yes
            case "$2" in
                --toupper) toupper=yes ;;
                --tolower) toupper=no ;;     # tolower is already the default...
            esac
            ;;
    esac

    # see https://bugs.freedesktop.org/show_bug.cgi?id=34164
    unset GREP_OPTIONS

    case "${XDG_CURRENT_DESKTOP}" in
        # only recently added to menu-spec, pre-spec X- still in use
        Budgie*)                      _desktop_=budgie ;;
        Cinnamon|X-Cinnamon)          _desktop_=cinnamon ;;
        DEEPIN|[Dd]eepin)             _desktop_=deepin ;;
        ENLIGHTENMENT|Enlightenment)  _desktop_=enlightenment ;;
        GNOME*|gnome)                 _desktop_=gnome ;;        # GNOME, GNOME-Classic:GNOME, or GNOME-Flashback:GNOME
        KDE)                          _desktop_=kde ;;
        LXDE)                         _desktop_=lxde ;;
        LXQt)                         _desktop_=lxqt ;;
        MATE)                         _desktop_=mate ;;
        XFCE)                         _desktop_=xfce ;;
        X-Generic)                    _desktop_=generic ;;      # ???
        i3)                           _desktop_=i3 ;;
    esac

    if [ -z "$_desktop_" ]; then      # fallback to checking $DESKTOP_SESSION
        case "$DESKTOP_SESSION" in
            gnome)                     _desktop_=gnome ;;
            LXDE|Lubuntu)              _desktop_=lxde ;;
            MATE)                      _desktop_=mate ;;
            plasma)                    _desktop_=kde ;;
            xfce|xfce4|'Xfce Session') _desktop_=xfce ;;
            openbox)                   _desktop_=openbox ;;
        esac
    fi

    if [ "$_desktop_" = "" ]; then
      # classic fallbacks
      if [ x"$KDE_FULL_SESSION" != x"" ]; then _desktop_=kde;
      elif [ x"$GNOME_DESKTOP_SESSION_ID" != x"" ]; then _desktop_=gnome;
      elif [ x"$MATE_DESKTOP_SESSION_ID" != x"" ]; then _desktop_=mate;
      elif dbus-send --print-reply --dest=org.freedesktop.DBus /org/freedesktop/DBus org.freedesktop.DBus.GetNameOwner string:org.gnome.SessionManager &>/dev/null ; then _desktop_=gnome;
      elif xprop -root _DT_SAVE_MODE 2> /dev/null | grep ' = \"xfce4\"$' >/dev/null 2>&1; then _desktop_=xfce;
      elif xprop -root 2> /dev/null | grep -i '^xfce_desktop_window' >/dev/null 2>&1; then _desktop_=xfce
      elif echo "$DESKTOP" | grep -q '^Enlightenment'; then _desktop_=enlightenment;
      elif [ x"$LXQT_SESSION_CONFIG" != x"" ]; then _desktop_=lxqt;

      elif [ -x /usr/bin/qtile ] && [ -d /usr/share/doc/qtile ] ; then
          _desktop_="qtile"
      elif eos_IsSway ; then
          _desktop_=sway
      elif eos_IsBspwm ; then
          _desktop_=bspwm
      fi
    fi

    if [ "$_desktop_" = "" ]; then
      # fallback to uname output for other platforms
      case "$(uname 2>/dev/null)" in 
        CYGWIN*)  _desktop_=cygwin ;;
        Darwin)   _desktop_=darwin ;;
      esac
    fi

    # if [ "$_desktop_" = "gnome" ]; then
        # gnome-default-applications-properties is only available in GNOME 2.x
        # but not in GNOME 3.x
        # which gnome-default-applications-properties > /dev/null 2>&1  || _desktop_="gnome3"        # This is no more needed (?)
    # fi

    if [ -z "$_desktop_" ]; then                                                                   # ??
        if [ -f "$XDG_RUNTIME_DIR/flatpak-info" ]; then
            _desktop_="flatpak"
        fi
    fi

    if [ -z "$_desktop_" ] ; then
        if [ -x /usr/bin/inxi ] ; then
            _desktop_=$(echo $(inxi -Sc0) | sed -E 's|.* Desktop\: ([^ ]*) .*|\1|')
            _desktop_=${_desktop_,,}                                                  # make all lowercase
        fi
    fi

    [ $toupper = yes ] && _desktop_=${_desktop_^^}

    case "$variable_from_arg" in
        no) [ -n "$_desktop_" ] && DE="$_desktop_" ;;
    esac
}

GetCurrentDesktop() {                      # Gives current desktop name in lowercase.
    local -n _de="$1"                      # Variable from caller, "returns" the desktop name to the caller.
    eos_yad__detectDE _de --tolower
}

# These 2 easy funcs display the DE/WM in upper case letters:
eos_yad_GetDesktopName() {
    local _de=""
    eos_yad__detectDE _de --toupper
    echo "$_de"
}
eos_GetDeOrWm() { eos_yad_GetDesktopName ; }

eos_IsSway()  {
    case "$DESKTOP_SESSION" in
        sway | */sway) return 0 ;;
    esac
    case "$XDG_SESSION_DESKTOP" in
        sway | */sway) return 0 ;;
    esac
    [ -x /usr/bin/swaybg ] && return 0
    return 1
}

eos_IsBspwm() {
    case "$DESKTOP_SESSION" in
        bspwm | */bspwm) return 0 ;;
    esac
    case "$XDG_SESSION_DESKTOP" in
        bspwm | */bspwm) return 0 ;;
    esac
    [ -x /usr/bin/bspwm ] && return 0
    return 1
}

# DIE()  { eos_yad_DIE  "$@" ; }    # deprecated
# WARN() { eos_yad_WARN "$@" ; }    # deprecated

#SetBrowser() {
#    local xx
#    for xx in xdg-open exo-open firefox chromium ; do  # use one of these browser commands
#        if [ -x /usr/bin/$xx ] ; then
#            _BROWSER=/usr/bin/$xx        # for showing external links
#            return
#        fi
#    done
#    DIE "${FUNCNAME[0]}: cannot find a browser"
#}

eos_yad_nothing_todo() {
    local text="$1"
    local timeout="$2"  # optional, default set below
    local title="$3"    # optional, default set below
    local image="$4"    # optional, default set below

    [ -n "$timeout" ] || timeout=10
    [ -n "$title" ]   || title="Info"
    [ -n "$image" ]   || image=$ICO_INFO

    local cmd=(
        eos_yad --form --text="$text" --title="$title" --no-focus --image="$image"
                --height=100 --width=300 --timeout="$timeout" --timeout-indicator=left
                --button=yad-close:0
    )
    "${cmd[@]}"
}


FindAvailableMonoFont() {
    local size="$1"
    local font="Mono"   # fallback

    [ -n "$size" ] || size=10

    if [ -r /usr/share/fonts/liberation/LiberationMono-Regular.ttf ] ; then
        font="Liberation Mono"
    elif [ -r /usr/share/fonts/TTF/DejaVuSansMono.ttf ] ; then
        font"DejaVu Sans Mono"
    elif [ -r /usr/share/fonts/noto/NotoSansMono-Regular.ttf ] ; then
        font="Noto Sans Mono"
    elif [ -r /usr/share/fonts/gsfonts/NimbusMonoPS-Regular.otf ] ; then
        font="Nimbus Mono"
    elif [ -r /usr/share/fonts/adobe-source-code-pro/SourceCodePro-Regular.otf ] ; then
        font="Source Code Pro"
    fi
    echo "$font $size"
}

ProgressBar() {         # This function was converted from the work of @Kresimir, thanks!
    local msg="$1"
    local percent="$2"
    local barlen="$3"
    local c
    local columns="$COLUMNS"                                # nr of columns on the terminal
    [ -n "$columns" ] || columns=80                         # guess nr of columns on the terminal
    local msglen=$((columns - barlen - 9))                  # max space for the msg
    [ "${#msg}" -gt "$msglen" ] && msg="${msg::$msglen}"    # msg must be truncated
    [ "${msg: -1}" = ":" ] || msg+=":"                      # make sure a colon is after msg

    printf "\r%-*s %3d%% [" "$msglen" "$msg" "$percent" >&2
    for ((c = 0; c < barlen; c++)) ; do
        if (( c <= percent * barlen / 100 )); then
            echo -ne "#" >&2
        else
            echo -ne " " >&2
        fi
    done;
    stdbuf -oL printf "]" >&2 # flush stdout
}
ProgressBarInit() {
    # progress bar initialization
    trap 'printf "\x1B[?25h" >&2' EXIT # cursor on
    printf "\x1B[?25l" >&2              # cursor off
}
ProgressBarEnd() {
    printf "\n" >&2
}

YadProgressPulsate() {
    local lockfile="$1"
    local text="$2"

    while true ; do
        [ -r "$lockfile" ] || return
        echo 0
        sleep 0.1s
    done | eos_yad --progress \
                   --title="Progress indicator" \
                   --text="$text" \
                   --auto-close \
                   --width=400 --hide-text --pulsate
}

eos_FormMonoText() {
    local txt="$1"
    local size="$2"   # optional

    [ -n "$size" ] && size=" $size"
    printf "<span font='Mono$size'>%s</span>" "$txt"
}


Wget2Curl() {
    # Convert a wget call to a curl call.
    local opts
    # shellcheck disable=SC2154       # don't complain about undefined $progname
    local _progname="$progname"                                # $progname may come from the user app
    [ -n "$_progname" ] || _progname="$(eos_GetProgName)"

    opts="$(/usr/bin/getopt -o=qO: --longoptions timeout:,user-agent: --name "$_progname" -- "$@")" || {
        PreLog "${FUNCNAME[0]}: option handling failed!"
        return 1
    }

    eval set -- "$opts"

    local cmd=(curl --fail --location)
    local retval=0

    while true ; do
        case "$1" in
            -q)           cmd+=(--silent)  ;;
            -O)           cmd+=(--output "$2"); shift ;;
            --user-agent) cmd+=(--user-agent "$2"); shift ;;
            --timeout)    cmd+=(--max-time "$2"); shift ;;

            --) shift ; break ;;
        esac
        shift
    done

    # [ "$foobar_test" = "yes" ] && echo "${cmd[*]} $*"

    "${cmd[@]}" "$@"

    retval=$?
    #if [ "$foobar_test" = "yes" ] ; then
    #    case "$retval" in
    #        23) echo "==> Write error. Curl could not write data to a local filesystem or similar." ;;
    #        0) ;;
    #        *) echo "==> curl returned $retval." ;;
    #    esac
    #fi
    return $retval
}


eos_is_installing() {
    if [ "$LOGNAME" = "liveuser" ] || [ "$USER" = "liveuser" ] ; then
        if [ -x /usr/bin/calamares ] ; then
            return 0     # installing
        fi
    fi
    return 1     # not installing
}

# check the config:
if eos_is_installing ; then
    export EOS_ROOTER="sudo bash -c"
else
    case "$EOS_ROOTER" in
        su           | "su -c"          | "/usr/bin/su -c" | "")     export EOS_ROOTER="eos-run-cmd-with-su" ;;          # default!
        sudo         | "sudo bash -c"   | "/usr/bin/sudo bash -c")   export EOS_ROOTER="sudo bash -c" ;;
        doas         | "doas bash -c"   | "/usr/bin/doas bash -c")   export EOS_ROOTER="doas bash -c" ;;                 # experimental!
        pkexec       | "pkexec bash -c" | "/usr/bin/pkexec bash -c") export EOS_ROOTER="/usr/bin/pkexec bash -c" ;;
        su-c_wrapper)                                                export EOS_ROOTER="/usr/bin/su-c_wrapper bash -c" ;;
        *)
            eos_yad_DIE "Error: configuration '$EOS_ROOTER' for EOS_ROOTER in file /etc/eos-script-lib-yad.conf is not supported!"
            ;;
    esac
fi

eos_running_kernel() {
    # Output the name of the running kernel, e.g. 'linux-lts'.
    # If unable to determine the running kernel, output nothing.
    # Note the alternative (fallback) implementations below.

    local running_kernel=""
    local pkgbase="/usr/lib/modules/$(uname --kernel-release)/pkgbase"

    if [ -r "$pkgbase" ] ; then
        # running_kernel="$(cat "$pkgbase")"
        running_kernel="$(< "$pkgbase")"
        case "$running_kernel" in
            linux*) echo "$running_kernel" ; return ;;
        esac
    fi

    if [ -r /proc/version ] ; then
        # running_kernel="$(cat /proc/version | sed 's|.*(\([^@]*\)@archlinux).*|\1|')"
        running_kernel=$(</proc/version)         # example line:  ...(linux@archlinux)...
        running_kernel=${running_kernel%%@*}     # drop all after "@", including
        running_kernel=${running_kernel##*\(}    # drop all before "(", including
        case "$running_kernel" in
            linux*) echo "$running_kernel" ; return ;;
        esac
    fi

    if [ -r /proc/cmdline ] ; then
        # running_kernel=$(cat /proc/cmdline | awk '{print $1}' | sed 's|^.*/vmlinuz-\(.*\)$|\1|')   # works only with grub
        running_kernel=$(</proc/cmdline)                      # example line: BOOT_IMAGE=/boot/vmlinuz-linux root=UUID=...
        running_kernel=${running_kernel##*/vmlinuz-}          # drop all before "linux"
        running_kernel=${running_kernel%% *}                  # drop all after " "
        case "$running_kernel" in
            linux*) echo "$running_kernel" ; return ;;
        esac
    fi
}

eos_is_in_chroot() {
    if [ "$EUID" != "0" ] ; then
        echo "==> $FUNCNAME(): elevated privileges required." >&2
        echo " -> $FUNCNAME() called from file ${BASH_SOURCE[1]}, line ${BASH_LINENO[0]}." >&2
        return 0
    fi
    [ "$(stat -c %d:%i /)" != "$(stat -c %d:%i /proc/1/root/.)" ]
}

_bg_or_bashdb() {
    # If the "main" function of a script should run in background *and*
    # sometimes the script needs to be debugged with bashdb,
    # the script can use this function to start running.
    # Assumption: the script has a single "main" function that starts the execution.
    # For example:
    #    _bg_or_bashdb  <main-function-of-a-script>  "$@"
    # See also: the 'akm' app.

    case "$_Dbg_debugger_name" in
        bashdb) "$@"   ;;         # when debugging with bashdb, do not run in background
        *)      "$@" & ;;         # otherwise run in background
    esac
}
