Started implementing key2report mapping for combos

This commit is contained in:
mame82 2018-06-07 17:25:07 +00:00
parent 1e14a80482
commit d580cc7627
2 changed files with 91 additions and 2 deletions

View File

@ -10,6 +10,7 @@ import (
"fmt"
"time"
"math/rand"
"regexp"
)
var (
@ -17,6 +18,12 @@ var (
ErrTimeout = errors.New("Timeout reached")
)
var (
//regex
rpSplit = regexp.MustCompile("(?m)\\s+")
rpSingleUpperLetter = regexp.MustCompile("(?m)^[A-Z]$")
)
func init() {
rand.Seed(time.Now().UnixNano())
}
@ -40,6 +47,8 @@ func NewKeyboard(devicePath string, resourcePath string) (keyboard *HIDKeyboard,
keyboard.KeyDelayJitter = 0
//ToDo: Load whole language map folder, for now single layout testing
err = keyboard.LoadLanguageMapFromFile(resourcePath + "/common.json")
if err != nil {return nil, err}
err = keyboard.LoadLanguageMapFromFile(resourcePath + "/DE_ASCII.json")
if err != nil {return nil, err}
@ -97,7 +106,7 @@ func (kbd *HIDKeyboard) SendString(str string) (err error) {
strRune := string(runeVal)
if reports,found := kbd.ActiveLanguageLayout.Mapping[strRune]; found {
//log.Printf("Sending reports (%T): %v\n", reports, reports)
err = kbd.PressKeyCombo(reports)
err = kbd.PressKeySequence(reports)
if err != nil {
//Abort typing
return err
@ -111,7 +120,77 @@ func (kbd *HIDKeyboard) SendString(str string) (err error) {
return nil
}
func (kbd *HIDKeyboard) PressKeyCombo(reports []KeyboardOutReport) (err error) {
// mapKeyStringToReports tries to translate a key expressed by a string description to a SINGLE
// report (with respect to the chosen language map), which could be sent to a keyboard device.
// Most printable characters like 'a' or 'A' could be represented by a single rune (f.e. `a` or `A`).
// mapKeyStringToReports translates uppercase alphabetical keys [A-Z] to the respective lower case,
// before trying to map, in order to avoid fetching reports with [SHIFT] modifiers (this assures
// that 'A' gets mapped to the USB key KEY_A, not to the USB key KEY_A combined with the [SHIFT]
// modifier).
// The parameter `keyDescription` is of type string (instead of rune) because there're keys which
// couldn't be described with a single rune, for example: 'F1', 'ESCAPE' ...
//
// mapKeyStringToReports could return a report, containing only modifiers (f.e. if
// keyDescription = 'CTRL'). Such reports could be used to build KeyCombos, by mixing them together.
//
// The language maps could contain mappings, containing multiple reports, as they sometime represent
// printable runes consisting of a sequence of multiple keys (f.e. `^` in the German layout maps
// to a report slice of the key for [^] followed by the key [SPACE], which is needed to print the character).
// mapKeyStringToReports returns ONLY THE FIRST REPORT of such slices, as this is closer to the representation.
// Additionally, single reports could be combimned into a key-combo, which wouldn't be possible with a ordered
// sequence of reports.
func (kbd *HIDKeyboard) TmapKeyStringToReports(keyDescription string) (report *KeyboardOutReport,err error) {
// Assure keyDescription contains no spaces, else error
r := rpSplit.Split(keyDescription, -1)
if len(r) > 1 {
return nil, errors.New("keyDescription mustn't contain spaces")
}
keyDescription = strings.ToUpper(r[0]) //reassign trimmed, upper case version
// If keyDescription consists of a single upper case letter, translate to lowercase
if rpSingleUpperLetter.MatchString(keyDescription) { keyDescription = strings.ToLower(keyDescription)}
// Try to find a matching mapping in 1) current language map, followed by 2) common map (the latter
// holds mappings like 'F1', 'CTRL' etc which are more or less language independent. The common
// map is only accessed, if there was no successful mapping in the chosen language map (priority
// as more specialized)
c,ok := kbd.LanguageMaps["COMMON"]
if !ok { return nil,errors.New("Keyboardmap 'common' not found")}
common := c.Mapping
reports := common[keyDescription]
if len(reports) > 1 {
//store first report as result
report = &reports[0]
}
fmt.Printf("Descr '%s': %+v\n", keyDescription, )
return
}
// PressKeySequence writes the output reports given in `reports` to the keyboard device in sequential
// order. A all empty report is automatically appended in order to release all keys after finishing
// the sequence (press in contrast to hold).
//
// There's a clear reason to use sequences of reports. For example the character 'à' on a German keyboard
// layout, is created by pressing the key with [`] in combination with [SHIFT], followed by the key [A].
// To represent the underlying key sequence three reports are needed:
// 1) A report containing the key [`] (key equal from US layout) along with the [SHIFT] modifier
// 2) A report containing the key [A] (the [A] key results in lower case 'a', as no [SHIFT] modifier is used)
// 3) A report containing no key and no modifier representing the release of all keys
//
// It is worth mentioning, that a single report could hold 8 different modifiers and up to 6 dedicated keys,
// for the keyboard type used here. Anyway, packing the keys [A] and [`] in a single report, along with the
// [SHIFT] modifier, would lead to a different result (if there's a result at all). The reason is, that the
// pressing order of the two keys [A] and [`] couldn't be determined anymore, neither would it be possible
// to distinguish if the [SHIFT] modifier should be combined with [A], [`] or both.
//
// As shown, even a single character could be represented by several reports in sequential order! This is why
// PressKeySequence is needed.
//
// A key combination, in contrast to a sequence, combines several keys in a single report (f.e. CTRL+ALT+A)
func (kbd *HIDKeyboard) PressKeySequence(reports []KeyboardOutReport) (err error) {
//iterate over reports and send them
for _,rep := range reports {
err = rep.WriteTo(kbd.DevicePath)

View File

@ -57,6 +57,16 @@ func main() {
*/
hidCtl, err := hid.NewHIDController("/dev/hidg0", "keymaps", "")
if err != nil {panic(err)}
_,err = hidCtl.Keyboard.TmapKeyStringToReports("INS")
if err != nil {panic(err)}
_,err = hidCtl.Keyboard.TmapKeyStringToReports("F1")
if err != nil {panic(err)}
_,err = hidCtl.Keyboard.TmapKeyStringToReports("F13")
if err != nil {panic(err)}
_,err = hidCtl.Keyboard.TmapKeyStringToReports(" F3 ")
if err != nil {panic(err)}
fmt.Println("Initial sleep to test if we capture LED state changes from the past, as soon as we start waiting (needed at boot)")
time.Sleep(3 * time.Second)