RetroPie forum home
    • Recent
    • Tags
    • Popular
    • Home
    • Docs
    • Register
    • Login

    Run emulationstation from X (script)

    Scheduled Pinned Locked Moved Ideas and Development
    xdesktopraspiswap-x-n-esscript
    3 Posts 3 Posters 3.1k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Z
      zithro
      last edited by

      Hi all,

      installing EmulationStation (via RetroPie) for my bro, I wanted something simple to switch from X to ES and vice versa. No cryptic commands in the shell. The setup is a Pi3 (vanilla Raspbian full), which will be used as a media center AND a gaming console, not as the same time though.

      I found a few posts talking about that switching, but with no clear answer (https://retropie.org.uk/forum/topic/2757/run-retropie-from-a-running-x-session and https://www.reddit.com/r/linuxquestions/comments/4w7zut/how_to_start_a_program_outside_of_an_x_session/). So I thought I had to find a way myself :)

      It's a bit hackish, but it works.

      There are three elements : a desktop shortcut, a little binary and a bash script.

      • The desktop shortcut simply launches the script from X.
      • The binary is writevt, compiled from open source C code, a program to write to a TTY. It's now part of the package "console-tools", but RasPis have kbd by default, so it needs to be compiled from source. No worries, it's really easy.
      • The script does:
      1. close X
      2. launch emulationstation
      3. restart X (or replay or shutdown if menu used)

      It works because the script uses two techniques:

      • the double-forking mechanism which allows the script to get attached to "init" instead of "X". So when X closes, the script still runs.
      • the use of writevt, which can write on a tty as if you were doing it with the keyboard.

      I'll put it on github some day, meanwhile I'll post everything here as it is used on my system !
      For people who can't script, if you follow the instructions there should be no problem.
      For the others, the code in the bash script is heavily commented.

      I. Pre-requisites

      - A free TTY1

      The console in tty1 MUST BE logged in and free to use. As my setup uses autologin into X, it's taken care of (raspi-config -> 3. boot options -> B1. Desktop/CLI -> B4. Desktop autologin).
      If you use console autologin and another tty than the first to "startx" (like tty6), that's fine too.
      If no autologin, do it manually or tamper with the TTY number in the bash script (hardcoded) to use another TTY.

      - Scripts folder

      Create a folder "zit-scripts" in your home, so you have "/home/pi/zit-scripts".
      That's where the bash script and writevt will reside.
      You can run

      cd && mkdir zit-scripts
      

      II. Desktop shortcut

      Create and edit a file "/home/pi/Desktop/zzconsole-desktop-link", using your preferred editor.
      Put this text inside, and save it:

      [Desktop Entry]
      Name=Play consooole
      GenericName=zLaunch EmuStation from X (zshortcut)
      Comment=zLaunch EmuStation from X (zshortcut)
      Exec=/home/pi/zit-scripts/zzconsole
      Icon=/usr/share/icons/gnome/32x32/devices/input-gaming.png
      Terminal=false
      Type=Application
      Categories=
      Keywords=
      

      PS: use an icon/path of your choice in the Icon line (this one is a SNES-like pad).

      III. writevt

      1. Download "https://raw.githubusercontent.com/grawity/code/master/thirdparty/writevt.c" to /home/pi/zit-scripts:
      cd && wget --directory-prefix=zit-scripts https://raw.githubusercontent.com/grawity/code/master/thirdparty/writevt.c
      
      1. Compile the C code to a binary:
      cd && cd zit-scripts && gcc -o zzwritevt writevt.c
      

      No need to give perm bit, it should have it already, if not just run "chmod +x zzwritevt"

      IV. Bash script

      This is the long one. Sorry for the French parts, just remove them, it's only help (kept for frenchies if needed).
      Simply copy this code into your preferred editor, and save it as "/home/pi/zit-scripts/zzconsole".
      You will usually need to set the perm bit with "chmod +x /home/pi/zit-scripts/zzconsole"

      #!/bin/bash
      
      # zit script to launch emulationstation FROM a Pi desktop
      # script:
      # - close X
      # - launch emulationstation
      # - restart X (or replay or shutdown)
      
      # the user Pi MUST be logged in tty1 for all of this to work !
      # this is automatic when booting to desktop (or CLI) automatically
      
      ##################
      #	CONFIG	#
      ##################
      ### script(s) paths
      # UNUSED - just replace all occurences of the path in front of "zzconsole" to use
      # could also use autodetection of current path ...
      SCRIPTS_PATH=/home/pi/zit-scripts
      
      ### where is zzwrite/writevt ?
      	# what is zzwrite/writevt ?
      	# writevt is a C program to write to a TTY
      	# it's now part of the package "console-tools", but Raspis have kbd by default, so I compiled from source
      	# zzwrite is the exec produced from writevt.c (https://raw.githubusercontent.com/grawity/code/master/thirdparty/writevt.c)
      	# to compile it yourself, dl the C code, and do $ gcc -o zzwritevt writevt.c
      	# NOTE FROM "https://unix.stackexchange.com/questions/48103/construct-a-command-by-putting-a-string-into-a-tty/48120#48120"
      	# 	"[...] you have to use '\r' (or '\x0D') instead of '\n' (or '\x0A') to send a return."
      # leave empty if its in the -SUDO- $PATH
      WRITEVT_PATH=/home/pi/zit-scripts
      # exec name (original: writevt)
      WRITEVT_EXEC=zzwritevt
      
      ### X commands
      XCMD_START="/etc/init.d/lightdm start"
      XCMD_STOP="/etc/init.d/lightdm stop"
      
      ##################
      #	CODE		#
      ##################
      
      # no-X mode
      # to test the script without killing/starting X (way faster)
      #~ NO_X_CMDS=1
      # command run ieof ES when no-X mode
      DBG_CMD="tail -n 1 -f /var/log/debug"
      				
      # get full writevt path
      if [[ "WRITEVT_PATH" = "" ]]; then
      	# no path given, is in sudo PATH
      	WRTVT=$WRITEVT_EXEC
      else
      	WRTVT=$WRITEVT_PATH/$WRITEVT_EXEC
      fi
      
      # if help requested
      if [[ "$1" = "-h" ]]; then
      	# display help
      	echo
      	echo "Info: le bureau sous Linux s'appelle X, pour X-window system"
      	echo "Si y'a un message genre \"X is running\", et qu'Emulation Station veut vraiment pas se lancer, faut faire:"
      	echo " 1. Faire 'CTRL-ALT-F7' (ou F1, F2, ...) pour ouvrir le(s) bureau(x) encore ouvert(s)"
      	echo " 2. Pour chaque bureau, taper 'CTRL-ALT-BACKSPACE', ça devrait fermer X et ramener à un prompt (à la DOS, 2-86 mode!)"
      	echo " 3. Lancer emulationstation manuellement"
      	echo " 4. Pour retourner au bureau, taper \"zzgui\" ou \"zzdesktop\""
      	echo
      	echo "NOTE: C'est IMPOSSIBLE d'avoir le bureau ET la console en même temps !"
      	echo "ndZit: test with X on tty1 (with startx, not svc) ? Well, i guess DISPLAY is still the :1 and blocks ES, maybe try X@:2"
      	echo
      fi
      
      # there is 3 steps to launch the emulator from X, so 3 IFs in code
      # 1. the script is re-launched but forked to init, to avoid being dependent on X and closed by it, then exits
      # 2. the fork switches TTY from 7 to 1 (X to prompt), kill X if needed, and re-launches itself FROM tty1, then exits
      # 3. the emulator is run, and when closed, a menu to ask what to do
      
      # if no args (==exit X mode, to keep Desktop shortcut simple)
      if [[ $# = 0 ]]; then
      	
      	# relaunch the script, but double-forked
      	# needed to resist X closing
      	(/home/pi/zit-scripts/zzconsole forked >/dev/tty1 2>&1 &) &
      	exit 0
      fi
      
      # this version is launched from init and outputs to tty1
      # it uses writevt to act on tty1, but echo to output things
      if [[ "$1" = "forked" ]]; then
      	
          ### Clear/clean tty1 to start on a new line AND a fresh new TTY
      	# clean tty1 current line with ^C in case it contained chars
      	sudo $WRTVT /dev/tty1 "`echo $'\003'`"
      	
          ### Change to vt1
      	sudo $WRTVT /dev/tty1 "chvt 1"$'\r'
      	# now clear it nicely, but not in no-X/debug mode to see output between different runs
      	if [[ ! $NO_X_CMDS ]]; then
      		sudo $WRTVT /dev/tty1 "clear"$'\r'
      		# wait 1s, otherwise output may start BEFORE seeing the tty
      		sleep 1
      	fi
      	
          ### Init script output
      	echo ""
      
          ### Warning if no-X mode
      	if [[ $NO_X_CMDS ]]; then
      		echo ""
      		echo "Debug mode, aka No-X mode"
      		echo "Will not kill and restart X, just switching TTYs and run script"
      		echo "DO NOT USE WITH AN ES LAUNCH, USE TAIL OR W/E"
      	else
          ### Close X
      		# first, the usual Pi way, if X was autobooted and is on tty7 (so launched via the lightdm service)
      		if [[ `/etc/init.d/lightdm status | grep "Active: active (running)" | wc -l` = 1 ]]; then
      			echo "X server shutting down (lightdm service) ..."
      			#~ echo "Attendre que X se ferme ..."
      			sudo $XCMD_STOP > /dev/null
      			# let it shutdown nicely all X services, including Xorg
      			# maybe its unneeded, but dont wanna test (if the shutdown script forks processes, they may continue running)
      			# ive seen dbus takes a long time to shutdown, but maybe launching ES at the same time doesnt hurt
      			sleep 5
      		else
      			echo "lightdm service not started"
      		fi
      	
      		# kill X when launched with "startx", and not via the lightdm service
      		XPID=`pgrep Xorg`
      		PGREP_RET=`echo $?`
      		# process(es) found (does not handle yet multi PIDs, or is it automatically ?)
      		if [[ $PGREP_RET = 0 ]]; then
      			echo "X server shutting down (startx) ..."
      			sudo kill $XPID >/dev/null 2>&1
      			sleep 5
      		else
      			echo "No (remaining) Xorg process"
      		fi
      		
      		# kill all remaining ssh-agents (dunno why theyre not killed with X session)
      		# q,w,e,i
      		sudo killall --quiet --wait --exact --ignore-case -- /usr/bin/ssh-agent -s
      	fi
      	
          ### Launch emulator and menu part
      	# reset term line first
      	sudo $WRTVT /dev/tty1 $'\r'
      	sudo $WRTVT /dev/tty1 "echo && /home/pi/zit-scripts/zzconsole play"$'\r'
      	
          ### done for this fork
      	exit 0
      fi
      
      # - run emulationstation
      # - after game menu
      # - after game action
      if [[ "$1" = "play" ]]; then
      	
      	# from now on, prevent Ctrl-C from being used in THIS script, so the menu cant get killed
      	# but propagate it to launched process
      # hmm, isnt ES using ^C too ?
      	# function launched on trap
      	_zINTrap() {
      		echo
      		echo "CTRL-C is blocked in this script (SIGINT signal trapped)"
      		echo "Continue as if nothing happened (ie. answer normally to a menu or question)"
      	}
      	
      	# trap ^C aka SIGINT
      	trap : INT
      	
      	# no GOTO in bash, need loop
      	# 1st exec forced params values (start loop and run ES)
      	loop="y"
      	bypass_emul=0
      	
      	while [ "$loop" = "y" ]; do
      		
      		## run emulationstation if requested
      		if [[ "$bypass_emul" = 0 ]]; then
      			# for dbg, run another persistent prog
      			if [[ $NO_X_CMDS ]]; then
      				echo "Launching debug command \"$DBG_CMD\" (Ctrl-C to quit) ..."
      				echo
      				$DBG_CMD
      			else
      				echo "Launching Emulation Station ..."
      				echo
      				#~ echo "Sleep 5s" && sleep 5 # when ES not installed, goes direct to after-game menu, so sleep for dbg
      				emulationstation
      			fi
      			
      			# custom trap for when ES not running, to preserve script
      			trap _zINTrap INT
      		fi
      		
      		## after game menu
      		# next line is to keep error messages from consecutive runs, except in no-X/debug mode
      		if [[ ! $NO_X_CMDS ]]; then
      			clear
      		fi
      		
      		echo
      		echo "----------------------------------------------------------------"
      		echo "     Playtime over ?"
      		echo
      		echo "   Yes, but get me back to X: ENTER"
      		echo "   Yes, and power off the Pi: q"
      		echo "   No, relaunch EmulationStation: j"
      		echo "   Maybe, stay in shell: c"
      		echo "----------------------------------------------------------------"
      		#~ echo
      		
      		menu_answer=
      		read menu_answer
      		
      		## after game action
      		# return to desktop if thats what the user wants
      		# covers ^d AND empty var
      		if [[ "$menu_answer" = "" ]]; then
      			
      			if [[ ! $NO_X_CMDS ]]; then
      				sudo $XCMD_START
      				echo "Restarting X, this will take a few seconds ..."
      				sleep 1 && chvt 1
      				sleep 5 && chvt 7
      				sudo $WRTVT /dev/tty1 "clear"$'\r'
      			else
      				echo "No X restart, but switching back to it"
      				echo "END"
      				# for fast back to desktop and rerun (app still focused so hit ENTER)
      				# comment to stay in tty1
      				chvt 7
      			fi
      			
      			exit 0
      		fi
      		# no need, loop will do it
      		if [[ "$menu_answer" = "j" ]]; then
      			loop="y"
      			bypass_emul=0
      			# reset to a no message trap
      			trap : INT
      			echo
      			continue
      		fi
      		
      		# stay in tty1
      		if [[ "$menu_answer" = "c" ]]; then
      			echo
      			echo "Return to prompt"
      			echo
      			exit 0
      		fi
      		
      		# power off Pi, really ?
      		if [[ "$menu_answer" = "q" ]]; then
      			echo "There's no need to power off those kind of devices as it draws almost nothing, but if you wanna, you can ^^"
      			echo "Just confirm by y"
      			pwroff=0
      			read pwroff
      			if [[ "$pwroff" = "y" ]]; then
      				echo
      				echo "Powering off the Pi !"
      				echo "When the green LED stops blinking fastly and stays off, you can safely unplug the power plug"
      				sleep 5
      				sudo poweroff
      			# redisp menu if user cancels
      			else
      				loop="y"
      				bypass_emul=1
      			fi
      		fi
      		
      		# unknown answer, loop on menu
      		loop="y"
      		bypass_emul=1
      		
      	# end while loop
      	done
      	exit 0
      # end interactive/play mode (never reached though)
      fi
      
      
      

      V. Tests

      Launch your Pi to X (remember the tty1 MUST be auto-logged AND free), and click on the desktop shortcut.
      It will swap to tty1 and should launch EmulationStation. When you shutdown ES, a menu will prompt you what you wanna do. Relaunch ES or X, shutown, stay in shell.

      VI. Evolutions

      A future or alternate version could remove the menu and restart X automatically. It was easier for development tests, and I think the relaunch/shutdown options may be useful day-to-day, but cannot ES do it too via its menus ? Need to test more ;)

      Hope it's useful, sorry for the long post ^^

      V 1 Reply Last reply Reply Quote 1
      • G
        goethe
        last edited by

        You are a sweetheart. Works as a charm!

        1 Reply Last reply Reply Quote 0
        • V
          VerVon @zithro
          last edited by

          @zithro impressive script. I was facing the same problem and used this approach:

          1 - create a shortcut on the desktop to a shell script (as you did above)
          2 - download and compile ttyecho (instructions here)
          3 - shell script contents:

          #!/bin/bash
          #swith to display to tty1
          sudo chvt 1
          
          #start emulationstation on tty1.  Switch back to tty7 (xwindows) when emulationstation is quit
          sudo ttyecho -n /dev/tty1 "emulationstation ; sudo chvt 7"
          

          To return to xwindows exit emulationstation using the start menu.

          p.s. note that you can switch back and forth between X and Emulationstation by using Ctrl+Alt+F7 and Ctrl+Alt+F1 if you like to keep both running at the same time.

          1 Reply Last reply Reply Quote 0
          • First post
            Last post

          Contributions to the project are always appreciated, so if you would like to support us with a donation you can do so here.

          Hosting provided by Mythic-Beasts. See the Hosting Information page for more information.