diff --git a/docs/examples/gamepad/devices.yml b/docs/examples/gamepad/devices.yml index 4064f25..b378800 100644 --- a/docs/examples/gamepad/devices.yml +++ b/docs/examples/gamepad/devices.yml @@ -1,19 +1,7 @@ devices: - name: primary type: virtual - num_axes: 6 - buttons: - - BTN_EAST - - BTN_SOUTH - - BTN_NORTH - - BTN_WEST - - BTN_TL - - BTN_TR - - BTN_SELECT - - BTN_START - - BTN_MODE - - BTN_THUMBL - - BTN_THUMBR + preset: gamepad - name: right-stick type: physical device_name: VIRPIL Controls 20220407 R-VPC Stick MT-50CM2 diff --git a/docs/examples/gamepad/readme.md b/docs/examples/gamepad/readme.md index 81941b1..9c87c8d 100644 --- a/docs/examples/gamepad/readme.md +++ b/docs/examples/gamepad/readme.md @@ -1,5 +1,5 @@ ## joystick -> gamepad mapping -This is an incomplete example for mapping dual flightsticks (Virpil Constellation Alphas) to gamepad outputs, to support dual-joystick play in games that expect a console-style gamepad. This has been tested on Steam, and it successfully recognizes this as a gamepad. +This is an incomplete example for mapping dual flightsticks (Virpil Constellation Alphas) to gamepad outputs, to support dual-joystick play in games that expect a console-style gamepad. This has been tested on Outer Wilds running in Steam. Not every possible input is mapped here, this is just a somewhat minimal example. \ No newline at end of file diff --git a/docs/examples/multiple_files/devices.yml b/docs/examples/multiple_files/devices.yml index 156e132..391e4c8 100644 --- a/docs/examples/multiple_files/devices.yml +++ b/docs/examples/multiple_files/devices.yml @@ -1,18 +1,13 @@ devices: - name: primary type: virtual - num_buttons: 74 - num_axes: 8 + preset: joystick - name: secondary type: virtual - num_buttons: 74 - num_axes: 3 + preset: joystick - name: mouse type: virtual - num_buttons: 0 - num_axes: 0 - rel_axes: - - REL_WHEEL + preset: mouse - name: right-stick type: physical device_name: VIRPIL Controls 20220407 R-VPC Stick MT-50CM2 diff --git a/docs/readme.md b/docs/readme.md index 84a8c74..50dd0fe 100644 --- a/docs/readme.md +++ b/docs/readme.md @@ -2,7 +2,7 @@ Configuration is divided into three sections: `devices`, `modes`, and `rules`. Each yaml file can have any number of these sections; joyful will combine the configuration from all files at runtime. -### Device configuration +## Device configuration Each entry in `devices` must have a couple of parameters: @@ -15,16 +15,17 @@ Each entry in `devices` must have a couple of parameters: `virtual` devices can additionally define these 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). * `buttons` or `num_buttons` - Either a list of explicit buttons or a number of buttons to create. (max 74 buttons) Linux-native games may not recognize all buttons created by Joyful. * `axes` or `num_axes` - An explicit list of `ABS_` axes or a number to create. * `relative_axes` or `num_relative_axes` - As above, but for `REL_` axes. A couple of additional notes on virtual devices: -* For all 3 of the above options, an explicit list will override the `num_` parameters if both are present. -* Some environments will only register mouse events if the device *only* supports mouse-like events, so it can be useful to isolate your `relative_axes` to their own virtual device and explicitly define the axes. +* For all 3 of the above options, there is a priority order. If you specify a `preset`, it will be used ignoring any other settings. An explicit list will override the corresponding `num_` parameter. +* Some environments/applications are prescriptive about what combinations make sense; for example, they will only register mouse events if the device *only* supports mouse-like events. The `presets` attempt to take this into account. If you are defining capabilities manually and attempt to mix and match button codes, you may also run into this problem. -### Rules configuration +## Rules configuration All `rules` must have a `type` parameter. Valid values for this parameter are: diff --git a/internal/config/devices.go b/internal/config/devices.go index d77147d..5bf5e77 100644 --- a/internal/config/devices.go +++ b/internal/config/devices.go @@ -23,10 +23,25 @@ func (parser *ConfigParser) CreateVirtualDevices() map[string]*evdev.InputDevice } name := fmt.Sprintf("joyful-%s", deviceConfig.Name) - 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), + + 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( diff --git a/internal/config/schema.go b/internal/config/schema.go index afb4940..d4e5e0d 100644 --- a/internal/config/schema.go +++ b/internal/config/schema.go @@ -20,6 +20,7 @@ type DeviceConfig struct { Type string `yaml:"type"` DeviceName string `yaml:"device_name,omitempty"` Uuid string `yaml:"uuid,omitempty"` + Preset string `yaml:"preset,omitempty"` NumButtons int `yaml:"num_buttons,omitempty"` NumAxes int `yaml:"num_axes,omitempty"` NumRelativeAxes int `yaml:"num_rel_axes"` diff --git a/internal/config/variables.go b/internal/config/variables.go index 7ca4335..e4e0bf0 100644 --- a/internal/config/variables.go +++ b/internal/config/variables.go @@ -8,6 +8,11 @@ const ( DeviceTypePhysical = "physical" DeviceTypeVirtual = "virtual" + DevicePresetKeyboard = "keyboard" + DevicePresetGamepad = "gamepad" + DevicePresetJoystick = "joystick" + DevicePresetMouse = "mouse" + RuleTypeButton = "button" RuleTypeButtonCombo = "button-combo" RuleTypeLatched = "button-latched" @@ -103,3 +108,281 @@ var ( 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, + }, + } +)