package hhkb import "strings" // Variant is the physical key layout of a model. type Variant int const ( US Variant = iota JIS ) func (v Variant) String() string { if v == JIS { return "JIS" } return "US" } // Model is a keyboard this tool understands. Keys is its physical layout, in // reading order; Hybrid marks the Hybrid/Type-S boards, which reserve a few Fn // keys in firmware. type Model struct { Name string Product uint16 Variant Variant Hybrid bool Keys []Key } // Models lists every supported model. The US/JIS split mirrors the official // tool; the product id groups the Classic, Hybrid, and Hybrid Type-S families. var Models = []Model{ {"HHKB Professional Classic (US)", 0x0020, US, false, ansiKeys}, {"HHKB Professional Classic (JIS)", 0x0020, JIS, false, jisKeys}, {"HHKB Professional Hybrid (US)", 0x0021, US, true, ansiKeys}, {"HHKB Professional Hybrid (JIS)", 0x0021, JIS, true, jisKeys}, {"HHKB Professional Hybrid Type-S (US)", 0x0022, US, true, ansiKeys}, {"HHKB Professional Hybrid Type-S (JIS)", 0x0022, JIS, true, jisKeys}, } // MatchModel picks the model for a USB product id and reported type number. // JIS boards carry "20" in their type number (PD-KB420, PD-KB820); the rest // are US. Falls back to any model sharing the product id. func MatchModel(product uint16, typeNumber string) (Model, bool) { want := US if strings.Contains(typeNumber, "20") { want = JIS } for _, m := range Models { if m.Product == product && m.Variant == want { return m, true } } for _, m := range Models { if m.Product == product { return m, true } } return Model{}, false } // DetectModel identifies the connected keyboard from its product id and info. func (d *Device) DetectModel() (Model, bool, error) { info, err := d.Info() if err != nil { return Model{}, false, err } m, ok := MatchModel(d.product, info.TypeNumber) return m, ok, nil } // ansiKeys is the US (60-key) layout. Geometry matches QMK's LAYOUT_60_hhkb: // {Num, Def, Row, X, W}, in key units. var ansiKeys = []Key{ {60, 0x29, 0, 0, 1}, {59, 0x1e, 0, 1, 1}, {58, 0x1f, 0, 2, 1}, {57, 0x20, 0, 3, 1}, {56, 0x21, 0, 4, 1}, {55, 0x22, 0, 5, 1}, {54, 0x23, 0, 6, 1}, {53, 0x24, 0, 7, 1}, {52, 0x25, 0, 8, 1}, {51, 0x26, 0, 9, 1}, {50, 0x27, 0, 10, 1}, {49, 0x2d, 0, 11, 1}, {48, 0x2e, 0, 12, 1}, {47, 0x31, 0, 13, 1}, {46, 0x35, 0, 14, 1}, {45, 0x2b, 1, 0, 1.5}, {44, 0x14, 1, 1.5, 1}, {43, 0x1a, 1, 2.5, 1}, {42, 0x08, 1, 3.5, 1}, {41, 0x15, 1, 4.5, 1}, {40, 0x17, 1, 5.5, 1}, {39, 0x1c, 1, 6.5, 1}, {38, 0x18, 1, 7.5, 1}, {37, 0x0c, 1, 8.5, 1}, {36, 0x12, 1, 9.5, 1}, {35, 0x13, 1, 10.5, 1}, {34, 0x2f, 1, 11.5, 1}, {33, 0x30, 1, 12.5, 1}, {32, 0x2a, 1, 13.5, 1.5}, {31, 0xe0, 2, 0, 1.75}, {30, 0x04, 2, 1.75, 1}, {29, 0x16, 2, 2.75, 1}, {28, 0x07, 2, 3.75, 1}, {27, 0x09, 2, 4.75, 1}, {26, 0x0a, 2, 5.75, 1}, {25, 0x0b, 2, 6.75, 1}, {24, 0x0d, 2, 7.75, 1}, {23, 0x0e, 2, 8.75, 1}, {22, 0x0f, 2, 9.75, 1}, {21, 0x33, 2, 10.75, 1}, {20, 0x34, 2, 11.75, 1}, {19, 0x28, 2, 12.75, 2.25}, {18, 0xe1, 3, 0, 2.25}, {17, 0x1d, 3, 2.25, 1}, {16, 0x1b, 3, 3.25, 1}, {15, 0x06, 3, 4.25, 1}, {14, 0x19, 3, 5.25, 1}, {13, 0x05, 3, 6.25, 1}, {12, 0x11, 3, 7.25, 1}, {11, 0x10, 3, 8.25, 1}, {10, 0x36, 3, 9.25, 1}, {9, 0x37, 3, 10.25, 1}, {8, 0x38, 3, 11.25, 1}, {7, 0xe5, 3, 12.25, 1.75}, {6, 0x01, 3, 14, 1}, // row 4 — key5/key1 are the narrow outer keys, key4/key2 the wide ones next to // Space. Which sends Alt vs GUI(◇) depends on the keyboard's mode (HHK vs Mac), // so the labels come from the live read; the Def values here are the HHK defaults. {5, 0xe3, 4, 1.5, 1}, {4, 0xe2, 4, 2.5, 1.5}, {3, 0x2c, 4, 4, 7}, {2, 0xe6, 4, 11, 1.5}, {1, 0xe7, 4, 12.5, 1}, } // jisKeys is the Japanese (69-key) layout. Key numbers and rows are fixed; the // geometry is a uniform grid for now (exact JIS widths are a later refinement), // and default scancodes are read from the device when connected. var jisKeys = buildJIS() func buildJIS() []Key { rows := [...][2]int{{69, 55}, {54, 41}, {40, 28}, {27, 14}, {13, 1}} var keys []Key for row, span := range rows { x := 0.0 for n := span[0]; n >= span[1]; n-- { keys = append(keys, Key{Num: n, Row: row, X: x, W: 1}) x++ } } return keys }