SOLVED Getting keyboard commands in GeDialog()

Hi folks,

I'm wanting to get various CTRL+X key presses in my dialog. I can get keys without the ctrl, and I can get the ctrl without keys, but I can't get any ctrl+key presses. In the dialog's Message() function I've tried variations like:

case BFM_INPUT:
{
BaseContainer KB;
GetInputState(BFM_INPUT_KEYBOARD,BFM_INPUT_CHANNEL,KB);
LONG qualifier = KB.GetLong(BFM_INPUT_QUALIFIER,NULL);
String input = msg.GetString(BFM_INPUT_ASC,"");
}

but none work. They key is always blank if control is pressed.

I've seen a couple of older posts that gave some suggestions, but nothing seemed to work.

Is there a particular way we have to do this? Or does it just not work for our own dialogs?

WP.

Hello @wickedp,

Thank you for reaching out to us. This line in your code,

GetInputState(BFM_INPUT_KEYBOARD, BFM_INPUT_CHANNEL, KB);

strikes me as a bit odd. Why are you passing BFM_INPUT_CHANNEL here as askchannel? Unless I am unaware of some special feature/meaning of this symbol in this context, this call makes little sense to me. You are supposed to pass here the channel, in case of the keyboard, the key, you want to poll. E.g.,

GetInputState(BFM_INPUT_KEYBOARD, KEY_ENTER, bc);
GetInputState(BFM_INPUT_KEYBOARD, static_cast<Int32>(someCharacter), bc);

Then also the qualifier keys should work correctly. When they do not, you could also still just poll the state in succession yourself, similar to the little Python script shown here.

Or could it be that you are here mixing up GetInputEvent and GetInputState? The former will allow you to poll for the current input event (whatever that might be), while the latter will allow you to poll for the truthiness of a certain assumed input event state.

Cheers,
Ferdinand

@ferdinand seems like we might be able to do this:

LONG asciivalue = msg.GetLong(BFM_INPUT_CHANNEL);

which will return the ascii value of the character. Instead of asking for the string, maybe this is a better way?

I probably confused myself on that line of code @ferdinanrd, that said, I've used both ways in previous plugins, and both seem to work. I don't know if it's just dumb luck, or if they built a fail-safe in so that if you pass the flag BFM_INPUT_CHANNEL it returns the value of the KEY_WXYZ instead? Or maybe GetInputState() also sets the BFM_INPUT_QUALIFIER flag (it seems to on mine..), and not just true/false for a particular key. Maybe Maxon could clarify for future reference?

I think the ascii route could work for me.

WP.

Hello @wickedp,

@wickedp said in Getting keyboard commands in GeDialog():

[...] they built a fail-safe in so that if you pass the flag BFM_INPUT_CHANNEL it returns the value of the KEY_WXYZ instead?

It could be. I did not look very closely at the implementation, because all that GUI stuff is usually very laborious to figure out, as you often have to dig around deeply in our GUI and things split into many things in the backend.

In one of the major implementations I did not find any indication that BFM_INPUT_CHANNEL has a special meaning (other than BFM_INPUT_QUALIFIER for example which can be used to poll for any qualifier key). I also searched our own code base and the SDK for instances of GetInputState(.*, BFM_INPUT_CHANNEL, i.e., calls which use it in this way as you did. I could not find anything. But that does not necessarily mean that this does not exist in some dark corner of our GUI backend . Which is why I said in my previous posting:

Unless I am unaware of some special feature/meaning of this symbol in this context [...]

This statement still holds true, as it not easy to decipher the old GUI backend in its entirety. But I would rather lean towards that this is a non-conformant usage, and if it worked, it was probably more an unintended effect.

If you want to query just an arbitrary key + qualifier combination, then you should use GetInputEvent. Find below an example in Python (feel free to ask for a C++ translation if you run into troubles).

Cheers,
Ferdinand

The code:

"""Demonstrates the difference between polling for a specific input event and polling for the 
current event.

This is tailored towards your case of pressing CTRL + X. You can run this in the Script Manager 
(and should either press CTRL + X or other key combinations while executing the script).
"""

import c4d

def main() -> None:
    """
    """
    bc: c4d.BaseContainer = c4d.BaseContainer()

    print ("Querying a specific input state:")
    # Note that you must poll for upper case characters, e.g., X instead of x in your case. ord()
    # is just the Python variant of casting a char to int (the ASCII value of the char).
    c4d.gui.GetInputState(c4d.BFM_INPUT_KEYBOARD, ord ("X"), bc)
    # Test if the queried key is indeed being pressed.
    print (f"{bc[c4d.BFM_INPUT_VALUE] == 1 = }")
    # Test if this did co-occur with a CTRL button press.
    print (f"{bc[c4d.BFM_INPUT_QUALIFIER] == c4d.QUALIFIER_CTRL = }")

    print ("Querying the current input state:")
    bc.FlushAll()
    c4d.gui.GetInputEvent(c4d.BFM_INPUT_KEYBOARD, bc)
    # chr () is the opposite to ord()
    print (f"{bc[c4d.BFM_INPUT_CHANNEL] = } ({chr (bc[c4d.BFM_INPUT_CHANNEL])})")
    print (f"{bc[c4d.BFM_INPUT_QUALIFIER] == c4d.QUALIFIER_CTRL = }")


if __name__ == '__main__':
    main()

This will spit out the following lines when pressing CTRL+X:

Querying a specific input state:
bc[c4d.BFM_INPUT_VALUE] == 1 = True
bc[c4d.BFM_INPUT_QUALIFIER] == c4d.QUALIFIER_CTRL = True
Querying the general input state:
bc[c4d.BFM_INPUT_CHANNEL] = 88 (X)
bc[c4d.BFM_INPUT_QUALIFIER] == c4d.QUALIFIER_CTRL = True

And these lines when pressing CTRL+Y for example:

Querying a specific input state:
bc[c4d.BFM_INPUT_VALUE] == 1 = False
bc[c4d.BFM_INPUT_QUALIFIER] == c4d.QUALIFIER_CTRL = True
Querying the general input state:
bc[c4d.BFM_INPUT_CHANNEL] = 89 (Y)
bc[c4d.BFM_INPUT_QUALIFIER] == c4d.QUALIFIER_CTRL = True

Thanks @ferdinand,

I'll use the KEY_WXYZ as you pointed out, if that's the correct usage, I should use it. And the python code you shared gave me an idea to convert the ASCII numbers to a String() as well. Things now seem to work the way I had intended them to.

I can appreciate not wanting to dig into old interface designs - it's what I'm attempting to rewrite at the moment! 🙂

Solved.

WP.