Run emulationstation from X (script)
-
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:
- close X
- launch emulationstation
- 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 runcd && 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
- 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
- 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 ^^
-
You are a sweetheart. Works as a charm!
-
@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.
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.