WhiteMagic / JoystickGremlin

A tool for configuring and managing joystick devices.
http://whitemagic.github.io/JoystickGremlin/
GNU General Public License v3.0
313 stars 46 forks source link

Bug: Activation Condition Comparison in XML overwritten when loaded in UI #361

Open VegaAntares opened 3 years ago

VegaAntares commented 3 years ago

Hello,

I found an annoying bug but hopefully easy to fix. The problem is the "comparison" item is set to "" (empty string) in the concrete classes (base_classes.VJoyCondition in my example) since the self.comparison is inherited from AbstractCondition that initialize it this default value because I guess correct initialization value depends of the InputType

So in my example such xml shows "pressed" in the comparison drop down:

   <button description="" id="5"">
      <container type="basic"><br/>
          <action-set>
              <map-to-keyboard>
                  <key extended="False" scan-code="37"/>  
                  <activation-condition rule="all">  
                      <condition comparison="released" condition-type="vjoy" id="31" input="button" vjoy-id="1"/>  
                  </activation-condition>  
              </map-to-keyboard> 
          </action-set>  
      </container> 
  </button>

Then VJoyConditionWidget.init receive the correct condition_data argument

class VJoyConditionWidget(AbstractConditionWidget):
   def __init__(self, condition_data, parent=None):
        # ...
        super().__init__(condition_data, parent)   # <- Set the comparison value
        # ...
        self._modify_vjoy(self.vjoy_selector.get_selection()) #<- override the value

So you should then only override the value if self.condition_data.comparison is an empty string

    if data["input_type"] == InputType.JoystickAxis:
        self.condition_data.comparison = "inside"
    elif data["input_type"] == InputType.JoystickButton:
        self.condition_data.comparison = "pressed"
    elif data["input_type"] == InputType.JoystickHat:
        self.condition_data.comparison = \
              util.hat_tuple_to_direction((0, 0))

And the same for all other condition Widgets Of course this bug is not seen when one use already the default value in the XML

WhiteMagic commented 3 years ago

This bug has been in there for a bit and I think only affects vJoy conditions, which is why it's not commonly encountered. There might already be an issue for this though I couldn't find it when looking just then. The fix for this bug is going to be that this entire piece of code won't exist anymore in the next release as the entire condition stuff, just like just about everything else, is being redone.

VegaAntares commented 3 years ago

Yes only vJoy. Actually this feature is pretty neat since I use the vJoy buttons as boolean for keeping track of states. I mean I do not remap them into the game but actions triggeted may depends on their state. This avoids some python plugins I made initially. Just stated that to give ideas to potential users and underline how cool it is you do support such feature.

WhiteMagic commented 3 years ago

Yeah, that's the most common use I've seen, kind of a real hacky way of doing things :-) Obviously, it would be cleaner if there were actual states assignable etc. but I don't see this ever happening, as at that stage my reply would be "you're better of writing a simple plugin" as those are easy to change and reuse.

JokerGrandma commented 3 years ago

The "released" condition comparison based on a VJoy button input is changed to "pressed" when the physical button/axis is displayed in Joystick Gremlin UI.

I can propose 2 workarounds to this bug until it get fixed:

  1. Don't display the command in the UI. Edit the .xml file and replace "pressed" with "released" where relevant. Load the edited file in Joystick Gremlin: the condition will remain "released" until you save it again. The X-axis is always displayed therefore a condition based on a Vjoy button is always overwritten.

  2. Only use the "pressed" condition. Chain several macros that press 1 Vjoy button and unpress another 1. And check which Vjoy button currently is "pressed". Like a radio-button. Example with 2 states:

A. selecting the state (either 1 or 2)

                <button description="channel selector" id="5">
                    <container timeout="0.0" type="chain">
                        <action-set>
                            <text-to-speech text="state 1"/>
                            <macro>
                                <properties/>
                                <actions>
                                    <vjoy input-id="2" input-type="button" value="False" vjoy-id="1"/>
                                    <vjoy input-id="1" input-type="button" value="TRUE" vjoy-id="1"/>
                                </actions>
                            </macro>
                        </action-set>
                        <action-set>
                            <text-to-speech text="state 2"/>
                            <macro>
                                <properties/>
                                <actions>
                                    <vjoy input-id="1" input-type="button" value="False" vjoy-id="1"/>
                                    <vjoy input-id="2" input-type="button" value="TRUE" vjoy-id="1"/>
                                </actions>
                            </macro>
                        </action-set>
                    </container>
                </button>

B. using that state:

                <button description="Push To Talk" id="6">
                    <container type="basic">
                        <action-set>
                            <description description="Discord voice channel"/>
                            <remap button="3" vjoy="1">
                                <activation-condition rule="all">
                                    <condition comparison="pressed" condition-type="vjoy" id="1" input="button" vjoy-id="1"/>
                                </activation-condition>
                            </remap>
                            <description description="Ingame voice channel"/>
                            <remap button="4" vjoy="1">
                                <activation-condition rule="all">
                                    <condition comparison="pressed" condition-type="vjoy" id="2" input="button" vjoy-id="1"/>
                                </activation-condition>
                            </remap>
                        </action-set>
                    </container>
                </button>

works with n states.

lorthirk commented 2 years ago

So... I just wanted to chime in on this because I'm also basically blocked by this bug. My scenario: I want a given button to act as a button if clicked, and as a shift if held. First approach I took: use the Tempo action with Remap as short and Temporary Mode Switch if long: failed, because when I release the button after the mode shift, it doesn't return to the default state (I saw a comment on r/Hotas I think where this is kinda a known issue). I then tried to map my Tempo Long action to a different vJoy button, so that I can use it as a condition for other actions (what @VegaAntares suggests in the first comment basically) and I have this bug instead. So... If I get it correctly, I am out of luck?

TheGoodIdeaFairy commented 1 year ago

Yes only vJoy. Actually this feature is pretty neat since I use the vJoy buttons as boolean for keeping track of states. I mean I do not remap them into the game but actions triggeted may depends on their state. This avoids some python plugins I made initially. Just stated that to give ideas to potential users and underline how cool it is you do support such feature.

Hey, didn't realize other people were using this method. I've also started using vJoy flags as binary encoders for more complex functions like timer based pseudo-macros. If you manually reassign the GUID of a Joystick type condition you can use either pressed or released on a vJoy and it's stable.

TheGoodIdeaFairy commented 1 year ago

So... I just wanted to chime in on this because I'm also basically blocked by this bug. My scenario: I want a given button to act as a button if clicked, and as a shift if held. First approach I took: use the Tempo action with Remap as short and Temporary Mode Switch if long: failed, because when I release the button after the mode shift, it doesn't return to the default state (I saw a comment on r/Hotas I think where this is kinda a known issue). I then tried to map my Tempo Long action to a different vJoy button, so that I can use it as a condition for other actions (what @VegaAntares suggests in the first comment basically) and I have this bug instead. So... If I get it correctly, I am out of luck?

I've found it to be less buggy if I use a mode switch to go to the secondary/momentary layer, then have the release action of that button within that secondary layer to be another mode switch back to the primary layer. This should solve your issue.