Documentation
¶
Overview ¶
Package kb provides the keyboard layouts published by kbdlayout.info as a Go library: pick a layout, read its keys, and ask what sits next to a character or above it on the Shift level.
board := kb.MustGet("kbdus")
board.Adjacent("e") // [w r d 3 4 s]
board.Shifted("4") // [$]
board.Translate("hello", kb.MustGet("kbdru")) // "руддщ"
A layout is identified by the Windows driver it ships in — "kbdus", "kbdfr", "kbdgr" — or by any of the KLIDs installed against that driver, so Get("00000409") and Get("kbdus") return the same board. ByLanguage selects layouts by BCP-47 tag.
Adjacency is geometric rather than tabular. Every key carries the scan code of the physical switch it sits on, and scan codes are a property of the keyboard rather than of the layout printed on it, so one position table serves every layout. Neighbours are then found by distance between key strike points, which respects the stagger between rows: on QWERTY the "e" key overhangs "d" far more than it overhangs "s", and Adjacent reports them in that order. A layout laid out as rows of equal-width columns cannot make that distinction, and neither can it tell an ANSI board from an ISO one, where the short left Shift puts an extra key below "a".
The dataset covers the alphanumeric block only. The function row and the navigation cluster type nothing, and the numeric keypad only duplicates digits the number row already has.
Index ¶
- Constants
- func IDs() []string
- func Languages() []string
- func MarshalCatalogue(entries []Entry) ([]byte, error)
- type Entry
- type Form
- type Key
- func (k Key) AltGr() string
- func (k Key) Base() string
- func (k Key) Blank() bool
- func (k *Key) Compact()
- func (k Key) MarshalJSON() ([]byte, error)
- func (k Key) Mods() []Mod
- func (k *Key) Set(m Mod, o Out)
- func (k Key) Shift() string
- func (k Key) String() string
- func (k Key) Text(m Mod) (Out, bool)
- func (k Key) Texts() []string
- func (k Key) Types(s string) bool
- func (k *Key) UnmarshalJSON(b []byte) error
- type Layout
- func (l *Layout) Adjacent(s string) []string
- func (l *Layout) AdjacentKeys(k Key, radius float64) []Key
- func (l *Layout) AdjacentWithin(s string, radius float64) []string
- func (l *Layout) AltGraphed(s string) []string
- func (l *Layout) Fprint(w io.Writer) error
- func (l *Layout) Key(sc string) (Key, bool)
- func (l *Layout) KeysFor(s string) []Key
- func (l *Layout) Languages() []string
- func (l *Layout) Marshal() ([]byte, error)
- func (l Layout) MarshalJSON() ([]byte, error)
- func (l *Layout) Print()
- func (l *Layout) Rows() [][]Key
- func (l *Layout) Shifted(s string) []string
- func (l *Layout) String() string
- func (l *Layout) Strokes(s string) []Stroke
- func (l *Layout) Translate(s string, as *Layout) string
- func (l *Layout) Type(strokes []Stroke) string
- func (l *Layout) Types(s string) bool
- func (l *Layout) Unmarshal(raw []byte) error
- func (l *Layout) UnmarshalJSON(b []byte) error
- func (l *Layout) Unshifted(s string) []string
- func (l *Layout) With(form Form) *Layout
- type Locale
- type Mod
- type Out
- type Pos
- type Stroke
Examples ¶
Constants ¶
const DefaultRadius = 1.3
DefaultRadius is how far apart two keys may be and still count as adjacent. At 1.3 units a letter key reaches its left and right neighbours (1.0), the two keys above and the two below (1.03 to 1.25), the space bar if it runs beneath it (1.0), and nothing else. Raising it to 1.9 pulls in the diagonal-but-one keys as well.
The stagger is what these numbers are for: on QWERTY the "e" key sits a quarter unit left of "d" and three quarters left of "s", so "d" comes back as the likelier slip. A layout stored as rows of equal-width columns cannot tell the two apart.
Variables ¶
This section is empty.
Functions ¶
func Languages ¶
func Languages() []string
Languages returns every language subtag with at least one layout, sorted.
Example ¶
package main
import (
"fmt"
"github.com/rangertaha/urlinsane/pkg/kb"
)
func main() {
langs := kb.Languages()
fmt.Println(len(langs), "languages")
fmt.Println(len(kb.List()), "layouts")
}
Output: 110 languages 203 layouts
func MarshalCatalogue ¶
MarshalCatalogue encodes the index of available layouts. The generator uses it; reading the dataset goes through List and Get.
Types ¶
type Entry ¶
type Entry struct {
// ID is the driver name, lowercased: "kbdus", "kbdfr".
ID string `json:"id"`
// Name is a human-readable name: "US", "French".
Name string `json:"name"`
// Form is the physical shape of the board.
Form Form `json:"form"`
// Locales are the language identities the layout is installed under.
Locales []Locale `json:"locales,omitempty"`
}
Entry is the summary of a layout in the catalogue, enough to choose one without paying to parse its keys.
func ByKeys ¶
ByKeys returns the layouts that can type every one of the given texts, in catalogue order. It answers "which keyboards is this reachable on" — the characters of a domain, a name, a package identifier:
kb.ByKeys("g", "o", "l", "e", ".", "c", "m")
kb.ByKeys(strings.Split("münchen", "")...)
A text matches if some key on the layout produces exactly it, in any modifier state, so "a" and "A" are different questions and a layout that reaches a character only through AltGr still counts. Passing nothing returns nothing, as does passing a text no layout can type.
Unlike ByLanguage this cannot be answered from the index: it has to look at the keys, so the first call decodes the whole catalogue. They are cached afterwards, and later calls are a map lookup per layout.
func ByLanguage ¶
ByLanguage returns the layouts installed for a language, given either a primary subtag ("de") or a full BCP-47 tag ("de-CH"). A full tag matches only the layouts carrying that exact locale; a bare subtag matches every layout for the language.
Like the other lookups here it returns nil when nothing matches, which ranges and counts the same as an empty slice.
Example ¶
package main
import (
"fmt"
"github.com/rangertaha/urlinsane/pkg/kb"
)
func main() {
// A bare subtag matches every layout for the language; a full tag
// matches only those carrying that exact locale.
for _, e := range kb.ByLanguage("de") {
fmt.Println(e.ID, e.Name)
}
fmt.Println("---")
for _, e := range kb.ByLanguage("de-CH") {
fmt.Println(e.ID, e.Name)
}
}
Output: kbdgr German kbdgr1 German (IBM) kbdgre1 German Extended (E1) kbdgre2 German Extended (E2) kbdsg Swiss German --- kbdsg Swiss German
func ByString ¶
ByString returns the layouts that can type every character of s, in catalogue order. It is ByKeys over the runes of s, deduplicated:
kb.ByString("google.com")
kb.ByString("münchen")
The question it answers is whether every character sits on a key of its own. A layout that reaches a character only by dead key — an acute accent on a German keyboard, pressed before "e" to make "é" — does not count, because the dataset records that the accent key is dead but not what it composes into. Characters written as a base letter followed by a combining mark are likewise treated as the two runes they are.
Example ¶
package main
import (
"fmt"
"github.com/rangertaha/urlinsane/pkg/kb"
)
func main() {
// Which keyboards have a key for every character of this string?
fmt.Println(len(kb.ByString("google.com")))
fmt.Println(len(kb.ByString("münchen")))
fmt.Println(len(kb.ByString("中文")))
}
Output: 98 20 0
func Find ¶
Find returns the layouts whose ID, name, or locale names contain q, matched case-insensitively. It is for interactive selection — a "--keyboard french" flag — rather than for programmatic lookup, which should use Get.
func List ¶
func List() []Entry
List returns the catalogue of available layouts, ordered by ID. It does not parse any of them.
func UnmarshalCatalogue ¶
UnmarshalCatalogue decodes the index of available layouts.
type Form ¶
type Form string
Form is the physical shape of the alphanumeric block. The three forms differ only in the bottom-left and right-hand edges: ISO and JIS keyboards carry extra keys that ANSI does not, which shifts where the Enter and Shift keys sit. Everything between Q and M is identical across all three.
const ( // ANSI is the 101/104-key form used in the US and most of Asia. It has a // wide left Shift and a backslash key above Enter. ANSI Form = "ansi" // ISO is the 102/105-key form used across Europe. It splits the left // Shift to make room for an extra key, and moves backslash next to Enter. ISO Form = "iso" // JIS is the 106/109-key Japanese form: ISO's tall Enter plus two extra // keys, one on the number row and one on the bottom letter row. JIS Form = "jis" )
type Key ¶
type Key struct {
// SC is the set 1 scan code, in uppercase hex. It identifies the switch
// by position and means the same thing on every layout.
SC string `json:"sc"`
// VK is the Windows virtual key the switch maps to, such as "VK_Q".
VK string `json:"vk,omitempty"`
// Name is the label printed on the key, when the layout gives one.
Name string `json:"name,omitempty"`
// Pos is where the key sits, filled in from the layout's form when the
// layout loads rather than stored in the dataset.
Pos Pos `json:"-"`
// contains filtered or unexported fields
}
Key is one physical switch on the board, together with everything it types.
Example ¶
package main
import (
"fmt"
"github.com/rangertaha/urlinsane/pkg/kb"
)
func main() {
// A key knows the switch it sits on and what it types in each state.
k, _ := kb.MustGet("kbdgr").Key("29")
fmt.Println(k.SC, k.VK)
o, _ := k.Text(kb.Base)
fmt.Printf("%q dead=%v\n", o.Text, o.Dead)
fmt.Printf("%q\n", k.Shift())
}
Output: 29 VK_OEM_5 "^" dead=true "°"
func NewKey ¶
NewKey builds a key on a scan code. Use Set to give it outputs. It is what the dataset generator calls, and what a caller building a layout of their own would use.
func (Key) Blank ¶
Blank reports whether the key types nothing at all — a modifier, Enter, or a switch the layout leaves unassigned.
func (*Key) Compact ¶
func (k *Key) Compact()
Compact drops the modifier states that Text would reproduce on its own, which is most of them: a letter key carries four states in the source data and needs to store two. It is called when the dataset is generated.
Like Set, it replaces the map rather than writing into it, so compacting one copy of a Key leaves every other copy alone.
func (Key) MarshalJSON ¶
MarshalJSON writes the key as JSON.
func (*Key) Set ¶
Set records what the key types in a modifier state. Setting a state twice keeps the first value, since the dataset lists the plainest spelling first.
A Key holds its outputs in a map, and copying a Key copies the reference to it, so writing through one copy would otherwise be visible through all of them — including through the layout the copy came from, which Get shares between every caller in the program. Set therefore replaces the map rather than writing into it, which makes a Key behave like the value it looks like.
func (Key) Text ¶
Text returns what the key types in the given modifier state, and whether it types anything at all.
A Caps Lock state the key does not list falls back to capsFallback, the ordinary behaviour of Caps Lock on a letter. Only the keys that depart from it — the digit row of a German keyboard, where Caps Lock does nothing at all — are stored.
func (Key) Texts ¶
Texts returns every distinct string the key can type, in modifier order. Dead keys contribute the spacing form of their accent.
func (*Key) UnmarshalJSON ¶
UnmarshalJSON reads a key from JSON. Position is left unset; the layout fills it in when it indexes its keys.
type Layout ¶
type Layout struct {
// ID is the driver name the layout is published under, lowercased, such
// as "kbdus" or "kbdfr". It is the layout's canonical identifier.
ID string `json:"id"`
// Name is a human-readable name, such as "US" or "French".
Name string `json:"name"`
// File is the Windows driver the layout comes from, such as "KBDUS.DLL".
File string `json:"file,omitempty"`
// Form is the physical board the layout was drawn for, which decides
// where the keys around the edges of the alphanumeric block sit. A
// layout can be typed on a board of another shape — use With to say so.
Form Form `json:"form"`
// Locales are the language identities the layout is installed under.
Locales []Locale `json:"locales,omitempty"`
// Keys are the keys of the alphanumeric block, ordered top-left to
// bottom-right.
Keys []Key `json:"keys"`
// contains filtered or unexported fields
}
Layout is a keyboard layout: a set of physical keys and the text each one types. Layouts are immutable once loaded and safe for concurrent use.
Get one from Get, New, or by unmarshalling; those position the keys and build the lookup tables. A Layout assembled as a struct literal has neither, and its keys will all sit on top of one another at the origin, so Key, KeysFor and Adjacent will not answer usefully.
func Get ¶
Get returns the layout for a driver name or KLID. Layouts are parsed on first use and cached, so repeated calls are cheap and return the same pointer. The returned layout must not be modified.
Example ¶
package main
import (
"fmt"
"github.com/rangertaha/urlinsane/pkg/kb"
)
func main() {
// A layout answers to its driver name, to the file that driver ships
// in, and to any of the KLIDs Windows installs it under.
for _, id := range []string{"kbdus", "KBDUS.DLL", "00000409", "409", "00000804"} {
l := kb.MustGet(id)
fmt.Printf("%-12s %s\n", id, l.ID)
}
}
Output: kbdus kbdus KBDUS.DLL kbdus 00000409 kbdus 409 kbdus 00000804 kbdus
func MustGet ¶
MustGet is Get for layouts known to exist, such as constants in a program's own source. It panics if the layout is missing.
func New ¶
New assembles a layout from a set of keys. Keys outside the alphanumeric block are discarded, and the rest are positioned according to the form. The dataset generator uses it, as would a caller defining a layout of their own.
func (*Layout) Adjacent ¶
Adjacent returns the characters typed by the keys physically next to the one that types s, nearest first and without duplicates.
The characters come from the same modifier state s was found in, so Adjacent("e") gives lowercase neighbours and Adjacent("E") gives uppercase ones. This is what separates the result from a naive row-and-column grid: the rows of a real keyboard are staggered, so "e" sits a quarter of a key left of "d" but three quarters left of "s", and "d" comes back first.
Example ¶
package main
import (
"fmt"
"github.com/rangertaha/urlinsane/pkg/kb"
)
func main() {
us := kb.MustGet("kbdus")
// Nearest first. "d" comes before "s" because the rows of a keyboard
// are staggered: "e" overhangs "d" by a quarter key and "s" by three
// quarters.
fmt.Println(us.Adjacent("e"))
// Shift travels with the character.
fmt.Println(us.Adjacent("E"))
}
Output: [w r d 3 4 s] [W R D # $ S]
Example (FollowsTheLayout) ¶
package main
import (
"fmt"
"github.com/rangertaha/urlinsane/pkg/kb"
)
func main() {
// The answer is read off the layout rather than assumed from QWERTY.
fmt.Println(kb.MustGet("kbdgr").Adjacent("z")) // QWERTZ
fmt.Println(kb.MustGet("kbdfr").Adjacent("a")) // AZERTY
fmt.Println(kb.MustGet("kbddv").Adjacent("e")) // Dvorak
}
Output: [t u h 6 7 g] [z q & é] [o u . q j p]
func (*Layout) AdjacentKeys ¶
AdjacentKeys returns the keys physically surrounding k, nearest first, within the given radius in key units. Pass DefaultRadius for the eight or so keys a finger could slip onto.
func (*Layout) AdjacentWithin ¶
AdjacentWithin is Adjacent with an explicit radius in key units. Widening it past about 1.9 starts to include keys two columns away.
func (*Layout) AltGraphed ¶
AltGraphed returns what the keys that type s produce with AltGr held. Most keys on most layouts produce nothing, and the result is then empty.
func (*Layout) KeysFor ¶
KeysFor returns every key that types s in some modifier state. It usually returns one key, but a layout may reach the same character two ways — on many European layouts a digit is on the number row unshifted and again as the shifted form of a letter key.
func (*Layout) Languages ¶
Languages returns the distinct primary language subtags the layout is used for, such as ["en"] for the US layout or ["de"] for German. Layouts shared across locales return one entry per language.
Subtags come back lowercased. BCP-47 tags are case-insensitive and the dataset carries them as Windows spells them, so "ar-SA" and "ar" have to fold together here — otherwise they would count as two languages, and the one that kept its capitals would be unreachable through ByLanguage, which lowercases what it is asked for.
func (Layout) MarshalJSON ¶
MarshalJSON writes the layout as JSON, which spells out everything it carries. It is for exporting and inspecting a layout, not for the dataset.
func (*Layout) Print ¶
func (l *Layout) Print()
Print writes the layout to standard output. It is the quickest way to see what a layout actually looks like:
kb.MustGet("kbdus").Print()
See String for what the drawing shows.
Example ¶
package main
import (
"github.com/rangertaha/urlinsane/pkg/kb"
)
func main() {
// Boxes are placed from the real geometry, so the indentation between
// rows is the physical stagger. Dead keys are bracketed, since they
// type nothing on their own.
kb.MustGet("kbdgr").Print()
}
Output: kbdgr — German (iso) +---+---+---+---+---+---+---+---+---+---+---+---+---+ | ° | ! | " | § | $ | % | & | / | ( | ) | = | ? |[`]| |[^]| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 | ß |[´]| +-----+---+---+---+---+---+---+---+---+---+---+---+---+ | | Q | W | E | R | T | Z | U | I | O | P | Ü | * | | | q | w | e | r | t | z | u | i | o | p | ü | + | +------+---+---+---+---+---+---+---+---+---+---+---+---+ | | A | S | D | F | G | H | J | K | L | Ö | Ä | ' | | | a | s | d | f | g | h | j | k | l | ö | ä | # | +----+---+---+---+---+---+---+---+---+---+---+---+-+---+ | | > | Y | X | C | V | B | N | M | ; | : | _ | | | < | y | x | c | v | b | n | m | , | . | - | +----+---+---+-+------------------------++---+---+ | | | | +------------------------+
func (*Layout) Rows ¶
Rows groups the keys by physical row, top row first and each row ordered left to right.
func (*Layout) Shifted ¶
Shifted returns what the keys that type s produce with Shift held — the uppercase of a letter, or the symbol printed above a digit. It returns nothing when s is only reachable shifted already.
Example ¶
package main
import (
"fmt"
"github.com/rangertaha/urlinsane/pkg/kb"
)
func main() {
us := kb.MustGet("kbdus")
fmt.Println(us.Shifted("4"))
fmt.Println(us.Unshifted("$"))
// AltGr is where the extra characters of a European layout live.
fmt.Println(kb.MustGet("kbdfr").AltGraphed("à"))
}
Output: [$] [4] [@]
func (*Layout) String ¶
String renders the layout as a keyboard diagram: one box per key, placed where the key physically sits, with the shifted character above the base one, as on a keycap.
The offset between rows is the real stagger, not decoration — it is what makes "e" nearer to "d" than to "s", and seeing it is usually the quickest way to check that a layout loaded the way you expected.
Keys that type nothing are drawn empty, which is how the modifiers and the space bar show up. A dead key is bracketed, since pressing it produces no character on its own. The drawing assumes every character takes one column, so a layout in a full-width script will not line up.
func (*Layout) Strokes ¶
Strokes returns the keystrokes that type s on this layout, one per rune. A rune the layout cannot type yields a zero Stroke, so the result always has as many entries as s has runes.
Where a character can be reached more than one way the plainest is chosen: unmodified before shifted, shifted before AltGr.
Example ¶
package main
import (
"fmt"
"github.com/rangertaha/urlinsane/pkg/kb"
)
func main() {
us, ru := kb.MustGet("kbdus"), kb.MustGet("kbdru")
// Strokes are keys and modifiers, with no text of their own. Reading
// them on another layout is what Translate does in one step.
fmt.Println(us.Strokes("abc"))
fmt.Println(ru.Type(us.Strokes("abc")))
}
Output: [{1E base} {30 base} {2E base}] фис
func (*Layout) Translate ¶
Translate returns what s becomes when the keystrokes that type it here land on another layout instead — someone typing a familiar word with the wrong layout selected:
us.Translate("hello", ru) // "руддщ"
us.Translate("google", ru) // "пщщпду"
Characters this layout cannot type, and keys the other layout leaves bare, are passed through unchanged rather than dropped, so a domain keeps its dots. Use Strokes and Type together when you would rather see exactly what fell through.
Two things follow from that which are easy to assume away:
The result is not always the same length as the input. A handful of layouts have ligature keys that type two characters at once — the Arabic boards reach "لا" on one key — so a rune can come back as two.
And it does not round-trip unless this layout can type every character given. A character that falls through untouched here is still an ordinary character on the way back, and will be translated then: Russian cannot type "abc", so it passes through, and translating the result back through Russian yields "фис" rather than "abc".
A key that is dead on the far layout contributes its accent, since that is what the dataset records; it would really arm the accent instead.
Example ¶
package main
import (
"fmt"
"github.com/rangertaha/urlinsane/pkg/kb"
)
func main() {
us, ru, fr := kb.MustGet("kbdus"), kb.MustGet("kbdru"), kb.MustGet("kbdfr")
// What someone types with the wrong layout selected.
fmt.Println(us.Translate("hello", ru))
fmt.Println(us.Translate("google", ru))
fmt.Println(us.Translate("google.com", ru))
// AZERTY moves the punctuation, which is what makes this a squatting
// vector rather than a curiosity.
fmt.Println(us.Translate("google.com", fr))
}
Output: руддщ пщщпду пщщпдуюсщь google:co,
func (*Layout) Type ¶
Type returns what the given keystrokes produce on this layout. Strokes that this layout has no key for, or whose key types nothing in that modifier state, contribute nothing.
ru.Type(us.Strokes("hello")) // "руддщ"
That pairing is the interesting one: the keys someone actually pressed, read through a different layout.
func (*Layout) Unmarshal ¶
Unmarshal decodes a layout from the dataset's storage format and indexes it, which positions the keys and discards any that sit outside the alphanumeric block.
func (*Layout) UnmarshalJSON ¶
UnmarshalJSON reads a layout from JSON and indexes it, which positions the keys and discards any that sit outside the alphanumeric block.
func (*Layout) Unshifted ¶
Unshifted returns what the keys that type s produce without Shift, which undoes Shifted.
func (*Layout) With ¶
With returns the layout as it would sit on a board of a different shape. The characters do not move between keys, but the keys move: an ISO layout typed on an ANSI board loses the extra key below the left Shift, and the backslash beside Enter moves up a row. Callers who know what hardware they are modelling can use this; the rest should take the layout as it comes.
Asking for the shape a layout already has returns the layout itself, not a copy, since neither may be modified in any case.
Example ¶
package main
import (
"fmt"
"github.com/rangertaha/urlinsane/pkg/kb"
)
func main() {
// An ISO board carries a key below the left Shift that an ANSI board
// does not, and it neighbours "a".
uk := kb.MustGet("kbduk")
fmt.Println(uk.Adjacent("a"))
fmt.Println(uk.With(kb.ANSI).Adjacent("a"))
}
Output: [s q \ z w] [s q z w]
type Locale ¶
type Locale struct {
// KLID is the eight hex digit Windows keyboard layout identifier, such
// as "00000409".
KLID string `json:"klid"`
// Tag is the BCP-47 language tag for the locale, such as "en-US".
Tag string `json:"tag,omitempty"`
// Name is how Windows names the layout for this locale.
Name string `json:"name,omitempty"`
}
Locale is one of the language identities a layout is installed under. Windows ships a single driver for several locales — KBDUS backs the US English layout as well as the US variants of Chinese and Bulgarian — so a layout carries a list of these rather than a single language.
type Mod ¶
type Mod uint8
Mod is a set of held modifiers, as a bitmask. Shift, AltGr and Caps Lock are modelled because between them they cover what almost every layout does.
Two things are left out, and they are not all control codes. Ctrl and Alt on their own do produce control codes rather than text, and go. But a couple of layouts reach real characters through a modifier this package has no bit for: the Japanese board puts its kana on a Kana lock, and the Canadian Multilingual Standard board hangs a level off VK_OEM_8. Those characters are absent from the dataset — kbdjpn is Latin-only here, and kbdcan is missing its ¹ ² ³ ¼ level. The generator reports what it dropped, so a rebuild says so rather than letting it pass unnoticed.
const ( // Base is the key pressed on its own. Base Mod = 0 // Shift is either Shift key held down. Shift Mod = 1 << 0 // AltGr is the right Alt key on layouts that treat it as AltGr, or the // Ctrl+Alt combination that stands in for it elsewhere. AltGr Mod = 1 << 1 // Caps is Caps Lock engaged. Caps Mod = 1 << 2 )
type Out ¶
type Out struct {
// Text is the character typed. It is occasionally more than one rune:
// a few layouts have ligature keys, and dead-key accents are given as
// the spacing form of the accent.
Text string `json:"t"`
// Dead reports that the key does not type Text directly but arms an
// accent that combines with the next keystroke. The circumflex on a
// French keyboard is a dead key: it types nothing until "o" follows,
// at which point "ô" appears.
Dead bool `json:"d,omitempty"`
}
Out is what a key produces in one modifier state.
type Pos ¶
Pos is a key's physical position on the board, measured in key units: one unit is the width of a plain letter key. X grows rightwards from the left edge of the alphanumeric block, Y grows downwards from the number row. The function row is not modelled, since it produces no text.
func Positioned ¶
Positioned reports whether a scan code belongs to the alphanumeric block, and returns where it sits on the given form. Scan codes outside the block — function keys, the navigation cluster, the numeric keypad — are not modelled: they either produce no text or duplicate text the block already covers, and including them would put two physical keys behind a digit.
A form this package does not know is read as ANSI, which is the shape the others are variations on. Scan codes are matched however they are spelled, since hex has two spellings and the source data does not agree with itself about which to use.
func (Pos) Center ¶
Center returns the coordinates of the middle of the key. It is where a finger aims, and for every ordinary key it is also where Distance measures from — see strike for the one case where it is not.
func (Pos) Distance ¶
Distance returns the distance between two keys, in key units. Two side-by-side letter keys are exactly 1.0 apart.
It is measured between the points a finger would plausibly strike rather than between the centers, which for ordinary one-unit keys is the same thing. It stops being the same thing for the space bar: that key is six units wide, so its center sits under "b" and a center-to-center reading would call "b" its only neighbour, when in truth the bar runs the length of the bottom row. A wide key is struck wherever the hand already is, and Distance treats it that way.
The measurement is symmetric — each key's strike point is taken against the other's center, so swapping the arguments only swaps the two terms.
type Stroke ¶
type Stroke struct {
// SC is the scan code of the key pressed.
SC string
// Mod is the set of modifiers held.
Mod Mod
}
Stroke is a key being pressed: which switch, and what was held down with it. It says nothing about what gets typed — that depends on the layout the keystroke lands on, which is the whole point.