Skip to content

Instantly share code, notes, and snippets.

@2ndBillingCycle
Forked from Stewmath/.xbindkeysrc
Last active March 26, 2023 09:02
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 2ndBillingCycle/52e3d0ade64304139519523275cd9fba to your computer and use it in GitHub Desktop.
Save 2ndBillingCycle/52e3d0ade64304139519523275cd9fba to your computer and use it in GitHub Desktop.
Livesplit global hotkeys workaround

Install and configuration

This is how I installed and setup LiveSplit on Manjaro KDE Plasma.

Required software

Using Manjaro's "Add/Remove Software", or the pacman command, I installed the following:

  • wine-mono
  • winetricks
  • zenity
  • xdotool
  • curl
  • base-devel
  • git

I used pacman with the following command. When it prompted which packages from base-devel I wanted to select, I hit Enter to select the default of "all":

sudo pacman -Sy --needed wine-mono winetricks zenity xdotool curl base-devel git

Installing Wine and LiveSplit

First, I downloaded LiveSplit. This can be done either through the website, or through GitHub.

Next, I created a folder in my home directory called apps, which I unpacked LiveSplit into.

You can use whichever unzipping utility you would like.

Then, I opened a terminal, changed directory to ~/apps/LiveSplit_1.8.10, and tried using wine to run LiveSplit.exe:

cd ~/apps/LiveSplit*/ && \
    wine LiveSplit.exe

wine asked to install lots of packages, and I eventually installed all of them.

When LiveSplit started, it showed the timer, but right clicking that brought up an empty menu. I selected the last blank menu item to quit LiveSplit.

In the terminal, there was a line that looked like:

0024:fixme:mscoree:parse_supported_runtime sku=L".NETFramework,Version=v4.6.1" not implemented

Installing .NET 4.6.1

I used winetricks to install .Net 4.6.1.

When I started winetricks, I received a warning about running with a 64 bit prefix, so I created a new wineprefix by selecting "Create new wineprefix", with "Architecture" set to 64, and named it "default32".

Then, with the "default32" prefix selected, I chose "Install a windows DLL or component", then "dotnet461" on the following menus.

In order to install .Net 4.6.1, winetricks had to install .Net 4.0, 4.5, 4.6, then finally 4.6.1, one after the other.

After finishing installing the frameworks, I returned to winetricks and selected "Run a commandline shell (for debugging)".

This opened a new terminal, which I used to check if LiveSplit would run, by typing the following:

cd ~/apps/LiveSplit*/ && wine LiveSplit.exe

Right clicking the timer popped up a menu with items that weren't blank.

To make sure I could replicate this without having to start this terminal every time, I looked at the environment and copied $WINEPREFIX for a script that we'll create in a moment, and made sure it was set in my environment:

env
echo 'export WINEPREFIX="'"${WINEPREFIX}"'"' | tee -a ~/.bash_profile >> ~/.profile

Note: If you have wine's default profile set up for running specific programs, instead of running the above command, you can run only env and copy the WINEPREFIX variable, to put on the second line of the runlivesplit script that we'll create later on, formatted like the following, exactly as it shows up in the output of env (using the value from mine as an example; if yours has a space in the path, it must be wrapped in "):

WINEPREFIX=/home/sec/.local/share/wineprefixes/default32

I started LiveSplit again in the same terminal, and confirmed that the global hotkeys weren't working, and closed the terminal.

The following sections use the scripts this gist is based on to configure xbindkeys and xdotool to enable global hotkeys. Thanks to Drenn1 for the original work.

Installing and configuring xbindkeys

xbindkeys is in the Arch User Repository (AUR). To install it, I'll be following the steps for manually installing an AUR package, but you can use any of the AUR helpers. I personally recommend yay.

First, I made sure I had all the packages necessary for building items from the AUR.

sudo pacman -Sy --needed base-devel git

Then, I cloned, built, and installed xbindkeys:

mkdir -p ~/apps
cd ~/apps
git clone --depth 1 'https://aur.archlinux.org/xbindkeys.git' ./xbindkeys
cd xbindkeys
makepkg -si

I followed the same steps for xbindkeys_config-gtk2, to make configuring xbindkeys easier:

mkdir -p ~/apps
cd ~/apps
git clone --depth 1 'https://aur.archlinux.org/xbindkeys_config-gtk2.git' ./xbindkeys_config
cd xbindkeys_config
makepkg -si

Configuring xbindkeys

First, I installed the script that we'll be using to send keypresses directly to LiveSplit.

Install livesplithk

This script uses xdotool, so I made sure that was installed:

sudo pacman -Sy --needed xdotool

To make sure I could start this script anywhere, I added ~/.local/bin to the $PATH variable, with some help from this answer.

First, I made the directory and added it to ~/.profile and ~/.bash_profile, and reloaded that in my terminal:

NEW="$HOME/.local/bin"
mkdir -p "${NEW}"
case ":${PATH:=$NEW}:" in
    *:"$NEW":*)  ;;
    *) echo 'export PATH="${PATH}:'"${NEW}"'"' | tee -a ~/.bash_profile >> ~/.profile  ;;
