Small fixes for config parsing, and start plumbing it into our main program.

This commit is contained in:
Anna Rose Wiggins 2025-06-28 22:30:59 -04:00
parent 02c8642ead
commit eeebb6d0b1
5 changed files with 144 additions and 124 deletions

View file

@ -4,61 +4,34 @@ import (
"fmt" "fmt"
"time" "time"
"git.annabunches.net/annabunches/joyful/internal/config"
"git.annabunches.net/annabunches/joyful/internal/logger" "git.annabunches.net/annabunches/joyful/internal/logger"
"git.annabunches.net/annabunches/joyful/internal/virtualdevice" "git.annabunches.net/annabunches/joyful/internal/virtualdevice"
"github.com/holoplot/go-evdev" "github.com/holoplot/go-evdev"
) )
func main() { func main() {
// STUB: parse virtual device config // parse configs
parser := &config.ConfigParser{}
parser.Parse("~/.config/joyful") // TODO: make ~ work here
// STUB: parse mapping config vDevices := parser.CreateVirtualDevices()
vBuffers := make(map[string]*virtualdevice.EventBuffer)
// Define virtual device for name, device := range vDevices {
// TODO: create virtual devices from config vBuffers[name] = virtualdevice.NewEventBuffer(device)
vDevice, err := evdev.CreateDevice(
"joyful-joystick",
evdev.InputID{
BusType: 0x03,
Vendor: 0x4711,
Product: 0x0816,
Version: 1,
},
map[evdev.EvType][]evdev.EvCode{
evdev.EV_KEY: jsButtons(),
evdev.EV_ABS: {
evdev.ABS_X,
evdev.ABS_Y,
evdev.ABS_Z,
evdev.ABS_RX,
evdev.ABS_RY,
evdev.ABS_RZ,
evdev.ABS_THROTTLE,
evdev.ABS_WHEEL,
},
},
)
logger.FatalIfError(err, "Failed to create virtual device")
buffer := virtualdevice.NewEventBuffer(vDevice)
name, err := vDevice.Name()
if err != nil {
name = "Unknown"
} }
fmt.Printf("Virtual device created as %s.\n", name)
pDevice, err := evdev.Open("/dev/input/event12") pDevice, err := evdev.Open("/dev/input/event12")
logger.FatalIfError(err, "Couldn't open physical device") logger.FatalIfError(err, "Couldn't open physical device")
name, err = pDevice.Name() name, err := pDevice.Name()
if err != nil { if err != nil {
name = "Unknown" name = "Unknown"
} }
fmt.Printf("Connected to physical device %s\n", name) fmt.Printf("Connected to physical device %s\n", name)
var combo int32 = 0 var combo int32 = 0
buffer := vBuffers["test"]
for { for {
last := combo last := combo
@ -126,21 +99,4 @@ func main() {
time.Sleep(1 * time.Millisecond) time.Sleep(1 * time.Millisecond)
} }
} }
func jsButtons() []evdev.EvCode {
buttons := make([]evdev.EvCode, 80)
i := 0
for code := 0x120; code <= 0x12f; code++ {
buttons[i] = evdev.EvCode(code)
i++
}
for code := 0x2c0; code <= 0x2ff; code++ {
buttons[i] = evdev.EvCode(code)
i++
}
return buttons
}

View file

