#!/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 Pi-Apps. Most likely you did not install Pi-Apps correctly. Please run the install script." fi if [[ $(id -u) == 0 ]]; then echo "Pi-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 "Pi-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 #Transition from the GUI that calls itself to the self-contained GUI: when called by the old GUI, kill that yad window and treat this subprocess as the new main thread if [ -z "$1" ] && [ ! -z "$2" ] && [ ! -z "$3" ] ;then kill $(ps -fC yad | grep "yad --class Pi-Apps --name Pi-Apps --center --title=Pi-Apps --image" | awk '{print $2}') unset YAD_XID #new gui, don't try to get dimensions of the now closed app list 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://raw.githubusercontent.com/Botspot/pi-apps/master/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://raw.githubusercontent.com/Botspot/pi-apps/master/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 pi-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 unzip debsums libgtk3-perl' # 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 #run is_supported_system once mostly to check for unsupported ubuntu/debian distros and warn the user with a blocking GUI popup runonce <<"EOF" if ! is_supported_system >/dev/null;then text="YOUR SYSTEM IS UNSUPPORTED: $(is_supported_system) Pi-Apps will disable the sending of any error reports until you have resolved the issue above. Your mileage may vary with using Pi-Apps in this state. Expect the majority of apps to be broken." yadflags=(--class Pi-Apps --name "Pi-Apps" --window-icon="${DIRECTORY}/icons/logo.png" --title="Pi-Apps") userinput_func "$text" "Ok" fi true 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 pi-apps usage link every time the GUI is run shlink_link usage active } #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 if [ -z "$screen_width" ] || [ -z "$screen_height" ];then 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")" fi 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) #skip calculating offsets if yad window is in the same place as last time if [ -z "$last_window_info" ] || [ -z "$geometry1" ] || [ -z "$geometry2" ] || [ "$last_window_info" != "$window_info" ];then # 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}" fi 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 || 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://raw.githubusercontent.com/Botspot/pi-apps-announcements/main/message)" echo "$announcements" > "${DIRECTORY}/data/announcements" fi & #use $announcements and & here to avoid delaying launch or emptying the file before download completes details_window() { #input: prefix/app local input="$1" [ -z "$input" ] && return # 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 local app="$(basename "$input")" local prefix="$(dirname "$input")" #add status heading local app_status="$(app_status "${app}")" #text to the right of the app icon local 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 local packages="$(pkgapp_packages_required "$app")" if [ -z "$packages" ]; then #returned required package list is empty. application cannot be installed #this case cannot be hit if the application is already hidden which it should be yad "${yadflags[@]}" --title=Results --width=310 \ --text=""\""$(list_apps | grep -i -m1 "^$query")"\"" is not compatible with your ${__os_desc} ${arch}-bit OS." \ --button=OK:0 return fi 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 local 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 local 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 local 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 local 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 local 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 "${yadflags[@]}" --text-info --fontname=12 --wrap --show-uri \ --text="$(sed 's/&/&/g' <<<"$abovetext")" \ "${imageline[@]}" \ --title="Details of ${app}" --window-icon="${DIRECTORY}/icons/logo.png" \ $geometry2 --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" & details_window "$@" #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 & details_window "$@" #open details window again ;; 14) #viewlog echo "Viewing error log of $app..." echo "Log filepath: $logfile" "${DIRECTORY}/etc/viewlog" "$logfile" details_window "$@" #open details window again ;; esac } 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 #create a named pipe to store detailspid detailspid="$(mktemp -u)" #get a random filename to work with mkfifo $detailspid #make the named pipe trap "rm -f $pipe; rm -f $detailspid" EXIT #remove the named pipes on exit echo pipe is $pipe echo detailspid is $detailspid #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")" 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 } kill_details() { #close the details window in 1 second for better UX. Uses the $detailspid variable. To close it immediately add an argument of 0 echo "" >> $detailspid & detailspidlist="$(cat $detailspid)" ( if [ "$1" != 0 ];then sleep 1.5 fi echo "$detailspidlist" | while read line ; do [ ! -z "$line" ] && pkill -P "$line" 2>/dev/null [ ! -z "$line" ] && kill "$line" 2>/dev/null done ) & } run_user_selection() { #Function that is run in yad gui to execute user selection yadflags=(--class Pi-Apps --name "Pi-Apps" --window-icon="${DIRECTORY}/icons/logo.png" --title="Pi-Apps" --separator='\n') echo "YAD_XID is $YAD_XID" input="$3" if [[ "$input" == *'Updates/' ]];then #updater window kill_details 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 kill_details details_window_check(){ local input read input [ -z "$input" ] && return details_window "/$input" } get_positions #show app details window for this selected app yadflags+=("$geometry2" --close-on-unfocus) app_search_gui | details_window_check & echo "$!" >> $detailspid & 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 elif [[ "$input" == 'YAD_XID is'* ]];then YAD_XID="$(echo "$input" | awk '{print $3}')" elif [ ! -z "$input" ];then #app details window get_positions #show app details window, and close the previous one kill_details details_window "$input" & echo "$!" >> $detailspid & fi } { yad "${yadflags[@]}" \ --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 "bash -c "\""run_user_selection %s"\""" --select-action="bash -c "\""run_user_selection %s"\""" \ --button="!${DIRECTORY}/icons/search.png"!'Search':"bash -c "\""run_user_selection '' '' Search/"\""" \ --button="!${DIRECTORY}/icons/options.png"!'Settings':2 \ "$geometry1"; button=$?; kill "$!"; } < <(exec tail -f --retry $pipe) kill_details 0 #immediately close details window without delay unset YAD_XID #avoid trying to get dimensions of a now closed yad window (fixes badWindow error in get_positions after returning from settings) if [ "$button" == 2 ];then # remove pipe early so that nothing attempts to write to it rm -f "$pipe" rm -f "$detailspid" "${DIRECTORY}/settings" "${DIRECTORY}/gui" "$@" #run new instance of this script fi #END of yad main window 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 "Pi-Apps: Raspberry Pi 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 pi-apps-homepage" \ --button "${DIRECTORY}/icons/settings-dark.png;;$(($width-140)),30;:exec echo pi-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 "Pi-Apps: Raspberry Pi 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 pi-apps-homepage" \ --button "${DIRECTORY}/icons/settings-light.png;;$(($width-140)),30;:exec echo pi-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 "Pi-Apps: Raspberry Pi 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 pi-apps-homepage" \ --button "${DIRECTORY}/icons/logo-text.png;;$([ -z "$prefix" ] && echo '45' || echo '65'),$([ -z "$prefix" ] && echo '10' || echo '0');:exec echo pi-apps-homepage" \ --button "${DIRECTORY}/icons/settings-light.png;;$(($width-140)),30;:exec echo pi-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 input; do echo "Received '$input'" if [ "$input" == 'Updates/' ];then echo > $pipe #clear list "${DIRECTORY}/updater" gui fast --skip-taskbar --close-on-unfocus exitcode=$? #0 if update successful, 1 if list of updates was closed #refresh main level "${DIRECTORY}/preload" xlunch '' > $pipe #category selected elif [[ "$input" == */ ]];then echo > $pipe #clear list "${DIRECTORY}/preload" xlunch "$(echo "$input" | sed 's+/$++g')" > $pipe #pi-apps logo button clicked - go to homepage in web browser elif [ "$input" == 'pi-apps-homepage' ];then x-www-browser "$(cat "${DIRECTORY}/etc/git_url")" & #Settings button clicked elif [ "$input" == 'pi-apps-settings' ];then #close xlunch echo ':quit' > $pipe #run settings, then run pi-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" details_window "$input" #show app list again "${DIRECTORY}/preload" xlunch "$(dirname "$input" | sed 's/^.$//g')" > $pipe fi done fi