Support live re-loading of rules. (#2)
Reviewed-on: #2 Co-authored-by: Anna Rose Wiggins <annabunches@gmail.com> Co-committed-by: Anna Rose Wiggins <annabunches@gmail.com>
This commit is contained in:
parent
4ebcbb4dc9
commit
e1940006d8
4 changed files with 102 additions and 25 deletions
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"git.annabunches.net/annabunches/joyful/internal/config"
|
"git.annabunches.net/annabunches/joyful/internal/config"
|
||||||
"git.annabunches.net/annabunches/joyful/internal/logger"
|
"git.annabunches.net/annabunches/joyful/internal/logger"
|
||||||
|
@ -63,24 +64,7 @@ func main() {
|
||||||
// Initialize physical devices
|
// Initialize physical devices
|
||||||
pDevices := initPhysicalDevices(config)
|
pDevices := initPhysicalDevices(config)
|
||||||
|
|
||||||
// Initialize rules
|
rules, eventChannel, doneChannel, wg := loadRules(config, pDevices, getVirtualDevices(vBuffersByName))
|
||||||
rules := config.BuildRules(pDevices, getVirtualDevices(vBuffersByName))
|
|
||||||
logger.Logf("Created %d mapping rules.", len(rules))
|
|
||||||
|
|
||||||
// start listening for events on devices and timers
|
|
||||||
eventChannel := make(chan ChannelEvent, 1000)
|
|
||||||
for _, device := range pDevices {
|
|
||||||
go eventWatcher(device, eventChannel)
|
|
||||||
}
|
|
||||||
|
|
||||||
timerCount := 0
|
|
||||||
for _, rule := range rules {
|
|
||||||
if timedRule, ok := rule.(mappingrules.TimedEventEmitter); ok {
|
|
||||||
go timerWatcher(timedRule, eventChannel)
|
|
||||||
timerCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.Logf("registered %d timers", timerCount)
|
|
||||||
|
|
||||||
// initialize the mode variable
|
// initialize the mode variable
|
||||||
mode := config.GetModes()[0]
|
mode := config.GetModes()[0]
|
||||||
|
@ -117,6 +101,50 @@ func main() {
|
||||||
vBuffersByDevice[channelEvent.Device].AddEvent(channelEvent.Event)
|
vBuffersByDevice[channelEvent.Device].AddEvent(channelEvent.Event)
|
||||||
// If we get a timer event, flush the output device buffer immediately
|
// If we get a timer event, flush the output device buffer immediately
|
||||||
vBuffersByDevice[channelEvent.Device].SendEvents()
|
vBuffersByDevice[channelEvent.Device].SendEvents()
|
||||||
|
|
||||||
|
case ChannelEventReload:
|
||||||
|
// stop existing channels
|
||||||
|
fmt.Println("Reloading rules.")
|
||||||
|
doneChannel <- true
|
||||||
|
fmt.Println("Waiting for existing listeners to exit. Provide input from each of your devices.")
|
||||||
|
wg.Wait()
|
||||||
|
fmt.Println("Listeners exited. Parsing config.")
|
||||||
|
config := readConfig() // reload the config
|
||||||
|
rules, eventChannel, doneChannel, wg = loadRules(config, pDevices, getVirtualDevices(vBuffersByName))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func loadRules(
|
||||||
|
config *config.ConfigParser,
|
||||||
|
pDevices map[string]*evdev.InputDevice,
|
||||||
|
vDevices map[string]*evdev.InputDevice) ([]mappingrules.MappingRule, <-chan ChannelEvent, chan bool, *sync.WaitGroup) {
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
eventChannel := make(chan ChannelEvent, 1000)
|
||||||
|
doneChannel := make(chan bool)
|
||||||
|
|
||||||
|
// Initialize rules
|
||||||
|
rules := config.BuildRules(pDevices, vDevices)
|
||||||
|
logger.Logf("Created %d mapping rules.", len(rules))
|
||||||
|
|
||||||
|
// start listening for events on devices and timers
|
||||||
|
for _, device := range pDevices {
|
||||||
|
wg.Add(1)
|
||||||
|
go eventWatcher(device, eventChannel, doneChannel, &wg)
|
||||||
|
}
|
||||||
|
|
||||||
|
timerCount := 0
|
||||||
|
for _, rule := range rules {
|
||||||
|
if timedRule, ok := rule.(mappingrules.TimedEventEmitter); ok {
|
||||||
|
wg.Add(1)
|
||||||
|
go timerWatcher(timedRule, eventChannel, doneChannel, &wg)
|
||||||
|
timerCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.Logf("registered %d timers", timerCount)
|
||||||
|
|
||||||
|
go consoleWatcher(eventChannel, &wg)
|
||||||
|
|
||||||
|
return rules, eventChannel, doneChannel, &wg
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,9 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.annabunches.net/annabunches/joyful/internal/logger"
|
"git.annabunches.net/annabunches/joyful/internal/logger"
|
||||||
|
@ -13,8 +16,24 @@ const (
|
||||||
DeviceCheckIntervalMs = 1
|
DeviceCheckIntervalMs = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
func eventWatcher(device *evdev.InputDevice, channel chan<- ChannelEvent) {
|
func eventWatcher(
|
||||||
|
device *evdev.InputDevice,
|
||||||
|
channel chan<- ChannelEvent,
|
||||||
|
done chan bool,
|
||||||
|
wg *sync.WaitGroup) {
|
||||||
|
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case cancel := <-done:
|
||||||
|
if cancel {
|
||||||
|
done <- true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
event, err := device.ReadOne()
|
event, err := device.ReadOne()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.LogError(err, "Error while reading event. Disconnecting device.")
|
logger.LogError(err, "Error while reading event. Disconnecting device.")
|
||||||
|
@ -28,8 +47,24 @@ func eventWatcher(device *evdev.InputDevice, channel chan<- ChannelEvent) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func timerWatcher(rule mappingrules.TimedEventEmitter, channel chan<- ChannelEvent) {
|
func timerWatcher(
|
||||||
|
rule mappingrules.TimedEventEmitter,
|
||||||
|
channel chan<- ChannelEvent,
|
||||||
|
done chan bool,
|
||||||
|
wg *sync.WaitGroup) {
|
||||||
|
|
||||||
|
defer wg.Done()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
select {
|
||||||
|
case cancel := <-done:
|
||||||
|
if cancel {
|
||||||
|
done <- true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
event := rule.TimerEvent()
|
event := rule.TimerEvent()
|
||||||
if event != nil {
|
if event != nil {
|
||||||
channel <- ChannelEvent{
|
channel <- ChannelEvent{
|
||||||
|
@ -41,3 +76,21 @@ func timerWatcher(rule mappingrules.TimedEventEmitter, channel chan<- ChannelEve
|
||||||
time.Sleep(TimerCheckIntervalMs * time.Millisecond)
|
time.Sleep(TimerCheckIntervalMs * time.Millisecond)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// consoleWatcher reads input from stdin, and on receiving anything
|
||||||
|
func consoleWatcher(channel chan<- ChannelEvent, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
stdin := bufio.NewReader(os.Stdin)
|
||||||
|
for {
|
||||||
|
_, err := stdin.ReadString('\n')
|
||||||
|
if err != nil {
|
||||||
|
logger.LogErrorf(err, "Error in console input thread")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
channel <- ChannelEvent{
|
||||||
|
Type: ChannelEventReload,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ type ChannelEventType int
|
||||||
const (
|
const (
|
||||||
ChannelEventInput ChannelEventType = iota
|
ChannelEventInput ChannelEventType = iota
|
||||||
ChannelEventTimer
|
ChannelEventTimer
|
||||||
|
ChannelEventReload
|
||||||
)
|
)
|
||||||
|
|
||||||
type ChannelEvent struct {
|
type ChannelEvent struct {
|
||||||
|
|
|
@ -3,7 +3,6 @@ package mappingrules
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.annabunches.net/annabunches/joyful/internal/logger"
|
|
||||||
"github.com/holoplot/go-evdev"
|
"github.com/holoplot/go-evdev"
|
||||||
"github.com/jonboulle/clockwork"
|
"github.com/jonboulle/clockwork"
|
||||||
)
|
)
|
||||||
|
@ -53,10 +52,6 @@ func (rule *MappingRuleAxisToRelaxis) MatchEvent(
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
|
||||||
logger.Logf("DEBUG: Rule '%s' nextEvent == '%v' with device value '%d'", rule.Name, rule.nextEvent, event.Value)
|
|
||||||
}()
|
|
||||||
|
|
||||||
// If we're inside the deadzone, unset the next event
|
// If we're inside the deadzone, unset the next event
|
||||||
if rule.Input.InDeadZone(event.Value) {
|
if rule.Input.InDeadZone(event.Value) {
|
||||||
rule.nextEvent = NoNextEvent
|
rule.nextEvent = NoNextEvent
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue