joyful/internal/config/configparser.go

169 lines
4.6 KiB
Go

package config
import (
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"git.annabunches.net/annabunches/joyful/internal/logger"
"github.com/goccy/go-yaml"
"github.com/holoplot/go-evdev"
)
type ConfigParser struct {
config Config
}
// Parse all the config files and store the config data for further use
func (parser *ConfigParser) Parse(directory string) error {
parser.config = Config{}
// Find the config files in the directory
dirEntries, err := os.ReadDir(directory)
if err != nil {
err = os.Mkdir(directory, 0755)
if err != nil {
return errors.New("Failed to create config directory at " + directory)
}
}
// Open each yaml file and add its contents to the global config
for _, file := range dirEntries {
name := file.Name()
if file.IsDir() || !(strings.HasSuffix(name, ".yaml") || strings.HasSuffix(name, ".yml")) {
continue
}
filePath := filepath.Join(directory, name)
if strings.HasSuffix(filePath, ".yaml") || strings.HasSuffix(filePath, ".yml") {
data, err := os.ReadFile(filePath)
if err != nil {
logger.LogError(err, "Error while opening config file")
continue
}
newConfig := Config{}
err = yaml.Unmarshal(data, &newConfig)
logger.LogIfError(err, "Error parsing YAML")
parser.config.Rules = append(parser.config.Rules, newConfig.Rules...)
parser.config.Devices = append(parser.config.Devices, newConfig.Devices...)
// parser.config.Groups = append(parser.config.Groups, newConfig.Groups...)
}
}
if len(parser.config.Devices) == 0 {
return errors.New("Found no devices in configuration. Please add configuration at " + directory)
}
return nil
}
// CreateVirtualDevices will register any configured devices with type = virtual
// using /dev/uinput, and return a map of those devices.
//
// This function assumes you have already called Parse() on the config directory.
//
// This function should only be called once, unless you want to create duplicate devices for some reason.
func (parser *ConfigParser) CreateVirtualDevices() map[string]*evdev.InputDevice {
deviceMap := make(map[string]*evdev.InputDevice)
for _, deviceConfig := range parser.config.Devices {
if strings.ToLower(deviceConfig.Type) != DeviceTypeVirtual {
continue
}
name := fmt.Sprintf("joyful-%s", deviceConfig.Name)
device, err := evdev.CreateDevice(
name,
// TODO: who knows what these should actually be
evdev.InputID{
BusType: 0x03,
Vendor: 0x4711,
Product: 0x0816,
Version: 1,
},
map[evdev.EvType][]evdev.EvCode{
evdev.EV_KEY: makeButtons(int(deviceConfig.Buttons)),
evdev.EV_ABS: makeAxes(int(deviceConfig.Axes)),
},
)
if err != nil {
logger.LogIfError(err, "Failed to create virtual device")
continue
}
deviceMap[deviceConfig.Name] = device
logger.Log(fmt.Sprintf("Created virtual device '%s'", name))
}
return deviceMap
}
// ConnectPhysicalDevices will create InputDevices corresponding to any registered
// devices with type = physical. It will also attempt to acquire exclusive access
// to those devices, to prevent the same inputs from being read on multiple devices.
//
// This function assumes you have already called Parse() on the config directory.
//
// This function should only be called once.
func (parser *ConfigParser) ConnectPhysicalDevices() map[string]*evdev.InputDevice {
deviceMap := make(map[string]*evdev.InputDevice)
for _, deviceConfig := range parser.config.Devices {
if strings.ToLower(deviceConfig.Type) != DeviceTypePhysical {
continue
}
device, err := evdev.OpenByName(deviceConfig.DeviceName)
if err != nil {
logger.LogError(err, "Failed to open physical device, skipping. Confirm the device name with 'evlist'. Watch out for spaces.")
continue
}
// TODO: grab exclusive access to device
logger.Log(fmt.Sprintf("Connected to '%s' as '%s'", deviceConfig.DeviceName, deviceConfig.Name))
deviceMap[deviceConfig.Name] = device
}
return deviceMap
}
func makeButtons(numButtons int) []evdev.EvCode {
if numButtons > 56 {
numButtons = 56
logger.Log("Limiting virtual device buttons to 56")
}
buttons := make([]evdev.EvCode, numButtons)
startCode := 0x120
for i := 0; i < numButtons && i < 16; i++ {
buttons[i] = evdev.EvCode(startCode + i)
}
if numButtons > 16 {
startCode = 0x2c0
for i := 0; i < numButtons-16; i++ {
buttons[16+i] = evdev.EvCode(startCode + i)
}
}
return buttons
}
func makeAxes(numAxes int) []evdev.EvCode {
if numAxes > 8 {
numAxes = 8
logger.Log("Limiting virtual device axes to 8")
}
axes := make([]evdev.EvCode, numAxes)
for i := 0; i < numAxes; i++ {
axes[i] = evdev.EvCode(i)
}
return axes
}