mirror of
https://github.com/RoganDawes/P4wnP1_aloa.git
synced 2025-04-12 22:19:02 +02:00
Started implementing key2report mapping for combos
This commit is contained in:
parent
1e14a80482
commit
d580cc7627
@ -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)
|
||||
|
10
testhid.go
10
testhid.go
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user