esac
source ~/.profile ~/.bash_profile

Then, I installed the script to ~/.local/bin:

mkdir -p ~/.local/bin
curl -L 'https://gist.github.com/2ndBillingCycle/52e3d0ade64304139519523275cd9fba/raw/livesplithk' --output ~/.local/bin/livesplithk
chmod a+x ~/.local/bin/livesplithk

I made sure I could run it by opening a brand new terminal, and typing the name:

[2nd@lab ~]$ livesplithk
Defaulting to search window name, class, and classname
You specified the wrong number of args.
Usage: key [options] <keysequence> [keysequence ...]
--clearmodifiers     - clear active keyboard modifiers during keystrokes
--delay DELAY        - Use DELAY milliseconds between keystrokes
--repeat TIMES       - How many times to repeat the key sequence
--repeat-delay DELAY - DELAY milliseconds between repetitions
--window WINDOW      - send keystrokes to a specific window
Each keysequence can be any number of modifiers and keys, separated by plus (+)
  For example: alt+r

Any letter or key symbol such as Shift_L, Return, Dollar, a, space are valid,
including those not currently available on your keyboard.

If no window is given, and there are windows in the stack, %1 is used. Otherwise
the currently-focused window is used
This command consumes all arguments after it, so you cannot chain
 additional commands after it.

The error message tells me that I have it installed. If I had seen something like:

[2nd@lab ~]$ livesplithk
-bash: livesplithk: command not found

I would have known that something had been misconfigured.

First, I started LiveSplit again, and reconfigured the hotkeys LiveSplit is looking for. The keys chosen are not sane choices, and were instead chosen so that this explanation could cover a range of possibilities.

You are encouraged to choose the keys that you would prefer.

I chose the following. Any hotkeys missing from this list were left to their defaults:

  • Sart / Split: a
  • Reset: F2
  • Undo split: NumPad2
  • Skip Split: Ctrl+Alt+Shift+q

Before running xbindkeys_config, I created an empty ~/.xbinkeysrc with the below command, because xbindkeys_config will crash if that file doesn't exist:

touch ~/.xbindkeysrc

Since installing xbindkeys_config-gtk2 didn't install a shortcut, I ran it from a terminal:

xbindkeys_config &

For this tutorial, I left the xbindkeys_config "Speed Config" as "Default".

I wanted to configure xbindkeys to watch for the same keys I configured LiveSplit to watch for, and "forward" those keypresses to LiveSplit, using the livesplithk script (that uses xdotool itself).

Ideally, with LiveSplit and xbindkeys running, the chain of events for starting the timer (using my very crazy choice of a for that hotkey) would look something like this:

  1. Press a
  2. xbindkeys intercepts the keypress
  3. xbindkeys calls the livesplithk script, to send a to LiveSplit
  4. livesplithk is called with a: livesplithk a
  5. Running the livesplithk script calls xdotool, telling it to send a to LiveSplit
  6. xdotool simulates a being pressed in LiveSplit
  7. LiveSplit receives a
  8. LiveSplit starts the timer

It seems like a lot, but only a few things had to be configured.

An important thing to note, though, is that xbindkeys can be configured to run any script for any keypress.

It's just that I was trying to keep things simple, and have it "forward" keypresses to LiveSplit, since Livesplit can't actually see them globally, and xbindkeys can.

So, I started by using xbindkeys_config to configure xbindkeys to watch for a being pressed, and to run a script when it sees that happen. The script will simulate a being pressed in LiveSplit.

