#!/bin/bash #this generates a yad-friendly app list. This is run every time the gui script is executed. #if this script detects nothing has changed since last run, then it will echo back the app list that was generated last time. if [ -z "$DIRECTORY" ];then DIRECTORY="$(readlink -f "$(dirname "$0")")" fi mktimestamps() { #these directories are checked for changes checkfiles="${DIRECTORY}/apps ${DIRECTORY}/data/settings ${DIRECTORY}/data/status ${DIRECTORY}/etc ${DIRECTORY}/icons/categories ${DIRECTORY}/preload ${DIRECTORY}/api ${DIRECTORY}/data/category-overrides" local IFS=$'\n' for i in $checkfiles do printf "$i $(stat -c %Y "${i}" 2>/dev/null) " if [ -d "$i" ];then find "$i" -type f -printf '%T@ %p\n' | sort -rn | head -n1 else printf '\n' fi done } [ "$1" == source ] && return 0 function error { echo -e "\e[91m$1\e[39m" 1>&2 exit 1 } #yad or xlunch format guimode="$1" if [ -z "$guimode" ];then format=yad elif [[ "$guimode" = xlunch* ]];then format=xlunch elif [[ "$guimode" = yad* ]];then format=yad elif [ "$guimode" != 'yad' ] && [ "$guimode" != 'xlunch' ];then error "Unknown list format '$format'!" fi #specifies a category to preload (can be left empty to load main page) prefix="$2" if [ "$prefix" == '/' ];then prefix= fi # if the timestamp in the settings folder has changed, we need to unset the text_color variable since the GTK_THEME may have changed and we need to re-generate it timestamp_settings="$( cat ${DIRECTORY}/data/preload/timestamps- | grep "^${DIRECTORY}/data/settings" )" timestamps="$(mktimestamps)" echo "$timestamps" | grep -q "$timestamp_settings" || unset text_color #For systems with older versions of yad, the text color column cannot be left blank. This python script determines the default text color from GTK bindings. if [ -z "${text_color+x}" ] && [ "$format" == yad ];then #0400 is the latest version yad_version="$(zcat /usr/share/doc/yad/NEWS.gz | head -n 1 | tr -cd '0123456789\n')" if [ $yad_version -lt 0400 ]; then if command -v python3 &>/dev/null; then python_version="python3" else python_version="python2" fi #the GTK_THEME may not be current depending on when the API script was last sourced #to avoid setting the text to the wrong color, check it again now right #set the system GTK theme for yad windows guimode="$(cat "${DIRECTORY}/data/settings/App List Style" 2>/dev/null || echo yad-default)" if [ "$guimode" == yad-default ];then export GTK_THEME='' elif [[ "$guimode" = yad* ]];then export GTK_THEME=${guimode//yad-/} elif [ "$guimode" == xlunch-light-3d ];then export GTK_THEME='' elif [ "$guimode" == xlunch-dark-3d ];then export GTK_THEME=Adwaita-dark elif [ "$guimode" == xlunch-dark ];then export GTK_THEME=Adwaita-dark fi export text_color=$(echo "import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk from gi.repository import Gdk tv = Gtk.TextView() style = tv.get_style_context() textcolor = style.get_color(Gtk.StateType.NORMAL) print(Gdk.RGBA.to_string(textcolor)) " | $python_version -) else export text_color="" fi fi timestampfile="${DIRECTORY}/data/preload/timestamps-$(echo "$prefix" | tr -d '/')" listfile="${DIRECTORY}/data/preload/LIST-$(echo "$prefix" | tr -d '/')" mkdir -p "${DIRECTORY}/data/preload" reloadlist=0 if [ -f "$timestampfile" ];then #get modified timestamps for files/directories in the Artian-apps folder timestamps="$(mktimestamps)" if [ "$timestamps" == "$(cat "$timestampfile")" ];then #if current timestamps and saved timestamps match, then don't reload the list reloadlist=0 echo "Timestamps match." 1>&2 else #timestamps don't match, so reload the list reloadlist=1 echo "Timestamps don't match" 1>&2 #echo -e "original file: $(cat "$timestampfile")\nnew timestamp: $timestamps" 1>&2 fi else #timestamp file not found reloadlist=1 fi if [ ! -f "$listfile" ] || [ ! -s "$listfile" ];then echo "list file for $prefix does not exist." 1>&2 reloadlist=1 else list_length=$(wc -l "$listfile" | awk '{print $1}') fi if [ -n "$list_length" ] && [ "$format" == yad ] && (( $list_length % 5 )) ; then echo "yad lists are a multiple of 5, $prefix does not have a list a multiple of 5 so it may be corrupted and needs regenerating." 1>&2 reloadlist=1 fi #If updates available, show special Updates category (returned separately to avoid re-preloading after update-check finishes) if [ -z "$prefix" ] && [ "$format" == yad ] && ([ -s "${DIRECTORY}/data/update-status/updatable-files" ] || [ -s "${DIRECTORY}/data/update-status/updatable-apps" ]);then #yad format echo "${DIRECTORY}/icons/categories/Updates.png Updates Updates/ Artian-Apps updates are available. Click here to update your apps. $text_color" elif [ -z "$prefix" ] && [ "$format" == xlunch ] && ([ -s "${DIRECTORY}/data/update-status/updatable-files" ] || [ -s "${DIRECTORY}/data/update-status/updatable-apps" ]);then #xlunch format echo "Updates;${DIRECTORY}/icons/categories/Updates-64.png;:exec "\""echo Updates/"\""" fi if [ $reloadlist == 1 ];then echo "Generating list for '$prefix'..." 1>&2 #for app_prefix_categories() and app_status() functions if ! command -v app_prefix_category >/dev/null ;then source "${DIRECTORY}/api" fi if [ ! -z "$prefix" ];then echo "Showing apps within $prefix/" 1>&2 vfiles="$(app_prefix_category "$prefix" | grep . | sort -f | uniq | sed "s+$prefix/++g")" #generate a virtual file system with apps in folders represented as subdirectories else vfiles="$(app_prefix_category "" | grep . | sort -f | uniq | grep -v '^hidden/')" fi #remove apps within categories - show this layer of stuff only. vfiles="$(sed 's+/.*+/+g' <<<"$vfiles" | uniq)" #get list of apps - excluding folders and apps that are incompatible with CPU architecture APPS="$(grep -v '/' <<<"$vfiles" | list_intersect "$(list_apps cpu_installable)")" #get list of folders - excluding apps. DIRS="$(grep '/' <<<"$vfiles" | tr -d '/')" #shuffle the list if enabled if [ "$(< "${DIRECTORY}/data/settings/Shuffle App list")" == 'Yes' ];then APPS="$(echo "$APPS" | shuf)" DIRS="$(echo "$DIRS" | shuf)" fi if [ "$format" == yad ];then IFS=$'\n' listfile_tmp="$listfile-$(mktemp -u | sed 's:/tmp/tmp.::g')" #initial value of listfile: if within a prefix, start with a Back button if [ ! -z "$prefix" ];then echo "${DIRECTORY}/icons/back.png Back $(dirname "$prefix" | sed 's+^\.$++g')/ Return to the previous location $text_color" | tee "$listfile_tmp" &>/dev/null else echo -n '' > "$listfile_tmp" fi declare -A dir_lookup dir_lookup=( ["Browsers"]="Internet browsers." ["All Apps"]="All Artian-Apps Applications in one long list." ["Appearance"]="Applications and Themes which modify the look and feel of your OS." ["System Management"]="Apps that help you keep track of system resources and general system management." ["Games"]="Games and Emulators" ["Installed"]="All Artian-Apps Apps that you have installed." ["Internet"]="Browsers, Chat Clients, Email Clients, and so much more." ["Multimedia"]="Video playback and creation, audio playback and creation, and streaming alternatives." ["Packages"]="Simple Apps that install directly from APT repos." ["Tools"]="An assortment of helpful programs that don't already fit into another category." ["Terminals"]="Alternative terminal programs built for the modern age as well as to replicate your old vintage computer." ["Programming"]="Code editors, IDEs, and other applications to help you write and make other programs." ["Creative Arts"]="Drawing, Painting, and Photo and Movie Editors" ["Engineering"]="3D Printing slicers, CAD/modeling, and general design software" ["Office"]="Office suites (document and slideshow editors), and other office tools." ["Emulation"]="Applications that help you run non-ARM or non-Linux software." ["Communication"]="Internet messaging, calling, video chatting, and email clients.") for dir in $DIRS do if [ -f "${DIRECTORY}/icons/categories/${dir}.png" ];then diricon="${DIRECTORY}/icons/categories/${dir}.png" else diricon="${DIRECTORY}/icons/categories/default.png" fi if [ ! -z "$prefix" ];then add="$diricon $dir $prefix/$dir/ "${dir_lookup["$dir"]}" $text_color" else add="$diricon $dir $dir/ "${dir_lookup["$dir"]}" $text_color" fi add="${add//[&]/&}" echo "$add" done >> "$listfile_tmp" #finished preloading categories #preload apps for app in $APPS do #get installation status of app unset status read -r 2>/dev/null status <"${DIRECTORY}/data/status/${app}" #determine app icon if [ -f "${DIRECTORY}/apps/${app}/icon-24.png" ];then add="${DIRECTORY}/apps/${app}/icon-24.png"$'\n' else add="${DIRECTORY}/icons/none-24.png"$'\n' fi #rest of list attributes if [ -z "$status" ];then read -r 2>/dev/null line <"${DIRECTORY}/apps/${app}/description" || line="Description unavailable" add+="$app $prefix/$app $line"$'\n' else read -r 2>/dev/null line <"${DIRECTORY}/apps/${app}/description" || line="Description unavailable" add+="$app $prefix/$app "\("$status"\)" $line"$'\n' fi #determine status text-color for app name (green for installed, red for uninstalled, yellow for corrupted) if [ -z "$status" ];then add+="$text_color" elif [ "$status" == installed ];then add+="#00AA00" elif [ "$status" == uninstalled ];then add+="#CC3333" elif [ "$status" == corrupted ];then add+="#888800" elif [ "$status" == disabled ];then add+="#FF0000" else # fallback incase unexpected status add+="$text_color" fi #output finished app lines add="${add//[&]/&}" echo "$add" done >> "$listfile_tmp" # write to the pipe all at once instead of in breaking chunks. this prevents errors when user clicks the back button too quickly when the list file is still generating mv "$listfile_tmp" "$listfile" # specifically use cat here so that all data enters the pipe at once cat "$listfile" echo "Finished preload for '$prefix'" 1>&2 #finished preloading apps elif [ "$format" == xlunch ];then #XLUNCH list format listfile_tmp="$listfile-$(mktemp -u | sed 's:/tmp/tmp.::g')" #initial value of listfile: if within a prefix, start with a Back button, or if on main page start with Updates if [ ! -z "$prefix" ];then echo "Back;${DIRECTORY}/icons/back-64.png;:exec "\""echo /"\""" | tee "$listfile_tmp" &>/dev/null else echo -n '' > "$listfile_tmp" fi IFS=$'\n' for dir in $DIRS do if [ -f "${DIRECTORY}/icons/categories/${dir}-64.png" ];then diricon="${DIRECTORY}/icons/categories/${dir}-64.png" else diricon="${DIRECTORY}/icons/categories/default-64.png" fi echo "${dir};${diricon};:exec "\""echo '${prefix}${dir}/'"\""" done >> "$listfile_tmp" for app in $APPS do if [ -f "${DIRECTORY}/data/status/${app}" ];then echo "${app} ($(while read line; do echo $line; done 2>/dev/null < "${DIRECTORY}/data/status/${app}"));${DIRECTORY}/apps/${app}/icon-64.png;:exec "\""echo '$prefix/${app}'"\""" else #status file missing - app has never been installed echo "${app};${DIRECTORY}/apps/${app}/icon-64.png;:exec "\""echo '$prefix/${app}'"\""" fi done >> "$listfile_tmp" # write to the pipe all at once instead of in breaking chunks. this prevents errors when user clicks the back button too quickly when the list file is still generating mv "$listfile_tmp" "$listfile" # specifically use cat here so that all data enters the pipe at once cat "$listfile" echo "Finished preload for $prefix" 1>&2 fi #save timestamps to file too if [ -z "$timestamps" ];then timestamps="$(mktimestamps)" fi echo "$timestamps" > "$timestampfile" else echo "Reading list file for '$prefix'..." 1>&2 # specifically use cat here so that all data enters the pipe at once cat "$listfile" fi #preload all categories in background "${DIRECTORY}/etc/preload-daemon" "$format" &>/dev/null &