KeyChord2 is my experiment for creating an intuitive way to define chording layouts, and a flexible way to adjust chord detection.
The challenge of chord detection is that
when we normally type, there will be some
overlap between the time a first key is
released and a second key is pressed.
In our key event timing language this looks like this:
+a +b -a -b
The time between pressing the second key and releasing the first we call overlap or crossover time. If you require that crossover is zero, in order to input single key inputs or seperate chords, then this can slow down the chord inputs. However, if you perfectly require users to input chords in a clean first in first out "fifo" stack order, then this will also slow down chorded input.
In order to optimize chord input, a balance is helpful, allowing some minimal crossover time between seperate key events, but not requiring chords to be pressed perfectly cleanly either in this stacked manner. The approach I have adopted is to have a sync filter where inputs are considered simultaneous, and therefored chorded, and a crossoverTime, where if two keys are held together for a minimum time they will always be a chord as well.
If chorded typists are forced to fully wait for all keys to be released, before pressing a new key, that adds delays to typing, especially for human input that cannot perfectly time the pressing and releasing of keys to not overlap.
Chord detection works in three phase process:
1000:+1 1020:+2 1040:-2 1060:-1
This means "at clock time 1000 milliseconds, press key #1, then press key #2, then release key #2, then release key #1". By default when key presses are "sandwhiched" or "stacked", in this kind of fifo order, they will alway register as a chord.
Right now we have implemented a "sync filter". If the sync time is 10 milliseconds, then any keyboard events that happen within 10 milliseconds after an initial event, will be considered simultaneous.
If two events are simultaneous, then their order won't matter for chord detection, but it could still change what 'sequence' the input matches. For example:
1000:+a 1005:+b 1035:-a 1040:-b
This sequence will register as a chord with a sync filter, even though the keys were not pressed in a 'fifo' stack order, because all events within 10 milliseconds after '+a' are considered simultaneous. But the fact that 'a' is pressed before 'b' could still matter depending on the layout, if the sequences 'a b' and 'b a' map to different layouts. Thus ordering for chord detection and ordering for identifying the input sequence are handled differently. If the layout has seperate sequences for both possible key orders, the event ordering matters no matter how close together they are.
The keylog buffer is processed at regular time intervals to detect chords. Any keyevent information that is not ready to be fully processed is stored internally in a datastructure called 'leftovers'.
Currently there are two chord detection algorithms available. 'stack' chord detection enforces a stack ordering, unless they key events are "sync"ed, or the "crossoverTime" is exceeded, where both keys are held at once for some minimal time.
The other chord detection algorithm available is a "sticky" chord detection algorithm. Once one key is pressed, a chord is not registered until all keys are released, and any key pressed in the meantime is included in the chord. Sticky chords are best suited for stenography style layouts, where thousands of unique words are mapped to chords, especially if the keyboard supports limited rollover or simultaneous keypresses. For keyboards with limited rollover sticky chord detection is essential as it allows you to input longer chord sequences by "rolling" through the keys rather than pressing them all at once which some basic keyboards cannot detect accurately.
Here is a simple example of the Chord Layout Language:
: a b c d
a b = e
a c = f
a d = g
b c > h
c b > i
b d > j
d b > k
c d > l
d c > m
a b : _ _ y z
a b c d = space
a b = e
Defines a single chord: Input keys can be pressed in any order.b d > j
Defines a single sequence, keys must be pressed in order.a b : _ _ y z
Defines multiple sequences using a prefix,
then iterating over the primary keys.
Because 'a' and 'b' are already in the sequence,
sequence, the placeholder outputs '_' should
never actually be triggered.
In this language, the line starting with ":" lists all your primary or homerow keys. These keys are pressed in isolation to emit their single characters. The chords and sequences are then defined using combinations of these primary keys.
The equal sign '=' is used to define a single chord. The keynames before the equal sign are the chordname, or inputs you press to make the chord, and everything after is the chord value, what is output when the chord is pressed. This should typically just be a single key, but support for multi-key output may be added in the future.
The greater than symbol '>' is used to define a sequence. Unlike a chord, where the keys can be pressed in any order, a sequence requires the keys to be pressed in that exact order.
The colon symbol ':' allows you to define sequences or chords using a prefix, and then iterating over all of the primary keys, rather than writing out full chords one at a time.
Some detailed information on the language parsing:
Keynames can be a single letter or a word.
Keynames are separated by one or more whitespace characters.
certain symbols are meaningful, but only the first symbol
on a particular line has syntactical significance, after
that, only whitespace has meaning. So for example
you can write a b = =
,
And that will assign the chord 'a b' to output '='.
Certain special control commands can be defined starting
with an exclamation point '!', followed by the command
on its own line. For example, if you
write !unordered
on a single line,
then every time you use the colon ':' after
that it will define chords using the prefix,
instead of sequences.