When in xbindkeys_config, to add a new key press to watch for, I pressed the "New" button.

Then, to tell xbindkeys_config which key press I wanted xbindkeys to watch for, I pressed the "Get Key" button. This caused a new window to open.

With this new window in focus I pressed the a key, which showed up in xbindkeys_config as a | m:0x0 + c:38.

The string a | m:0x0 + c:38 is what xbindkeys "sees" when I press the key a.

Now I can configure what script I want xbindkeys to run when it sees a is pressed.

For simplicity's sake, I asked it to simulate a being pressed in LiveSplit.

In the "Action" text box, I put livesplithk and then the name of the key I wanted the script to send to LiveSplit, as an argument to the script.

Since the livesplithk script calls xdotool, I needed to know what to call a so that xdotool would send a.

I was pretty sure the proper formatting for the name of the key is, in fact, "a", but I checked this list of keysyms anyways to make sure.

Note: keysyms ideally represent the printed information on the cap of the corresponding key, that was pressed to generate the key code that xbindkeys shows as a or m:0x0 + c:38 (the | character means "or"). To read more about how the X Window System interprets keyboard events, read this article.

It turns out it's capitalized: "A" is the keysym for a

To test out that using the livesplithk script to tell xdotool to send "A" really did result in the key press a being simulated in LiveSplit, and the timer starting, I ran the script manually.

I opened a new terminal, and tried the following, watching the LiveSplit timer to see if it started:

livesplithk A

For me, I ran the script a couple times, and nothing happened: no response in LiveSplit.

In LiveSplit's settings, I checked the box for "Global Hotkeys", and then ran the script again, and it worked: LiveSplit's timer started.

So back in xbindkeys_config I set the "Action" to livesplithk A.

I wanted to try out the xbindkeys configuration before continuing, which meant I needed to run xbindkeys.

So in xbindkeys_config, I pressed the "Apply" button to save the configuration for xbindkeys, and then in a terminal I ran the following to start xbindkeys:

xbindkeys -n

The -n flag tells xbindkeys to stay in the foreground, which means that once I was done testing the configuration, I could go back to the terminal I ran the xbindkeys -n command in and press Ctrl+c to stop it.

With xbindkeys running, I made sure LiveSplit was started, and double checked to see that it was configured to start the timer when a was pressed.

I confirmed that pressing a started the timer, whether LiveSplit was in focus or not.

I then went back to xbindkeys_config to configure the rest of the keys, starting with F2 for "Reset".

Note: Even if you do not need any other functionality, I would still suggest reading through to the end, as there are some problems that could come up on different keyboards that may be able to be addressed with the configuration arrived at in the section that follows this one.

I clicked "New" to add a new key to watch for, and repeated the same process as I did to set up the configuration for a: Press "Get Key", and then fill in the "Action" with the appropriate call to livesplithk, which, in this case, turned out to be livesplithk F2.

I got everything configured in xbindkeys_config, hit "Apply", and, since xbindkeys was already running, and LiveSplit was already configured to "Reset" when it received F2, I hit a to start the timer, which did start, and then F2 to reset it.

Unfortunately, nothing happened.

This was because xbindkeys was still running with the old configuration.

I went back to the terminal I started xbindkeys from, and pressed Ctrl+c to stop it, then immediately reran it with the -n flag to restart it with the new configuration.

Once it was running, I pressed F2, and the timer reset.

On to "Undo Split".

I went through the same steps as for the previous two keys, but this time, after pressing "Get Key" and pressing NumPad2, xbindkeys_config showed the key as Mod2 + KP_Down | m:0x10 + c:88.

This one had two components. With the previous strings, I ignored the part after the | character, which always started with m:0x0.

This time, that part was m:0x10, and there was the Mod2 + component in the first part.

On my keyboard, to type a 1 using the NumPad, I have to make sure NumLock is enabled.

This key, along with ScrollLock and CapsLock, are "Lock Keys". They are normally distinct from "Modifier Keys" like Shift, Ctrl, Alt, etc., but xbindkeys appears to refer to them as modifier keys.

It looks as if "KP_Down" refers to the key I pressed while "Mod2 +" confers that NumLock was active while it was pressed.

