@hackage spartacon0.1.0.0

A unix-style (read from stdin, write to stdout) global hotkey daemon

spartacon is a Unix-style global hotkey daemon. It embraces the philosophies of "do one thing, and do it well" and "text in, text out". It allows you to capture hotkeys on X11, without having any strong opinions about what actions should happen when those keys are pressed.

There are other hotkey applications out there; for example, modern desktop environments typically have a way to bind keys to actions, and there are standalone apps like HyperKeys. Typically, these implementations assume that the thing you want to happen when a key is hit is to launch a process. If what you really want is to just add hotkey functionality to an existing application without it needing to know how to do that, those are typically not suitable, because the tool being controlled is already running.

spartacon sits in that space: when you want to control a "text in, text out"-style program with hotkeys, this is the tool for you. (And if you decide to pipe spartacon to something that launches processes, that's no skin off my teeth!)

Installation

  1. Install GHC and cabal in your favorite way. ghcup is nice, or most package managers have them.
  2. Install the development variants of your X11 libraries; it probably makes the most sense to do this through your package manager. The names of the packages that provide these vary a lot from distribution to distribution. If you don't install anything, the build process below will output some text about which library is missing that may help you search your package manager.
  3. Clone the repo. git clone https://github.com/dmwit/spartacon && cd spartacon
  4. Build and install. cabal install
  5. If this is your first time using cabal, you will likely need to add its output directory to your PATH.

Usage

spartacon accepts text on standard input in the config-value format. A simple example looks like this:

ignore: [NL, M5]
bindings:
    C-A-y: "foo"
    F11: quit

The ignore field is modifiers that you want to be "ignored" -- that is, where you want your keybindings to fire whether those modifiers are on or not. It's optional, and the default is [NL] for numlock (see table below for other choices). So, for example, in the above file, spartacon will print out the string foo for any of the keypresses C-A-y, NL-C-A-y, M5-C-A-y, or NL-M5-C-A-y. The ignore field is never required to get a desired reaction from spartacon; it is only shorthand for binding many modifier variants at a time.

The bindings field is a collection (possibly empty) of bindings and actions. It's optional, and the default has no bindings or actions. Each binding has a modifier part and a key part.

The modifier part is a sequence (possibly empty), where each element of the sequence is one of the following strings followed by a -:

| string | meaning                                     |
|--------|---------------------------------------------|
| S      | shift                                       |
| L      | lock (usually capslock)                     |
| C      | control                                     |
| A      | alt (=mod1)                                 |
| M1     | mod1 (usually alt)                          |
| NL     | numlock (=mod2)                             |
| M2     | mod2 (usually numlock)                      |
| M3     | mod3                                        |
| M4     | mod4 (usually the Windows key or similar)   |
| M5     | mod5                                        |
| B      | no (additional) modifier; see next sentence |

The B- prefix is required for unmodified keycodes and unmodified keysyms that do not start with a letter because config-value atoms must start with a letter.

You may also use lower case, but the case must be consistent within each string (e.g. nL is not allowed). Only the presence or absence of one of these strings matters; if there are multiple appearances, all but the first are ignored. For example, C-S-, C-S-C-, c-S-, and c-b-s-b-C-B-S- all mean the same thing: for this keybinding to fire, both control and shift (and nothing else) must be held down.

The key part of a binding may be either a keysym or a keycode. Keysyms are exactly those accepted by XStringToKeysym; you can find out exactly how to spell a keysym name with xev or similar. Keycodes are smallish numbers (no larger than 255); to distinguish these from keysyms, you must prepend a . to the number.

For those among you not intimately familiar with X11 terminology, the distinction between keysym and keycode is that a keysym is the logical interpretation of a key while a keycode is the physical location of a key. For example, y means that to fire this keybinding, you must press the y key, wherever that may be in the current layout -- just to the right of T in the QWERTY layout but just to the right of P in the Dvorak layout. In some layouts, a single keysym may be available from multiple physical keys; spartacon will correctly respond to any of the physical keys for that keysym. On my computer, in the QWERTY layout, Y is associated with keycode 29, so as a second example, .29 means the sixth letter key from the left in the top row, no matter what letter that's actually associated with in the current layout -- Y in the QWERTY layout, F in the Dvorak layout, etc. If you request a binding with a keysym, then change layout, spartacon will modify its hotkey definitions appropriately.

It is possible to accidentally request multiple actions for the same key; for example, by requesting a keysym and a keycode that both correspond to the same key, or by requesting the "upper-case" and "lower-case" keysym variants associated with a single key. In this case, spartacon will print a short warning message to stderr and choose one of the proposed actions arbitrarily. A short warning will also be printed to stderr if a requested keysym is not associated with any key in the current layout.

An action is one of the following things:

  • The literal atom quit. This instructs spartacon to successfully exit.
  • The literal atom exit. Ditto.
  • An arbitrary string. This instructs spartacon to print that string to stdout.