From a078dcb19374a3f4ee6cf01a085cf581b2910157 Mon Sep 17 00:00:00 2001 From: Anna Rose Wiggins Date: Wed, 2 Jul 2025 17:01:17 -0400 Subject: [PATCH] Read events from multiple devices. --- cmd/joyful/main.go | 68 +++++++++++++++++++------------ internal/config/rules.go | 4 +- internal/mappingrules/matching.go | 8 ++++ internal/mappingrules/types.go | 10 +++-- 4 files changed, 59 insertions(+), 31 deletions(-) diff --git a/cmd/joyful/main.go b/cmd/joyful/main.go index acf4486..9c56829 100644 --- a/cmd/joyful/main.go +++ b/cmd/joyful/main.go @@ -1,9 +1,9 @@ package main import ( + "fmt" "os" "path/filepath" - "time" "git.annabunches.net/annabunches/joyful/internal/config" "git.annabunches.net/annabunches/joyful/internal/logger" @@ -63,38 +63,56 @@ func main() { // Initialize rules rules := config.BuildRules(pDevices, getVirtualDevices(vBuffers)) - // TEST CODE - testDriver(vBuffers, pDevices, rules) + // Listen for events and map them forever + mapEvents(vBuffers, pDevices, rules) } -func testDriver(vBuffers map[string]*virtualdevice.EventBuffer, pDevices map[string]*evdev.InputDevice, rules []mappingrules.MappingRule) { - pDevice := pDevices["right-stick"] - buffer := vBuffers["main"] - for { - // Get the first event for this report - event, err := pDevice.ReadOne() - logger.LogIfError(err, "Error while reading event") +type ChannelEvent struct { + Device *evdev.InputDevice + Event *evdev.InputEvent +} - for event.Code != evdev.SYN_REPORT { +func mapEvents(vBuffers map[string]*virtualdevice.EventBuffer, pDevices map[string]*evdev.InputDevice, rules []mappingrules.MappingRule) { + // start listening for events on all devices + eventChannel := make(chan *ChannelEvent, 1000) + for _, device := range pDevices { + go eventWatcher(device, eventChannel) + } + + fmt.Println("Joyful Running! Press Ctrl+C to quit.") + for { + // Get an event (blocks if necessary) + wrapper := <-eventChannel + + switch wrapper.Event.Type { + case evdev.EV_SYN: + // We've received a SYN_REPORT, so now we send all of our pending events + for _, buffer := range vBuffers { + buffer.SendEvents() + } + + case evdev.EV_KEY: + case evdev.EV_ABS: + // We have a matchable event type. Check all the events for _, rule := range rules { - event := rule.MatchEvent(pDevice, event) - if event == nil { + outputEvent := rule.MatchEvent(wrapper.Device, wrapper.Event) + if outputEvent == nil { continue } - buffer.AddEvent(event) + vBuffers[rule.OutputName()].AddEvent(outputEvent) } - - // Get the next event - event, err = pDevice.ReadOne() - logger.LogIfError(err, "Error while reading event") } - - // We've received a SYN_REPORT, so now we can send all of our events - // TODO: how shall we handle this when dealing with multiple devices? - buffer.SendEvents() - - time.Sleep(1 * time.Millisecond) } - // END TEST CODE +} + +func eventWatcher(device *evdev.InputDevice, channel chan *ChannelEvent) { + for { + event, err := device.ReadOne() + if err != nil { + logger.LogError(err, "Error while reading event") + continue + } + channel <- &ChannelEvent{Device: device, Event: event} + } } diff --git a/internal/config/rules.go b/internal/config/rules.go index 90e0ca4..cdacb75 100644 --- a/internal/config/rules.go +++ b/internal/config/rules.go @@ -64,6 +64,7 @@ func makeRuleTarget(targetConfig RuleTargetConfig, devs map[string]*evdev.InputD ruleTarget.Type = eventType ruleTarget.Code = eventCode ruleTarget.Inverted = targetConfig.Inverted + ruleTarget.DeviceName = targetConfig.Device return ruleTarget, nil } @@ -83,8 +84,7 @@ func decodeRuleTargetValues(target RuleTargetConfig) (evdev.EvType, evdev.EvCode if !ok { return 0, 0, fmt.Errorf("skipping rule due to invalid button code '%s'", target.Button) } - } - if target.Axis != "" { + } else if target.Axis != "" { eventType = evdev.EV_ABS eventCode, ok = evdev.ABSFromString[target.Axis] if !ok { diff --git a/internal/mappingrules/matching.go b/internal/mappingrules/matching.go index 6c1cad6..9c2e3e0 100644 --- a/internal/mappingrules/matching.go +++ b/internal/mappingrules/matching.go @@ -78,3 +78,11 @@ func (rule *ComboMappingRule) MatchEvent(device *evdev.InputDevice, event *evdev } return nil } + +func (rule *SimpleMappingRule) OutputName() string { + return rule.Output.DeviceName +} + +func (rule *ComboMappingRule) OutputName() string { + return rule.Output.DeviceName +} diff --git a/internal/mappingrules/types.go b/internal/mappingrules/types.go index 1589a54..88d2a9d 100644 --- a/internal/mappingrules/types.go +++ b/internal/mappingrules/types.go @@ -4,6 +4,7 @@ import "github.com/holoplot/go-evdev" type MappingRule interface { MatchEvent(*evdev.InputDevice, *evdev.InputEvent) *evdev.InputEvent + OutputName() string } // A Simple Mapping Rule can map a button to a button or an axis to an axis. @@ -22,8 +23,9 @@ type ComboMappingRule struct { } type RuleTarget struct { - Device *evdev.InputDevice - Type evdev.EvType - Code evdev.EvCode - Inverted bool + DeviceName string + Device *evdev.InputDevice + Type evdev.EvType + Code evdev.EvCode + Inverted bool }