To test this out, I turned off NumLock, and pressed "Get Key", and NumPad2 showed up as KP_Down | m:0x0 + c:88.

So how to translate this for xdotool?

You can tell xdotool to send multiple keys at once by specifying the two keysyms separated by a +.

Looking in the list of keysyms, it looks like NumLock is "Num_lock", and I guessed the NumPad2/NumPad⬇️ key is called "KP_Down".

Since LiveSplit was already running and configured, I tried sending the keysyms using the livesplithk script:

livesplithk Num_Lock+KP_Down

Worked perfectly.

I added it to the xbindkeys_config interface, and moved to the next one.

The next one is unlikely to be useful for anything other than demonstration purposes:

  • xbindkeys_config: Control+Shift+Alt + q | m:0xd + c:24
  • xdotool: Control_L+Alt_L+Shift_L+Q

There are actually a few ways to translate this.

xdotool accepts it's own custom notation for the Modifier Keys. From the documentation:

Generally, any valid X Keysym string will work. Multiple keys are separated by '+'. Aliases exist for "alt", "ctrl", "shift", "super", and "meta" which all map to Foo_L, such as Alt_L and Control_L, etc.

The rest of the documentation can be read by running man xdotool, but that's the important part for this tutorial.

Also, it didn't appear that LiveSplit cared whether it was the left Shift or the right one that was pressed, so Control_R+Alt_R+Shift_R+Q seemed to work just as well, too.

Troubleshooting

I was having difficulty sending some of the NumPad keys to LiveSplit. Sometimes, LiveSplit wouldn't respond to 8 out of every 10 times a particular key combination was sent using the script, but would respond immediately if the timer was in focus.

Usually I could fix this by re-starting LiveSplit and xbindkeys, and double checking that Livesplit hadn't unchecked the "Global Hotkeys" check box.

However, this never happened with the simpler key combinations.

It should be noted that this seems to be a failing of running LiveSplit under wine.

In particular, xbindkeys never failed to correctly respond to even weird key combinations, and neither the livesplithk script nor xdotool itself failed to simulate those keypresses in LiveSplit.

This meant that, for me, it was much easier to reconfigure LiveSplit to watch for letter keys (a, b, c, etc) for the functions, as in the following:

LiveSplit configuration

And xbindkeys_config as the following:

xbindkeys_config configuration

In this configuration, I mapped the NumPad1 through NumPad7 keys to trigger sending a through g to LiveSplit.

Since LiveSplit can't really see global keypresses, it's okay to have it watch for letter keys, and have xbindkeys watch for the keypresses you actually want to use.

Now we can wrap this all up by making it much easier to start and stop LiveSplit and xbindkeys.

Installing script for starting LiveSplit and xkeybinds

To make starting LiveSplit easier, I modified one of the scripts this gist is based on. Thanks to Drenn1 for the original.

I pasted the contents of runlivesplit into a file of the same name in ~/.local/bin, and made it executable:

curl -L 'https://gist.github.com/2ndBillingCycle/52e3d0ade64304139519523275cd9fba/raw/runlivesplit' --output ~/.local/bin/runlivesplit
chmod a+x ~/.local/bin/runlivesplit

Since ~/.local/bin is already on my $PATH, I can start xkeybinds and LiveSplit opening a terminal and typing runlivesplit.

When I'm done, I press Enter in the terminal window to stop xkeybind and LiveSplit.

What still doesn't work

There's a ~1 second delay between the hotkey being pressed, and LiveSplit responding.

Versions that worked for me

For reference, these are the versions of software that I used, and got working for me. Hopefully this will continue to work with newer versions.

To use specific versions of some of these tools, it may be necessary to use git to manually check out the repositories to a specific version. Something like:

git checkout v9.8.7

This requires some exploring around in the repository to find which commit is associated with a particular version, and checking out that commit.

For more information on using git, check out the book.

#!/bin/bash
xdotool key --window $( xdotool search --limit 1 --all --pid $( pgrep LiveSplit ) --name LiveSplit ) "$1"
#!/bin/bash
cd ~/apps/LiveSplit*
xbindkeys -n &
XBK_PID=$!
wine LiveSplit.exe &
WINE_PID=$!
echo "Press ENTER to close"
read
for pid in "${XBK_PID}" "${WINE_PID}" ; do
(kill $pid) ;
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment