Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add builtin binding support #473

Open
wants to merge 9 commits into
base: r13
Choose a base branch
from

Conversation

edwardwbarber
Copy link

@edwardwbarber edwardwbarber commented Dec 30, 2022

This pull request adds bindings: a new way to select VJoy outputs for remap containers. A binding string, when assigned to a VJoy output, becomes an alternative dropdown selection within the remap VJoy selector. I have a short write up of how this binding process compares to R13 here. The short version is, bindings can simplify profile management within Joystick Gremlin by displaying the intended link from physical inputs to sim buttons/axes directly, instead of only showing the VJoy intermediary.

For example, if the binding "pitch" is set to VJoy 1 Y Axis, then the user can choose to remap a physical axis to VJoy 1 Y Axis (as usual) OR select "pitch" from the binding dropdown. In either case, the unused selector(s) will automatically synchronize with the chosen binding/VJoy output: in the end the remap will show the usual VJoy 1 / Y Axis dropdowns and, now, the "pitch" binding selection. The binding selection does not remove the existing method - it is simply an alternative option if the user wishes to use it.

Since most profiles can use large numbers of bindings, new Binding Exporter and Binding Importer tools are also included in this pull request. These allow VJoy-binding pairs both to be exported to, and imported from, sim configuration files. Sample scripts are provided for XPlane 11 and IL-2 Cliffs of Dover. An importer is also provided for CSV files, which can be convenient for importing from a generic command list. Custom scripts can also be added by users as needed. Guides for writing custom exporters and importers are included in exporter_plugins/README.md and importer_plugins/README.md.

To make all this work under the hood, this pull request introduces a new BoundVJoy class to track all VJoy InputItems which correspond to a specific binding over all Modes. Additional logic is added within the Profile class to allow for binding-based look-ups and to handle when conflicting bindings are added. A profile conversion is included to update old profiles (version 9 and lower) to include blank "binding" attributes in the profile XML file. However, since the output used by Remap is the same, all existing profile functionality is preserved.

In the UI, there are four main changes: (1) output VJoy device tabs now display a binding input box, similar to the existing description input box; (2) VJoy buttons are shown on VJoy device tabs, along with their bindings; (3) remap VJoy selectors now have a third dropdown menu for binding selection; (4) Binding Export and Binding Import dialogs are now available under Tools. Examples of these are included below.

New UI Example Change Description
new_vjoy_tab VJoy tab with buttons, binding entry box, and binding text display
new_remap Remap includes binding dropdown
new_toolsbinding_importbinding_export Binding import and export tools

For this pull request, I've combined much of the work into a few "feature-complete" commits, to help better identify which sets of changes go together. The commits themselves should lend more detail on the reasons for the specific changes added. Hopefully this makes it easier to see where the main changes / additions to the existing code are and why.

