* Move all physical device initialization logic to main functions
* Move all virtual device initialization to virtualbuffer package. * Factor out common eventcode helper logic into a new package.
This commit is contained in:
parent
1b374bccc6
commit
727985f91c
17 changed files with 777 additions and 771 deletions
|
@ -5,7 +5,8 @@ import (
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
// TODO: using config here feels like bad coupling... ButtonFromIndex might need a refactor / move
|
// TODO: using config here feels like bad coupling... ButtonFromIndex might need a refactor / move
|
||||||
"git.annabunches.net/annabunches/joyful/internal/configparser"
|
|
||||||
|
"git.annabunches.net/annabunches/joyful/internal/eventcodes"
|
||||||
"git.annabunches.net/annabunches/joyful/internal/logger"
|
"git.annabunches.net/annabunches/joyful/internal/logger"
|
||||||
"github.com/holoplot/go-evdev"
|
"github.com/holoplot/go-evdev"
|
||||||
flag "github.com/spf13/pflag"
|
flag "github.com/spf13/pflag"
|
||||||
|
@ -20,7 +21,7 @@ func isJoystickLike(device *evdev.InputDevice) bool {
|
||||||
if slices.Contains(types, evdev.EV_KEY) {
|
if slices.Contains(types, evdev.EV_KEY) {
|
||||||
buttons := device.CapableEvents(evdev.EV_KEY)
|
buttons := device.CapableEvents(evdev.EV_KEY)
|
||||||
|
|
||||||
for _, code := range configparser.ButtonFromIndex {
|
for _, code := range eventcodes.ButtonFromIndex {
|
||||||
if slices.Contains(buttons, code) {
|
if slices.Contains(buttons, code) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
129
cmd/joyful/config.go
Normal file
129
cmd/joyful/config.go
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"git.annabunches.net/annabunches/joyful/internal/configparser"
|
||||||
|
"git.annabunches.net/annabunches/joyful/internal/logger"
|
||||||
|
"git.annabunches.net/annabunches/joyful/internal/mappingrules"
|
||||||
|
"git.annabunches.net/annabunches/joyful/internal/virtualdevice"
|
||||||
|
"github.com/holoplot/go-evdev"
|
||||||
|
)
|
||||||
|
|
||||||
|
func initPhysicalDevices(conf *configparser.Config) map[string]*evdev.InputDevice {
|
||||||
|
pDeviceMap := make(map[string]*evdev.InputDevice)
|
||||||
|
|
||||||
|
for _, devConfig := range conf.Devices {
|
||||||
|
if strings.ToLower(devConfig.Type) != configparser.DeviceTypePhysical {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
innerConfig := devConfig.Config.(configparser.DeviceConfigPhysical)
|
||||||
|
name, device, err := initPhysicalDevice(innerConfig)
|
||||||
|
if err != nil {
|
||||||
|
logger.LogError(err, "Failed to initialize physical device")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pDeviceMap[name] = device
|
||||||
|
|
||||||
|
displayName := innerConfig.DeviceName
|
||||||
|
if innerConfig.DevicePath != "" {
|
||||||
|
displayName = innerConfig.DevicePath
|
||||||
|
}
|
||||||
|
logger.Logf("Connected to '%s' as '%s'", displayName, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(pDeviceMap) == 0 {
|
||||||
|
logger.Log("Warning: no physical devices found in configuration. No rules will work.")
|
||||||
|
}
|
||||||
|
return pDeviceMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func initPhysicalDevice(config configparser.DeviceConfigPhysical) (string, *evdev.InputDevice, error) {
|
||||||
|
name := config.Name
|
||||||
|
var device *evdev.InputDevice
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if config.DevicePath != "" {
|
||||||
|
device, err = evdev.Open(config.DevicePath)
|
||||||
|
} else {
|
||||||
|
device, err = evdev.OpenByName(config.DeviceName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.Lock && err == nil {
|
||||||
|
grabErr := device.Grab()
|
||||||
|
logger.LogIfError(grabErr, "Failed to lock device for exclusive access")
|
||||||
|
}
|
||||||
|
|
||||||
|
return name, device, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: juggling all these maps is a pain. Is there a better solution here?
|
||||||
|
func initVirtualBuffers(config *configparser.Config) (map[string]*evdev.InputDevice,
|
||||||
|
map[string]*virtualdevice.EventBuffer,
|
||||||
|
map[*evdev.InputDevice]*virtualdevice.EventBuffer) {
|
||||||
|
|
||||||
|
vDevicesByName := make(map[string]*evdev.InputDevice)
|
||||||
|
vBuffersByName := make(map[string]*virtualdevice.EventBuffer)
|
||||||
|
vBuffersByDevice := make(map[*evdev.InputDevice]*virtualdevice.EventBuffer)
|
||||||
|
|
||||||
|
for _, devConfig := range config.Devices {
|
||||||
|
if strings.ToLower(devConfig.Type) != configparser.DeviceTypeVirtual {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
vConfig := devConfig.Config.(configparser.DeviceConfigVirtual)
|
||||||
|
buffer, err := virtualdevice.NewEventBuffer(vConfig)
|
||||||
|
if err != nil {
|
||||||
|
logger.LogError(err, "Failed to create virtual device, skipping")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
vDevicesByName[buffer.Name] = buffer.Device.(*evdev.InputDevice)
|
||||||
|
vBuffersByName[buffer.Name] = buffer
|
||||||
|
vBuffersByDevice[buffer.Device.(*evdev.InputDevice)] = buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(vDevicesByName) == 0 {
|
||||||
|
logger.Log("Warning: no virtual devices found in configuration. No rules will work.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return vDevicesByName, vBuffersByName, vBuffersByDevice
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadRules(
|
||||||
|
config *configparser.Config,
|
||||||
|
pDevices map[string]*evdev.InputDevice,
|
||||||
|
vDevices map[string]*evdev.InputDevice,
|
||||||
|
modes []string) ([]mappingrules.MappingRule, <-chan ChannelEvent, func(), *sync.WaitGroup) {
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
eventChannel := make(chan ChannelEvent, 1000)
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
// Initialize rules
|
||||||
|
rules := configparser.InitRules(config.Rules, pDevices, vDevices, modes)
|
||||||
|
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, ctx, &wg)
|
||||||
|
}
|
||||||
|
|
||||||
|
timerCount := 0
|
||||||
|
for _, rule := range rules {
|
||||||
|
if timedRule, ok := rule.(mappingrules.TimedEventEmitter); ok {
|
||||||
|
wg.Add(1)
|
||||||
|
go timerWatcher(timedRule, eventChannel, ctx, &wg)
|
||||||
|
timerCount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.Logf("Registered %d timers.", timerCount)
|
||||||
|
|
||||||
|
go consoleWatcher(eventChannel)
|
||||||
|
|
||||||
|
return rules, eventChannel, cancel, &wg
|
||||||
|
}
|
|
@ -1,47 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"git.annabunches.net/annabunches/joyful/internal/configparser"
|
|
||||||
"git.annabunches.net/annabunches/joyful/internal/logger"
|
|
||||||
"github.com/holoplot/go-evdev"
|
|
||||||
)
|
|
||||||
|
|
||||||
func initPhysicalDevices(conf *configparser.Config) map[string]*evdev.InputDevice {
|
|
||||||
pDeviceMap := make(map[string]*evdev.InputDevice)
|
|
||||||
|
|
||||||
for _, devConfig := range conf.Devices {
|
|
||||||
if devConfig.Type != configparser.DeviceTypePhysical {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
name, device, err := initPhysicalDevice(devConfig.Config.(configparser.DeviceConfigPhysical))
|
|
||||||
if err != nil {
|
|
||||||
logger.LogError(err, "Failed to initialize device")
|
|
||||||
}
|
|
||||||
pDeviceMap[name] = device
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(pDeviceMap) == 0 {
|
|
||||||
logger.Log("Warning: no physical devices found in configuration. No rules will work.")
|
|
||||||
}
|
|
||||||
return pDeviceMap
|
|
||||||
}
|
|
||||||
|
|
||||||
func initPhysicalDevice(config configparser.DeviceConfigPhysical) (string, *evdev.InputDevice, error) {
|
|
||||||
name := config.Name
|
|
||||||
var device *evdev.InputDevice
|
|
||||||
var err error
|
|
||||||
|
|
||||||
if config.DevicePath != "" {
|
|
||||||
device, err = evdev.Open(config.DevicePath)
|
|
||||||
} else {
|
|
||||||
device, err = evdev.OpenByName(config.DeviceName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if config.Lock && err == nil {
|
|
||||||
grabErr := device.Grab()
|
|
||||||
logger.LogIfError(grabErr, "Failed to lock device for exclusive access")
|
|
||||||
}
|
|
||||||
|
|
||||||
return name, device, err
|
|
||||||
}
|
|
|
@ -1,19 +1,15 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/holoplot/go-evdev"
|
"github.com/holoplot/go-evdev"
|
||||||
flag "github.com/spf13/pflag"
|
flag "github.com/spf13/pflag"
|
||||||
|
|
||||||
"git.annabunches.net/annabunches/joyful/internal/configparser"
|
"git.annabunches.net/annabunches/joyful/internal/configparser"
|
||||||
"git.annabunches.net/annabunches/joyful/internal/logger"
|
"git.annabunches.net/annabunches/joyful/internal/logger"
|
||||||
"git.annabunches.net/annabunches/joyful/internal/mappingrules"
|
|
||||||
"git.annabunches.net/annabunches/joyful/internal/virtualdevice"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getConfigDir(dir string) string {
|
func getConfigDir(dir string) string {
|
||||||
|
@ -21,24 +17,6 @@ func getConfigDir(dir string) string {
|
||||||
return os.ExpandEnv(configDir)
|
return os.ExpandEnv(configDir)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initVirtualBuffers(config *configparser.ConfigParser) (map[string]*evdev.InputDevice,
|
|
||||||
map[string]*virtualdevice.EventBuffer,
|
|
||||||
map[*evdev.InputDevice]*virtualdevice.EventBuffer) {
|
|
||||||
|
|
||||||
vDevices := config.InitVirtualDevices()
|
|
||||||
if len(vDevices) == 0 {
|
|
||||||
logger.Log("Warning: no virtual devices found in configuration. No rules will work.")
|
|
||||||
}
|
|
||||||
|
|
||||||
vBuffersByName := make(map[string]*virtualdevice.EventBuffer)
|
|
||||||
vBuffersByDevice := make(map[*evdev.InputDevice]*virtualdevice.EventBuffer)
|
|
||||||
for name, device := range vDevices {
|
|
||||||
vBuffersByName[name] = virtualdevice.NewEventBuffer(device)
|
|
||||||
vBuffersByDevice[device] = vBuffersByName[name]
|
|
||||||
}
|
|
||||||
return vDevices, vBuffersByName, vBuffersByDevice
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// parse command-line
|
// parse command-line
|
||||||
var configFlag string
|
var configFlag string
|
||||||
|
@ -62,20 +40,26 @@ func main() {
|
||||||
// Initialize physical devices
|
// Initialize physical devices
|
||||||
pDevices := initPhysicalDevices(config)
|
pDevices := initPhysicalDevices(config)
|
||||||
|
|
||||||
// Load the rules
|
// initialize the mode variables
|
||||||
rules, eventChannel, cancel, wg := loadRules(config, pDevices, vDevicesByName)
|
var mode string
|
||||||
|
modes := config.Modes
|
||||||
|
if len(modes) == 0 {
|
||||||
|
mode = "*"
|
||||||
|
} else {
|
||||||
|
mode = config.Modes[0]
|
||||||
|
}
|
||||||
|
|
||||||
// initialize the mode variable
|
// Load the rules
|
||||||
mode := config.GetModes()[0]
|
rules, eventChannel, cancel, wg := loadRules(config, pDevices, vDevicesByName, modes)
|
||||||
|
|
||||||
// initialize TTS phrases for modes
|
// initialize TTS phrases for modes
|
||||||
for _, m := range config.GetModes() {
|
for _, m := range modes {
|
||||||
tts.AddMessage(m)
|
tts.AddMessage(m)
|
||||||
logger.LogDebugf("Added TTS message '%s'", m)
|
logger.LogDebugf("Added TTS message '%s'", m)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Joyful Running! Press Ctrl+C to quit. Press Enter to reload rules.")
|
fmt.Println("Joyful Running! Press Ctrl+C to quit. Press Enter to reload rules.")
|
||||||
if len(config.GetModes()) > 1 {
|
if len(modes) > 0 {
|
||||||
logger.Logf("Initial mode set to '%s'", mode)
|
logger.Logf("Initial mode set to '%s'", mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,13 +97,18 @@ func main() {
|
||||||
|
|
||||||
case ChannelEventReload:
|
case ChannelEventReload:
|
||||||
// stop existing channels
|
// stop existing channels
|
||||||
|
config, err := configparser.ParseConfig(configDir) // reload the config
|
||||||
|
if err != nil {
|
||||||
|
logger.LogError(err, "Failed to parse config, no changes made")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println("Reloading rules.")
|
fmt.Println("Reloading rules.")
|
||||||
cancel()
|
cancel()
|
||||||
fmt.Println("Waiting for existing listeners to exit. Provide input from each of your devices.")
|
fmt.Println("Waiting for existing listeners to exit. Provide input from each of your devices.")
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
fmt.Println("Listeners exited. Parsing config.")
|
fmt.Println("Listeners exited. Loading new rules.")
|
||||||
config := readConfig(configDir) // reload the config
|
rules, eventChannel, cancel, wg = loadRules(config, pDevices, vDevicesByName, modes)
|
||||||
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.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -128,37 +117,3 @@ func main() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadRules(
|
|
||||||
config *configparser.ConfigParser,
|
|
||||||
pDevices map[string]*evdev.InputDevice,
|
|
||||||
vDevices map[string]*evdev.InputDevice) ([]mappingrules.MappingRule, <-chan ChannelEvent, func(), *sync.WaitGroup) {
|
|
||||||
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
eventChannel := make(chan ChannelEvent, 1000)
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
|
|
||||||
// Initialize rules
|
|
||||||
rules := config.InitRules(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, ctx, &wg)
|
|
||||||
}
|
|
||||||
|
|
||||||
timerCount := 0
|
|
||||||
for _, rule := range rules {
|
|
||||||
if timedRule, ok := rule.(mappingrules.TimedEventEmitter); ok {
|
|
||||||
wg.Add(1)
|
|
||||||
go timerWatcher(timedRule, eventChannel, ctx, &wg)
|
|
||||||
timerCount++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.Logf("Registered %d timers.", timerCount)
|
|
||||||
|
|
||||||
go consoleWatcher(eventChannel)
|
|
||||||
|
|
||||||
return rules, eventChannel, cancel, &wg
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,74 +8,6 @@ import (
|
||||||
"github.com/holoplot/go-evdev"
|
"github.com/holoplot/go-evdev"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitVirtualDevices will register any configured devices with type = virtual
|
|
||||||
// using /dev/uinput, and return a map of those devices.
|
|
||||||
//
|
|
||||||
// This function assumes Parse() has been called.
|
|
||||||
//
|
|
||||||
// This function should only be called once, unless we want to create duplicate devices for some reason.
|
|
||||||
func (parser *ConfigParser) InitVirtualDevices() map[string]*evdev.InputDevice {
|
|
||||||
deviceMap := make(map[string]*evdev.InputDevice)
|
|
||||||
|
|
||||||
for _, deviceConfig := range parser.config.Devices {
|
|
||||||
if strings.ToLower(deviceConfig.Type) != DeviceTypeVirtual {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
deviceConfig := deviceConfig.Config.(DeviceConfigVirtual)
|
|
||||||
|
|
||||||
name := fmt.Sprintf("joyful-%s", deviceConfig.Name)
|
|
||||||
|
|
||||||
var capabilities map[evdev.EvType][]evdev.EvCode
|
|
||||||
|
|
||||||
// todo: add tests for presets
|
|
||||||
switch deviceConfig.Preset {
|
|
||||||
case DevicePresetGamepad:
|
|
||||||
capabilities = CapabilitiesPresetGamepad
|
|
||||||
case DevicePresetKeyboard:
|
|
||||||
capabilities = CapabilitiesPresetKeyboard
|
|
||||||
case DevicePresetJoystick:
|
|
||||||
capabilities = CapabilitiesPresetJoystick
|
|
||||||
case DevicePresetMouse:
|
|
||||||
capabilities = CapabilitiesPresetMouse
|
|
||||||
default:
|
|
||||||
capabilities = map[evdev.EvType][]evdev.EvCode{
|
|
||||||
evdev.EV_KEY: makeButtons(deviceConfig.NumButtons, deviceConfig.Buttons),
|
|
||||||
evdev.EV_ABS: makeAxes(deviceConfig.NumAxes, deviceConfig.Axes),
|
|
||||||
evdev.EV_REL: makeRelativeAxes(deviceConfig.NumRelativeAxes, deviceConfig.RelativeAxes),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
device, err := evdev.CreateDevice(
|
|
||||||
name,
|
|
||||||
// TODO: who knows what these should actually be
|
|
||||||
evdev.InputID{
|
|
||||||
BusType: 0x03,
|
|
||||||
Vendor: 0x4711,
|
|
||||||
Product: 0x0816,
|
|
||||||
Version: 1,
|
|
||||||
},
|
|
||||||
capabilities,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
logger.LogIfError(err, "Failed to create virtual device")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
deviceMap[deviceConfig.Name] = device
|
|
||||||
logger.Log(fmt.Sprintf(
|
|
||||||
"Created virtual device '%s' with %d buttons, %d axes, and %d relative axes",
|
|
||||||
name,
|
|
||||||
len(capabilities[evdev.EV_KEY]),
|
|
||||||
len(capabilities[evdev.EV_ABS]),
|
|
||||||
len(capabilities[evdev.EV_REL]),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
return deviceMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// InitPhysicalDevices will create InputDevices corresponding to any registered
|
// InitPhysicalDevices will create InputDevices corresponding to any registered
|
||||||
// devices with type = physical.
|
// devices with type = physical.
|
||||||
//
|
//
|
||||||
|
@ -123,99 +55,3 @@ func (parser *ConfigParser) InitPhysicalDevices() map[string]*evdev.InputDevice
|
||||||
|
|
||||||
return deviceMap
|
return deviceMap
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: these functions have a lot of duplication; we need to figure out how to refactor it cleanly
|
|
||||||
// without losing logging context...
|
|
||||||
func makeButtons(numButtons int, buttonList []string) []evdev.EvCode {
|
|
||||||
if numButtons > 0 && len(buttonList) > 0 {
|
|
||||||
logger.Log("'num_buttons' and 'buttons' both specified, ignoring 'num_buttons'")
|
|
||||||
}
|
|
||||||
|
|
||||||
if numButtons > VirtualDeviceMaxButtons {
|
|
||||||
numButtons = VirtualDeviceMaxButtons
|
|
||||||
logger.Logf("Limiting virtual device buttons to %d", VirtualDeviceMaxButtons)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(buttonList) > 0 {
|
|
||||||
buttons := make([]evdev.EvCode, 0, len(buttonList))
|
|
||||||
for _, codeStr := range buttonList {
|
|
||||||
code, err := parseCode(codeStr, "BTN")
|
|
||||||
if err != nil {
|
|
||||||
logger.LogError(err, "Failed to create button, skipping")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
buttons = append(buttons, code)
|
|
||||||
}
|
|
||||||
return buttons
|
|
||||||
}
|
|
||||||
|
|
||||||
buttons := make([]evdev.EvCode, numButtons)
|
|
||||||
|
|
||||||
for i := 0; i < numButtons; i++ {
|
|
||||||
buttons[i] = ButtonFromIndex[i]
|
|
||||||
}
|
|
||||||
|
|
||||||
return buttons
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeAxes(numAxes int, axisList []string) []evdev.EvCode {
|
|
||||||
if numAxes > 0 && len(axisList) > 0 {
|
|
||||||
logger.Log("'num_axes' and 'axes' both specified, ignoring 'num_axes'")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(axisList) > 0 {
|
|
||||||
axes := make([]evdev.EvCode, 0, len(axisList))
|
|
||||||
for _, codeStr := range axisList {
|
|
||||||
code, err := parseCode(codeStr, "ABS")
|
|
||||||
if err != nil {
|
|
||||||
logger.LogError(err, "Failed to create axis, skipping")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
axes = append(axes, code)
|
|
||||||
}
|
|
||||||
return axes
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
func makeRelativeAxes(numAxes int, axisList []string) []evdev.EvCode {
|
|
||||||
if numAxes > 0 && len(axisList) > 0 {
|
|
||||||
logger.Log("'num_rel_axes' and 'rel_axes' both specified, ignoring 'num_rel_axes'")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(axisList) > 0 {
|
|
||||||
axes := make([]evdev.EvCode, 0, len(axisList))
|
|
||||||
for _, codeStr := range axisList {
|
|
||||||
code, err := parseCode(codeStr, "REL")
|
|
||||||
if err != nil {
|
|
||||||
logger.LogError(err, "Failed to create axis, skipping")
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
axes = append(axes, code)
|
|
||||||
}
|
|
||||||
return axes
|
|
||||||
}
|
|
||||||
|
|
||||||
if numAxes > 10 {
|
|
||||||
numAxes = 10
|
|
||||||
logger.Log("Limiting virtual device relative axes to 10")
|
|
||||||
}
|
|
||||||
|
|
||||||
axes := make([]evdev.EvCode, numAxes)
|
|
||||||
for i := 0; i < numAxes; i++ {
|
|
||||||
axes[i] = evdev.EvCode(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
return axes
|
|
||||||
}
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.annabunches.net/annabunches/joyful/internal/eventcodes"
|
||||||
"git.annabunches.net/annabunches/joyful/internal/mappingrules"
|
"git.annabunches.net/annabunches/joyful/internal/mappingrules"
|
||||||
"github.com/holoplot/go-evdev"
|
"github.com/holoplot/go-evdev"
|
||||||
)
|
)
|
||||||
|
@ -14,7 +15,7 @@ func makeRuleTargetButton(targetConfig RuleTargetConfigButton, devs map[string]D
|
||||||
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
||||||
}
|
}
|
||||||
|
|
||||||
eventCode, err := parseCodeButton(targetConfig.Button)
|
eventCode, err := eventcodes.ParseCodeButton(targetConfig.Button)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -37,7 +38,7 @@ func makeRuleTargetAxis(targetConfig RuleTargetConfigAxis, devs map[string]Devic
|
||||||
return nil, errors.New("deadzone_end must be greater than deadzone_start")
|
return nil, errors.New("deadzone_end must be greater than deadzone_start")
|
||||||
}
|
}
|
||||||
|
|
||||||
eventCode, err := parseCode(targetConfig.Axis, CodePrefixAxis)
|
eventCode, err := eventcodes.ParseCode(targetConfig.Axis, eventcodes.CodePrefixAxis)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -63,7 +64,7 @@ func makeRuleTargetRelaxis(targetConfig RuleTargetConfigRelaxis, devs map[string
|
||||||
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
||||||
}
|
}
|
||||||
|
|
||||||
eventCode, err := parseCode(targetConfig.Axis, CodePrefixRelaxis)
|
eventCode, err := eventcodes.ParseCode(targetConfig.Axis, eventcodes.CodePrefixRelaxis)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -83,11 +84,6 @@ func makeRuleTargetModeSelect(targetConfig RuleTargetConfigModeSelect, allModes
|
||||||
return mappingrules.NewRuleTargetModeSelect(targetConfig.Modes)
|
return mappingrules.NewRuleTargetModeSelect(targetConfig.Modes)
|
||||||
}
|
}
|
||||||
|
|
||||||
// hasError exists solely to switch on errors in case statements
|
|
||||||
func hasError(_ any, err error) bool {
|
|
||||||
return err != nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
||||||
|
|
|
@ -14,9 +14,8 @@ 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) InitRules(pInputDevs map[string]*evdev.InputDevice, vInputDevs map[string]*evdev.InputDevice) []mappingrules.MappingRule {
|
func InitRules(config []RuleConfig, pInputDevs map[string]*evdev.InputDevice, vInputDevs map[string]*evdev.InputDevice, modes []string) []mappingrules.MappingRule {
|
||||||
rules := make([]mappingrules.MappingRule, 0)
|
rules := make([]mappingrules.MappingRule, 0)
|
||||||
modes := parser.GetModes()
|
|
||||||
|
|
||||||
// Golang can't inspect the concrete map type to determine interface conformance,
|
// Golang can't inspect the concrete map type to determine interface conformance,
|
||||||
// so we handle that here.
|
// so we handle that here.
|
||||||
|
@ -29,7 +28,7 @@ func (parser *ConfigParser) InitRules(pInputDevs map[string]*evdev.InputDevice,
|
||||||
vDevs[name] = dev
|
vDevs[name] = dev
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ruleConfig := range parser.config.Rules {
|
for _, ruleConfig := range config {
|
||||||
var newRule mappingrules.MappingRule
|
var newRule mappingrules.MappingRule
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
|
|
|
@ -1,18 +1,9 @@
|
||||||
package configparser
|
package configparser
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/holoplot/go-evdev"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DeviceTypePhysical = "physical"
|
DeviceTypePhysical = "physical"
|
||||||
DeviceTypeVirtual = "virtual"
|
DeviceTypeVirtual = "virtual"
|
||||||
|
|
||||||
DevicePresetKeyboard = "keyboard"
|
|
||||||
DevicePresetGamepad = "gamepad"
|
|
||||||
DevicePresetJoystick = "joystick"
|
|
||||||
DevicePresetMouse = "mouse"
|
|
||||||
|
|
||||||
RuleTypeButton = "button"
|
RuleTypeButton = "button"
|
||||||
RuleTypeButtonCombo = "button-combo"
|
RuleTypeButtonCombo = "button-combo"
|
||||||
RuleTypeButtonLatched = "button-latched"
|
RuleTypeButtonLatched = "button-latched"
|
||||||
|
@ -21,368 +12,4 @@ const (
|
||||||
RuleTypeAxisToButton = "axis-to-button"
|
RuleTypeAxisToButton = "axis-to-button"
|
||||||
RuleTypeAxisToRelaxis = "axis-to-relaxis"
|
RuleTypeAxisToRelaxis = "axis-to-relaxis"
|
||||||
RuleTypeModeSelect = "mode-select"
|
RuleTypeModeSelect = "mode-select"
|
||||||
|
|
||||||
CodePrefixButton = "BTN"
|
|
||||||
CodePrefixKey = "KEY"
|
|
||||||
CodePrefixAxis = "ABS"
|
|
||||||
CodePrefixRelaxis = "REL"
|
|
||||||
|
|
||||||
VirtualDeviceMaxButtons = 74
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
ButtonFromIndex = []evdev.EvCode{
|
|
||||||
evdev.BTN_TRIGGER,
|
|
||||||
evdev.BTN_THUMB,
|
|
||||||
evdev.BTN_THUMB2,
|
|
||||||
evdev.BTN_TOP,
|
|
||||||
evdev.BTN_TOP2,
|
|
||||||
evdev.BTN_PINKIE,
|
|
||||||
evdev.BTN_BASE,
|
|
||||||
evdev.BTN_BASE2,
|
|
||||||
evdev.BTN_BASE3,
|
|
||||||
evdev.BTN_BASE4,
|
|
||||||
evdev.BTN_BASE5,
|
|
||||||
evdev.BTN_BASE6,
|
|
||||||
evdev.EvCode(0x12c), // decimal 300
|
|
||||||
evdev.EvCode(0x12d), // decimal 301
|
|
||||||
evdev.EvCode(0x12e), // decimal 302
|
|
||||||
evdev.BTN_DEAD,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY1,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY2,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY3,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY4,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY5,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY6,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY7,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY8,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY9,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY10,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY11,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY12,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY13,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY14,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY15,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY16,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY17,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY18,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY19,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY20,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY21,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY22,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY23,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY24,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY25,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY26,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY27,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY28,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY29,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY30,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY31,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY32,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY33,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY34,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY35,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY36,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY37,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY38,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY39,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY40,
|
|
||||||
evdev.EvCode(0x2e8),
|
|
||||||
evdev.EvCode(0x2e9),
|
|
||||||
evdev.EvCode(0x2f0),
|
|
||||||
evdev.EvCode(0x2f1),
|
|
||||||
evdev.EvCode(0x2f2),
|
|
||||||
evdev.EvCode(0x2f3),
|
|
||||||
evdev.EvCode(0x2f4),
|
|
||||||
evdev.EvCode(0x2f5),
|
|
||||||
evdev.EvCode(0x2f6),
|
|
||||||
evdev.EvCode(0x2f7),
|
|
||||||
evdev.EvCode(0x2f8),
|
|
||||||
evdev.EvCode(0x2f9),
|
|
||||||
evdev.EvCode(0x2fa),
|
|
||||||
evdev.EvCode(0x2fb),
|
|
||||||
evdev.EvCode(0x2fc),
|
|
||||||
evdev.EvCode(0x2fd),
|
|
||||||
evdev.EvCode(0x2fe),
|
|
||||||
evdev.EvCode(0x2ff),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// Device Presets
|
|
||||||
var (
|
|
||||||
CapabilitiesPresetGamepad = map[evdev.EvType][]evdev.EvCode{
|
|
||||||
evdev.EV_ABS: {
|
|
||||||
evdev.ABS_X,
|
|
||||||
evdev.ABS_Y,
|
|
||||||
evdev.ABS_Z,
|
|
||||||
evdev.ABS_RX,
|
|
||||||
evdev.ABS_RY,
|
|
||||||
evdev.ABS_RZ,
|
|
||||||
evdev.ABS_HAT0X,
|
|
||||||
evdev.ABS_HAT0Y,
|
|
||||||
},
|
|
||||||
evdev.EV_KEY: {
|
|
||||||
evdev.BTN_NORTH, // Xbox 'X', Playstation 'Square'
|
|
||||||
evdev.BTN_SOUTH, // Xbox 'A', Plastation 'X'
|
|
||||||
evdev.BTN_WEST, // Xbox 'Y', Playstation 'Triangle'
|
|
||||||
evdev.BTN_EAST, // Xbox 'B', Playstation 'O'
|
|
||||||
evdev.BTN_THUMBL,
|
|
||||||
evdev.BTN_THUMBR,
|
|
||||||
evdev.BTN_TL,
|
|
||||||
evdev.BTN_TR,
|
|
||||||
evdev.BTN_SELECT,
|
|
||||||
evdev.BTN_START,
|
|
||||||
evdev.BTN_MODE,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
CapabilitiesPresetJoystick = map[evdev.EvType][]evdev.EvCode{
|
|
||||||
evdev.EV_ABS: {
|
|
||||||
evdev.ABS_X,
|
|
||||||
evdev.ABS_Y,
|
|
||||||
evdev.ABS_Z,
|
|
||||||
evdev.ABS_RX,
|
|
||||||
evdev.ABS_RY,
|
|
||||||
evdev.ABS_RZ,
|
|
||||||
evdev.ABS_THROTTLE, // Also called "Slider" or "Slider1"
|
|
||||||
evdev.ABS_RUDDER, // Also called "Dial", "Slider2", or "RSlider"
|
|
||||||
},
|
|
||||||
evdev.EV_KEY: {
|
|
||||||
evdev.BTN_TRIGGER,
|
|
||||||
evdev.BTN_THUMB,
|
|
||||||
evdev.BTN_THUMB2,
|
|
||||||
evdev.BTN_TOP,
|
|
||||||
evdev.BTN_TOP2,
|
|
||||||
evdev.BTN_PINKIE,
|
|
||||||
evdev.BTN_BASE,
|
|
||||||
evdev.BTN_BASE2,
|
|
||||||
evdev.BTN_BASE3,
|
|
||||||
evdev.BTN_BASE4,
|
|
||||||
evdev.BTN_BASE5,
|
|
||||||
evdev.BTN_BASE6,
|
|
||||||
evdev.EvCode(0x12c), // decimal 300
|
|
||||||
evdev.EvCode(0x12d), // decimal 301
|
|
||||||
evdev.EvCode(0x12e), // decimal 302
|
|
||||||
evdev.BTN_DEAD,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY1,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY2,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY3,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY4,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY5,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY6,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY7,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY8,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY9,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY10,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY11,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY12,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY13,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY14,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY15,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY16,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY17,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY18,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY19,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY20,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY21,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY22,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY23,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY24,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY25,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY26,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY27,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY28,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY29,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY30,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY31,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY32,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY33,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY34,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY35,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY36,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY37,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY38,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY39,
|
|
||||||
evdev.BTN_TRIGGER_HAPPY40,
|
|
||||||
evdev.EvCode(0x2e8),
|
|
||||||
evdev.EvCode(0x2e9),
|
|
||||||
evdev.EvCode(0x2f0),
|
|
||||||
evdev.EvCode(0x2f1),
|
|
||||||
evdev.EvCode(0x2f2),
|
|
||||||
evdev.EvCode(0x2f3),
|
|
||||||
evdev.EvCode(0x2f4),
|
|
||||||
evdev.EvCode(0x2f5),
|
|
||||||
evdev.EvCode(0x2f6),
|
|
||||||
evdev.EvCode(0x2f7),
|
|
||||||
evdev.EvCode(0x2f8),
|
|
||||||
evdev.EvCode(0x2f9),
|
|
||||||
evdev.EvCode(0x2fa),
|
|
||||||
evdev.EvCode(0x2fb),
|
|
||||||
evdev.EvCode(0x2fc),
|
|
||||||
evdev.EvCode(0x2fd),
|
|
||||||
evdev.EvCode(0x2fe),
|
|
||||||
evdev.EvCode(0x2ff),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
CapabilitiesPresetKeyboard = map[evdev.EvType][]evdev.EvCode{
|
|
||||||
evdev.EV_KEY: {
|
|
||||||
evdev.KEY_ESC,
|
|
||||||
evdev.KEY_1,
|
|
||||||
evdev.KEY_2,
|
|
||||||
evdev.KEY_3,
|
|
||||||
evdev.KEY_4,
|
|
||||||
evdev.KEY_5,
|
|
||||||
evdev.KEY_6,
|
|
||||||
evdev.KEY_7,
|
|
||||||
evdev.KEY_8,
|
|
||||||
evdev.KEY_9,
|
|
||||||
evdev.KEY_0,
|
|
||||||
evdev.KEY_MINUS,
|
|
||||||
evdev.KEY_EQUAL,
|
|
||||||
evdev.KEY_BACKSPACE,
|
|
||||||
evdev.KEY_TAB,
|
|
||||||
evdev.KEY_Q,
|
|
||||||
evdev.KEY_W,
|
|
||||||
evdev.KEY_E,
|
|
||||||
evdev.KEY_R,
|
|
||||||
evdev.KEY_T,
|
|
||||||
evdev.KEY_Y,
|
|
||||||
evdev.KEY_U,
|
|
||||||
evdev.KEY_I,
|
|
||||||
evdev.KEY_O,
|
|
||||||
evdev.KEY_P,
|
|
||||||
evdev.KEY_LEFTBRACE,
|
|
||||||
evdev.KEY_RIGHTBRACE,
|
|
||||||
evdev.KEY_ENTER,
|
|
||||||
evdev.KEY_LEFTCTRL,
|
|
||||||
evdev.KEY_A,
|
|
||||||
evdev.KEY_S,
|
|
||||||
evdev.KEY_D,
|
|
||||||
evdev.KEY_F,
|
|
||||||
evdev.KEY_G,
|
|
||||||
evdev.KEY_H,
|
|
||||||
evdev.KEY_J,
|
|
||||||
evdev.KEY_K,
|
|
||||||
evdev.KEY_L,
|
|
||||||
evdev.KEY_SEMICOLON,
|
|
||||||
evdev.KEY_APOSTROPHE,
|
|
||||||
evdev.KEY_GRAVE,
|
|
||||||
evdev.KEY_LEFTSHIFT,
|
|
||||||
evdev.KEY_BACKSLASH,
|
|
||||||
evdev.KEY_Z,
|
|
||||||
evdev.KEY_X,
|
|
||||||
evdev.KEY_C,
|
|
||||||
evdev.KEY_V,
|
|
||||||
evdev.KEY_B,
|
|
||||||
evdev.KEY_N,
|
|
||||||
evdev.KEY_M,
|
|
||||||
evdev.KEY_COMMA,
|
|
||||||
evdev.KEY_DOT,
|
|
||||||
evdev.KEY_SLASH,
|
|
||||||
evdev.KEY_RIGHTSHIFT,
|
|
||||||
evdev.KEY_KPASTERISK,
|
|
||||||
evdev.KEY_LEFTALT,
|
|
||||||
evdev.KEY_SPACE,
|
|
||||||
evdev.KEY_CAPSLOCK,
|
|
||||||
evdev.KEY_F1,
|
|
||||||
evdev.KEY_F2,
|
|
||||||
evdev.KEY_F3,
|
|
||||||
evdev.KEY_F4,
|
|
||||||
evdev.KEY_F5,
|
|
||||||
evdev.KEY_F6,
|
|
||||||
evdev.KEY_F7,
|
|
||||||
evdev.KEY_F8,
|
|
||||||
evdev.KEY_F9,
|
|
||||||
evdev.KEY_F10,
|
|
||||||
evdev.KEY_NUMLOCK,
|
|
||||||
evdev.KEY_SCROLLLOCK,
|
|
||||||
evdev.KEY_KP7,
|
|
||||||
evdev.KEY_KP8,
|
|
||||||
evdev.KEY_KP9,
|
|
||||||
evdev.KEY_KPMINUS,
|
|
||||||
evdev.KEY_KP4,
|
|
||||||
evdev.KEY_KP5,
|
|
||||||
evdev.KEY_KP6,
|
|
||||||
evdev.KEY_KPPLUS,
|
|
||||||
evdev.KEY_KP1,
|
|
||||||
evdev.KEY_KP2,
|
|
||||||
evdev.KEY_KP3,
|
|
||||||
evdev.KEY_KP0,
|
|
||||||
evdev.KEY_KPDOT,
|
|
||||||
evdev.KEY_ZENKAKUHANKAKU,
|
|
||||||
evdev.KEY_102ND,
|
|
||||||
evdev.KEY_F11,
|
|
||||||
evdev.KEY_F12,
|
|
||||||
evdev.KEY_RO,
|
|
||||||
evdev.KEY_KATAKANA,
|
|
||||||
evdev.KEY_HIRAGANA,
|
|
||||||
evdev.KEY_HENKAN,
|
|
||||||
evdev.KEY_KATAKANAHIRAGANA,
|
|
||||||
evdev.KEY_MUHENKAN,
|
|
||||||
evdev.KEY_KPJPCOMMA,
|
|
||||||
evdev.KEY_KPENTER,
|
|
||||||
evdev.KEY_RIGHTCTRL,
|
|
||||||
evdev.KEY_KPSLASH,
|
|
||||||
evdev.KEY_SYSRQ,
|
|
||||||
evdev.KEY_RIGHTALT,
|
|
||||||
evdev.KEY_LINEFEED,
|
|
||||||
evdev.KEY_HOME,
|
|
||||||
evdev.KEY_UP,
|
|
||||||
evdev.KEY_PAGEUP,
|
|
||||||
evdev.KEY_LEFT,
|
|
||||||
evdev.KEY_RIGHT,
|
|
||||||
evdev.KEY_END,
|
|
||||||
evdev.KEY_DOWN,
|
|
||||||
evdev.KEY_PAGEDOWN,
|
|
||||||
evdev.KEY_INSERT,
|
|
||||||
evdev.KEY_DELETE,
|
|
||||||
evdev.KEY_MACRO,
|
|
||||||
evdev.KEY_MUTE,
|
|
||||||
evdev.KEY_VOLUMEDOWN,
|
|
||||||
evdev.KEY_VOLUMEUP,
|
|
||||||
evdev.KEY_KPEQUAL,
|
|
||||||
evdev.KEY_KPPLUSMINUS,
|
|
||||||
evdev.KEY_PAUSE,
|
|
||||||
evdev.KEY_SCALE,
|
|
||||||
evdev.KEY_KPCOMMA,
|
|
||||||
evdev.KEY_HANGEUL,
|
|
||||||
evdev.KEY_HANJA,
|
|
||||||
evdev.KEY_YEN,
|
|
||||||
evdev.KEY_LEFTMETA,
|
|
||||||
evdev.KEY_RIGHTMETA,
|
|
||||||
evdev.KEY_COMPOSE,
|
|
||||||
evdev.KEY_F13,
|
|
||||||
evdev.KEY_F14,
|
|
||||||
evdev.KEY_F15,
|
|
||||||
evdev.KEY_F16,
|
|
||||||
evdev.KEY_F17,
|
|
||||||
evdev.KEY_F18,
|
|
||||||
evdev.KEY_F19,
|
|
||||||
evdev.KEY_F20,
|
|
||||||
evdev.KEY_F21,
|
|
||||||
evdev.KEY_F22,
|
|
||||||
evdev.KEY_F23,
|
|
||||||
evdev.KEY_F24,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
CapabilitiesPresetMouse = map[evdev.EvType][]evdev.EvCode{
|
|
||||||
evdev.EV_REL: {
|
|
||||||
evdev.REL_X,
|
|
||||||
evdev.REL_Y,
|
|
||||||
evdev.REL_WHEEL,
|
|
||||||
evdev.REL_HWHEEL,
|
|
||||||
},
|
|
||||||
evdev.EV_KEY: {
|
|
||||||
evdev.BTN_LEFT,
|
|
||||||
evdev.BTN_MIDDLE,
|
|
||||||
evdev.BTN_RIGHT,
|
|
||||||
evdev.BTN_SIDE,
|
|
||||||
evdev.BTN_EXTRA,
|
|
||||||
evdev.BTN_FORWARD,
|
|
||||||
evdev.BTN_BACK,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package configparser
|
package eventcodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -8,17 +8,17 @@ import (
|
||||||
"github.com/holoplot/go-evdev"
|
"github.com/holoplot/go-evdev"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseCodeButton(code string) (evdev.EvCode, error) {
|
func ParseCodeButton(code string) (evdev.EvCode, error) {
|
||||||
prefix := CodePrefixButton
|
prefix := CodePrefixButton
|
||||||
|
|
||||||
if strings.HasPrefix(code, CodePrefixKey+"_") {
|
if strings.HasPrefix(code, CodePrefixKey+"_") {
|
||||||
prefix = CodePrefixKey
|
prefix = CodePrefixKey
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseCode(code, prefix)
|
return ParseCode(code, prefix)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseCode(code, prefix string) (evdev.EvCode, error) {
|
func ParseCode(code, prefix string) (evdev.EvCode, error) {
|
||||||
code = strings.ToUpper(code)
|
code = strings.ToUpper(code)
|
||||||
|
|
||||||
var codeLookup map[string]evdev.EvCode
|
var codeLookup map[string]evdev.EvCode
|
||||||
|
@ -70,3 +70,8 @@ func parseCode(code, prefix string) (evdev.EvCode, error) {
|
||||||
return eventCode, nil
|
return eventCode, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hasError exists solely to switch on errors in conditional and case statements
|
||||||
|
func hasError(_ any, err error) bool {
|
||||||
|
return err != nil
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package configparser
|
package eventcodes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -18,7 +18,7 @@ func TestRunnerEventCodeParserTests(t *testing.T) {
|
||||||
|
|
||||||
func parseCodeTestCase(t *EventCodeParserTests, in string, out evdev.EvCode, prefix string) {
|
func parseCodeTestCase(t *EventCodeParserTests, in string, out evdev.EvCode, prefix string) {
|
||||||
t.Run(fmt.Sprintf("%s: %s", prefix, in), func() {
|
t.Run(fmt.Sprintf("%s: %s", prefix, in), func() {
|
||||||
code, err := parseCode(in, prefix)
|
code, err := ParseCode(in, prefix)
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.EqualValues(out, code)
|
t.EqualValues(out, code)
|
||||||
})
|
})
|
||||||
|
@ -38,7 +38,7 @@ func (t *EventCodeParserTests) TestParseCodeButton() {
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(testCase.in, func() {
|
t.Run(testCase.in, func() {
|
||||||
code, err := parseCodeButton(testCase.in)
|
code, err := ParseCodeButton(testCase.in)
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.EqualValues(code, testCase.out)
|
t.EqualValues(code, testCase.out)
|
||||||
})
|
})
|
||||||
|
@ -134,7 +134,7 @@ func (t *EventCodeParserTests) TestParseCode() {
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
t.Run(fmt.Sprintf("%s - '%s'", testCase.prefix, testCase.in), func() {
|
t.Run(fmt.Sprintf("%s - '%s'", testCase.prefix, testCase.in), func() {
|
||||||
_, err := parseCode(testCase.in, testCase.prefix)
|
_, err := ParseCode(testCase.in, testCase.prefix)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
90
internal/eventcodes/variables.go
Normal file
90
internal/eventcodes/variables.go
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
package eventcodes
|
||||||
|
|
||||||
|
import "github.com/holoplot/go-evdev"
|
||||||
|
|
||||||
|
const (
|
||||||
|
CodePrefixButton = "BTN"
|
||||||
|
CodePrefixKey = "KEY"
|
||||||
|
CodePrefixAxis = "ABS"
|
||||||
|
CodePrefixRelaxis = "REL"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// Map joystick buttons to integer indices
|
||||||
|
ButtonFromIndex = []evdev.EvCode{
|
||||||
|
evdev.BTN_TRIGGER,
|
||||||
|
evdev.BTN_THUMB,
|
||||||
|
evdev.BTN_THUMB2,
|
||||||
|
evdev.BTN_TOP,
|
||||||
|
evdev.BTN_TOP2,
|
||||||
|
evdev.BTN_PINKIE,
|
||||||
|
evdev.BTN_BASE,
|
||||||
|
evdev.BTN_BASE2,
|
||||||
|
evdev.BTN_BASE3,
|
||||||
|
evdev.BTN_BASE4,
|
||||||
|
evdev.BTN_BASE5,
|
||||||
|
evdev.BTN_BASE6,
|
||||||
|
evdev.EvCode(0x12c), // decimal 300
|
||||||
|
evdev.EvCode(0x12d), // decimal 301
|
||||||
|
evdev.EvCode(0x12e), // decimal 302
|
||||||
|
evdev.BTN_DEAD,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY1,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY2,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY3,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY4,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY5,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY6,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY7,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY8,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY9,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY10,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY11,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY12,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY13,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY14,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY15,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY16,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY17,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY18,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY19,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY20,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY21,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY22,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY23,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY24,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY25,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY26,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY27,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY28,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY29,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY30,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY31,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY32,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY33,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY34,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY35,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY36,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY37,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY38,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY39,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY40,
|
||||||
|
evdev.EvCode(0x2e8),
|
||||||
|
evdev.EvCode(0x2e9),
|
||||||
|
evdev.EvCode(0x2f0),
|
||||||
|
evdev.EvCode(0x2f1),
|
||||||
|
evdev.EvCode(0x2f2),
|
||||||
|
evdev.EvCode(0x2f3),
|
||||||
|
evdev.EvCode(0x2f4),
|
||||||
|
evdev.EvCode(0x2f5),
|
||||||
|
evdev.EvCode(0x2f6),
|
||||||
|
evdev.EvCode(0x2f7),
|
||||||
|
evdev.EvCode(0x2f8),
|
||||||
|
evdev.EvCode(0x2f9),
|
||||||
|
evdev.EvCode(0x2fa),
|
||||||
|
evdev.EvCode(0x2fb),
|
||||||
|
evdev.EvCode(0x2fc),
|
||||||
|
evdev.EvCode(0x2fd),
|
||||||
|
evdev.EvCode(0x2fe),
|
||||||
|
evdev.EvCode(0x2ff),
|
||||||
|
}
|
||||||
|
)
|
|
@ -1,35 +0,0 @@
|
||||||
// Functions for cleaning up stale virtual devices
|
|
||||||
|
|
||||||
package virtualdevice
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/holoplot/go-evdev"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CleanupStaleVirtualDevices() {
|
|
||||||
devices, err := evdev.ListDevicePaths()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Couldn't list devices while running cleanup: %s\n", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, devicePath := range devices {
|
|
||||||
if strings.HasPrefix(devicePath.Name, "joyful-joystick") {
|
|
||||||
device, err := evdev.Open(devicePath.Path)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Failed to open existing joyful device at '%s': %s\n", devicePath.Path, err.Error())
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
err = evdev.DestroyDevice(device)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Failed to destroy existing joyful device '%s' at '%s': %s\n", devicePath.Name, devicePath.Path, err.Error())
|
|
||||||
} else {
|
|
||||||
fmt.Printf("Destroyed stale joyful device '%s'\n", devicePath.Path)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,13 +11,7 @@ import (
|
||||||
type EventBuffer struct {
|
type EventBuffer struct {
|
||||||
events []*evdev.InputEvent
|
events []*evdev.InputEvent
|
||||||
Device VirtualDevice
|
Device VirtualDevice
|
||||||
}
|
Name string
|
||||||
|
|
||||||
func NewEventBuffer(device VirtualDevice) *EventBuffer {
|
|
||||||
return &EventBuffer{
|
|
||||||
events: make([]*evdev.InputEvent, 0, 100),
|
|
||||||
Device: device,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (buffer *EventBuffer) AddEvent(event *evdev.InputEvent) {
|
func (buffer *EventBuffer) AddEvent(event *evdev.InputEvent) {
|
||||||
|
|
|
@ -11,10 +11,11 @@ import (
|
||||||
|
|
||||||
type EventBufferTests struct {
|
type EventBufferTests struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
device *VirtualDeviceMock
|
device *VirtualDeviceMock
|
||||||
writeOneCall *mock.Call
|
buffer *EventBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mocks
|
||||||
type VirtualDeviceMock struct {
|
type VirtualDeviceMock struct {
|
||||||
mock.Mock
|
mock.Mock
|
||||||
}
|
}
|
||||||
|
@ -24,65 +25,65 @@ func (m *VirtualDeviceMock) WriteOne(event *evdev.InputEvent) error {
|
||||||
return args.Error(0)
|
return args.Error(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setup
|
||||||
func TestRunnerEventBufferTests(t *testing.T) {
|
func TestRunnerEventBufferTests(t *testing.T) {
|
||||||
suite.Run(t, new(EventBufferTests))
|
suite.Run(t, new(EventBufferTests))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *EventBufferTests) SetupTest() {
|
|
||||||
t.device = new(VirtualDeviceMock)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *EventBufferTests) SetupSubTest() {
|
func (t *EventBufferTests) SetupSubTest() {
|
||||||
t.device = new(VirtualDeviceMock)
|
t.device = new(VirtualDeviceMock)
|
||||||
t.writeOneCall = t.device.On("WriteOne").Return(nil)
|
t.buffer = &EventBuffer{Device: t.device}
|
||||||
}
|
|
||||||
|
|
||||||
func (t *EventBufferTests) TearDownSubTest() {
|
|
||||||
t.writeOneCall.Unset()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests
|
||||||
func (t *EventBufferTests) TestNewEventBuffer() {
|
func (t *EventBufferTests) TestNewEventBuffer() {
|
||||||
buffer := NewEventBuffer(t.device)
|
t.Equal(t.device, t.buffer.Device)
|
||||||
t.Equal(t.device, buffer.Device)
|
t.Len(t.buffer.events, 0)
|
||||||
t.Len(buffer.events, 0)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *EventBufferTests) TestEventBufferAddEvent() {
|
func (t *EventBufferTests) TestEventBuffer() {
|
||||||
buffer := NewEventBuffer(t.device)
|
|
||||||
buffer.AddEvent(&evdev.InputEvent{})
|
t.Run("AddEvent", func() {
|
||||||
buffer.AddEvent(&evdev.InputEvent{})
|
t.buffer.AddEvent(&evdev.InputEvent{})
|
||||||
buffer.AddEvent(&evdev.InputEvent{})
|
t.buffer.AddEvent(&evdev.InputEvent{})
|
||||||
t.Len(buffer.events, 3)
|
t.buffer.AddEvent(&evdev.InputEvent{})
|
||||||
}
|
t.Len(t.buffer.events, 3)
|
||||||
|
})
|
||||||
func (t *EventBufferTests) TestEventBufferSendEvents() {
|
|
||||||
t.Run("3 Events", func() {
|
t.Run("SendEvents", func() {
|
||||||
buffer := NewEventBuffer(t.device)
|
t.Run("3 Events", func() {
|
||||||
buffer.AddEvent(&evdev.InputEvent{})
|
writeOneCall := t.device.On("WriteOne").Return(nil)
|
||||||
buffer.AddEvent(&evdev.InputEvent{})
|
|
||||||
buffer.AddEvent(&evdev.InputEvent{})
|
t.buffer.AddEvent(&evdev.InputEvent{})
|
||||||
errs := buffer.SendEvents()
|
t.buffer.AddEvent(&evdev.InputEvent{})
|
||||||
|
t.buffer.AddEvent(&evdev.InputEvent{})
|
||||||
t.Len(errs, 0)
|
errs := t.buffer.SendEvents()
|
||||||
t.device.AssertNumberOfCalls(t.T(), "WriteOne", 4)
|
|
||||||
})
|
t.Len(errs, 0)
|
||||||
|
t.device.AssertNumberOfCalls(t.T(), "WriteOne", 4)
|
||||||
t.Run("No Events", func() {
|
|
||||||
buffer := NewEventBuffer(t.device)
|
writeOneCall.Unset()
|
||||||
errs := buffer.SendEvents()
|
})
|
||||||
|
|
||||||
t.Len(errs, 0)
|
t.Run("No Events", func() {
|
||||||
t.device.AssertNumberOfCalls(t.T(), "WriteOne", 0)
|
writeOneCall := t.device.On("WriteOne").Return(nil)
|
||||||
})
|
|
||||||
|
errs := t.buffer.SendEvents()
|
||||||
t.Run("Bad Event", func() {
|
|
||||||
t.writeOneCall.Unset()
|
t.Len(errs, 0)
|
||||||
t.writeOneCall = t.device.On("WriteOne").Return(errors.New("Fail"))
|
t.device.AssertNumberOfCalls(t.T(), "WriteOne", 0)
|
||||||
|
|
||||||
buffer := NewEventBuffer(t.device)
|
writeOneCall.Unset()
|
||||||
buffer.AddEvent(&evdev.InputEvent{})
|
})
|
||||||
errs := buffer.SendEvents()
|
|
||||||
t.Len(errs, 2)
|
t.Run("Bad Event", func() {
|
||||||
})
|
writeOneCall := t.device.On("WriteOne").Return(errors.New("Fail"))
|
||||||
|
|
||||||
|
t.buffer.AddEvent(&evdev.InputEvent{})
|
||||||
|
errs := t.buffer.SendEvents()
|
||||||
|
t.Len(errs, 2)
|
||||||
|
|
||||||
|
writeOneCall.Unset()
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
165
internal/virtualdevice/init.go
Normal file
165
internal/virtualdevice/init.go
Normal file
|
@ -0,0 +1,165 @@
|
||||||
|
package virtualdevice
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.annabunches.net/annabunches/joyful/internal/configparser"
|
||||||
|
"git.annabunches.net/annabunches/joyful/internal/eventcodes"
|
||||||
|
"git.annabunches.net/annabunches/joyful/internal/logger"
|
||||||
|
"github.com/holoplot/go-evdev"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewEventBuffer takes a virtual device config specification, creates the underlying
|
||||||
|
// evdev.InputDevice, and wraps it in a buffered event emitter.
|
||||||
|
func NewEventBuffer(config configparser.DeviceConfigVirtual) (*EventBuffer, error) {
|
||||||
|
deviceMap := make(map[string]*evdev.InputDevice)
|
||||||
|
|
||||||
|
name := fmt.Sprintf("joyful-%s", config.Name)
|
||||||
|
|
||||||
|
var capabilities map[evdev.EvType][]evdev.EvCode
|
||||||
|
|
||||||
|
// todo: add tests for presets
|
||||||
|
switch config.Preset {
|
||||||
|
case DevicePresetGamepad:
|
||||||
|
capabilities = CapabilitiesPresetGamepad
|
||||||
|
case DevicePresetKeyboard:
|
||||||
|
capabilities = CapabilitiesPresetKeyboard
|
||||||
|
case DevicePresetJoystick:
|
||||||
|
capabilities = CapabilitiesPresetJoystick
|
||||||
|
case DevicePresetMouse:
|
||||||
|
capabilities = CapabilitiesPresetMouse
|
||||||
|
default:
|
||||||
|
capabilities = map[evdev.EvType][]evdev.EvCode{
|
||||||
|
evdev.EV_KEY: makeButtons(config.NumButtons, config.Buttons),
|
||||||
|
evdev.EV_ABS: makeAxes(config.NumAxes, config.Axes),
|
||||||
|
evdev.EV_REL: makeRelativeAxes(config.NumRelativeAxes, config.RelativeAxes),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
device, err := evdev.CreateDevice(
|
||||||
|
name,
|
||||||
|
// TODO: placeholders. Who knows what these should actually be...
|
||||||
|
evdev.InputID{
|
||||||
|
BusType: 0x03,
|
||||||
|
Vendor: 0x4711,
|
||||||
|
Product: 0x0816,
|
||||||
|
Version: 1,
|
||||||
|
},
|
||||||
|
capabilities,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceMap[config.Name] = device
|
||||||
|
logger.Log(fmt.Sprintf(
|
||||||
|
"Created virtual device '%s' with %d buttons, %d axes, and %d relative axes",
|
||||||
|
name,
|
||||||
|
len(capabilities[evdev.EV_KEY]),
|
||||||
|
len(capabilities[evdev.EV_ABS]),
|
||||||
|
len(capabilities[evdev.EV_REL]),
|
||||||
|
))
|
||||||
|
|
||||||
|
return &EventBuffer{
|
||||||
|
events: make([]*evdev.InputEvent, 0, 100),
|
||||||
|
Device: device,
|
||||||
|
Name: config.Name,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: these functions have a lot of duplication; we need to figure out how to refactor it cleanly
|
||||||
|
// without losing logging context...
|
||||||
|
func makeButtons(numButtons int, buttonList []string) []evdev.EvCode {
|
||||||
|
if numButtons > 0 && len(buttonList) > 0 {
|
||||||
|
logger.Log("'num_buttons' and 'buttons' both specified, ignoring 'num_buttons'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if numButtons > VirtualDeviceMaxButtons {
|
||||||
|
numButtons = VirtualDeviceMaxButtons
|
||||||
|
logger.Logf("Limiting virtual device buttons to %d", VirtualDeviceMaxButtons)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(buttonList) > 0 {
|
||||||
|
buttons := make([]evdev.EvCode, 0, len(buttonList))
|
||||||
|
for _, codeStr := range buttonList {
|
||||||
|
code, err := eventcodes.ParseCode(codeStr, "BTN")
|
||||||
|
if err != nil {
|
||||||
|
logger.LogError(err, "Failed to create button, skipping")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buttons = append(buttons, code)
|
||||||
|
}
|
||||||
|
return buttons
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons := make([]evdev.EvCode, numButtons)
|
||||||
|
|
||||||
|
for i := 0; i < numButtons; i++ {
|
||||||
|
buttons[i] = eventcodes.ButtonFromIndex[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return buttons
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeAxes(numAxes int, axisList []string) []evdev.EvCode {
|
||||||
|
if numAxes > 0 && len(axisList) > 0 {
|
||||||
|
logger.Log("'num_axes' and 'axes' both specified, ignoring 'num_axes'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(axisList) > 0 {
|
||||||
|
axes := make([]evdev.EvCode, 0, len(axisList))
|
||||||
|
for _, codeStr := range axisList {
|
||||||
|
code, err := eventcodes.ParseCode(codeStr, "ABS")
|
||||||
|
if err != nil {
|
||||||
|
logger.LogError(err, "Failed to create axis, skipping")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
axes = append(axes, code)
|
||||||
|
}
|
||||||
|
return axes
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeRelativeAxes(numAxes int, axisList []string) []evdev.EvCode {
|
||||||
|
if numAxes > 0 && len(axisList) > 0 {
|
||||||
|
logger.Log("'num_rel_axes' and 'rel_axes' both specified, ignoring 'num_rel_axes'")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(axisList) > 0 {
|
||||||
|
axes := make([]evdev.EvCode, 0, len(axisList))
|
||||||
|
for _, codeStr := range axisList {
|
||||||
|
code, err := eventcodes.ParseCode(codeStr, "REL")
|
||||||
|
if err != nil {
|
||||||
|
logger.LogError(err, "Failed to create axis, skipping")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
axes = append(axes, code)
|
||||||
|
}
|
||||||
|
return axes
|
||||||
|
}
|
||||||
|
|
||||||
|
if numAxes > 10 {
|
||||||
|
numAxes = 10
|
||||||
|
logger.Log("Limiting virtual device relative axes to 10")
|
||||||
|
}
|
||||||
|
|
||||||
|
axes := make([]evdev.EvCode, numAxes)
|
||||||
|
for i := 0; i < numAxes; i++ {
|
||||||
|
axes[i] = evdev.EvCode(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return axes
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package configparser
|
package virtualdevice
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -7,15 +7,15 @@ import (
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DevicesConfigTests struct {
|
type InitTests struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunnerDevicesConfig(t *testing.T) {
|
func TestRunnerInit(t *testing.T) {
|
||||||
suite.Run(t, new(DevicesConfigTests))
|
suite.Run(t, new(InitTests))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *DevicesConfigTests) TestMakeButtons() {
|
func (t *InitTests) TestMakeButtons() {
|
||||||
t.Run("Maximum buttons", func() {
|
t.Run("Maximum buttons", func() {
|
||||||
buttons := makeButtons(VirtualDeviceMaxButtons, []string{})
|
buttons := makeButtons(VirtualDeviceMaxButtons, []string{})
|
||||||
t.Equal(VirtualDeviceMaxButtons, len(buttons))
|
t.Equal(VirtualDeviceMaxButtons, len(buttons))
|
||||||
|
@ -44,7 +44,7 @@ func (t *DevicesConfigTests) TestMakeButtons() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *DevicesConfigTests) TestMakeAxes() {
|
func (t *InitTests) TestMakeAxes() {
|
||||||
t.Run("8 axes", func() {
|
t.Run("8 axes", func() {
|
||||||
axes := makeAxes(8, []string{})
|
axes := makeAxes(8, []string{})
|
||||||
t.Equal(8, len(axes))
|
t.Equal(8, len(axes))
|
||||||
|
@ -81,7 +81,7 @@ func (t *DevicesConfigTests) TestMakeAxes() {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *DevicesConfigTests) TestMakeRelativeAxes() {
|
func (t *InitTests) TestMakeRelativeAxes() {
|
||||||
t.Run("10 axes", func() {
|
t.Run("10 axes", func() {
|
||||||
axes := makeRelativeAxes(10, []string{})
|
axes := makeRelativeAxes(10, []string{})
|
||||||
t.Equal(10, len(axes))
|
t.Equal(10, len(axes))
|
290
internal/virtualdevice/variables.go
Normal file
290
internal/virtualdevice/variables.go
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
package virtualdevice
|
||||||
|
|
||||||
|
import "github.com/holoplot/go-evdev"
|
||||||
|
|
||||||
|
const (
|
||||||
|
DevicePresetKeyboard = "keyboard"
|
||||||
|
DevicePresetGamepad = "gamepad"
|
||||||
|
DevicePresetJoystick = "joystick"
|
||||||
|
DevicePresetMouse = "mouse"
|
||||||
|
|
||||||
|
VirtualDeviceMaxButtons = 74
|
||||||
|
)
|
||||||
|
|
||||||
|
// Device Presets
|
||||||
|
var (
|
||||||
|
CapabilitiesPresetGamepad = map[evdev.EvType][]evdev.EvCode{
|
||||||
|
evdev.EV_ABS: {
|
||||||
|
evdev.ABS_X,
|
||||||
|
evdev.ABS_Y,
|
||||||
|
evdev.ABS_Z,
|
||||||
|
evdev.ABS_RX,
|
||||||
|
evdev.ABS_RY,
|
||||||
|
evdev.ABS_RZ,
|
||||||
|
evdev.ABS_HAT0X,
|
||||||
|
evdev.ABS_HAT0Y,
|
||||||
|
},
|
||||||
|
evdev.EV_KEY: {
|
||||||
|
evdev.BTN_NORTH, // Xbox 'X', Playstation 'Square'
|
||||||
|
evdev.BTN_SOUTH, // Xbox 'A', Plastation 'X'
|
||||||
|
evdev.BTN_WEST, // Xbox 'Y', Playstation 'Triangle'
|
||||||
|
evdev.BTN_EAST, // Xbox 'B', Playstation 'O'
|
||||||
|
evdev.BTN_THUMBL,
|
||||||
|
evdev.BTN_THUMBR,
|
||||||
|
evdev.BTN_TL,
|
||||||
|
evdev.BTN_TR,
|
||||||
|
evdev.BTN_SELECT,
|
||||||
|
evdev.BTN_START,
|
||||||
|
evdev.BTN_MODE,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
CapabilitiesPresetJoystick = map[evdev.EvType][]evdev.EvCode{
|
||||||
|
evdev.EV_ABS: {
|
||||||
|
evdev.ABS_X,
|
||||||
|
evdev.ABS_Y,
|
||||||
|
evdev.ABS_Z,
|
||||||
|
evdev.ABS_RX,
|
||||||
|
evdev.ABS_RY,
|
||||||
|
evdev.ABS_RZ,
|
||||||
|
evdev.ABS_THROTTLE, // Also called "Slider" or "Slider1"
|
||||||
|
evdev.ABS_RUDDER, // Also called "Dial", "Slider2", or "RSlider"
|
||||||
|
},
|
||||||
|
evdev.EV_KEY: {
|
||||||
|
evdev.BTN_TRIGGER,
|
||||||
|
evdev.BTN_THUMB,
|
||||||
|
evdev.BTN_THUMB2,
|
||||||
|
evdev.BTN_TOP,
|
||||||
|
evdev.BTN_TOP2,
|
||||||
|
evdev.BTN_PINKIE,
|
||||||
|
evdev.BTN_BASE,
|
||||||
|
evdev.BTN_BASE2,
|
||||||
|
evdev.BTN_BASE3,
|
||||||
|
evdev.BTN_BASE4,
|
||||||
|
evdev.BTN_BASE5,
|
||||||
|
evdev.BTN_BASE6,
|
||||||
|
evdev.EvCode(0x12c), // decimal 300
|
||||||
|
evdev.EvCode(0x12d), // decimal 301
|
||||||
|
evdev.EvCode(0x12e), // decimal 302
|
||||||
|
evdev.BTN_DEAD,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY1,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY2,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY3,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY4,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY5,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY6,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY7,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY8,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY9,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY10,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY11,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY12,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY13,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY14,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY15,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY16,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY17,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY18,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY19,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY20,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY21,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY22,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY23,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY24,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY25,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY26,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY27,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY28,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY29,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY30,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY31,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY32,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY33,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY34,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY35,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY36,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY37,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY38,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY39,
|
||||||
|
evdev.BTN_TRIGGER_HAPPY40,
|
||||||
|
evdev.EvCode(0x2e8),
|
||||||
|
evdev.EvCode(0x2e9),
|
||||||
|
evdev.EvCode(0x2f0),
|
||||||
|
evdev.EvCode(0x2f1),
|
||||||
|
evdev.EvCode(0x2f2),
|
||||||
|
evdev.EvCode(0x2f3),
|
||||||
|
evdev.EvCode(0x2f4),
|
||||||
|
evdev.EvCode(0x2f5),
|
||||||
|
evdev.EvCode(0x2f6),
|
||||||
|
evdev.EvCode(0x2f7),
|
||||||
|
evdev.EvCode(0x2f8),
|
||||||
|
evdev.EvCode(0x2f9),
|
||||||
|
evdev.EvCode(0x2fa),
|
||||||
|
evdev.EvCode(0x2fb),
|
||||||
|
evdev.EvCode(0x2fc),
|
||||||
|
evdev.EvCode(0x2fd),
|
||||||
|
evdev.EvCode(0x2fe),
|
||||||
|
evdev.EvCode(0x2ff),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
CapabilitiesPresetKeyboard = map[evdev.EvType][]evdev.EvCode{
|
||||||
|
evdev.EV_KEY: {
|
||||||
|
evdev.KEY_ESC,
|
||||||
|
evdev.KEY_1,
|
||||||
|
evdev.KEY_2,
|
||||||
|
evdev.KEY_3,
|
||||||
|
evdev.KEY_4,
|
||||||
|
evdev.KEY_5,
|
||||||
|
evdev.KEY_6,
|
||||||
|
evdev.KEY_7,
|
||||||
|
evdev.KEY_8,
|
||||||
|
evdev.KEY_9,
|
||||||
|
evdev.KEY_0,
|
||||||
|
evdev.KEY_MINUS,
|
||||||
|
evdev.KEY_EQUAL,
|
||||||
|
evdev.KEY_BACKSPACE,
|
||||||
|
evdev.KEY_TAB,
|
||||||
|
evdev.KEY_Q,
|
||||||
|
evdev.KEY_W,
|
||||||
|
evdev.KEY_E,
|
||||||
|
evdev.KEY_R,
|
||||||
|
evdev.KEY_T,
|
||||||
|
evdev.KEY_Y,
|
||||||
|
evdev.KEY_U,
|
||||||
|
evdev.KEY_I,
|
||||||
|
evdev.KEY_O,
|
||||||
|
evdev.KEY_P,
|
||||||
|
evdev.KEY_LEFTBRACE,
|
||||||
|
evdev.KEY_RIGHTBRACE,
|
||||||
|
evdev.KEY_ENTER,
|
||||||
|
evdev.KEY_LEFTCTRL,
|
||||||
|
evdev.KEY_A,
|
||||||
|
evdev.KEY_S,
|
||||||
|
evdev.KEY_D,
|
||||||
|
evdev.KEY_F,
|
||||||
|
evdev.KEY_G,
|
||||||
|
evdev.KEY_H,
|
||||||
|
evdev.KEY_J,
|
||||||
|
evdev.KEY_K,
|
||||||
|
evdev.KEY_L,
|
||||||
|
evdev.KEY_SEMICOLON,
|
||||||
|
evdev.KEY_APOSTROPHE,
|
||||||
|
evdev.KEY_GRAVE,
|
||||||
|
evdev.KEY_LEFTSHIFT,
|
||||||
|
evdev.KEY_BACKSLASH,
|
||||||
|
evdev.KEY_Z,
|
||||||
|
evdev.KEY_X,
|
||||||
|
evdev.KEY_C,
|
||||||
|
evdev.KEY_V,
|
||||||
|
evdev.KEY_B,
|
||||||
|
evdev.KEY_N,
|
||||||
|
evdev.KEY_M,
|
||||||
|
evdev.KEY_COMMA,
|
||||||
|
evdev.KEY_DOT,
|
||||||
|
evdev.KEY_SLASH,
|
||||||
|
evdev.KEY_RIGHTSHIFT,
|
||||||
|
evdev.KEY_KPASTERISK,
|
||||||
|
evdev.KEY_LEFTALT,
|
||||||
|
evdev.KEY_SPACE,
|
||||||
|
evdev.KEY_CAPSLOCK,
|
||||||
|
evdev.KEY_F1,
|
||||||
|
evdev.KEY_F2,
|
||||||
|
evdev.KEY_F3,
|
||||||
|
evdev.KEY_F4,
|
||||||
|
evdev.KEY_F5,
|
||||||
|
evdev.KEY_F6,
|
||||||
|
evdev.KEY_F7,
|
||||||
|
evdev.KEY_F8,
|
||||||
|
evdev.KEY_F9,
|
||||||
|
evdev.KEY_F10,
|
||||||
|
evdev.KEY_NUMLOCK,
|
||||||
|
evdev.KEY_SCROLLLOCK,
|
||||||
|
evdev.KEY_KP7,
|
||||||
|
evdev.KEY_KP8,
|
||||||
|
evdev.KEY_KP9,
|
||||||
|
evdev.KEY_KPMINUS,
|
||||||
|
evdev.KEY_KP4,
|
||||||
|
evdev.KEY_KP5,
|
||||||
|
evdev.KEY_KP6,
|
||||||
|
evdev.KEY_KPPLUS,
|
||||||
|
evdev.KEY_KP1,
|
||||||
|
evdev.KEY_KP2,
|
||||||
|
evdev.KEY_KP3,
|
||||||
|
evdev.KEY_KP0,
|
||||||
|
evdev.KEY_KPDOT,
|
||||||
|
evdev.KEY_ZENKAKUHANKAKU,
|
||||||
|
evdev.KEY_102ND,
|
||||||
|
evdev.KEY_F11,
|
||||||
|
evdev.KEY_F12,
|
||||||
|
evdev.KEY_RO,
|
||||||
|
evdev.KEY_KATAKANA,
|
||||||
|
evdev.KEY_HIRAGANA,
|
||||||
|
evdev.KEY_HENKAN,
|
||||||
|
evdev.KEY_KATAKANAHIRAGANA,
|
||||||
|
evdev.KEY_MUHENKAN,
|
||||||
|
evdev.KEY_KPJPCOMMA,
|
||||||
|
evdev.KEY_KPENTER,
|
||||||
|
evdev.KEY_RIGHTCTRL,
|
||||||
|
evdev.KEY_KPSLASH,
|
||||||
|
evdev.KEY_SYSRQ,
|
||||||
|
evdev.KEY_RIGHTALT,
|
||||||
|
evdev.KEY_LINEFEED,
|
||||||
|
evdev.KEY_HOME,
|
||||||
|
evdev.KEY_UP,
|
||||||
|
evdev.KEY_PAGEUP,
|
||||||
|
evdev.KEY_LEFT,
|
||||||
|
evdev.KEY_RIGHT,
|
||||||
|
evdev.KEY_END,
|
||||||
|
evdev.KEY_DOWN,
|
||||||
|
evdev.KEY_PAGEDOWN,
|
||||||
|
evdev.KEY_INSERT,
|
||||||
|
evdev.KEY_DELETE,
|
||||||
|
evdev.KEY_MACRO,
|
||||||
|
evdev.KEY_MUTE,
|
||||||
|
evdev.KEY_VOLUMEDOWN,
|
||||||
|
evdev.KEY_VOLUMEUP,
|
||||||
|
evdev.KEY_KPEQUAL,
|
||||||
|
evdev.KEY_KPPLUSMINUS,
|
||||||
|
evdev.KEY_PAUSE,
|
||||||
|
evdev.KEY_SCALE,
|
||||||
|
evdev.KEY_KPCOMMA,
|
||||||
|
evdev.KEY_HANGEUL,
|
||||||
|
evdev.KEY_HANJA,
|
||||||
|
evdev.KEY_YEN,
|
||||||
|
evdev.KEY_LEFTMETA,
|
||||||
|
evdev.KEY_RIGHTMETA,
|
||||||
|
evdev.KEY_COMPOSE,
|
||||||
|
evdev.KEY_F13,
|
||||||
|
evdev.KEY_F14,
|
||||||
|
evdev.KEY_F15,
|
||||||
|
evdev.KEY_F16,
|
||||||
|
evdev.KEY_F17,
|
||||||
|
evdev.KEY_F18,
|
||||||
|
evdev.KEY_F19,
|
||||||
|
evdev.KEY_F20,
|
||||||
|
evdev.KEY_F21,
|
||||||
|
evdev.KEY_F22,
|
||||||
|
evdev.KEY_F23,
|
||||||
|
evdev.KEY_F24,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
CapabilitiesPresetMouse = map[evdev.EvType][]evdev.EvCode{
|
||||||
|
evdev.EV_REL: {
|
||||||
|
evdev.REL_X,
|
||||||
|
evdev.REL_Y,
|
||||||
|
evdev.REL_WHEEL,
|
||||||
|
evdev.REL_HWHEEL,
|
||||||
|
},
|
||||||
|
evdev.EV_KEY: {
|
||||||
|
evdev.BTN_LEFT,
|
||||||
|
evdev.BTN_MIDDLE,
|
||||||
|
evdev.BTN_RIGHT,
|
||||||
|
evdev.BTN_SIDE,
|
||||||
|
evdev.BTN_EXTRA,
|
||||||
|
evdev.BTN_FORWARD,
|
||||||
|
evdev.BTN_BACK,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
Loading…
Add table
Add a link
Reference in a new issue