@ -8,6 +8,7 @@ import (
"git.annabunches.net/annabunches/joyful/internal/logger" "git.annabunches.net/annabunches/joyful/internal/logger"
"github.com/goccy/go-yaml" "github.com/goccy/go-yaml"
"github.com/holoplot/go-evdev"
) )
type ConfigParser struct { type ConfigParser struct {
@ -19,7 +20,10 @@ type ConfigParser struct {
func (parser *ConfigParser) Parse(directory string) { func (parser *ConfigParser) Parse(directory string) {
// Find the config files in the directory // Find the config files in the directory
dirEntries, err := os.ReadDir(directory) dirEntries, err := os.ReadDir(directory)
logger.FatalIfError(err, "Failed to read config directory") if err != nil {
err = os.Mkdir(directory, 0755)
logger.FatalIfError(err, "Failed to create config directory at "+directory)
}
for _, file := range dirEntries { for _, file := range dirEntries {
name := file.Name() name := file.Name()
@ -54,9 +58,76 @@ func (parser *ConfigParser) parseConfigFiles() []byte {
} }
if len(rawData) == 0 { if len(rawData) == 0 {
logger.Log("No config data found") logger.Log("No config data found. Write .yml config files in ~/.config/joyful")
return nil return nil
} }
return rawData return rawData
} }
func (parser *ConfigParser) CreateVirtualDevices() map[string]*evdev.InputDevice {
deviceMap := make(map[string]*evdev.InputDevice)
for _, deviceConfig := range parser.config.Devices.Virtual {
vDevice, err := evdev.CreateDevice(
fmt.Sprintf("joyful-%s", deviceConfig.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] = vDevice
}
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
}

View file

@ -5,66 +5,58 @@ import (
) )
var ( var (
EvCodeMap = map[string]evdev.EvCode{ ButtonFromIndex = []evdev.EvCode{
"ABS_X": evdev.ABS_X, evdev.BTN_TRIGGER,
"ABS_Y": evdev.ABS_Y, evdev.BTN_THUMB,
"ABS_Z": evdev.ABS_Z, evdev.BTN_THUMB2,
"ABS_RX": evdev.ABS_RX, evdev.BTN_TOP,
"ABS_RY": evdev.ABS_RY, evdev.BTN_TOP2,
"ABS_RZ": evdev.ABS_RZ, evdev.BTN_PINKIE,
"ABS_THROTTLE": evdev.ABS_THROTTLE, evdev.BTN_BASE,
"ABS_WHEEL": evdev.ABS_WHEEL, evdev.BTN_BASE2,
"BTN_TRIGGER": evdev.BTN_TRIGGER, evdev.BTN_BASE3,
"BTN_THUMB": evdev.BTN_THUMB, evdev.BTN_BASE4,
"BTN_THUMB2": evdev.BTN_THUMB2, evdev.BTN_BASE5,
"BTN_TOP": evdev.BTN_TOP, evdev.BTN_BASE6,
"BTN_TOP2": evdev.BTN_TOP2, evdev.BTN_TRIGGER_HAPPY1,
"BTN_PINKIE": evdev.BTN_PINKIE, evdev.BTN_TRIGGER_HAPPY2,
"BTN_BASE": evdev.BTN_BASE, evdev.BTN_TRIGGER_HAPPY3,
"BTN_BASE2": evdev.BTN_BASE2, evdev.BTN_TRIGGER_HAPPY4,
"BTN_BASE3": evdev.BTN_BASE3, evdev.BTN_TRIGGER_HAPPY5,
"BTN_BASE4": evdev.BTN_BASE4, evdev.BTN_TRIGGER_HAPPY6,
"BTN_BASE5": evdev.BTN_BASE5, evdev.BTN_TRIGGER_HAPPY7,
"BTN_BASE6": evdev.BTN_BASE6, evdev.BTN_TRIGGER_HAPPY8,
"BTN_TRIGGER_HAPPY1": evdev.BTN_TRIGGER_HAPPY1, evdev.BTN_TRIGGER_HAPPY9,
"BTN_TRIGGER_HAPPY2": evdev.BTN_TRIGGER_HAPPY2, evdev.BTN_TRIGGER_HAPPY10,
"BTN_TRIGGER_HAPPY3": evdev.BTN_TRIGGER_HAPPY3, evdev.BTN_TRIGGER_HAPPY11,
"BTN_TRIGGER_HAPPY4": evdev.BTN_TRIGGER_HAPPY4, evdev.BTN_TRIGGER_HAPPY12,
"BTN_TRIGGER_HAPPY5": evdev.BTN_TRIGGER_HAPPY5, evdev.BTN_TRIGGER_HAPPY13,
"BTN_TRIGGER_HAPPY6": evdev.BTN_TRIGGER_HAPPY6, evdev.BTN_TRIGGER_HAPPY14,
"BTN_TRIGGER_HAPPY7": evdev.BTN_TRIGGER_HAPPY7, evdev.BTN_TRIGGER_HAPPY15,
"BTN_TRIGGER_HAPPY8": evdev.BTN_TRIGGER_HAPPY8, evdev.BTN_TRIGGER_HAPPY16,
"BTN_TRIGGER_HAPPY9": evdev.BTN_TRIGGER_HAPPY9, evdev.BTN_TRIGGER_HAPPY17,
"BTN_TRIGGER_HAPPY10": evdev.BTN_TRIGGER_HAPPY10, evdev.BTN_TRIGGER_HAPPY18,
"BTN_TRIGGER_HAPPY11": evdev.BTN_TRIGGER_HAPPY11, evdev.BTN_TRIGGER_HAPPY19,
"BTN_TRIGGER_HAPPY12": evdev.BTN_TRIGGER_HAPPY12, evdev.BTN_TRIGGER_HAPPY20,
"BTN_TRIGGER_HAPPY13": evdev.BTN_TRIGGER_HAPPY13, evdev.BTN_TRIGGER_HAPPY21,
"BTN_TRIGGER_HAPPY14": evdev.BTN_TRIGGER_HAPPY14, evdev.BTN_TRIGGER_HAPPY22,
"BTN_TRIGGER_HAPPY15": evdev.BTN_TRIGGER_HAPPY15, evdev.BTN_TRIGGER_HAPPY23,
"BTN_TRIGGER_HAPPY16": evdev.BTN_TRIGGER_HAPPY16, evdev.BTN_TRIGGER_HAPPY24,
"BTN_TRIGGER_HAPPY17": evdev.BTN_TRIGGER_HAPPY17, evdev.BTN_TRIGGER_HAPPY25,
"BTN_TRIGGER_HAPPY18": evdev.BTN_TRIGGER_HAPPY18, evdev.BTN_TRIGGER_HAPPY26,
"BTN_TRIGGER_HAPPY19": evdev.BTN_TRIGGER_HAPPY19, evdev.BTN_TRIGGER_HAPPY27,
"BTN_TRIGGER_HAPPY20": evdev.BTN_TRIGGER_HAPPY20, evdev.BTN_TRIGGER_HAPPY28,
"BTN_TRIGGER_HAPPY21": evdev.BTN_TRIGGER_HAPPY21, evdev.BTN_TRIGGER_HAPPY29,
"BTN_TRIGGER_HAPPY22": evdev.BTN_TRIGGER_HAPPY22, evdev.BTN_TRIGGER_HAPPY30,
"BTN_TRIGGER_HAPPY23": evdev.BTN_TRIGGER_HAPPY23, evdev.BTN_TRIGGER_HAPPY31,
"BTN_TRIGGER_HAPPY24": evdev.BTN_TRIGGER_HAPPY24, evdev.BTN_TRIGGER_HAPPY32,
"BTN_TRIGGER_HAPPY25": evdev.BTN_TRIGGER_HAPPY25, evdev.BTN_TRIGGER_HAPPY33,
"BTN_TRIGGER_HAPPY26": evdev.BTN_TRIGGER_HAPPY26, evdev.BTN_TRIGGER_HAPPY34,
"BTN_TRIGGER_HAPPY27": evdev.BTN_TRIGGER_HAPPY27, evdev.BTN_TRIGGER_HAPPY35,
"BTN_TRIGGER_HAPPY28": evdev.BTN_TRIGGER_HAPPY28, evdev.BTN_TRIGGER_HAPPY36,
"BTN_TRIGGER_HAPPY29": evdev.BTN_TRIGGER_HAPPY29, evdev.BTN_TRIGGER_HAPPY37,
"BTN_TRIGGER_HAPPY30": evdev.BTN_TRIGGER_HAPPY30, evdev.BTN_TRIGGER_HAPPY38,
"BTN_TRIGGER_HAPPY31": evdev.BTN_TRIGGER_HAPPY31, evdev.BTN_TRIGGER_HAPPY39,
"BTN_TRIGGER_HAPPY32": evdev.BTN_TRIGGER_HAPPY32, evdev.BTN_TRIGGER_HAPPY40,
"BTN_TRIGGER_HAPPY33": evdev.BTN_TRIGGER_HAPPY33,
"BTN_TRIGGER_HAPPY34": evdev.BTN_TRIGGER_HAPPY34,
"BTN_TRIGGER_HAPPY35": evdev.BTN_TRIGGER_HAPPY35,
"BTN_TRIGGER_HAPPY36": evdev.BTN_TRIGGER_HAPPY36,
"BTN_TRIGGER_HAPPY37": evdev.BTN_TRIGGER_HAPPY37,
"BTN_TRIGGER_HAPPY38": evdev.BTN_TRIGGER_HAPPY38,
"BTN_TRIGGER_HAPPY39": evdev.BTN_TRIGGER_HAPPY39,
"BTN_TRIGGER_HAPPY40": evdev.BTN_TRIGGER_HAPPY40,
} }
) )

View file

@ -16,8 +16,9 @@ type PhysicalDeviceConfig struct {
} }
type VirtualDeviceConfig struct { type VirtualDeviceConfig struct {
NumButtons int `yaml:"num_buttons"` Name string `yaml:"name"`
NumAxes int `yaml:"num_axes"` Buttons int `yaml:"buttons"`
Axes int `yaml:"axes"`
} }
type RuleConfig struct { type RuleConfig struct {

View file

@ -10,13 +10,13 @@ import (
type EventBuffer struct { type EventBuffer struct {
events []*evdev.InputEvent events []*evdev.InputEvent
device *evdev.InputDevice Device *evdev.InputDevice
} }
func NewEventBuffer(device *evdev.InputDevice) *EventBuffer { func NewEventBuffer(device *evdev.InputDevice) *EventBuffer {
return &EventBuffer{ return &EventBuffer{
events: make([]*evdev.InputEvent, 0), events: make([]*evdev.InputEvent, 0),
device: device, Device: device,
} }
} }
@ -29,10 +29,10 @@ func (buffer *EventBuffer) SendEvents() {
for i := 0; i < len(buffer.events); i++ { for i := 0; i < len(buffer.events); i++ {
buffer.events[i].Time = eventTime buffer.events[i].Time = eventTime
buffer.device.WriteOne(buffer.events[i]) buffer.Device.WriteOne(buffer.events[i])
} }
buffer.device.WriteOne(&evdev.InputEvent{ buffer.Device.WriteOne(&evdev.InputEvent{
Time: eventTime, Time: eventTime,
Type: evdev.EV_SYN, Type: evdev.EV_SYN,
Code: evdev.SYN_REPORT, Code: evdev.SYN_REPORT,