As a final note, I am well aware this does not fit into the scheme for the R14 release. Nonetheless, it is a feature I wanted for myself, so I went ahead and made it. On my own develop fork, I've included this work and a few R13 bugfixes (unrelated to this pull request) - if you want to give a working version a spin my that would be a good place to start. I've personally been using this setup for configuring new profiles over the past month or so. Finally, I added a conda environment file to make the build process a little easier, but that's the topic of another pull request... cheers!

    - Mirrors functionality of `vjoy_id_from_guid`
    - Required for efficient binding setting
    Modify Profile class:
    - Provide new functions to vjoy inputs
        - Replace `list_unused_vjoy_inputs` with `get_unused_vjoy_inputs`
        - Add `get_unbound_vjoy_inputs`
        - Add `get_first_free_vjoy_input_of_type`
        - Add `get_all_vjoy_inputs`
        - Add `get_vjoy_from_binding`
        - Add `get_bindings_of_type`
        - Add `get_all_bound_vjoys`
        - Add `get_binding_from_vjoy`
        - Add `has_unbound_vjoys`
    - Provide new functions to manage profile-wide bindings
        - Add `update_bound_vjoy_description`
        - Add `sync_device_bindings`
    - Add `_empty_input_type_dict` helper
    - Save bindings to file in `to_xml`
    - Read bindings from file in `from_xml`
    - Increment profile version to 10 and add converter

    Add BoundVJoy class:
    - Manages binding assigned to VJoy input item across modes

    Modify Device class:
    - Populate existing bindings to new modes during creation

    Modify Mode class:
    - Replace hard-coded functionality with helper from Profile
    - Add `all_input_items_of_type` helper

    Modify InputItem class:
    - Add binding data to properties
    - Add binding data to xml to/from
    - Add `clear_binding` helper function to clear description as well
    - Add sister helper functions `get_mode`, `get_device`, and `get_profile`
    - Add binding edit line to VJoy device tabs
    - Link description and binding lines to profile backend
    - Update `InputItemButton` to show current binding
    - Show button items on VJoy device tabs
    - Add binding support to VJoySelector

    This update allows the user to manually enter bindings on each VJoy
    input and to see those bindings on each VJoy device tab. Most
    critically, this changes the behavior of VJoySelector to support
    selection via binding. That is, if a binding is selected, the
    corresponding VJoy device and input is shown. Similarly, if a VJoy
    device and input are selected, the corresponding binding is shown,
    if any.

    There is no change to the data returned by the VJoy selector to
    downstream plugins (Remap, etc.). The VJoy ID, input type, and
    input id are returned. Binding data is only used by the selector
    itself.

    These changes allow Joystick Gremlin to retain all previous
    functionality, with the added ability to include a "binding" as an
    alternative way to identify a specific VJoy input.
    When a VJoy output device is toggled to an "input" device, any
    bindings which were previously assigned should no longer be
    accessible. This update clears all bindings for VJoy input devices.
    Removed bindings are logged. No attempt is made to restore binding
    data if that VJoy device is subsequently turned back into an
    "output" device.

    - Add `clear_device_bindings` to Profile class
    - Call `clear_device_bindings` from VJoyAsInputWidget
    - Remap uses new VJoySelector with binding support
    - Unused inputs are now found with `profile.get_unused_vjoy_inputs()`
      instead of now-defunct `profile.list_unused_vjoy_inputs()`
    - Can now find other vjoys with unused inputs (previously
      limited to vjoy 1)
    - On save: associated binding saved to remap xml node, if any
    - On read: updates vjoy selection to match given binding, if any

    This fix is compatible with old profiles. Remap nodes in old
    profiles will be updated on first save.
  This change provides the user with a means to bulk import and export
  profile bindings from/to program-specific files. Two new UI dialogs
  are added to support these functions, as well as additional back-end
  profile functions, and additional data written to profile and to
  Gremlin's config file.

  - Add UI elements and dialog boxes for exporter/importer utilities
  - Add note about import/export utilities to VJoy device tab
  - Add custom importer plugin readme
  - Add custom exporter plugin readme
  - Add profile-agnostic importer/exporter data to Gremlin config
  - Add profile-specific importer/exporter data to profile settings
  - Add importer/exporter settings to xml to/from methods
  - Add `update_bound_vjoy_registry` function to Profile
  - Add `update_bound_vjoy_registry_from_dict` function to Profile
  - Add importer/exporter plugin error handling

  Profile-agnostic import/export preferences, such as the path to custom
  import/export scripts, are added to Gremlin's config settings.
  Profile-specific import/export settings are saved to profile.

  Back-end functions are added to Profile to allow for batch binding
  import. These check for ill-formed bindings, duplicates, and excess
  numbers of bindings for the available number of VJoy inputs. Errors
  are thrown / cautions logged as appropriate.
    Use of `profile.list_unused_vjoy_inputs()` is replaced with
    `profile.get_unused_vjoy_inputs()`. New function uses input_type
    directly, so the previous intermediary dict has been removed.
@WhiteMagic
Copy link
Owner

Nice, and quite a lot of work, as I'm preventing myself from touching R13 I can't really integrate it in but anyone that's keen on trying this out should definitely give your repo a spin.

As for how it relates to R14. Well all of the UI and profile related aspects are not reusable as that changed (some is still changing). There won't be something as elaborate as you have done here but it will have the option to assign existing, lets call them "action trees", i.e. what currently is a container , to an input rather than specifying it from scratch. The main purpose of this is to copy things around and speed up minor modifications. Though that opens the door to have a "library" of such "action trees" that perform specific mappings and have a name.

The other bit that I'm trying to work out at the moment is how to get rid of the need to have vJoy tabs. The only reason they exist is that merge axis lives outside the normal input -> action realm and thus can't benefit from response curves etc. The currently plan is to make it so every action lives on physical inputs. However, that then requires an "intermediate output" layer that can be mapped to vJoy devices and have all kinds of other operations applied to it.

Another upside of R14 is that it shouldn't be too hard to add an action that has your remap logic in it. How to feed it with data is a bit of a different story but probably not impossible to figure out either. The other bit that goes along with that is that Gremlin should also be able to load actions not shipped with Gremlin directly by telling it where to look for them. Technically that was possible in R13 and before ... the code just never got told to do so.

@edwardwbarber
Copy link
Author

Thanks for the kind words. I respect wanting to focus your effort on R14. I'm not sure I completely follow what you have in mind for "action trees" in R14 but I'm eager to see those changes when they are ready. Happy to adapt features of what I have here to the new release too, if applicable (I'm quite pleased with how the import/export functions worked out, for example).

@WhiteMagic
Copy link
Owner

They are a bit weird to explain in just words but effectively they are just a unit of stuff to do that are stored in a central library (per profile) and then can be assigned to the individual inputs. They can still be created on the fly as normal but once created it's possible to reuse them. Them being stored in a library means it's possible to import entire collection of them into a profile.

@artesim
Copy link

artesim commented Feb 8, 2023

Hi, just wanted to drop a big "thank you", because this pull request is exactly what I need, I'll definitely check it out.
Yesterday I was writing a little script to generate an Elite Dangerous config file where every single possible binding is assigned its own vjoy button, and extracted a map of "ED action" => "vjoy button" from all that.
And my next step was supposed to be checking out Gremlin's code and see if I could implement exactly that...
So yes, you're not alone, there's a real need for what you did here, great job ! :)

@edwardwbarber
Copy link
Author

@artesim thanks for the kind words. I hope this suits your needs - if you have any questions, feel free to ping me on the gremlin discord channel. I don't have an importer/exporter written for Elite Dangerous, but it's probably not too hard to write one. If you do decide to, I'd be interested to see what you come up with. Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants