Improve config yaml schema (#16)
Leverages custom unmarshaling to be more declarative for our config specification. Reviewed-on: #16 Co-authored-by: Anna Rose Wiggins <annabunches@gmail.com> Co-committed-by: Anna Rose Wiggins <annabunches@gmail.com>
This commit is contained in:
parent
1a7b288083
commit
d9babf5dc0
8 changed files with 364 additions and 238 deletions
|
@ -28,8 +28,11 @@ func readConfig(configDir string) *config.ConfigParser {
|
||||||
return parser
|
return parser
|
||||||
}
|
}
|
||||||
|
|
||||||
func initVirtualBuffers(config *config.ConfigParser) (map[string]*virtualdevice.EventBuffer, map[*evdev.InputDevice]*virtualdevice.EventBuffer) {
|
func initVirtualBuffers(config *config.ConfigParser) (map[string]*evdev.InputDevice,
|
||||||
vDevices := config.CreateVirtualDevices()
|
map[string]*virtualdevice.EventBuffer,
|
||||||
|
map[*evdev.InputDevice]*virtualdevice.EventBuffer) {
|
||||||
|
|
||||||
|
vDevices := config.InitVirtualDevices()
|
||||||
if len(vDevices) == 0 {
|
if len(vDevices) == 0 {
|
||||||
logger.Log("Warning: no virtual devices found in configuration. No rules will work.")
|
logger.Log("Warning: no virtual devices found in configuration. No rules will work.")
|
||||||
}
|
}
|
||||||
|
@ -40,20 +43,11 @@ func initVirtualBuffers(config *config.ConfigParser) (map[string]*virtualdevice.
|
||||||
vBuffersByName[name] = virtualdevice.NewEventBuffer(device)
|
vBuffersByName[name] = virtualdevice.NewEventBuffer(device)
|
||||||
vBuffersByDevice[device] = vBuffersByName[name]
|
vBuffersByDevice[device] = vBuffersByName[name]
|
||||||
}
|
}
|
||||||
return vBuffersByName, vBuffersByDevice
|
return vDevices, vBuffersByName, vBuffersByDevice
|
||||||
}
|
|
||||||
|
|
||||||
// Extracts the evdev devices from a list of virtual buffers and returns them.
|
|
||||||
func getVirtualDevices(buffers map[string]*virtualdevice.EventBuffer) map[string]*evdev.InputDevice {
|
|
||||||
devices := make(map[string]*evdev.InputDevice)
|
|
||||||
for name, buffer := range buffers {
|
|
||||||
devices[name] = buffer.Device.(*evdev.InputDevice)
|
|
||||||
}
|
|
||||||
return devices
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func initPhysicalDevices(config *config.ConfigParser) map[string]*evdev.InputDevice {
|
func initPhysicalDevices(config *config.ConfigParser) map[string]*evdev.InputDevice {
|
||||||
pDeviceMap := config.ConnectPhysicalDevices()
|
pDeviceMap := config.InitPhysicalDevices()
|
||||||
if len(pDeviceMap) == 0 {
|
if len(pDeviceMap) == 0 {
|
||||||
logger.Log("Warning: no physical devices found in configuration. No rules will work.")
|
logger.Log("Warning: no physical devices found in configuration. No rules will work.")
|
||||||
}
|
}
|
||||||
|
@ -77,13 +71,13 @@ func main() {
|
||||||
logger.LogIfError(err, "Failed to initialize TTS")
|
logger.LogIfError(err, "Failed to initialize TTS")
|
||||||
|
|
||||||
// Initialize virtual devices with event buffers
|
// Initialize virtual devices with event buffers
|
||||||
vBuffersByName, vBuffersByDevice := initVirtualBuffers(config)
|
vDevicesByName, vBuffersByName, vBuffersByDevice := initVirtualBuffers(config)
|
||||||
|
|
||||||
// Initialize physical devices
|
// Initialize physical devices
|
||||||
pDevices := initPhysicalDevices(config)
|
pDevices := initPhysicalDevices(config)
|
||||||
|
|
||||||
// Load the rules
|
// Load the rules
|
||||||
rules, eventChannel, cancel, wg := loadRules(config, pDevices, getVirtualDevices(vBuffersByName))
|
rules, eventChannel, cancel, wg := loadRules(config, pDevices, vDevicesByName)
|
||||||
|
|
||||||
// initialize the mode variable
|
// initialize the mode variable
|
||||||
mode := config.GetModes()[0]
|
mode := config.GetModes()[0]
|
||||||
|
@ -139,7 +133,7 @@ func main() {
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
fmt.Println("Listeners exited. Parsing config.")
|
fmt.Println("Listeners exited. Parsing config.")
|
||||||
config := readConfig(configDir) // reload the config
|
config := readConfig(configDir) // reload the config
|
||||||
rules, eventChannel, cancel, wg = loadRules(config, pDevices, getVirtualDevices(vBuffersByName))
|
rules, eventChannel, cancel, wg = loadRules(config, pDevices, vDevicesByName)
|
||||||
fmt.Println("Config re-loaded. Only rule changes applied. Device and Mode changes require restart.")
|
fmt.Println("Config re-loaded. Only rule changes applied. Device and Mode changes require restart.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -159,7 +153,7 @@ func loadRules(
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
// Initialize rules
|
// Initialize rules
|
||||||
rules := config.BuildRules(pDevices, vDevices)
|
rules := config.InitRules(pDevices, vDevices)
|
||||||
logger.Logf("Created %d mapping rules.", len(rules))
|
logger.Logf("Created %d mapping rules.", len(rules))
|
||||||
|
|
||||||
// start listening for events on devices and timers
|
// start listening for events on devices and timers
|
||||||
|
|
|
@ -8,13 +8,13 @@ import (
|
||||||
"github.com/holoplot/go-evdev"
|
"github.com/holoplot/go-evdev"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateVirtualDevices will register any configured devices with type = virtual
|
// InitVirtualDevices will register any configured devices with type = virtual
|
||||||
// using /dev/uinput, and return a map of those devices.
|
// using /dev/uinput, and return a map of those devices.
|
||||||
//
|
//
|
||||||
// This function assumes you have already called Parse() on the config directory.
|
// This function assumes Parse() has been called.
|
||||||
//
|
//
|
||||||
// This function should only be called once, unless you want to create duplicate devices for some reason.
|
// This function should only be called once, unless we want to create duplicate devices for some reason.
|
||||||
func (parser *ConfigParser) CreateVirtualDevices() map[string]*evdev.InputDevice {
|
func (parser *ConfigParser) InitVirtualDevices() map[string]*evdev.InputDevice {
|
||||||
deviceMap := make(map[string]*evdev.InputDevice)
|
deviceMap := make(map[string]*evdev.InputDevice)
|
||||||
|
|
||||||
for _, deviceConfig := range parser.config.Devices {
|
for _, deviceConfig := range parser.config.Devices {
|
||||||
|
@ -22,6 +22,8 @@ func (parser *ConfigParser) CreateVirtualDevices() map[string]*evdev.InputDevice
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deviceConfig := deviceConfig.Config.(DeviceConfigVirtual)
|
||||||
|
|
||||||
name := fmt.Sprintf("joyful-%s", deviceConfig.Name)
|
name := fmt.Sprintf("joyful-%s", deviceConfig.Name)
|
||||||
|
|
||||||
var capabilities map[evdev.EvType][]evdev.EvCode
|
var capabilities map[evdev.EvType][]evdev.EvCode
|
||||||
|
@ -74,13 +76,13 @@ func (parser *ConfigParser) CreateVirtualDevices() map[string]*evdev.InputDevice
|
||||||
return deviceMap
|
return deviceMap
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConnectPhysicalDevices will create InputDevices corresponding to any registered
|
// InitPhysicalDevices will create InputDevices corresponding to any registered
|
||||||
// devices with type = physical.
|
// devices with type = physical.
|
||||||
//
|
//
|
||||||
// This function assumes you have already called Parse() on the config directory.
|
// This function assumes Parse() has been called.
|
||||||
//
|
//
|
||||||
// This function should only be called once.
|
// This function should only be called once.
|
||||||
func (parser *ConfigParser) ConnectPhysicalDevices() map[string]*evdev.InputDevice {
|
func (parser *ConfigParser) InitPhysicalDevices() map[string]*evdev.InputDevice {
|
||||||
deviceMap := make(map[string]*evdev.InputDevice)
|
deviceMap := make(map[string]*evdev.InputDevice)
|
||||||
|
|
||||||
for _, deviceConfig := range parser.config.Devices {
|
for _, deviceConfig := range parser.config.Devices {
|
||||||
|
@ -88,6 +90,8 @@ func (parser *ConfigParser) ConnectPhysicalDevices() map[string]*evdev.InputDevi
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deviceConfig := deviceConfig.Config.(DeviceConfigPhysical)
|
||||||
|
|
||||||
var infoName string
|
var infoName string
|
||||||
var device *evdev.InputDevice
|
var device *evdev.InputDevice
|
||||||
var err error
|
var err error
|
||||||
|
|
|
@ -8,7 +8,7 @@ import (
|
||||||
"github.com/holoplot/go-evdev"
|
"github.com/holoplot/go-evdev"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeRuleTargetButton(targetConfig RuleTargetConfig, devs map[string]Device) (*mappingrules.RuleTargetButton, error) {
|
func makeRuleTargetButton(targetConfig RuleTargetConfigButton, devs map[string]Device) (*mappingrules.RuleTargetButton, error) {
|
||||||
device, ok := devs[targetConfig.Device]
|
device, ok := devs[targetConfig.Device]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
||||||
|
@ -27,7 +27,7 @@ func makeRuleTargetButton(targetConfig RuleTargetConfig, devs map[string]Device)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeRuleTargetAxis(targetConfig RuleTargetConfig, devs map[string]Device) (*mappingrules.RuleTargetAxis, error) {
|
func makeRuleTargetAxis(targetConfig RuleTargetConfigAxis, devs map[string]Device) (*mappingrules.RuleTargetAxis, error) {
|
||||||
device, ok := devs[targetConfig.Device]
|
device, ok := devs[targetConfig.Device]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
||||||
|
@ -57,7 +57,7 @@ func makeRuleTargetAxis(targetConfig RuleTargetConfig, devs map[string]Device) (
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeRuleTargetRelaxis(targetConfig RuleTargetConfig, devs map[string]Device) (*mappingrules.RuleTargetRelaxis, error) {
|
func makeRuleTargetRelaxis(targetConfig RuleTargetConfigRelaxis, devs map[string]Device) (*mappingrules.RuleTargetRelaxis, error) {
|
||||||
device, ok := devs[targetConfig.Device]
|
device, ok := devs[targetConfig.Device]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
||||||
|
@ -72,11 +72,10 @@ func makeRuleTargetRelaxis(targetConfig RuleTargetConfig, devs map[string]Device
|
||||||
targetConfig.Device,
|
targetConfig.Device,
|
||||||
device,
|
device,
|
||||||
eventCode,
|
eventCode,
|
||||||
targetConfig.Inverted,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeRuleTargetModeSelect(targetConfig RuleTargetConfig, allModes []string) (*mappingrules.RuleTargetModeSelect, error) {
|
func makeRuleTargetModeSelect(targetConfig RuleTargetConfigModeSelect, allModes []string) (*mappingrules.RuleTargetModeSelect, error) {
|
||||||
if ok := validateModes(targetConfig.Modes, allModes); !ok {
|
if ok := validateModes(targetConfig.Modes, allModes); !ok {
|
||||||
return nil, errors.New("undefined mode in mode select list")
|
return nil, errors.New("undefined mode in mode select list")
|
||||||
}
|
}
|
||||||
|
@ -92,7 +91,7 @@ func hasError(_ any, err error) bool {
|
||||||
// calculateDeadzones produces the deadzone start and end values in absolute terms
|
// calculateDeadzones produces the deadzone start and end values in absolute terms
|
||||||
// TODO: on the one hand, this logic feels betten encapsulated in mappingrules. On the other hand,
|
// TODO: on the one hand, this logic feels betten encapsulated in mappingrules. On the other hand,
|
||||||
// passing even more parameters to NewRuleTargetAxis feels terrible
|
// passing even more parameters to NewRuleTargetAxis feels terrible
|
||||||
func calculateDeadzones(targetConfig RuleTargetConfig, device Device, axis evdev.EvCode) (int32, int32, error) {
|
func calculateDeadzones(targetConfig RuleTargetConfigAxis, device Device, axis evdev.EvCode) (int32, int32, error) {
|
||||||
|
|
||||||
var deadzoneStart, deadzoneEnd int32
|
var deadzoneStart, deadzoneEnd int32
|
||||||
deadzoneStart = 0
|
deadzoneStart = 0
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/holoplot/go-evdev"
|
"github.com/holoplot/go-evdev"
|
||||||
|
@ -12,7 +13,6 @@ type MakeRuleTargetsTests struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
devs map[string]Device
|
devs map[string]Device
|
||||||
deviceMock *DeviceMock
|
deviceMock *DeviceMock
|
||||||
config RuleTargetConfig
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type DeviceMock struct {
|
type DeviceMock struct {
|
||||||
|
@ -47,198 +47,197 @@ func (t *MakeRuleTargetsTests) SetupSuite() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MakeRuleTargetsTests) SetupSubTest() {
|
|
||||||
t.config = RuleTargetConfig{
|
|
||||||
Device: "test",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *MakeRuleTargetsTests) TestMakeRuleTargetButton() {
|
func (t *MakeRuleTargetsTests) TestMakeRuleTargetButton() {
|
||||||
|
config := RuleTargetConfigButton{Device: "test"}
|
||||||
|
|
||||||
t.Run("Standard keycode", func() {
|
t.Run("Standard keycode", func() {
|
||||||
t.config.Button = "BTN_TRIGGER"
|
config.Button = "BTN_TRIGGER"
|
||||||
rule, err := makeRuleTargetButton(t.config, t.devs)
|
rule, err := makeRuleTargetButton(config, t.devs)
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.EqualValues(evdev.BTN_TRIGGER, rule.Button)
|
t.EqualValues(evdev.BTN_TRIGGER, rule.Button)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Hex code", func() {
|
t.Run("Hex code", func() {
|
||||||
t.config.Button = "0x2fd"
|
config.Button = "0x2fd"
|
||||||
rule, err := makeRuleTargetButton(t.config, t.devs)
|
rule, err := makeRuleTargetButton(config, t.devs)
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.EqualValues(evdev.EvCode(0x2fd), rule.Button)
|
t.EqualValues(evdev.EvCode(0x2fd), rule.Button)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Index", func() {
|
t.Run("Index", func() {
|
||||||
t.config.Button = "3"
|
config.Button = "3"
|
||||||
rule, err := makeRuleTargetButton(t.config, t.devs)
|
rule, err := makeRuleTargetButton(config, t.devs)
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.EqualValues(evdev.BTN_TOP, rule.Button)
|
t.EqualValues(evdev.BTN_TOP, rule.Button)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Index too high", func() {
|
t.Run("Index too high", func() {
|
||||||
t.config.Button = "74"
|
config.Button = "74"
|
||||||
_, err := makeRuleTargetButton(t.config, t.devs)
|
_, err := makeRuleTargetButton(config, t.devs)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Un-prefixed keycode", func() {
|
t.Run("Un-prefixed keycode", func() {
|
||||||
t.config.Button = "pinkie"
|
config.Button = "pinkie"
|
||||||
rule, err := makeRuleTargetButton(t.config, t.devs)
|
rule, err := makeRuleTargetButton(config, t.devs)
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.EqualValues(evdev.BTN_PINKIE, rule.Button)
|
t.EqualValues(evdev.BTN_PINKIE, rule.Button)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Invalid keycode", func() {
|
t.Run("Invalid keycode", func() {
|
||||||
t.config.Button = "foo"
|
config.Button = "foo"
|
||||||
_, err := makeRuleTargetButton(t.config, t.devs)
|
_, err := makeRuleTargetButton(config, t.devs)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() {
|
func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() {
|
||||||
t.Run("Standard code", func() {
|
codeTestCases := []struct {
|
||||||
t.config.Axis = "ABS_X"
|
input string
|
||||||
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
output evdev.EvCode
|
||||||
t.Nil(err)
|
}{
|
||||||
t.EqualValues(evdev.ABS_X, rule.Axis)
|
{"ABS_X", evdev.ABS_X},
|
||||||
})
|
{"0x01", evdev.ABS_Y},
|
||||||
|
{"x", evdev.ABS_X},
|
||||||
|
}
|
||||||
|
|
||||||
t.Run("Hex code", func() {
|
for _, tc := range codeTestCases {
|
||||||
t.config.Axis = "0x01"
|
t.Run(fmt.Sprintf("KeyCode %s", tc.input), func() {
|
||||||
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
config := RuleTargetConfigAxis{Device: "test"}
|
||||||
t.Nil(err)
|
config.Axis = tc.input
|
||||||
t.EqualValues(evdev.ABS_Y, rule.Axis)
|
rule, err := makeRuleTargetAxis(config, t.devs)
|
||||||
})
|
t.Nil(err)
|
||||||
|
t.EqualValues(tc.output, rule.Axis)
|
||||||
|
|
||||||
t.Run("Un-prefixed code", func() {
|
})
|
||||||
t.config.Axis = "x"
|
}
|
||||||
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
|
||||||
t.Nil(err)
|
|
||||||
t.EqualValues(evdev.ABS_X, rule.Axis)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Invalid code", func() {
|
t.Run("Invalid code", func() {
|
||||||
t.config.Axis = "foo"
|
config := RuleTargetConfigAxis{Device: "test"}
|
||||||
_, err := makeRuleTargetAxis(t.config, t.devs)
|
config.Axis = "foo"
|
||||||
|
_, err := makeRuleTargetAxis(config, t.devs)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Invalid deadzone", func() {
|
t.Run("Invalid deadzone", func() {
|
||||||
t.config.Axis = "x"
|
config := RuleTargetConfigAxis{Device: "test"}
|
||||||
t.config.DeadzoneEnd = 100
|
config.Axis = "x"
|
||||||
t.config.DeadzoneStart = 1000
|
config.DeadzoneEnd = 100
|
||||||
_, err := makeRuleTargetAxis(t.config, t.devs)
|
config.DeadzoneStart = 1000
|
||||||
|
_, err := makeRuleTargetAxis(config, t.devs)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Deadzone center/size", func() {
|
relDeadzoneTestCases := []struct {
|
||||||
t.config.Axis = "x"
|
inCenter int32
|
||||||
t.config.DeadzoneCenter = 5000
|
inSize int32
|
||||||
t.config.DeadzoneSize = 1000
|
outStart int32
|
||||||
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
outEnd int32
|
||||||
t.Nil(err)
|
}{
|
||||||
t.EqualValues(4500, rule.DeadzoneStart)
|
{5000, 1000, 4500, 5500},
|
||||||
t.EqualValues(5500, rule.DeadzoneEnd)
|
{0, 500, 0, 500},
|
||||||
})
|
{10000, 500, 9500, 10000},
|
||||||
|
}
|
||||||
|
|
||||||
t.Run("Deadzone center/size lower boundary", func() {
|
for _, tc := range relDeadzoneTestCases {
|
||||||
t.config.Axis = "x"
|
t.Run(fmt.Sprintf("Relative Deadzone %d +- %d", tc.inCenter, tc.inSize), func() {
|
||||||
t.config.DeadzoneCenter = 0
|
config := RuleTargetConfigAxis{
|
||||||
t.config.DeadzoneSize = 500
|
Device: "test",
|
||||||
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
Axis: "x",
|
||||||
t.Nil(err)
|
DeadzoneCenter: tc.inCenter,
|
||||||
t.EqualValues(0, rule.DeadzoneStart)
|
DeadzoneSize: tc.inSize,
|
||||||
t.EqualValues(500, rule.DeadzoneEnd)
|
}
|
||||||
})
|
rule, err := makeRuleTargetAxis(config, t.devs)
|
||||||
|
|
||||||
t.Run("Deadzone center/size upper boundary", func() {
|
t.Nil(err)
|
||||||
t.config.Axis = "x"
|
t.Equal(tc.outStart, rule.DeadzoneStart)
|
||||||
t.config.DeadzoneCenter = 10000
|
t.Equal(tc.outEnd, rule.DeadzoneEnd)
|
||||||
t.config.DeadzoneSize = 500
|
})
|
||||||
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
}
|
||||||
t.Nil(err)
|
|
||||||
t.EqualValues(9500, rule.DeadzoneStart)
|
|
||||||
t.EqualValues(10000, rule.DeadzoneEnd)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Deadzone center/size invalid center", func() {
|
t.Run("Deadzone center/size invalid center", func() {
|
||||||
t.config.Axis = "x"
|
config := RuleTargetConfigAxis{
|
||||||
t.config.DeadzoneCenter = 20000
|
Device: "test",
|
||||||
t.config.DeadzoneSize = 500
|
Axis: "x",
|
||||||
_, err := makeRuleTargetAxis(t.config, t.devs)
|
DeadzoneCenter: 20000,
|
||||||
|
DeadzoneSize: 500,
|
||||||
|
}
|
||||||
|
_, err := makeRuleTargetAxis(config, t.devs)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Deadzone center/percent", func() {
|
relDeadzonePercentTestCases := []struct {
|
||||||
t.config.Axis = "x"
|
inCenter int32
|
||||||
t.config.DeadzoneCenter = 5000
|
inSizePercent int32
|
||||||
t.config.DeadzoneSizePercent = 10
|
outStart int32
|
||||||
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
outEnd int32
|
||||||
t.Nil(err)
|
}{
|
||||||
t.EqualValues(4500, rule.DeadzoneStart)
|
{5000, 10, 4500, 5500},
|
||||||
t.EqualValues(5500, rule.DeadzoneEnd)
|
{0, 10, 0, 1000},
|
||||||
})
|
{10000, 10, 9000, 10000},
|
||||||
|
}
|
||||||
|
|
||||||
t.Run("Deadzone center/percent lower boundary", func() {
|
for _, tc := range relDeadzonePercentTestCases {
|
||||||
t.config.Axis = "x"
|
t.Run(fmt.Sprintf("Relative percent deadzone %d +- %d%%", tc.inCenter, tc.inSizePercent), func() {
|
||||||
t.config.DeadzoneCenter = 0
|
config := RuleTargetConfigAxis{
|
||||||
t.config.DeadzoneSizePercent = 10
|
Device: "test",
|
||||||
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
Axis: "x",
|
||||||
t.Nil(err)
|
DeadzoneCenter: tc.inCenter,
|
||||||
t.EqualValues(0, rule.DeadzoneStart)
|
DeadzoneSizePercent: tc.inSizePercent,
|
||||||
t.EqualValues(1000, rule.DeadzoneEnd)
|
}
|
||||||
})
|
rule, err := makeRuleTargetAxis(config, t.devs)
|
||||||
|
|
||||||
t.Run("Deadzone center/percent upper boundary", func() {
|
t.Nil(err)
|
||||||
t.config.Axis = "x"
|
t.Equal(tc.outStart, rule.DeadzoneStart)
|
||||||
t.config.DeadzoneCenter = 10000
|
t.Equal(tc.outEnd, rule.DeadzoneEnd)
|
||||||
t.config.DeadzoneSizePercent = 10
|
})
|
||||||
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
}
|
||||||
t.Nil(err)
|
|
||||||
t.EqualValues(9000, rule.DeadzoneStart)
|
|
||||||
t.EqualValues(10000, rule.DeadzoneEnd)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Deadzone center/percent invalid center", func() {
|
t.Run("Deadzone center/percent invalid center", func() {
|
||||||
t.config.Axis = "x"
|
config := RuleTargetConfigAxis{
|
||||||
t.config.DeadzoneCenter = 20000
|
Device: "test",
|
||||||
t.config.DeadzoneSizePercent = 10
|
Axis: "x",
|
||||||
_, err := makeRuleTargetAxis(t.config, t.devs)
|
DeadzoneCenter: 20000,
|
||||||
|
DeadzoneSizePercent: 10,
|
||||||
|
}
|
||||||
|
_, err := makeRuleTargetAxis(config, t.devs)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MakeRuleTargetsTests) TestMakeRuleTargetRelaxis() {
|
func (t *MakeRuleTargetsTests) TestMakeRuleTargetRelaxis() {
|
||||||
|
config := RuleTargetConfigRelaxis{Device: "test"}
|
||||||
|
|
||||||
t.Run("Standard keycode", func() {
|
t.Run("Standard keycode", func() {
|
||||||
t.config.Axis = "REL_WHEEL"
|
config.Axis = "REL_WHEEL"
|
||||||
rule, err := makeRuleTargetRelaxis(t.config, t.devs)
|
rule, err := makeRuleTargetRelaxis(config, t.devs)
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.EqualValues(evdev.REL_WHEEL, rule.Axis)
|
t.EqualValues(evdev.REL_WHEEL, rule.Axis)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Hex keycode", func() {
|
t.Run("Hex keycode", func() {
|
||||||
t.config.Axis = "0x00"
|
config.Axis = "0x00"
|
||||||
rule, err := makeRuleTargetRelaxis(t.config, t.devs)
|
rule, err := makeRuleTargetRelaxis(config, t.devs)
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.EqualValues(evdev.REL_X, rule.Axis)
|
t.EqualValues(evdev.REL_X, rule.Axis)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Un-prefixed keycode", func() {
|
t.Run("Un-prefixed keycode", func() {
|
||||||
t.config.Axis = "wheel"
|
config.Axis = "wheel"
|
||||||
rule, err := makeRuleTargetRelaxis(t.config, t.devs)
|
rule, err := makeRuleTargetRelaxis(config, t.devs)
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.EqualValues(evdev.REL_WHEEL, rule.Axis)
|
t.EqualValues(evdev.REL_WHEEL, rule.Axis)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Invalid keycode", func() {
|
t.Run("Invalid keycode", func() {
|
||||||
t.config.Axis = "foo"
|
config.Axis = "foo"
|
||||||
_, err := makeRuleTargetRelaxis(t.config, t.devs)
|
_, err := makeRuleTargetRelaxis(config, t.devs)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Incorrect axis type", func() {
|
t.Run("Incorrect axis type", func() {
|
||||||
t.config.Axis = "ABS_X"
|
config.Axis = "ABS_X"
|
||||||
_, err := makeRuleTargetRelaxis(t.config, t.devs)
|
_, err := makeRuleTargetRelaxis(config, t.devs)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import (
|
||||||
// This would speed up rule matching by only checking relevant rules for a given input event.
|
// This would speed up rule matching by only checking relevant rules for a given input event.
|
||||||
// We could take this further and make it a map[<struct of *inputdevice, type, and code>][]rule
|
// We could take this further and make it a map[<struct of *inputdevice, type, and code>][]rule
|
||||||
// For very large rule-bases this may be helpful for staying performant.
|
// For very large rule-bases this may be helpful for staying performant.
|
||||||
func (parser *ConfigParser) BuildRules(pInputDevs map[string]*evdev.InputDevice, vInputDevs map[string]*evdev.InputDevice) []mappingrules.MappingRule {
|
func (parser *ConfigParser) InitRules(pInputDevs map[string]*evdev.InputDevice, vInputDevs map[string]*evdev.InputDevice) []mappingrules.MappingRule {
|
||||||
rules := make([]mappingrules.MappingRule, 0)
|
rules := make([]mappingrules.MappingRule, 0)
|
||||||
modes := parser.GetModes()
|
modes := parser.GetModes()
|
||||||
|
|
||||||
|
@ -42,21 +42,21 @@ func (parser *ConfigParser) BuildRules(pInputDevs map[string]*evdev.InputDevice,
|
||||||
|
|
||||||
switch strings.ToLower(ruleConfig.Type) {
|
switch strings.ToLower(ruleConfig.Type) {
|
||||||
case RuleTypeButton:
|
case RuleTypeButton:
|
||||||
newRule, err = makeMappingRuleButton(ruleConfig, pDevs, vDevs, base)
|
newRule, err = makeMappingRuleButton(ruleConfig.Config.(RuleConfigButton), pDevs, vDevs, base)
|
||||||
case RuleTypeButtonCombo:
|
case RuleTypeButtonCombo:
|
||||||
newRule, err = makeMappingRuleCombo(ruleConfig, pDevs, vDevs, base)
|
newRule, err = makeMappingRuleCombo(ruleConfig.Config.(RuleConfigButtonCombo), pDevs, vDevs, base)
|
||||||
case RuleTypeLatched:
|
case RuleTypeButtonLatched:
|
||||||
newRule, err = makeMappingRuleLatched(ruleConfig, pDevs, vDevs, base)
|
newRule, err = makeMappingRuleLatched(ruleConfig.Config.(RuleConfigButtonLatched), pDevs, vDevs, base)
|
||||||
case RuleTypeAxis:
|
case RuleTypeAxis:
|
||||||
newRule, err = makeMappingRuleAxis(ruleConfig, pDevs, vDevs, base)
|
newRule, err = makeMappingRuleAxis(ruleConfig.Config.(RuleConfigAxis), pDevs, vDevs, base)
|
||||||
case RuleTypeAxisCombined:
|
case RuleTypeAxisCombined:
|
||||||
newRule, err = makeMappingRuleAxisCombined(ruleConfig, pDevs, vDevs, base)
|
newRule, err = makeMappingRuleAxisCombined(ruleConfig.Config.(RuleConfigAxisCombined), pDevs, vDevs, base)
|
||||||
case RuleTypeAxisToButton:
|
case RuleTypeAxisToButton:
|
||||||
newRule, err = makeMappingRuleAxisToButton(ruleConfig, pDevs, vDevs, base)
|
newRule, err = makeMappingRuleAxisToButton(ruleConfig.Config.(RuleConfigAxisToButton), pDevs, vDevs, base)
|
||||||
case RuleTypeAxisToRelaxis:
|
case RuleTypeAxisToRelaxis:
|
||||||
newRule, err = makeMappingRuleAxisToRelaxis(ruleConfig, pDevs, vDevs, base)
|
newRule, err = makeMappingRuleAxisToRelaxis(ruleConfig.Config.(RuleConfigAxisToRelaxis), pDevs, vDevs, base)
|
||||||
case RuleTypeModeSelect:
|
case RuleTypeModeSelect:
|
||||||
newRule, err = makeMappingRuleModeSelect(ruleConfig, pDevs, modes, base)
|
newRule, err = makeMappingRuleModeSelect(ruleConfig.Config.(RuleConfigModeSelect), pDevs, modes, base)
|
||||||
default:
|
default:
|
||||||
err = fmt.Errorf("bad rule type '%s' for rule '%s'", ruleConfig.Type, ruleConfig.Name)
|
err = fmt.Errorf("bad rule type '%s' for rule '%s'", ruleConfig.Type, ruleConfig.Name)
|
||||||
}
|
}
|
||||||
|
@ -72,7 +72,14 @@ func (parser *ConfigParser) BuildRules(pInputDevs map[string]*evdev.InputDevice,
|
||||||
return rules
|
return rules
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMappingRuleButton(ruleConfig RuleConfig,
|
// TODO: how much of these functions could we fold into the unmarshaling logic itself? The main problem
|
||||||
|
// is that we don't have access to the device maps in those functions... could we set device names
|
||||||
|
// as stand-ins and do a post-processing pass that *just* handles device linking and possibly mode
|
||||||
|
// checking?
|
||||||
|
//
|
||||||
|
// In other words - can we unmarshal the config directly into our target structs and remove most of
|
||||||
|
// this library?
|
||||||
|
func makeMappingRuleButton(ruleConfig RuleConfigButton,
|
||||||
pDevs map[string]Device,
|
pDevs map[string]Device,
|
||||||
vDevs map[string]Device,
|
vDevs map[string]Device,
|
||||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButton, error) {
|
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButton, error) {
|
||||||
|
@ -90,7 +97,7 @@ func makeMappingRuleButton(ruleConfig RuleConfig,
|
||||||
return mappingrules.NewMappingRuleButton(base, input, output), nil
|
return mappingrules.NewMappingRuleButton(base, input, output), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMappingRuleCombo(ruleConfig RuleConfig,
|
func makeMappingRuleCombo(ruleConfig RuleConfigButtonCombo,
|
||||||
pDevs map[string]Device,
|
pDevs map[string]Device,
|
||||||
vDevs map[string]Device,
|
vDevs map[string]Device,
|
||||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButtonCombo, error) {
|
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButtonCombo, error) {
|
||||||
|
@ -112,7 +119,7 @@ func makeMappingRuleCombo(ruleConfig RuleConfig,
|
||||||
return mappingrules.NewMappingRuleButtonCombo(base, inputs, output), nil
|
return mappingrules.NewMappingRuleButtonCombo(base, inputs, output), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMappingRuleLatched(ruleConfig RuleConfig,
|
func makeMappingRuleLatched(ruleConfig RuleConfigButtonLatched,
|
||||||
pDevs map[string]Device,
|
pDevs map[string]Device,
|
||||||
vDevs map[string]Device,
|
vDevs map[string]Device,
|
||||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButtonLatched, error) {
|
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButtonLatched, error) {
|
||||||
|
@ -130,7 +137,7 @@ func makeMappingRuleLatched(ruleConfig RuleConfig,
|
||||||
return mappingrules.NewMappingRuleButtonLatched(base, input, output), nil
|
return mappingrules.NewMappingRuleButtonLatched(base, input, output), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMappingRuleAxis(ruleConfig RuleConfig,
|
func makeMappingRuleAxis(ruleConfig RuleConfigAxis,
|
||||||
pDevs map[string]Device,
|
pDevs map[string]Device,
|
||||||
vDevs map[string]Device,
|
vDevs map[string]Device,
|
||||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxis, error) {
|
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxis, error) {
|
||||||
|
@ -148,7 +155,7 @@ func makeMappingRuleAxis(ruleConfig RuleConfig,
|
||||||
return mappingrules.NewMappingRuleAxis(base, input, output), nil
|
return mappingrules.NewMappingRuleAxis(base, input, output), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMappingRuleAxisCombined(ruleConfig RuleConfig,
|
func makeMappingRuleAxisCombined(ruleConfig RuleConfigAxisCombined,
|
||||||
pDevs map[string]Device,
|
pDevs map[string]Device,
|
||||||
vDevs map[string]Device,
|
vDevs map[string]Device,
|
||||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisCombined, error) {
|
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisCombined, error) {
|
||||||
|
@ -171,7 +178,7 @@ func makeMappingRuleAxisCombined(ruleConfig RuleConfig,
|
||||||
return mappingrules.NewMappingRuleAxisCombined(base, inputLower, inputUpper, output), nil
|
return mappingrules.NewMappingRuleAxisCombined(base, inputLower, inputUpper, output), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMappingRuleAxisToButton(ruleConfig RuleConfig,
|
func makeMappingRuleAxisToButton(ruleConfig RuleConfigAxisToButton,
|
||||||
pDevs map[string]Device,
|
pDevs map[string]Device,
|
||||||
vDevs map[string]Device,
|
vDevs map[string]Device,
|
||||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisToButton, error) {
|
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisToButton, error) {
|
||||||
|
@ -189,7 +196,7 @@ func makeMappingRuleAxisToButton(ruleConfig RuleConfig,
|
||||||
return mappingrules.NewMappingRuleAxisToButton(base, input, output, ruleConfig.RepeatRateMin, ruleConfig.RepeatRateMax), nil
|
return mappingrules.NewMappingRuleAxisToButton(base, input, output, ruleConfig.RepeatRateMin, ruleConfig.RepeatRateMax), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMappingRuleAxisToRelaxis(ruleConfig RuleConfig,
|
func makeMappingRuleAxisToRelaxis(ruleConfig RuleConfigAxisToRelaxis,
|
||||||
pDevs map[string]Device,
|
pDevs map[string]Device,
|
||||||
vDevs map[string]Device,
|
vDevs map[string]Device,
|
||||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisToRelaxis, error) {
|
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisToRelaxis, error) {
|
||||||
|
@ -211,7 +218,7 @@ func makeMappingRuleAxisToRelaxis(ruleConfig RuleConfig,
|
||||||
ruleConfig.Increment), nil
|
ruleConfig.Increment), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMappingRuleModeSelect(ruleConfig RuleConfig,
|
func makeMappingRuleModeSelect(ruleConfig RuleConfigModeSelect,
|
||||||
pDevs map[string]Device,
|
pDevs map[string]Device,
|
||||||
modes []string,
|
modes []string,
|
||||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleModeSelect, error) {
|
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleModeSelect, error) {
|
||||||
|
|
|
@ -1,79 +1,213 @@
|
||||||
// These types comprise the YAML schema for configuring Joyful.
|
// These types comprise the YAML schema for configuring Joyful.
|
||||||
// The config files will be combined and then unmarshalled into this
|
// The config files will be combined and then unmarshalled into this
|
||||||
//
|
|
||||||
// TODO: currently the types in here aren't especially strong; each one is
|
|
||||||
// decomposed into a different object based on the Type fields. We should implement
|
|
||||||
// some sort of delayed unmarshalling technique, for example see ideas at
|
|
||||||
// https://stackoverflow.com/questions/70635636/unmarshaling-yaml-into-different-struct-based-off-yaml-field
|
|
||||||
// Then we can be more explicit about the interface here.
|
|
||||||
|
|
||||||
package config
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Devices []DeviceConfig `yaml:"devices"`
|
Devices []DeviceConfig
|
||||||
Modes []string `yaml:"modes,omitempty"`
|
Modes []string
|
||||||
Rules []RuleConfig `yaml:"rules"`
|
Rules []RuleConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// These top-level structs use custom unmarshaling to unpack each available sub-type
|
||||||
type DeviceConfig struct {
|
type DeviceConfig struct {
|
||||||
Name string `yaml:"name"`
|
Type string
|
||||||
Type string `yaml:"type"`
|
Config interface{}
|
||||||
DeviceName string `yaml:"device_name,omitempty"`
|
|
||||||
DevicePath string `yaml:"device_path,omitempty"`
|
|
||||||
Preset string `yaml:"preset,omitempty"`
|
|
||||||
NumButtons int `yaml:"num_buttons,omitempty"`
|
|
||||||
NumAxes int `yaml:"num_axes,omitempty"`
|
|
||||||
NumRelativeAxes int `yaml:"num_rel_axes"`
|
|
||||||
Buttons []string `yaml:"buttons,omitempty"`
|
|
||||||
Axes []string `yaml:"axes,omitempty"`
|
|
||||||
RelativeAxes []string `yaml:"rel_axes,omitempty"`
|
|
||||||
Lock bool `yaml:"lock,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuleConfig struct {
|
type RuleConfig struct {
|
||||||
Name string `yaml:"name,omitempty"`
|
Type string
|
||||||
Type string `yaml:"type"`
|
Name string
|
||||||
Input RuleTargetConfig `yaml:"input,omitempty"`
|
Modes []string
|
||||||
InputLower RuleTargetConfig `yaml:"input_lower,omitempty"`
|
Config interface{}
|
||||||
InputUpper RuleTargetConfig `yaml:"input_upper,omitempty"`
|
|
||||||
Inputs []RuleTargetConfig `yaml:"inputs,omitempty"`
|
|
||||||
Output RuleTargetConfig `yaml:"output"`
|
|
||||||
Modes []string `yaml:"modes,omitempty"`
|
|
||||||
RepeatRateMin int `yaml:"repeat_rate_min,omitempty"`
|
|
||||||
RepeatRateMax int `yaml:"repeat_rate_max,omitempty"`
|
|
||||||
Increment int `yaml:"increment,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuleTargetConfig struct {
|
type DeviceConfigPhysical struct {
|
||||||
Device string `yaml:"device,omitempty"`
|
Name string
|
||||||
Button string `yaml:"button,omitempty"`
|
DeviceName string `yaml:"device_name,omitempty"`
|
||||||
Axis string `yaml:"axis,omitempty"`
|
DevicePath string `yaml:"device_path,omitempty"`
|
||||||
DeadzoneCenter int32 `yaml:"deadzone_center,omitempty"`
|
Lock bool
|
||||||
DeadzoneSize int32 `yaml:"deadzone_size,omitempty"`
|
}
|
||||||
DeadzoneSizePercent int32 `yaml:"deadzone_size_percent,omitempty"`
|
|
||||||
DeadzoneStart int32 `yaml:"deadzone_start,omitempty"`
|
// TODO: configure custom unmarshaling so we can overload Buttons, Axes, and RelativeAxes...
|
||||||
DeadzoneEnd int32 `yaml:"deadzone_end,omitempty"`
|
type DeviceConfigVirtual struct {
|
||||||
Inverted bool `yaml:"inverted,omitempty"`
|
Name string
|
||||||
Modes []string `yaml:"modes,omitempty"`
|
Preset string
|
||||||
|
NumButtons int `yaml:"num_buttons,omitempty"`
|
||||||
|
NumAxes int `yaml:"num_axes,omitempty"`
|
||||||
|
NumRelativeAxes int `yaml:"num_rel_axes"`
|
||||||
|
Buttons []string
|
||||||
|
Axes []string
|
||||||
|
RelativeAxes []string `yaml:"rel_axes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleConfigButton struct {
|
||||||
|
Input RuleTargetConfigButton
|
||||||
|
Output RuleTargetConfigButton
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleConfigButtonCombo struct {
|
||||||
|
Inputs []RuleTargetConfigButton
|
||||||
|
Output RuleTargetConfigButton
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleConfigButtonLatched struct {
|
||||||
|
Input RuleTargetConfigButton
|
||||||
|
Output RuleTargetConfigButton
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleConfigAxis struct {
|
||||||
|
Input RuleTargetConfigAxis
|
||||||
|
Output RuleTargetConfigAxis
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleConfigAxisCombined struct {
|
||||||
|
InputLower RuleTargetConfigAxis `yaml:"input_lower,omitempty"`
|
||||||
|
InputUpper RuleTargetConfigAxis `yaml:"input_upper,omitempty"`
|
||||||
|
Output RuleTargetConfigAxis
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleConfigAxisToButton struct {
|
||||||
|
RepeatRateMin int `yaml:"repeat_rate_min,omitempty"`
|
||||||
|
RepeatRateMax int `yaml:"repeat_rate_max,omitempty"`
|
||||||
|
Input RuleTargetConfigAxis
|
||||||
|
Output RuleTargetConfigButton
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleConfigAxisToRelaxis struct {
|
||||||
|
RepeatRateMin int `yaml:"repeat_rate_min"`
|
||||||
|
RepeatRateMax int `yaml:"repeat_rate_max"`
|
||||||
|
Increment int
|
||||||
|
Input RuleTargetConfigAxis
|
||||||
|
Output RuleTargetConfigRelaxis
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleConfigModeSelect struct {
|
||||||
|
Input RuleTargetConfigButton
|
||||||
|
Output RuleTargetConfigModeSelect
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleTargetConfigButton struct {
|
||||||
|
Device string
|
||||||
|
Button string
|
||||||
|
Inverted bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleTargetConfigAxis struct {
|
||||||
|
Device string
|
||||||
|
Axis string
|
||||||
|
DeadzoneCenter int32 `yaml:"deadzone_center,omitempty"`
|
||||||
|
DeadzoneSize int32 `yaml:"deadzone_size,omitempty"`
|
||||||
|
DeadzoneSizePercent int32 `yaml:"deadzone_size_percent,omitempty"`
|
||||||
|
DeadzoneStart int32 `yaml:"deadzone_start,omitempty"`
|
||||||
|
DeadzoneEnd int32 `yaml:"deadzone_end,omitempty"`
|
||||||
|
Inverted bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleTargetConfigRelaxis struct {
|
||||||
|
Device string
|
||||||
|
Axis string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleTargetConfigModeSelect struct {
|
||||||
|
Modes []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *DeviceConfig) UnmarshalYAML(unmarshal func(data interface{}) error) error {
|
||||||
|
metaConfig := &struct {
|
||||||
|
Type string
|
||||||
|
}{}
|
||||||
|
err := unmarshal(metaConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dc.Type = metaConfig.Type
|
||||||
|
|
||||||
|
err = nil
|
||||||
|
switch metaConfig.Type {
|
||||||
|
case DeviceTypePhysical:
|
||||||
|
config := DeviceConfigPhysical{}
|
||||||
|
err = unmarshal(&config)
|
||||||
|
dc.Config = config
|
||||||
|
case DeviceTypeVirtual:
|
||||||
|
config := DeviceConfigVirtual{}
|
||||||
|
err = unmarshal(&config)
|
||||||
|
dc.Config = config
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("invalid device type '%s'", dc.Type)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dc *RuleConfig) UnmarshalYAML(unmarshal func(data interface{}) error) error {
|
||||||
|
metaConfig := &struct {
|
||||||
|
Type string
|
||||||
|
Name string
|
||||||
|
Modes []string
|
||||||
|
}{}
|
||||||
|
err := unmarshal(metaConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dc.Type = metaConfig.Type
|
||||||
|
dc.Name = metaConfig.Name
|
||||||
|
dc.Modes = metaConfig.Modes
|
||||||
|
|
||||||
|
switch dc.Type {
|
||||||
|
case RuleTypeButton:
|
||||||
|
config := RuleConfigButton{}
|
||||||
|
err = unmarshal(&config)
|
||||||
|
dc.Config = config
|
||||||
|
case RuleTypeButtonCombo:
|
||||||
|
config := RuleConfigButtonCombo{}
|
||||||
|
err = unmarshal(&config)
|
||||||
|
dc.Config = config
|
||||||
|
case RuleTypeButtonLatched:
|
||||||
|
config := RuleConfigButtonLatched{}
|
||||||
|
err = unmarshal(&config)
|
||||||
|
dc.Config = config
|
||||||
|
case RuleTypeAxis:
|
||||||
|
config := RuleConfigAxis{}
|
||||||
|
err = unmarshal(&config)
|
||||||
|
dc.Config = config
|
||||||
|
case RuleTypeAxisCombined:
|
||||||
|
config := RuleConfigAxisCombined{}
|
||||||
|
err = unmarshal(&config)
|
||||||
|
dc.Config = config
|
||||||
|
case RuleTypeAxisToButton:
|
||||||
|
config := RuleConfigAxisToButton{}
|
||||||
|
err = unmarshal(&config)
|
||||||
|
dc.Config = config
|
||||||
|
case RuleTypeAxisToRelaxis:
|
||||||
|
config := RuleConfigAxisToRelaxis{}
|
||||||
|
err = unmarshal(&config)
|
||||||
|
dc.Config = config
|
||||||
|
case RuleTypeModeSelect:
|
||||||
|
config := RuleConfigModeSelect{}
|
||||||
|
err = unmarshal(&config)
|
||||||
|
dc.Config = config
|
||||||
|
default:
|
||||||
|
err = fmt.Errorf("invalid rule type '%s'", dc.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: custom yaml unmarshaling is obtuse; do we really need to do all of this work
|
// TODO: custom yaml unmarshaling is obtuse; do we really need to do all of this work
|
||||||
// just to set a single default value?
|
// just to set a single default value?
|
||||||
func (dc *DeviceConfig) UnmarshalYAML(unmarshal func(data interface{}) error) error {
|
func (dc *DeviceConfigPhysical) UnmarshalYAML(unmarshal func(data interface{}) error) error {
|
||||||
var raw struct {
|
var raw struct {
|
||||||
Name string
|
Name string
|
||||||
Type string
|
DeviceName string `yaml:"device_name"`
|
||||||
DeviceName string `yaml:"device_name"`
|
DevicePath string `yaml:"device_path"`
|
||||||
DevicePath string `yaml:"device_path"`
|
Lock bool `yaml:"lock,omitempty"`
|
||||||
Preset string
|
|
||||||
NumButtons int `yaml:"num_buttons"`
|
|
||||||
NumAxes int `yaml:"num_axes"`
|
|
||||||
NumRelativeAxes int `yaml:"num_rel_axes"`
|
|
||||||
Buttons []string
|
|
||||||
Axes []string
|
|
||||||
RelativeAxes []string `yaml:"relative_axes"`
|
|
||||||
Lock bool `yaml:"lock,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set non-standard defaults
|
||||||
raw.Lock = true
|
raw.Lock = true
|
||||||
|
|
||||||
err := unmarshal(&raw)
|
err := unmarshal(&raw)
|
||||||
|
@ -81,19 +215,11 @@ func (dc *DeviceConfig) UnmarshalYAML(unmarshal func(data interface{}) error) er
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
*dc = DeviceConfig{
|
*dc = DeviceConfigPhysical{
|
||||||
Name: raw.Name,
|
Name: raw.Name,
|
||||||
Type: raw.Type,
|
DeviceName: raw.DeviceName,
|
||||||
DeviceName: raw.DeviceName,
|
DevicePath: raw.DevicePath,
|
||||||
DevicePath: raw.DevicePath,
|
Lock: raw.Lock,
|
||||||
Preset: raw.Preset,
|
|
||||||
NumButtons: raw.NumButtons,
|
|
||||||
NumAxes: raw.NumAxes,
|
|
||||||
NumRelativeAxes: raw.NumRelativeAxes,
|
|
||||||
Buttons: raw.Buttons,
|
|
||||||
Axes: raw.Axes,
|
|
||||||
RelativeAxes: raw.RelativeAxes,
|
|
||||||
Lock: raw.Lock,
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,12 +15,12 @@ const (
|
||||||
|
|
||||||
RuleTypeButton = "button"
|
RuleTypeButton = "button"
|
||||||
RuleTypeButtonCombo = "button-combo"
|
RuleTypeButtonCombo = "button-combo"
|
||||||
RuleTypeLatched = "button-latched"
|
RuleTypeButtonLatched = "button-latched"
|
||||||
RuleTypeAxis = "axis"
|
RuleTypeAxis = "axis"
|
||||||
RuleTypeAxisCombined = "axis-combined"
|
RuleTypeAxisCombined = "axis-combined"
|
||||||
RuleTypeModeSelect = "mode-select"
|
|
||||||
RuleTypeAxisToButton = "axis-to-button"
|
RuleTypeAxisToButton = "axis-to-button"
|
||||||
RuleTypeAxisToRelaxis = "axis-to-relaxis"
|
RuleTypeAxisToRelaxis = "axis-to-relaxis"
|
||||||
|
RuleTypeModeSelect = "mode-select"
|
||||||
|
|
||||||
CodePrefixButton = "BTN"
|
CodePrefixButton = "BTN"
|
||||||
CodePrefixKey = "KEY"
|
CodePrefixKey = "KEY"
|
||||||
|
|
|
@ -8,19 +8,16 @@ type RuleTargetRelaxis struct {
|
||||||
DeviceName string
|
DeviceName string
|
||||||
Device Device
|
Device Device
|
||||||
Axis evdev.EvCode
|
Axis evdev.EvCode
|
||||||
Inverted bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRuleTargetRelaxis(device_name string,
|
func NewRuleTargetRelaxis(device_name string,
|
||||||
device Device,
|
device Device,
|
||||||
axis evdev.EvCode,
|
axis evdev.EvCode) (*RuleTargetRelaxis, error) {
|
||||||
inverted bool) (*RuleTargetRelaxis, error) {
|
|
||||||
|
|
||||||
return &RuleTargetRelaxis{
|
return &RuleTargetRelaxis{
|
||||||
DeviceName: device_name,
|
DeviceName: device_name,
|
||||||
Device: device,
|
Device: device,
|
||||||
Axis: axis,
|
Axis: axis,
|
||||||
Inverted: inverted,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue