For the moment I’m out of hardware topics, so let’s look at some stuff that might be more widely applicable to folks who don’t make my exact hardware choices.
In a minute I’ll show you a script I wrote for testing input. But first…
Quitting EmulationStation
I usually don’t have a keyboard hooked up to the arcade Pi. I have one wireless keyboard/trackpad and it roams from Pi to Pi based on my needs. The more I can do from a remote session the better.
You might remember that RetroPie uses a frontend called EmulationStation. For reasons that will soon become clear, I wanted a quick way to close EmulationStation gracefully from a remote session. This is what the quit-emulationstation
script does.
#!/bin/bash
print_help_and_exit() {
echo "Usage: quit-emulationstation [options]"
echo ""
echo "Exits EmulationStation gracefully (if it is running)."
exit 0
}
while true ; do
case "$1" in
-h) print_help_and_exit ;;
*) break ;;
esac
shift
done
pid=`ps -ef | awk '/emulationstation\/emulationstation$/ {print $2}'`
if [ -z $pid ]; then
echo "EmulationStation doesn't appear to be running."
exit 1
else
printf "Quitting EmulationStation..."
kill $pid && echo -e "done.\n"
exit 0
fi
This is almost comically verbose for a script that does a really simple thing. That’s on me. My assumption with these scripts, as with most of the code I write, is that I will not remember how to use them six months later.
Like the others, this lives in /home/pi/bin
and is just a few keystrokes away.
Testing controls
When I was wiring up the control panel, I took advantage of the fact that the I‐Pac turns button presses into simulated keyboard presses. At any point I could plug the half‐finished panel into the nearest computer via USB and make sure the button I just wired up was actually doing what I thought. Typically I’d open an empty document in TextEdit or something just so I could have a sandbox for observing key presses.
Things got harder after I put the control panel in place. Say a button seems to stop working in the middle of gameplay — what then? I could quit the game and launch a text editor, but to do that I’d have to dig out the wireless keyboard and start a direct terminal session. This is at odds with my preferred debugging strategy: SSHing into the Pi from my laptop. It’s easier to type on my laptop than on a small wireless keyboard, plus the second screen comes in handy.
But if I SSH into the Pi and open a text editor, input from the I‐Pac won’t be reflected in that document. If local input affected remote sessions, Linux wouldn’t truly be multi‐user.
So I did some research to see how I could detect input from the I‐Pac’s pseudo‐keyboard from a remote SSH session. I eventually found a Ruby gem called libdevinput that would let me listen to raw input from a specific device.
Since I wrote this script, libdevinput has been deprecated in favor of device_input, so in preparation for this article I ended up tweaking this script for the latter library’s slightly different API.
#!/usr/bin/env ruby
require 'pathname'
begin
require 'highline'
require 'device_input'
rescue LoadError => e
puts "This program requires Highline and device_input:"
puts " $ gem install highline device_input"
exit 1
end
KEY_MAP = {
"Player 1 Button 1 (Red)" => 29, # LeftControl
"Player 1 Button 2 (Yellow)" => 56, # LeftAlt
"Player 1 Button 3 (Green)" => 57, # Space
"Player 1 Button 4 (Blue)" => 42, # LeftShift
"Player 1 Button 5 (White)" => 44, # Z
"Player 1 Button 6 (White)" => 45, # X
"Player 1 Joystick UP" => 103, # Up
"Player 1 Joystick DOWN" => 108, # Down
"Player 1 Joystick LEFT" => 105, # Left
"Player 1 Joystick RIGHT" => 106, # Right
"Player 2 Button 1 (Red)" => 30, # A
"Player 2 Button 2 (Yellow)" => 31, # S
"Player 2 Button 3 (Green)" => 16, # Q
"Player 2 Button 4 (Blue)" => 17, # W
"Player 2 Button 5 (White)" => 37, # K
"Player 2 Button 6 (White)" => 23, # I
"Player 2 Joystick UP" => 19, # R
"Player 2 Joystick DOWN" => 33, # F
"Player 2 Joystick LEFT" => 32, # D
"Player 2 Joystick RIGHT" => 34, # G
"Player 1 Coin" => 6, # 5
"Player 2 Coin" => 7, # 6
"Player 1 Start" => 2, # 1
"Player 2 Start" => 3, # 2
"Pause" => 25, # P
"Enter" => 28, # Enter
"Tab" => 15, # Tab
"Escape" => 1, # Escape
}
$cli = HighLine.new
Signal.trap('INT') do
puts ""
exit 1
end
def choose_device
base_dir = Pathname.new('/dev/input/by-id')
# Will get all non-hidden files.
list = Pathname::glob( base_dir.join('*') )
table = {}
list.each do |p|
table[p.basename.to_s] = p
end
names = table.keys
puts "Select the device you want to test:"
choice = $cli.choose do |menu|
menu.prompt = "Choice: "
menu.choices(*names)
end
table[choice]
end
$device = choose_device
def keypress?(event)
return false if !event
return false if event.data.type != 1
return false if event.data.value != 1
true
end
def description_from_code(event)
KEY_MAP.key(event.data.code)
end
puts "Listening for presses..."
File.open($device, 'r') do |dev|
DeviceInput.read_loop(dev) do |event|
if keypress?(event)
desc = description_from_code(event)
puts desc unless desc.nil?
end
end
end
Notes
- The script requires the aforementioned
device_input
gem. It also requireshighline
to generate the list of devices to choose from. -
You’ll have noticed the
KEY_MAP
hash. That’s the part you’d need to customize. It maps my human‐readable button names to the keys they should be mapped to, as represented by key codes.If you’re expecting the key codes to be the same as the ones you’d get from a key press in a web browser, I’ve got some awful news for you. To figure out the right codes for the keys you want to test, run
sudo showkey --keycodes
, then press some keys on the keyboard that’s hooked up to the system — not the keyboard belonging to the computer running your SSH session. With each key press it’ll log that key’s integer value. -
This script works wonderfully for I‐Pac users, since an I‐Pac presents itself as a keyboard to the system. Some other USB encoders for arcade controls present themselves as joypads or mice rather than keyboards, but since they’re all HID devices, I’d guess that this approach would work without much alteration. You’d just have to figure out the codes produced by the buttons and joystick directions.
-
When you run this script, you’ll choose a device from the options listed (mine is
usb-Ultimarc_IPAC_2_Ultimarc_IPAC_2_9-if01-event-kbd
), at which point the script will go into a loop waiting for keypresses. Any keypress that maps to a button will get that button’s name logged; press Player 1’s first button and you should seePlayer 1 Button 1 (Red)
(or whatever you named it). -
My original version of this script cycled through all the defined keys and had you press each one in turn. Later I decided it was much simpler to have the script wait in a loop and tell you when you pressed a key it recognized.
Next time
There are more scripts to cover, but since they all revolve around editing game metadata, I’m saving them for their own post.
Comments