mirror of
https://github.com/RoganDawes/P4wnP1_aloa.git
synced 2025-03-27 01:51:45 +01:00
Reworked keyboard LED monitoring, for better GO channel usage
This commit is contained in:
parent
4154a82def
commit
353c16dc39
@ -1,3 +1,4 @@
|
||||
|
||||
package hid
|
||||
|
||||
import (
|
||||
@ -5,6 +6,7 @@ import (
|
||||
"log"
|
||||
"time"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
|
||||
@ -13,7 +15,11 @@ const (
|
||||
|
||||
)
|
||||
|
||||
var halt = errors.New("Stahp")
|
||||
var (
|
||||
halt = errors.New("Stahp")
|
||||
ErrAbort = errors.New("Event listening aborted")
|
||||
ErrNotAllowed = errors.New("Calling not allowed (currently disabled)")
|
||||
)
|
||||
|
||||
type AsyncOttoVM struct {
|
||||
vm *otto.Otto
|
||||
@ -72,12 +78,7 @@ func (avm *AsyncOttoVM) RunAsync(src interface{}) (error) {
|
||||
|
||||
// fmt.Println("Running vm")
|
||||
tmpValue, tmpErr := avm.vm.Run(src)
|
||||
// if tmpErr == nil {
|
||||
// fmt.Println("Run ended")
|
||||
// } else {
|
||||
// fmt.Printf("Run ended with error: %v\n", tmpErr)
|
||||
// }
|
||||
avm.ResultValue = tmpValue //stroe value first, to have it accessible when error is retrieved from channel
|
||||
avm.ResultValue = tmpValue //store value first, to have it accessible when error is retrieved from channel
|
||||
avm.ResultErr = &tmpErr
|
||||
avm.Finished <- true
|
||||
// fmt.Println("Stored vm result")
|
||||
@ -256,7 +257,6 @@ func (ctl *HIDController) CancelAllBackgroundJobs() error {
|
||||
}
|
||||
|
||||
//Function declarations for master VM
|
||||
//ToDo: Global mutex for VM callbacks (or better for atomar part of Keyboard.StringToPressKeySequence)
|
||||
func (ctl *HIDController) jsType(call otto.FunctionCall) (res otto.Value) {
|
||||
arg0 := call.Argument(0)
|
||||
//fmt.Printf("JS type() called with: `%s` (%s)\n", arg0, arg0)
|
||||
@ -411,7 +411,11 @@ func (ctl *HIDController) jsWaitLED(call otto.FunctionCall) (res otto.Value) {
|
||||
changed,err := ctl.Keyboard.WaitLEDStateChange(mask, timeout)
|
||||
//fmt.Printf("Changed %+v\n", changed)
|
||||
|
||||
errStr := ""
|
||||
if err != nil {errStr = fmt.Sprintf("%v",err)}
|
||||
res,_ = call.Otto.ToValue(struct{
|
||||
ERROR bool
|
||||
ERRORTEXT string
|
||||
TIMEOUT bool
|
||||
NUM bool
|
||||
CAPS bool
|
||||
@ -419,6 +423,8 @@ func (ctl *HIDController) jsWaitLED(call otto.FunctionCall) (res otto.Value) {
|
||||
COMPOSE bool
|
||||
KANA bool
|
||||
}{
|
||||
ERROR: err != nil,
|
||||
ERRORTEXT: errStr,
|
||||
TIMEOUT: err == ErrTimeout,
|
||||
NUM: err == nil && changed.NumLock,
|
||||
CAPS: err == nil && changed.CapsLock,
|
||||
@ -508,7 +514,11 @@ func (ctl *HIDController) jsWaitLEDRepeat(call otto.FunctionCall) (res otto.Valu
|
||||
changed,err := ctl.Keyboard.WaitLEDStateChangeRepeated(mask, repeatCount, maxInterval, timeout)
|
||||
//fmt.Printf("Changed %+v\n", changed)
|
||||
|
||||
errStr := ""
|
||||
if err != nil {errStr = fmt.Sprintf("%v",err)}
|
||||
res,_ = call.Otto.ToValue(struct{
|
||||
ERROR bool
|
||||
ERRORTEXT string
|
||||
TIMEOUT bool
|
||||
NUM bool
|
||||
CAPS bool
|
||||
@ -516,6 +526,8 @@ func (ctl *HIDController) jsWaitLEDRepeat(call otto.FunctionCall) (res otto.Valu
|
||||
COMPOSE bool
|
||||
KANA bool
|
||||
}{
|
||||
ERROR: err != nil,
|
||||
ERRORTEXT: errStr,
|
||||
TIMEOUT: err == ErrTimeout,
|
||||
NUM: err == nil && changed.NumLock,
|
||||
CAPS: err == nil && changed.CapsLock,
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
"math/rand"
|
||||
"regexp"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -30,10 +31,11 @@ func init() {
|
||||
}
|
||||
|
||||
type HIDKeyboard struct {
|
||||
lock *sync.Mutex
|
||||
DevicePath string
|
||||
ActiveLanguageLayout *HIDKeyboardLanguageMap
|
||||
LanguageMaps map[string]*HIDKeyboardLanguageMap //available language maps
|
||||
LEDWatcher *HIDKeyboardLEDStateWatcher
|
||||
LEDWatcher *KeyboardLEDStateWatcher
|
||||
KeyDelay int
|
||||
KeyDelayJitter int
|
||||
}
|
||||
@ -44,22 +46,29 @@ type HIDKeyboard struct {
|
||||
func NewKeyboard(devicePath string, resourcePath string) (keyboard *HIDKeyboard, err error) {
|
||||
//ToDo: check existence of deviceFile (+ is writable)
|
||||
|
||||
keyboard = &HIDKeyboard{}
|
||||
keyboard.DevicePath = devicePath
|
||||
keyboard.KeyDelay = 0
|
||||
keyboard.KeyDelayJitter = 0
|
||||
keyboard = &HIDKeyboard{
|
||||
lock: &sync.Mutex{},
|
||||
DevicePath: devicePath,
|
||||
KeyDelay: 0,
|
||||
KeyDelayJitter: 0,
|
||||
}
|
||||
|
||||
//Load available language maps
|
||||
err = keyboard.LoadLanguageMapDir(resourcePath)
|
||||
if err != nil {return nil, err}
|
||||
|
||||
//Init LED sate
|
||||
keyboard.LEDWatcher, err = newHIDKeyboardLEDStateWatcher(devicePath)
|
||||
keyboard.LEDWatcher, err = NewLEDStateWatcher(devicePath)
|
||||
if err != nil {return nil, err}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (kbd *HIDKeyboard) Close() {
|
||||
kbd.LEDWatcher.Stop()
|
||||
}
|
||||
|
||||
|
||||
func (kbd *HIDKeyboard) LoadLanguageMapDir(dirpath string) (err error) {
|
||||
folder,err := filepath.Abs(dirpath)
|
||||
if err != nil { return err }
|
||||
@ -222,6 +231,7 @@ func (kbd *HIDKeyboard) StringToKeyCombo(comboStr string) (result *KeyboardOutRe
|
||||
}
|
||||
|
||||
func (kbd *HIDKeyboard) StringToPressKeySequence(str string) (err error) {
|
||||
|
||||
//ToDo: Check if keyboard device file exists
|
||||
if kbd.ActiveLanguageLayout == nil {
|
||||
return errors.New("No language mapping active, couldn't send key sequence!")
|
||||
@ -362,6 +372,14 @@ func combineReports(reports []*KeyboardOutReport) (result *KeyboardOutReport, er
|
||||
//
|
||||
// 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) {
|
||||
//Synchronize the whole sequence output, as this is considered atomar.
|
||||
// f.e. This is used to type out character which are brought up a sequence consisting of
|
||||
// [some deadkey + modifiers, some normal key + modifier, zero report for key release]
|
||||
// if another key is pressed right after the deadkey, by a different go routine, we end
|
||||
// up with unpredictable output
|
||||
kbd.lock.Lock()
|
||||
defer kbd.lock.Unlock()
|
||||
|
||||
//iterate over reports and send them
|
||||
for _,rep := range reports {
|
||||
err = rep.WriteTo(kbd.DevicePath)
|
||||
|
@ -5,8 +5,13 @@ import (
|
||||
"os"
|
||||
"time"
|
||||
"log"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var (
|
||||
//privateCurrentLEDWatcher *HIDKeyboardLEDStateWatcher = nil
|
||||
privateCurrentKeyboardLEDWatcher *KeyboardLEDStateWatcher = nil
|
||||
)
|
||||
|
||||
const (
|
||||
MaskNumLock = 1 << 0
|
||||
@ -57,57 +62,229 @@ func (s HIDLEDState) Changes(other HIDLEDState) (result HIDLEDState) {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
type HIDKeyboardLEDStateWatcher struct {
|
||||
ledState *HIDLEDState //global LED state
|
||||
listeners *listenersmap //map of registered listeners
|
||||
addListeners chan *HIDKeyboardLEDListener //channel which takes new listeners, used to block LED state dispatching in case there isn't at least one listenr in listeners
|
||||
hasInitialSate bool
|
||||
}
|
||||
|
||||
type listenersmap struct {
|
||||
type lockableListenerMap struct {
|
||||
sync.Mutex
|
||||
m map[*HIDKeyboardLEDListener]*HIDKeyboardLEDListener
|
||||
m map[*KeyboardLEDStateListener]bool
|
||||
}
|
||||
|
||||
type HIDKeyboardLEDListener struct {
|
||||
ledWatcher *HIDKeyboardLEDStateWatcher //the parent LEDWatcher, containing global ledState
|
||||
type KeyboardLEDStateWatcher struct {
|
||||
ledState *HIDLEDState //latest global LED state
|
||||
listeners *lockableListenerMap //map of registered listeners
|
||||
ledStateFile *os.File
|
||||
hasInitialSate bool //marks if the initial state is define (gets true after first LED state ha been read)
|
||||
|
||||
//listener which should be registered are put into a zero length channel, to allow blocking till a listener is added
|
||||
//in case there isn't already a listener (avoid reading LED states, without having a single listener consuming them)
|
||||
listenersToAdd chan *KeyboardLEDStateListener
|
||||
readerToDispatcher chan HIDLEDState
|
||||
interrupt chan struct{}
|
||||
isUsable bool
|
||||
}
|
||||
|
||||
|
||||
func NewLEDStateWatcher(devFilePath string) (res *KeyboardLEDStateWatcher,err error) {
|
||||
//try to open the devFile
|
||||
devFile, err := os.Open(devFilePath)
|
||||
if err != nil { return }
|
||||
|
||||
if privateCurrentKeyboardLEDWatcher != nil {
|
||||
privateCurrentKeyboardLEDWatcher.Stop()
|
||||
}
|
||||
|
||||
|
||||
|
||||
res = &KeyboardLEDStateWatcher{
|
||||
ledState: &HIDLEDState{},
|
||||
hasInitialSate: false,
|
||||
ledStateFile: devFile,
|
||||
listeners: &lockableListenerMap{
|
||||
m: make(map[*KeyboardLEDStateListener]bool),
|
||||
},
|
||||
// Buffer at least one listener, to avoid blocking when one is added, the channel is only used to block the
|
||||
// dispatchLoop, in case there's no registered listener (by reading from the listenerToAdd chanel
|
||||
listenersToAdd: make(chan *KeyboardLEDStateListener,1),
|
||||
readerToDispatcher: make(chan HIDLEDState), //communicates new LED states to from file reader loop to dispatcher loop, blocks till consumed
|
||||
interrupt: make(chan struct{},1), // used to interrupt reader and dispatcher loop
|
||||
|
||||
|
||||
|
||||
/*
|
||||
ledState: &HIDLEDState{},
|
||||
listeners: &listenersmap{m: make(map[*HIDKeyboardLEDListener]*HIDKeyboardLEDListener)},
|
||||
addListeners: make(chan *HIDKeyboardLEDListener,1), //Buffer at least one, to avoid blocking `CreateAndAddNewListener` (we only want to block `dispatchListeners` in case there's no listener)
|
||||
*/
|
||||
}
|
||||
|
||||
go res.readLoop()
|
||||
go res.dispatchLoop()
|
||||
privateCurrentKeyboardLEDWatcher = res
|
||||
res.isUsable = true
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
func (w *KeyboardLEDStateWatcher) RetrieveNewListener() (l *KeyboardLEDStateListener, err error) {
|
||||
if !w.isUsable { return nil, ErrNotAllowed }
|
||||
|
||||
//create listener and assgin watcher as parent
|
||||
l = &KeyboardLEDStateListener{
|
||||
isMarkedForDeletion: false,
|
||||
changedLeds: make(chan HIDLEDState),
|
||||
interrupt: make(chan struct{}),
|
||||
ledWatcher: w,
|
||||
}
|
||||
|
||||
//addListener to map
|
||||
w.listenersToAdd <- l
|
||||
|
||||
return l,nil
|
||||
}
|
||||
|
||||
|
||||
// - unregisters all listeners
|
||||
// - listeners which are still processed receive an interrupt on their interrupt channel, it is the responsibility
|
||||
// of the listener to deal with this interrupt and close the channel after the interrupt is consumed (wrting to the channel
|
||||
// happens only once)
|
||||
// - close ledStateFile
|
||||
func (w *KeyboardLEDStateWatcher) Stop() error {
|
||||
w.ledStateFile.Close() //produces an error in readLoop which gets translated to an interrupt
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
|
||||
//reads the LED state from device file, till os.File object is closed
|
||||
func (w *KeyboardLEDStateWatcher) readLoop() {
|
||||
fmt.Println("***Starting LED reader")
|
||||
|
||||
defer w.ledStateFile.Close() //Assure File object is closed after this loop, if the closed file wasn't the reason for loop abort
|
||||
|
||||
buf := make([]byte, 1)
|
||||
for {
|
||||
//fmt.Println("-----------\nLED READ LOOP\n-------------------")
|
||||
n,err := w.ledStateFile.Read(buf)
|
||||
if err != nil {
|
||||
log.Printf("Keyboard LED watcher: LED file seems to be closed %s: %v\n...interrupting dispatcher loop!\n", w.ledStateFile.Name(), err)
|
||||
//mark watcher as unusable
|
||||
w.isUsable = false
|
||||
|
||||
//interrupt the dispatcher loop, by closing the interrupt channel
|
||||
close(w.interrupt)
|
||||
|
||||
break
|
||||
}
|
||||
for i:=0; i<n; i++ {
|
||||
newState := HIDLEDState{}
|
||||
newState.fillState(buf[i])
|
||||
|
||||
w.readerToDispatcher <- newState
|
||||
}
|
||||
}
|
||||
fmt.Println("***Stopped LED reader")
|
||||
}
|
||||
|
||||
func (w *KeyboardLEDStateWatcher) dispatchLoop() {
|
||||
fmt.Println("***Starting LED dispatcher")
|
||||
|
||||
//try to consume a new LEDState (blocking wait if no interrupt)
|
||||
L:
|
||||
for {
|
||||
select {
|
||||
case newState:= <- w.readerToDispatcher:
|
||||
fmt.Printf("**** HANDLING new LED state\n")
|
||||
|
||||
|
||||
//Translate received LED state to state change (if first received state, everything is considered as change
|
||||
ledStateChange := w.ledState.Changes(newState)
|
||||
if w.hasInitialSate == false {
|
||||
ledStateChange.fillState(MaskAny)
|
||||
w.hasInitialSate = true
|
||||
}
|
||||
|
||||
//Store new LED state
|
||||
w.ledState = &newState //Note: as this method blocks, in case there's no LED state listener, global LED state is only update in case a listener is registered
|
||||
|
||||
// check if there's at least one listener, if not block till a new one is registered
|
||||
// Blocking, in case there's no listener, doesn't allgin to the usual approach of event driven
|
||||
//
|
||||
if len(w.listeners.m) == 0 {
|
||||
//fmt.Println("Waiting fo at least one listener")
|
||||
w.listeners.Lock()
|
||||
w.listeners.m[<-w.listenersToAdd] = true
|
||||
//fmt.Println("At least one listener added")
|
||||
w.listeners.Unlock()
|
||||
}
|
||||
|
||||
deleteList := make([]*KeyboardLEDStateListener,0)
|
||||
//fan out to every listener, block if one doesn't consume the change
|
||||
w.listeners.Lock()
|
||||
for l,_ := range w.listeners.m {
|
||||
// Beware the DeadLock:
|
||||
// If the listener decides to remove itself (sets l.isMarkedForDeletion) based on the ledStateChange
|
||||
// received on the l.changedLeds channel, without continuing consuming data, this could lead to a dead lock
|
||||
// on w.listeners.
|
||||
// Example: The listener consumes a single state change (by reading from l.changedLeds) does some time
|
||||
// consuming processing and, as a result of this time consuming processing, decides to set l.isMarkedForDeletion to
|
||||
// true. This means the listener consumes only the first state change from l.changedLeds. As isMarkedForDeletion
|
||||
// isn't set to true immediately, this loop, again, tries to write data to the channel (if changed LED states are
|
||||
// produced frequently or have been queued already), BUT WRITING BLOCKS as the listener is going to decide to mark
|
||||
// itself for deletion after he is done with processing the first state change. This means the next line of code blocks,
|
||||
// as the data written to the channel is never consumed by the listener (and the channel is marked
|
||||
// for deletion too late, which would prevent writing to it). This again means, that the outer for loop,
|
||||
// which iterates over the registered listeners, will never exit, ULTIMATELY LEAVING w.listeners IN LOCKED
|
||||
// STATE. This becomes a deadlock, as soon as another routine tries to remove a listener from w.listeners,
|
||||
// as this routine has to call w.listeners.Lock() itself. But exactly the case of trying to remove the listener
|
||||
// from w.listeners is the expected one, after setting isMarkedForDeletion to true.
|
||||
//
|
||||
// Solution: No matter where l.isMarkedForDeletion gets set to true, it is the responsibility of exactly
|
||||
// the same part of code, to consume all remaining channel data from l.changedLeds afterwards.
|
||||
if !l.isMarkedForDeletion {
|
||||
l.changedLeds <- ledStateChange
|
||||
} else {
|
||||
deleteList = append(deleteList, l)
|
||||
}
|
||||
}
|
||||
|
||||
//delete listeners which aren't used anymore
|
||||
for _,l := range deleteList {
|
||||
delete(w.listeners.m, l)
|
||||
}
|
||||
w.listeners.Unlock()
|
||||
case <- w.interrupt:
|
||||
//inform all listeners about the interrupt
|
||||
w.listeners.Lock()
|
||||
|
||||
for l,_ := range w.listeners.m {
|
||||
l.Unregister()
|
||||
}
|
||||
w.listeners.Unlock()
|
||||
|
||||
//close Watcher channels
|
||||
close(w.listenersToAdd)
|
||||
|
||||
//End the dispatcher loop
|
||||
break L
|
||||
case newListener := <-w.listenersToAdd:
|
||||
w.listeners.Lock()
|
||||
w.listeners.m[newListener] = true
|
||||
w.listeners.Unlock()
|
||||
}
|
||||
}
|
||||
//fmt.Println("***Stopped LED dispatcher")
|
||||
}
|
||||
|
||||
type KeyboardLEDStateListener struct {
|
||||
ledWatcher *KeyboardLEDStateWatcher //the parent LEDWatcher, containing global ledState
|
||||
changedLeds chan HIDLEDState //changedLeds represents the LEDs which change since last report as bitfield (MaskNumLock, MaskCapsLock ...) the actual state has to be fetched from the respective field of the ledWatcher.ledState
|
||||
interrupt chan struct{}
|
||||
isMarkedForDeletion bool
|
||||
}
|
||||
|
||||
|
||||
func newHIDKeyboardLEDStateWatcher(devicePath string) (watcher *HIDKeyboardLEDStateWatcher,err error) {
|
||||
//llock := &sync.Mutex{}
|
||||
watcher = &HIDKeyboardLEDStateWatcher{
|
||||
ledState: &HIDLEDState{},
|
||||
listeners: &listenersmap{m: make(map[*HIDKeyboardLEDListener]*HIDKeyboardLEDListener)},
|
||||
//listenerNonZeroCond: &sync.Cond{L:&sync.Mutex{}},
|
||||
addListeners: make(chan *HIDKeyboardLEDListener,1), //Buffer at least one, to avoid blocking `CreateAndAddNewListener` (we only want to block `dispatchListeners` in case there's no listener)
|
||||
}
|
||||
|
||||
//start go routine reading LED output reports from keyboard device
|
||||
//ToDo: this should happen only once
|
||||
|
||||
go watcher.start(devicePath)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (watcher *HIDKeyboardLEDStateWatcher) CreateAndAddNewListener() (l *HIDKeyboardLEDListener) {
|
||||
l = &HIDKeyboardLEDListener{
|
||||
ledWatcher: watcher,
|
||||
changedLeds: make(chan HIDLEDState),
|
||||
}
|
||||
watcher.addListeners <- l
|
||||
return
|
||||
}
|
||||
|
||||
func (watcher *HIDKeyboardLEDStateWatcher) removeListener(l *HIDKeyboardLEDListener) {
|
||||
l.isMarkedForDeletion = true //mark listener for deletion to avoid that dispatcher write to channel
|
||||
//consume remaining channel data to unlock dispatchListeners loop
|
||||
L:
|
||||
func (l *KeyboardLEDStateListener) Unregister() {
|
||||
if l.isMarkedForDeletion {return}
|
||||
l.isMarkedForDeletion = true
|
||||
//consume remaining input data from channel
|
||||
L:
|
||||
for {
|
||||
select {
|
||||
case <-l.changedLeds:
|
||||
@ -118,99 +295,9 @@ L:
|
||||
|
||||
}
|
||||
|
||||
//lock listener map and delete listener
|
||||
watcher.listeners.Lock()
|
||||
delete(watcher.listeners.m, l)
|
||||
watcher.listeners.Unlock()
|
||||
|
||||
|
||||
close(l.changedLeds) //close channel (there should be no further read access)
|
||||
|
||||
}
|
||||
|
||||
func (watcher *HIDKeyboardLEDStateWatcher) start(devicePath string) {
|
||||
f, err := os.Open(devicePath)
|
||||
defer f.Close()
|
||||
if err != nil { panic(err) }
|
||||
buf := make([]byte, 1)
|
||||
|
||||
//ToDo: implement cancel() and allow blocking read to be interrupted (select ??)
|
||||
for {
|
||||
n,err := f.Read(buf)
|
||||
if err != nil { panic(err) }
|
||||
for i:=0; i<n; i++ {
|
||||
watcher.dispatchListeners(buf[i]) //dispatchListeners implements the logic to remove listeners which are marked for remove and should be issued after every read of a single byte
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func (watcher *HIDKeyboardLEDStateWatcher) dispatchListeners(state byte) {
|
||||
//log.Printf("New LED state %x\n", state)
|
||||
newState := HIDLEDState{}
|
||||
newState.fillState(state)
|
||||
|
||||
ledsChanged := watcher.ledState.Changes(newState)
|
||||
watcher.ledState.NumLock = newState.NumLock
|
||||
watcher.ledState.CapsLock = newState.CapsLock
|
||||
watcher.ledState.ScrollLock = newState.ScrollLock
|
||||
watcher.ledState.Compose = newState.Compose
|
||||
watcher.ledState.Kana = newState.Kana
|
||||
|
||||
//fmt.Printf("Dispatcher LEDS changed: %+v\n", ledsChanged)
|
||||
hasChanged := ledsChanged.AnyOn()
|
||||
|
||||
if !watcher.hasInitialSate {
|
||||
//This is the first led state reported, so former state is undefined and we consider everything as a change
|
||||
|
||||
ledsChanged.NumLock = true
|
||||
ledsChanged.CapsLock = true
|
||||
ledsChanged.ScrollLock = true
|
||||
ledsChanged.Compose = true
|
||||
ledsChanged.Kana = true
|
||||
|
||||
hasChanged = true
|
||||
|
||||
watcher.hasInitialSate = true //don't do this again
|
||||
}
|
||||
|
||||
|
||||
|
||||
//Inform listeners about change
|
||||
if hasChanged {
|
||||
//log.Printf("INFORMING LISTENERS Changed elements %+v for new LED sate: %+v\n", ledsChanged, watcher.ledState)
|
||||
watcher.listeners.Lock()
|
||||
|
||||
//check if we have listeners left to consume the produced LED state, blocking wait otherwise
|
||||
for len(watcher.listeners.m) == 0 {
|
||||
//fmt.Println("Waiting for new LED Listener ...")
|
||||
l := <-watcher.addListeners //get first listener with blocking wait
|
||||
watcher.listeners.m[l] = l
|
||||
//fmt.Println("... done waiting , at least one LED state listener registered!")
|
||||
}
|
||||
//add remaining listeners
|
||||
L:
|
||||
for {
|
||||
select {
|
||||
case l:= <- watcher.addListeners:
|
||||
watcher.listeners.m[l] = l
|
||||
default:
|
||||
break L
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for _,listener := range watcher.listeners.m {
|
||||
//log.Printf("Sending listener the led state change %+v\n", ledsChanged)
|
||||
//idx+=1
|
||||
|
||||
//only push to channel if listener isn't marked for deletion meanwhile
|
||||
if !listener.isMarkedForDeletion { listener.changedLeds <- ledsChanged }
|
||||
}
|
||||
watcher.listeners.Unlock()
|
||||
//log.Println("...END INFORMING LISTENERS")
|
||||
}
|
||||
//If there's no change, we ignore the new state
|
||||
//close channels
|
||||
close(l.interrupt)
|
||||
close(l.changedLeds)
|
||||
}
|
||||
|
||||
/*
|
||||
@ -220,8 +307,10 @@ return value changed: Mask values combined with logical or, indicating which LED
|
||||
*/
|
||||
func (kbd *HIDKeyboard) WaitLEDStateChange(intendedChange byte, timeout time.Duration) (changed *HIDLEDState,err error) {
|
||||
//register state change listener
|
||||
l := kbd.LEDWatcher.CreateAndAddNewListener()
|
||||
defer kbd.LEDWatcher.removeListener(l)
|
||||
l,err := kbd.LEDWatcher.RetrieveNewListener()
|
||||
if err!= nil { return nil,err }
|
||||
//defer kbd.LEDWatcher.removeListener(l)
|
||||
defer l.Unregister()
|
||||
|
||||
startTime := time.Now()
|
||||
remaining := timeout
|
||||
@ -251,6 +340,8 @@ func (kbd *HIDKeyboard) WaitLEDStateChange(intendedChange byte, timeout time.Dur
|
||||
return &relevantChanges, nil
|
||||
}
|
||||
//If here, there was a LED state change, but not one we want to use for triggering (continue outer loop, consuming channel data)
|
||||
case <-l.interrupt:
|
||||
return nil, ErrAbort
|
||||
case <- time.After(remaining):
|
||||
return nil, ErrTimeout
|
||||
}
|
||||
@ -259,8 +350,10 @@ func (kbd *HIDKeyboard) WaitLEDStateChange(intendedChange byte, timeout time.Dur
|
||||
|
||||
func (kbd *HIDKeyboard) WaitLEDStateChangeRepeated(intendedChange byte, repeatCount int, minRepeatDelay time.Duration, timeout time.Duration) (changed *HIDLEDState,err error) {
|
||||
//register state change listener
|
||||
l := kbd.LEDWatcher.CreateAndAddNewListener()
|
||||
defer kbd.LEDWatcher.removeListener(l)
|
||||
l,err := kbd.LEDWatcher.RetrieveNewListener()
|
||||
if err!= nil { return nil,err }
|
||||
//defer kbd.LEDWatcher.removeListener(l)
|
||||
defer l.Unregister()
|
||||
|
||||
startTime := time.Now()
|
||||
remaining := timeout
|
||||
@ -356,6 +449,8 @@ func (kbd *HIDKeyboard) WaitLEDStateChangeRepeated(intendedChange byte, repeatCo
|
||||
//return &relevantChanges, nil
|
||||
}
|
||||
//If here, there was a LED state change, but not one we want to use for triggering (continue outer loop, consuming channel data)
|
||||
case <-l.interrupt:
|
||||
return nil, ErrAbort
|
||||
case <- time.After(remaining):
|
||||
return nil, ErrTimeout
|
||||
}
|
||||
|
@ -21,9 +21,10 @@ func GetDefaultNetworkSettingsWiFi() (*pb.EthernetInterfaceSettings) {
|
||||
ifSettings := &pb.EthernetInterfaceSettings {
|
||||
Enabled: true,
|
||||
Name: "wlan0",
|
||||
Mode: pb.EthernetInterfaceSettings_MANUAL,
|
||||
Mode: pb.EthernetInterfaceSettings_DHCP_SERVER,
|
||||
IpAddress4: "172.24.0.1",
|
||||
Netmask4: "255.255.255.252",
|
||||
Netmask4: "255.255.255.0",
|
||||
DhcpServerSettings: GetDefaultDHCPConfigWiFi(),
|
||||
}
|
||||
return ifSettings
|
||||
}
|
||||
@ -50,6 +51,25 @@ func GetDefaultDHCPConfigUSB() (settings *pb.DHCPServerSettings) {
|
||||
return
|
||||
}
|
||||
|
||||
func GetDefaultDHCPConfigWiFi() (settings *pb.DHCPServerSettings) {
|
||||
settings = &pb.DHCPServerSettings{
|
||||
//CallbackScript: "/bin/evilscript",
|
||||
DoNotBindInterface: false, //only bind to given interface
|
||||
ListenInterface: "wlan0",
|
||||
LeaseFile: "/tmp/dnsmasq_wlan0.leases",
|
||||
ListenPort: 0, //No DNS, DHCP only
|
||||
NotAuthoritative: false, //be authoritative
|
||||
Ranges: []*pb.DHCPServerRange{
|
||||
&pb.DHCPServerRange{RangeLower: "172.24.0.2", RangeUpper: "172.24.0.20", LeaseTime: "5m"},
|
||||
},
|
||||
Options: map[uint32]string{
|
||||
3: "", //Disable option: Router
|
||||
6: "", //Disable option: DNS
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func GetDefaultLEDSettings() (res pb.LEDSettings) {
|
||||
return pb.LEDSettings{
|
||||
BlinkCount: 254,
|
||||
|
44
testhid.go
44
testhid.go
@ -111,7 +111,7 @@ func TestMultiLEDTrigges(hidCtl *hid.HIDController, triggerMask byte) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConcurrentLEDTrigges(hidCtl *hid.HIDController) {
|
||||
func TestConcurrentLEDTriggers(hidCtl *hid.HIDController) {
|
||||
go TestMultiLEDTrigges(hidCtl, hid.MaskNumLock)
|
||||
go TestMultiLEDTrigges(hidCtl, hid.MaskCapsLock)
|
||||
go TestMultiLEDTrigges(hidCtl, hid.MaskCapsLock | hid.MaskScrollLock)
|
||||
@ -258,6 +258,11 @@ func main() {
|
||||
|
||||
|
||||
hidCtl, err := hid.NewHIDController("/dev/hidg0", "keymaps", "/dev/hidg1")
|
||||
|
||||
// hidCtl.StartScriptAsBackgroundJob("waitLED(ANY)")
|
||||
// time.Sleep(1*time.Second)
|
||||
// hidCtl, err = hid.NewHIDController("/dev/hidg0", "keymaps", "/dev/hidg1")
|
||||
|
||||
if err != nil {panic(err)}
|
||||
hidCtl.Keyboard.KeyDelay = 100
|
||||
// hidCtl.Keyboard.KeyDelayJitter = 200
|
||||
@ -270,15 +275,50 @@ func main() {
|
||||
|
||||
/* tests */
|
||||
|
||||
|
||||
|
||||
//TestComboPress(hidCtl)
|
||||
//TestLEDTriggers(hidCtl)
|
||||
//TestStringTyping(hidCtl)
|
||||
//TestConcurrentLEDTrigges(hidCtl)
|
||||
//TestConcurrentLEDTriggers(hidCtl)
|
||||
//TestMouseNoScript(hidCtl)
|
||||
//TestCombinedScript(hidCtl)
|
||||
//TestMouseCircle(hidCtl)
|
||||
|
||||
|
||||
/*
|
||||
go func() {
|
||||
time.Sleep(3*time.Second)
|
||||
fmt.Printf("=======================\nClosing LED watcher\n======================\n")
|
||||
hidCtl.Keyboard.Close()
|
||||
}()
|
||||
|
||||
scriptConcurrent := `
|
||||
console.log("Starting script with job ID: " + JID);
|
||||
delay(1000);
|
||||
console.log("Script with job ID: " + JID + " finished");
|
||||
res = waitLEDRepeat(ANY,5);
|
||||
console.log("****Finished Waiting for LED " + JSON.stringify(res));
|
||||
`
|
||||
|
||||
|
||||
for i:=0; i<25; i++ {
|
||||
fmt.Printf("Starting background script, run : %d\n",i)
|
||||
_,_,errBG := hidCtl.StartScriptAsBackgroundJob(scriptConcurrent)
|
||||
if errBG != nil { fmt.Printf("Error: %v\n", errBG)}
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
||||
}
|
||||
|
||||
|
||||
time.Sleep(2*time.Second)
|
||||
_,err = hidCtl.RunScript(scriptConcurrent)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
//try to load script file
|
||||
filepath := "./hidtest1.js"
|
||||
if scriptFile, err := ioutil.ReadFile(filepath); err != nil {
|
||||
|
Loading…
x
Reference in New Issue
Block a user