Support specifying physical devices via device file instead of device name. (#15)
Fixes https://codeberg.org/annabunches/joyful/issues/2 Reviewed-on: #15 Co-authored-by: Anna Rose Wiggins <annabunches@gmail.com> Co-committed-by: Anna Rose Wiggins <annabunches@gmail.com>
This commit is contained in:
parent
890c19f1dc
commit
329058b4b5
5 changed files with 64 additions and 10 deletions
|
@ -43,6 +43,32 @@ func printDevice(devPath evdev.InputPath) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get metadata
|
||||||
|
// metadata := struct {
|
||||||
|
// uuid string
|
||||||
|
// vendor string
|
||||||
|
// product string
|
||||||
|
// version string
|
||||||
|
// }{}
|
||||||
|
|
||||||
|
// uuid, err := device.UniqueID()
|
||||||
|
// if err != nil {
|
||||||
|
// metadata.uuid = "unknown"
|
||||||
|
// } else {
|
||||||
|
// metadata.uuid = uuid
|
||||||
|
// }
|
||||||
|
|
||||||
|
// inputId, err := device.InputID()
|
||||||
|
// if err != nil {
|
||||||
|
// metadata.vendor = "unknown"
|
||||||
|
// metadata.product = "unknown"
|
||||||
|
// metadata.version = "unknown"
|
||||||
|
// } else {
|
||||||
|
// metadata.vendor = "0x" + strconv.FormatUint(uint64(inputId.Vendor), 16)
|
||||||
|
// metadata.product = "0x" + strconv.FormatUint(uint64(inputId.Product), 16)
|
||||||
|
// metadata.version = strconv.FormatUint(uint64(inputId.Version), 10)
|
||||||
|
// }
|
||||||
|
|
||||||
// Get axis info
|
// Get axis info
|
||||||
var axisOutputs []string
|
var axisOutputs []string
|
||||||
absInfos, err := device.AbsInfos()
|
absInfos, err := device.AbsInfos()
|
||||||
|
@ -56,8 +82,13 @@ func printDevice(devPath evdev.InputPath) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Print everything
|
||||||
fmt.Printf("%s:\n", devPath.Path)
|
fmt.Printf("%s:\n", devPath.Path)
|
||||||
fmt.Printf("\tName: '%s'\n", devPath.Name)
|
fmt.Printf("\tName:\t\t'%s'\n", devPath.Name)
|
||||||
|
// fmt.Printf("\tUUID:\t\t'%s'\n", metadata.uuid)
|
||||||
|
// fmt.Printf("\tVendor:\t\t'%s'\n", metadata.vendor)
|
||||||
|
// fmt.Printf("\tProduct:\t'%s'\n", metadata.product)
|
||||||
|
// fmt.Printf("\tVersion:\t'%s'\n", metadata.version)
|
||||||
if len(axisOutputs) > 0 {
|
if len(axisOutputs) > 0 {
|
||||||
fmt.Println("\tAxes:")
|
fmt.Println("\tAxes:")
|
||||||
for _, str := range axisOutputs {
|
for _, str := range axisOutputs {
|
||||||
|
@ -76,7 +107,7 @@ func printDeviceQuiet(devPath evdev.InputPath) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("'%s'\n", devPath.Name)
|
fmt.Printf("'%s': '%s'\n", devPath.Path, devPath.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: it would be nice to be able to specify a device by name or device file and get axis info
|
// TODO: it would be nice to be able to specify a device by name or device file and get axis info
|
||||||
|
|
|
@ -9,11 +9,21 @@ Each entry in `devices` must have these parameters:
|
||||||
* `name` - This is an identifier that your rules will use to refer to the device. It is recommended to avoid spaces or special characters.
|
* `name` - This is an identifier that your rules will use to refer to the device. It is recommended to avoid spaces or special characters.
|
||||||
* `type` - 'physical' for an input device, 'virtual' for an output device.
|
* `type` - 'physical' for an input device, 'virtual' for an output device.
|
||||||
|
|
||||||
|
### Physical Devices
|
||||||
|
|
||||||
`physical` devices have these additional parameters:
|
`physical` devices have these additional parameters:
|
||||||
|
|
||||||
* `device_name` (required) - The name of the device as reported by the included `evinfo` command. If your device name ends with a space, use quotation marks (`""`) around the name.
|
* `device_name` - The name of the device as reported by the included `evinfo` command. If your device name ends with a space, use quotation marks (`""`) around the name.
|
||||||
|
* `device_path` - If you have multiple devices that report the same name, you can use `device_path` instead of `device_name`. Setting this will cause the device to be opened directly via the device file.
|
||||||
|
* It is recommended to use the `by-path` symlinks, e.g., `/dev/input/by-path/pci-0000:0d:00.0-usbv2-0:3:1.0-event-joystick`.
|
||||||
|
* Note that this method may be slightly unreliable since these identifiers may change if they are plugged into different USB ports or in the rare case that the USB topology changes (e.g., you add a new USB hub).
|
||||||
|
* On the other hand, this method causes the device to be opened considerably faster, lowering Joyful's startup time substantially. If this is important to you this method may be preferable.
|
||||||
* `lock` - If set to 'true', the device will be locked for exclusive access. This means that your game will not see any events from the device, so you'll need to make sure you map every button you want to use. Setting this to 'false' might be useful if you're just mapping a few joystick buttons to keyboard buttons. This value defaults to 'true'.
|
* `lock` - If set to 'true', the device will be locked for exclusive access. This means that your game will not see any events from the device, so you'll need to make sure you map every button you want to use. Setting this to 'false' might be useful if you're just mapping a few joystick buttons to keyboard buttons. This value defaults to 'true'.
|
||||||
|
|
||||||
|
`device_path` is given higher priority than `device_name`; if both are specified, `device_path` will be used.
|
||||||
|
|
||||||
|
### Virtual Devices
|
||||||
|
|
||||||
`virtual` devices have these additional parameters:
|
`virtual` devices have these additional parameters:
|
||||||
|
|
||||||
* `preset` - Can be 'joystick', 'gamepad', 'mouse', or 'keyboard', and will configure the virtual device to look like and emit an appropriate set of outputs based on the name. For exactly which axes and buttons are defined for each type, see the `Capabilities` values in [internal/config/variables.go](internal/config/variables.go).
|
* `preset` - Can be 'joystick', 'gamepad', 'mouse', or 'keyboard', and will configure the virtual device to look like and emit an appropriate set of outputs based on the name. For exactly which axes and buttons are defined for each type, see the `Capabilities` values in [internal/config/variables.go](internal/config/variables.go).
|
||||||
|
|
|
@ -88,21 +88,32 @@ func (parser *ConfigParser) ConnectPhysicalDevices() map[string]*evdev.InputDevi
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
device, err := evdev.OpenByName(deviceConfig.DeviceName)
|
var infoName string
|
||||||
|
var device *evdev.InputDevice
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if deviceConfig.DevicePath != "" {
|
||||||
|
infoName = deviceConfig.DevicePath
|
||||||
|
device, err = evdev.Open(deviceConfig.DevicePath)
|
||||||
|
} else {
|
||||||
|
infoName = deviceConfig.DeviceName
|
||||||
|
device, err = evdev.OpenByName(deviceConfig.DeviceName)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.LogError(err, "Failed to open physical device, skipping. Confirm the device name with 'evlist'. Watch out for spaces.")
|
logger.LogError(err, "Failed to open physical device, skipping. Confirm the device name or path with 'evinfo'")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if deviceConfig.Lock {
|
if deviceConfig.Lock {
|
||||||
logger.LogDebugf("Locking device '%s'", deviceConfig.DeviceName)
|
logger.LogDebugf("Locking device '%s'", infoName)
|
||||||
err := device.Grab()
|
err := device.Grab()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.LogError(err, "Failed to grab device for exclusive access")
|
logger.LogError(err, "Failed to grab device for exclusive access")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Log(fmt.Sprintf("Connected to '%s' as '%s'", deviceConfig.DeviceName, deviceConfig.Name))
|
logger.Log(fmt.Sprintf("Connected to '%s' as '%s'", infoName, deviceConfig.Name))
|
||||||
deviceMap[deviceConfig.Name] = device
|
deviceMap[deviceConfig.Name] = device
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ type DeviceConfig struct {
|
||||||
Name string `yaml:"name"`
|
Name string `yaml:"name"`
|
||||||
Type string `yaml:"type"`
|
Type string `yaml:"type"`
|
||||||
DeviceName string `yaml:"device_name,omitempty"`
|
DeviceName string `yaml:"device_name,omitempty"`
|
||||||
Uuid string `yaml:"uuid,omitempty"`
|
DevicePath string `yaml:"device_path,omitempty"`
|
||||||
Preset string `yaml:"preset,omitempty"`
|
Preset string `yaml:"preset,omitempty"`
|
||||||
NumButtons int `yaml:"num_buttons,omitempty"`
|
NumButtons int `yaml:"num_buttons,omitempty"`
|
||||||
NumAxes int `yaml:"num_axes,omitempty"`
|
NumAxes int `yaml:"num_axes,omitempty"`
|
||||||
|
@ -64,7 +64,7 @@ func (dc *DeviceConfig) UnmarshalYAML(unmarshal func(data interface{}) error) er
|
||||||
Name string
|
Name string
|
||||||
Type string
|
Type string
|
||||||
DeviceName string `yaml:"device_name"`
|
DeviceName string `yaml:"device_name"`
|
||||||
Uuid string
|
DevicePath string `yaml:"device_path"`
|
||||||
Preset string
|
Preset string
|
||||||
NumButtons int `yaml:"num_buttons"`
|
NumButtons int `yaml:"num_buttons"`
|
||||||
NumAxes int `yaml:"num_axes"`
|
NumAxes int `yaml:"num_axes"`
|
||||||
|
@ -85,7 +85,7 @@ func (dc *DeviceConfig) UnmarshalYAML(unmarshal func(data interface{}) error) er
|
||||||
Name: raw.Name,
|
Name: raw.Name,
|
||||||
Type: raw.Type,
|
Type: raw.Type,
|
||||||
DeviceName: raw.DeviceName,
|
DeviceName: raw.DeviceName,
|
||||||
Uuid: raw.Uuid,
|
DevicePath: raw.DevicePath,
|
||||||
Preset: raw.Preset,
|
Preset: raw.Preset,
|
||||||
NumButtons: raw.NumButtons,
|
NumButtons: raw.NumButtons,
|
||||||
NumAxes: raw.NumAxes,
|
NumAxes: raw.NumAxes,
|
||||||
|
|
|
@ -48,6 +48,8 @@ If you are on Arch or an Arch-based distro, you can get the latest Joyful releas
|
||||||
yay -S joyful
|
yay -S joyful
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You may also need to add the user(s) who will be running joyful to the `input` group.
|
||||||
|
|
||||||
### Manual Install
|
### Manual Install
|
||||||
|
|
||||||
To build joyful manually, first use your distribution's package manager to install the following dependencies:
|
To build joyful manually, first use your distribution's package manager to install the following dependencies:
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue