#!/bin/bash error() { #red text and exit 1 echo -e "\e[91m$1\e[0m" 1>&2 exit 1 } if ! command -v yad &>/dev/null; then error "YAD needs to be installed to run Artian-Apps." fi if [[ $(id -u) == 0 ]]; then echo "Artian-Apps is not designed to be run as root! Please try again as a regular user." | yad --center --window-icon="${DIRECTORY}/icons/logo.png" \ --width=700 --height=300 --text-info --title="Error" \ --image="${DIRECTORY}/icons/error.png" --image-on-top --fontname=12 \ --button='OK' error "Artian-Apps is not designed to be run as root! Please try again as a regular user." fi # set GUI format versioning export GUI_FORMAT_VERSION=2 #display first 4 arguments this script receives (for debugging) if false;then echo "1 $1" 1>&2 echo "2 $2" 1>&2 echo "3 $3" 1>&2 echo "4 $4" 1>&2 fi #if this script runs itself with a 3rd argument, then yad used it. if [ ! -z "$3" ];then input="$3" #the 3rd argument is the "sysname" column - invisible and used internally for scripts. echo "Selection: $input" 1>&2 refresh_list() { #Refresh the current list of apps in the event of a change if [ ! -z "$pipe" ];then echo -e "\f" > "$pipe" "${DIRECTORY}/preload" yad "$prefix" > "$pipe" 2>/dev/null fi } if [[ "$input" == *'Updates/' ]];then #updater window get_positions "${DIRECTORY}/updater" gui fast $geometry2 --skip-taskbar --close-on-unfocus exitcode=$? #0 if update successful, 1 if list of updates was closed #figure out which prefix we are on and refresh the app list prefix="$(echo "$input" | sed 's+Updates/$++g')" if [ $exitcode == 0 ];then refresh_list fi elif [ "$input" == 'Search/' ];then #search window get_positions source "${DIRECTORY}/api" #to set the yadflags array variable app="$(yadflags+=("$geometry2") ; app_search_gui)" if [ ! -z "$app" ];then "${DIRECTORY}/gui" '' "$app" "/$app" "$app" fi elif [[ "$input" == */ ]];then #app folder selected echo -e '\f' > $pipe #clear the yad list "${DIRECTORY}/preload" yad "${input::-1}" >> $pipe #send yad the app-list - derived from folder name but dropping the final slash character else #app details window # make sure terminal_manage function is available # the old updater did not source the new api script typeset -f terminal_manage &>/dev/null || { set -a; source "${DIRECTORY}/api"; } #app is in format $prefix/$app, so split them out app="$(basename "$input")" prefix="$(dirname "$input")" #add status heading app_status="$(app_status "${app}")" #text to the right of the app icon abovetext="$app ($(sed 's/corrupted/corrupted - installation failed/g' <<<"$app_status" | sed 's/disabled/disabled - installation is prevented on your system/g'))" #If package-app, show what packages it installs if [ -f "${DIRECTORY}/apps/${app}/packages" ];then packages="$(cat "${DIRECTORY}/apps/${app}/packages")" if [ "$(wc -w <<<"$packages")" == 1 ];then #if package-app uses only 1 package, use singular case abovetext+=$'\n'"- This app installs the ${packages} package." else #if package-app uses multiple packages, use plural case abovetext+=$'\n'"- This app installs these packages: $(sed 's/ /, /g' <<<"$packages")" fi fi #show app's website if available if [ -f "${DIRECTORY}/apps/${app}/website" ];then website="$(head -n1 "${DIRECTORY}/apps/${app}/website")" abovetext+=$'\n'"- Website: $website" #show credits link if available - on same line as website link if [ -f "${DIRECTORY}/apps/${app}/credits" ];then abovetext+=" | Credits" fi else #website unavailable #show credits link standalone, if available if [ -f "${DIRECTORY}/apps/${app}/credits" ];then abovetext+=$'\n'"- Credits" fi fi num_users="$(usercount "$app")" if [ ! -z "$num_users" ] && [ "$num_users" -gt 20 ];then #list the number of users, using this printf command to add commas (,) for every thousand number abovetext+=$'\n'"- $(printf "%'d" "$num_users") users" if [ "$num_users" -ge 10000 ];then #if this app has over 10,000 users, add two exclamation points!! abovetext+="!!" elif [ "$num_users" -ge 1500 ];then #if this app has over 1500 users, add an exclamation point! abovetext+="!" fi fi #array holding various buttons that may be passed to yad whichbutton=() if [ -f "${DIRECTORY}/apps/$app/uninstall" ] || [ -f "${DIRECTORY}/apps/$app/install" ] || [ -f "${DIRECTORY}/apps/$app/install-$arch" ];then whichbutton+=("--button=Scripts!${DIRECTORY}/icons/shellscript.png!Feel free to see how an app is installed!"$'\n'"Perfect for learning or troubleshooting.:6") fi if [ "$(cat "${DIRECTORY}/data/settings/Show Edit button")" == 'Yes' ];then #if edit button enabled, show it whichbutton+=("--button=Edit!${DIRECTORY}/icons/edit.png!Make changes to the app:8") fi #display buttons based on app's installation status if [ "$app_status" == 'installed' ];then #if installed, display uninstall button whichbutton+=("--button=Uninstall!${DIRECTORY}/icons/uninstall.png:2") elif [ "$app_status" == 'uninstalled' ];then #if uninstalled, display install button whichbutton+=("--button=Install!${DIRECTORY}/icons/install.png:4") elif [ "$app_status" == 'disabled' ];then #if disabled, display only an 'enable' button whichbutton+=("--button=Enable!!Force this app to install on your system."$'\n'"This app was disabled for a reason so if you enable it..."$'\n'"YOU HAVE BEEN WARNED.:12") else #if app status is 'corrupted', and a log file exists for this app, then display a button to view the log file if [ "$app_status" == 'corrupted' ];then logfile="$(ls "$DIRECTORY/logs"/* -t | grep "fail-${app}" | head -n1)" if [ ! -z "$logfile" ];then whichbutton+=("--button=Errors!${DIRECTORY}/icons/log-file.png!$app failed to $(echo "$(basename "$logfile")" | awk -F'-' '{print $1}'). Click this button to view the error output saved in the log file.:14") fi fi #if status is corrupted or unknown, then show both buttons whichbutton+=("--button=Uninstall!${DIRECTORY}/icons/uninstall.png:2" "--button=Install!${DIRECTORY}/icons/install.png:4") fi #only display app icon if it exists if [ -f "${DIRECTORY}/apps/${app}/icon-64.png" ];then imageline=(--image="${DIRECTORY}/apps/${app}/icon-64.png" --image-on-top) fi #Make sure app details window is focused (necessary for openbox window manager) (i=0 while [ "$(xdotool getactivewindow getwindowname)" != "Details of ${app}" ] && [ $i -lt 20 ]; do wmctrl -a "Details of ${app}" #Raise the window named "Details of ${app}" sleep 0.2 i=$((i+1)) done) & #display app details window get_positions (cat "${DIRECTORY}/apps/$app/description" || echo "Description unavailable") | yad --class Artian-Apps --name "Artian-Apps" --text-info --fontname=12 --wrap --show-uri \ --text="$(sed 's/&/&/g' <<<"$abovetext")" \ "${imageline[@]}" \ --title="Details of ${app}" --window-icon="${DIRECTORY}/icons/logo.png" \ $geometry2 --close-on-unfocus --skip-taskbar \ "${whichbutton[@]}" button=$? case $button in 4) #install terminal_manage install "$app" & ;; 2) #uninstall terminal_manage uninstall "$app" & ;; 6) #scripts install_script="${DIRECTORY}/apps/${app}/$(script_name_cpu "$app")" uninstall_script="${DIRECTORY}/apps/${app}/uninstall" text_editor "$uninstall_script" & sleep 0.1 text_editor "$install_script" & "${DIRECTORY}/gui" "$@" #open details window again ;; 8) #edit "${DIRECTORY}/createapp" "$app" refresh_list ;; 12) #enable #remove status file containing 'disabled' rm -f "${DIRECTORY}/data/status/${app}" refresh_list & "${DIRECTORY}/gui" "$@" #open details window again ;; 14) #viewlog echo "Viewing error log of $app..." echo "Log filepath: $logfile" "${DIRECTORY}/etc/viewlog" "$logfile" "${DIRECTORY}/gui" "$@" #open details window again ;; *) #unknown button exit 0 ;; esac fi exit 0 fi DIRECTORY="$(readlink -f "$(dirname "$0")")" #check mission-critical scripts and re-download them if they contain a syntax error ( sleep 10 command -v shellcheck >/dev/null || exit 0 if shellcheck "${DIRECTORY}/updater" --color=always | grep '\[31m' --before 1 ;then echo "Downloading updater script to repair syntax error" errors="$(wget -O "${DIRECTORY}/updater" 'https://git.fdc.abrish.ir/a.kamyar/PublicFiles/Artian-Apps/updater' 2>&1)" || echo "$errors" fi | sed 's/]31m//g' if shellcheck "${DIRECTORY}/api" --color=always | grep '\[31m' --before 1 ;then echo "Downloading api script to repair syntax error" errors="$(wget -O "${DIRECTORY}/api" 'https://git.fdc.abrish.ir/a.kamyar/PublicFiles/Artian-Apps/api' 2>&1)" || echo "$errors" fi | sed 's/]31m//g' ) & set -a #make all functions in the api available to subprocesses source "${DIRECTORY}/api" || error "failed to source ${DIRECTORY}/api" #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}" ];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 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 #display the Artian-Apps logo in the terminal generate_logo & #install dependencies runonce <<"EOF" dependencies='yad curl wget aria2 lsb-release software-properties-common apt-utils apt-transport-https gnupg imagemagick bc librsvg2-bin locales shellcheck git wmctrl xdotool x11-utils rsync' # Install dependencies if necessary if ! dpkg -s $dependencies >/dev/null 2>&1; then sudo_popup apt install $dependencies -y -f --no-install-recommends fi EOF #Various stuff to run in background (enclosed in brackets so Geany IDE can collapse the code) { mkdir -p "${DIRECTORY}/data/status" "${DIRECTORY}/data/update-status" \ "${DIRECTORY}/data/preload" "${DIRECTORY}/data/settings" \ "${DIRECTORY}/data/status" "${DIRECTORY}/data/update-status" \ "${DIRECTORY}/data/categories" #check for updates "${DIRECTORY}/updater" set-status & trap "kill $! &>/dev/null" EXIT #kill the above subprocess on exit #Click Artian-Apps usage link every time the GUI is run only if the 'Enable Analytics' setting is enabled if [ "$(cat "${DIRECTORY}/data/settings/Enable analytics")" == 'Yes' ];then #click it curl -s -X 'GET' "https://git.fdc.abrish.ir/a.kamyar/PublicFiles/Artian-Apps" -H 'accept: image/gif' -A "Artian-Apps software manager for Artian company" >/dev/null & fi } #Determine the app list mode. Allowed values: 'yad-*', 'xlunch-*' guimode="$(cat "${DIRECTORY}/data/settings/App List Style")" [ -z "$guimode" ] && guimode=yad-default #In YAD mode, two windows are handled in a side-by-side configuration. As a group, they must be centered on the the screen. #In Xlunch mode, the window must be centered, but xlunch only handles absolute offsets. get_positions() { #determine screen_width and screen_height screen_dimensions="$(xrandr --nograb --current | awk -F 'connected |\\+|\\(' '/ connected.*[0-9]+x[0-9]+\+/ && $2 {printf $2 ", "}' | sed -n -e 's/^.*primary //p' | tr 'x+' ' ' | tr ',+' ' ')" if [ -z "$screen_dimensions" ];then # if screen_dimensions is empty, this could be a single monitor wayland display which does not have the word "primary" in the output # workaround is to get the first output returned for the connected display screen_dimensions="$(xrandr --nograb --current | awk -F 'connected |\\+|\\(' '/ connected.*[0-9]+x[0-9]+\+/ && $2 {printf $2 ", "}' | tr 'x+' ' ' | tr ',+' ' ')" fi screen_width="$(awk '{print $1}' <<<"$screen_dimensions")" screen_height="$(awk '{print $2}' <<<"$screen_dimensions")" unset screen_dimensions if [[ "$guimode" == yad* ]];then # if this is being run by yad, there should be a yad window id # store in arrary of x and y # this allows for windows to be moved by the user and the second window to still show up where expected main_yad_window=$YAD_XID if [ -n "${main_yad_window}" ]; then window_info=$(xwininfo -id $main_yad_window) # for speed of execution, we are assuming the order of matches from xwininfo # this is safe as the output order has been standardized for over a decade # this is the location of where the window content starts inside the title bar and window borders (pos x then pos y) main_yad_window_pos=($(echo "$window_info" | sed -n 's/Absolute upper-left.*://p')) # these are the dimensions of where the window content starts inside the title bar and window borders (dim x then dim y) main_yad_window_dim=($(echo "$window_info" | sed -n 's/Width://p;s/Height://p')) # again, for speed, all border widths are saved to one array # left, right, title, bottom [0, 1, 2, 3] # this is the size of the window borders (including title bar) border_widths=($(xprop _NET_FRAME_EXTENTS -id "$main_yad_window" | sed -n 's/_NET_FRAME_EXTENTS.*=//p' | tr ',' '\n')) #total dimensions for both yad windows side by side if [ $screen_width -le 1000 ] || [ $screen_height -le 600 ];then #gui size for small screens height=${main_yad_window_dim[1]} width=600 #width of first window width1=${main_yad_window_dim[0]} #width of second window width2=350 else #gui size for large screens height=${main_yad_window_dim[1]} width=800 #width of first window width1=${main_yad_window_dim[0]} #width of second window width2=480 fi #how far down the top of the title bar of yad window is from the top of the screen yoffset=$((main_yad_window_pos[1]-border_widths[2])) #screen offsets for window 1 #use current position of the left side of the main window border xoffset1=$((main_yad_window_pos[0]-border_widths[0])) #screen offsets for window 2 #use current position of the right side of the main window border xoffset2=$((xoffset1+border_widths[0]+width1+border_widths[1])) #set completed location arguments for both yad windows geometry1="--geometry=${width1}x${height}+${xoffset1}+${yoffset}" geometry2="--geometry=${width2}x${height}+${xoffset2}+${yoffset}" else # default positions when the main window does not exist yet #total dimensions for both yad windows side by side if [ $screen_width -le 1000 ] || [ $screen_height -le 600 ];then #gui size for small screens height=400 width=600 #width of first window width1=250 #width of second window width2=$((width - width1)) else #gui size for large screens height=600 width=800 #width of first window width1=320 #width of second window width2=$((width - width1)) fi #how far down the top of the title bar of yad window is from the top of the screen yoffset=$(((screen_height/2)-(height/2))) #screen offsets for window 1 xoffset1=$(((screen_width/2)-(width/2))) #screen offsets for window 2 xoffset2=$(((screen_width/2)-(width/2)+width1)) #set completed location arguments for both yad windows geometry1="--geometry=${width1}x${height}+${xoffset1}+${yoffset}" geometry2="--geometry=${width2}x${height}+${xoffset2}+${yoffset}" fi elif [[ "$guimode" == xlunch* ]];then #desired dimensions for xlunch window height=700 width=800 #determine xoffset=$(((screen_width/2)-(width/2))) yoffset=$(((screen_height/2)-(height/2))) else error "Unrecognized app list style '$guimode'!" fi } # run get_positions once incase sometime requires the position value immediatly get_positions #Compile xlunch if required if [[ "$guimode" == xlunch* ]] && ([ ! -d "${DIRECTORY}/xlunch" ] || [ ! -f /usr/bin/xlunch ]);then #signal files rm -f /tmp/xlunchfailed /tmp/xlunchfinished /tmp/terminalexit echo '' > /tmp/terminalexit "${DIRECTORY}/etc/terminal-run" " function error { echo -e "\""\e[91m$1\e[39m"\"" echo 'Close this terminal to exit.' echo '' > /tmp/xlunchfailed sleep infinity } trap 'echo "\"""\"" > /tmp/terminalexit' EXIT #uninstall xlunch first sudo rm -rf /etc/xlunch /usr/share/xlunch /usr/bin/xlunch /usr/bin/genentries /usr/bin/updateentries /usr/bin/genentries.desktop.sh /usr/share/icons/hicolor/48x48/apps/xlunch_ghost.png /usr/share/icons/hicolor/48x48/apps/xlunch.png /usr/share/applications/genentries.desktop rm -f /tmp/terminalexit sudo rm -rf /usr/bin/xlunch "\""$DIRECTORY/xlunch"\"" 2>/dev/null sudo apt install -y libimlib2-dev libx11-dev || error 'APT failed to install libimlib2-dev and libx11-dev packages!' cd "\""$DIRECTORY"\"" git clone https://github.com/Tomas-M/xlunch.git || error 'Failed to clone xlunch repository!' cd "\""$DIRECTORY/xlunch"\"" echo 'Running make...' echo "\"""\$"(cat '${DIRECTORY}/xlunch/Makefile' | grep -v 'genentries \|cp -r svgicons/')"\"" > '${DIRECTORY}/xlunch/Makefile' make -j8 || error 'make command failed!' echo 'Running sudo make install...' sudo make install || error 'sudo make install failed!' sudo rm -f /usr/share/applications/genentries.desktop cd $HOME if [ ! -f /usr/bin/xlunch ];then error 'xlunch should be installed now, but /usr/bin/xlunch does not exist!' fi echo '' > /tmp/xlunchfinished " 'Compiling xlunch...' #if terminal doesn't start in 3 seconds, then /tmp/terminalexit will exist. sleep 3 #check for an exit status code from the running terminal while true; do if [ -f /tmp/xlunchfinished ];then echo "xlunch finished installing." break elif [ -f /tmp/xlunchfailed ];then #revert back to yad echo 'yad-default' > "{DIRECTORY}/data/settings/App List Style" error "xlunch failed to compile!" elif [ -f /tmp/terminalexit ];then #if terminal doesn't start in 3 seconds, then /tmp/terminalexit will exist. #revert back to yad echo 'yad-default' > "{DIRECTORY}/data/settings/App List Style" error "The xlunch compilation terminal exited prematurely." else sleep 1 fi done fi #Determine message of the day. If announcements file missing or over a day old, download it. if [ ! -f "${DIRECTORY}/data/announcements" ] || [ ! -z "$(find "${DIRECTORY}/data/announcements" -mtime +1 -print)" ]; then announcements="$(wget -qO- https://git.fdc.abrish.ir/a.kamyar/PublicFiles/Artian-Apps)" echo "$announcements" > "${DIRECTORY}/data/announcements" fi & #use $announcements and & here to avoid delaying launch or emptying the file before download completes if [[ "$guimode" == yad* ]];then #create a named pipe to send app list through to yad pipe="$(mktemp -u)" #get a random filename to work with mkfifo $pipe #make the named pipe trap "rm -f $pipe" EXIT #remove this named pipe on exit echo pipe is $pipe while true;do #main operating loop for yad window #in background process, send initial app list to yad; first echo line waits until yad is accepting input, then use small delay to prevent items from being missed (echo -e '\f' > $pipe sleep 0.5 "${DIRECTORY}/preload" yad '' >> $pipe) & #retrieve a random line from the announcements file for this session motd="$(shuf -n 1 "${DIRECTORY}/data/announcements")" #constantly read from the pipe and send all output to yad #if an item is selected, this script will be run with the row's contents { yad --class Artian-Apps --name "Artian-Apps" --center --title='Artian-Apps' \ --image="${DIRECTORY}/icons/logo-64.png" --image-on-top --text "$motd" \ --list --no-headers --column=:IMG --column=Name --column=Sysname:HD --column=tip:HD --column=@fore@:HD \ --separator='\n' --window-icon="${DIRECTORY}/icons/logo.png" \ --tooltip-column=4 \ --dclick-action "env pipe=$pipe '${DIRECTORY}/gui'" --select-action "env pipe=$pipe '${DIRECTORY}/gui'" \ --button="!${DIRECTORY}/icons/search.png"!'Search':"env pipe=$pipe '${DIRECTORY}/gui' '' '' Search/ Search/" \ --button="!${DIRECTORY}/icons/options.png"!'Settings':2 \ "$geometry1"; button=$?; kill "$!"; } < <(exec tail -f --retry $pipe) case $button in 2) #settings # remove pipe early so that nothing attempts to write to it rm -f "$pipe" "${DIRECTORY}/settings" "${DIRECTORY}/gui" "$@" #run new instance of this script exit 0 ;; 3) #search app="$(yadflags+=("$geometry2") ; app_search_gui)" if [ ! -z "$app" ];then "${DIRECTORY}/gui" '' "$app" "/$app" "$app" fi ;; *) #unknown button exit 0 ;; esac done #END of yad main window loop elif [[ "$guimode" == xlunch* ]];then #create a named pipe to send app list through to yad pipe="$(mktemp -u)" #get a random filename to work with mkfifo $pipe #make the named pipe trap "rm $pipe" EXIT #remove this named pipe on exit echo pipe is $pipe #disabled, but too cool to remove: this can make the transparent background of xlunch blurry - like Windows 7 if false;then scrot -a "$((xoffset+1)),$((yoffset+33)),${width},${height}" blur.png #blur_init.png convert -blur 10x5 ~/blur.png ~/blur.png fi #Depending on the xlunch theme, different arguments will be used. if [ "$guimode" == xlunch-light-3d ];then #light mode xlunchflags=(--multiple --dontquit -WoCS -s 64 --bc e0e0e000 --tc 000000 --pc 6060ffff --hc ffffff50 \ -p "Search: " -a -c $([ $width -lt 550 ] && echo 1 || echo 2) --title "Artian-Apps: Artian app store" \ --icon "${DIRECTORY}/icons/logo.png" --scrollbarcolor ffffff40 --scrollindicatorcolor 0000ff80 \ --width $width --height $height --xposition $xoffset --yposition $yoffset \ --button "${DIRECTORY}/icons/logo-3d.png;;$((($width/2)-(300/2))),0;:exec echo Artian-apps-homepage" \ --button "${DIRECTORY}/icons/settings-dark.png;;$(($width-140)),30;:exec echo Artian-apps-settings" \ -g "${DIRECTORY}/icons/background-3d.png") elif [ "$guimode" == xlunch-dark-3d ];then #dark mode, 3d opaque version xlunchflags=(--multiple --dontquit -WoCS -s 64 --bc ffffff00 --tc DCDDDE --pc ffffffa0 --hc ffffff30 \ -p "Search: " -a -c $([ $width -lt 550 ] && echo 1 || echo 2) --title "Artian-Apps: Artian app store" \ --icon "${DIRECTORY}/icons/logo.png" --scrollbarcolor ffffff20 --scrollindicatorcolor ffffff40 \ --width $width --height $height --xposition $xoffset --yposition $yoffset \ --button "${DIRECTORY}/icons/logo-3d-dark.png;;$((($width/2)-(300/2))),0;:exec echo Artian-apps-homepage" \ --button "${DIRECTORY}/icons/settings-light.png;;$(($width-140)),30;:exec echo Artian-apps-settings" \ -g "${DIRECTORY}/icons/background-3d-dark.png") else #dark mode, transparent version xlunchflags=(--multiple --dontquit -WoCS -s 64 --bc 000000A0 --tc ffffffff --pc 6060ffff --hc ffffff30 \ -p "Search: " -a -c $([ $width -lt 550 ] && echo 1 || echo 2) --title "Artian-Apps: Artian app store" \ --icon "${DIRECTORY}/icons/logo.png" --scrollbarcolor ffffff40 --scrollindicatorcolor 0000ff80 \ --width $width --height $height --xposition $xoffset --yposition $yoffset \ --button "${DIRECTORY}/icons/logo-128.png;;$((($width/2)-(128/2))),0;:exec echo Artian-apps-homepage" \ --button "${DIRECTORY}/icons/logo-text.png;;$([ -z "$prefix" ] && echo '45' || echo '65'),$([ -z "$prefix" ] && echo '10' || echo '0');:exec echo Artian-apps-homepage" \ --button "${DIRECTORY}/icons/settings-light.png;;$(($width-140)),30;:exec echo Artian-apps-settings") fi #in background process, send initial app list to xlunch; first echo line waits until xlunch is accepting input ( #set initial value to make xlunch keep listening echo 'botspot is cool' > $pipe echo "$("${DIRECTORY}/preload" xlunch '')" > $pipe) & #read pipe pipe to xlunch read output of xlunch tail -f --retry $pipe | xlunch "${xlunchflags[@]}" | while read -r line; do echo "Received '$line'" if [ "$line" == 'Updates/' ];then echo > $pipe #clear list #in subprocess, open details window for app geometry2="--center --height=$((height/2)) --width=$width" pipe='' "${DIRECTORY}/gui" '' '' "$line" "$line" #back to main level "${DIRECTORY}/preload" xlunch '' > $pipe #category selected elif [[ "$line" == */ ]];then echo > $pipe #clear list "${DIRECTORY}/preload" xlunch "$(echo "$line" | sed 's+/$++g')" > $pipe #Artian-apps logo button clicked elif [ "$line" == 'Artian-apps-homepage' ];then x-www-browser "$(cat "https://artianhpc.ir/")" & # x-www-browser "$(cat "${DIRECTORY}/etc/git_url")" & #Settings button clicked elif [ "$line" == 'Artian-apps-settings' ];then #close xlunch echo ':quit' > $pipe #run settings, then run Artian-apps gui "${DIRECTORY}/settings" "${DIRECTORY}/gui" "$@" #exit this script exit 0 #app selected else echo > $pipe #clear list #in subprocess, open details window for app geometry2="--center --height=$((height/2)) --width=$width" pipe='' "${DIRECTORY}/gui" '' '' "$line" "$line" #show app list again "${DIRECTORY}/preload" xlunch "$(dirname "$line" | sed 's/^.$//g')" > $pipe fi done fi