Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Getting keyboard commands in GeDialog()

    Cinema 4D SDK
    c++ sdk
    3
    7
    1.3k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • WickedPW
      WickedP
      last edited by

      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.

      wickedp.com

      ferdinandF 1 Reply Last reply Reply Quote 1
      • ferdinandF
        ferdinand @WickedP
        last edited by ferdinand

        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

        MAXON SDK Specialist
        developers.maxon.net

        WickedPW 1 Reply Last reply Reply Quote 0
        • WickedPW
          WickedP @ferdinand
          last edited by

          @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.

          wickedp.com

          ferdinandF 1 Reply Last reply Reply Quote 0
          • ferdinandF
            ferdinand @WickedP
            last edited by ferdinand

            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
            

            MAXON SDK Specialist
            developers.maxon.net

            WickedPW 1 Reply Last reply Reply Quote 0
            • WickedPW
              WickedP @ferdinand
              last edited by

              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.

              wickedp.com

              1 Reply Last reply Reply Quote 0
              • C4DSC
                C4DS
                last edited by

                I didn't follow this topic in detail when it was originally posted, but I now noticed that in several of my plugins I do also use
                GetInputState(BFM_INPUT_KEYBOARD, BFM_INPUT_CHANNEL, KB);
                in order to check for possible qualifiers being pressed.

                I guess this must have been picked up in the old forum, as the code is quite some years old.
                It may not have been the intended way, but it seems to have been working all those years. At least till R21, as I haven't worked on more recent versions.

                In the old forums there were quite some pieces of "example" code scattered around in posts, and back then it was a way to find out how to use some of the available functionality, due to limited documentation.

                Speaking of documentation:
                I just noticed that https://developers.maxon.net/docs/cpp/2023_2/class_scene_hook_data.html
                lists MouseInput and KeyboardInput both pointing to "Input Events" for their last function argument
                [in] msg The keyboard message container. See Input Events.
                But the link just points back to the beginning of the page.
                Where to find more details about these "Input Events" ???

                ferdinandF 1 Reply Last reply Reply Quote 0
                • ferdinandF
                  ferdinand @C4DS
                  last edited by ferdinand

                  Hello @c4ds,

                  thank you for reaching out to us.

                  GetInputState()

                  Just as a FYI, while you not might remember me, I was actually already around on the old forum 😉 So, I had a look again, looking at our:

                  1. Codebase: Nothing is using BFM_INPUT_CHANNEL in this manner, not even really, really, really old code.
                  2. Plugin Café History: Yes, there are some mentions of GetInputState(BFM_INPUT_KEYBOARD, BFM_INPUT_CHANNEL, but these come exclusively from users who
                    a. Have problems with retreiveing the pressed key
                    b. Not use the method to retreive any speciifc key and are only intersted in modifiers or other data provided in the response container.
                  3. I did not find any code examples posted by Maxon using this form.

                  I do not want to be rude here, but what you are doing is not correct and likely never was. This code floated around on Plugin Café and apparently people were copying each other but the code is at least now simply wrong (when one is interested in polling specific keys).

                  Yes, you can use GetInputState(BFM_INPUT_KEYBOARD, BFM_INPUT_CHANNEL, bc) and this will not crash or any thing, but it is also meaningless. You could also call GetInputState(BFM_INPUT_KEYBOARD, 123456789, bc) and it would be as meaningful. If you are not interested in a specfic key being pressed, than this will also 'work' for you as the askchannel argument is then irrelevant.

                  But we should really stop indicating that this is the right way to go, because that is where all the confusion comes from. Find a Python script at the end of the posting which in detail demonstrates this. Please stop indicating that this is a valid approach, as people will read this and then the cycle starts over again.

                  Input Events Page for the C++ Documentation

                  Thank you for making me aware of this. The target of these links, page_input_events has long been removed. I still have access to that archived page, and while the page was not too useful, I can only speculate why it has been done in this form. I will fix this this in an upcoming release. In the meantime, you should look at:

                  • the BFM Symbol Group: This however does not contain only input event symbols, but all GUI related event symbols. Input event related symbols folllow the form BFM_INPUT_... plus things like the KEY symbols or
                  • the Input Events page of the Python docs which is much better. Something like this unfournetely never existed in C++ the old C++ page was entierly different. Maybee it got removed in order to make room for something similar to the Python page, and then things were somehow not carried out?

                  Long story short, I will provide a meaningful input events page for C++ in the upcoming release. In the meantime I would recommend the Python page.

                  Cheers,
                  Ferdinand

                  The code for GetInputState(BFM_INPUT_KEYBOARD, BFM_INPUT_CHANNEL, bc) being not a meaningful thing:

                  """Demonstrates that #BFM_INPUT_CHANNEL does not carry any meaning as the argument #askchannel for
                  GetInputState.
                  """
                  
                  import c4d
                  
                  def main() -> None:
                      """
                      """
                      bc: c4d.BaseContainer = c4d.BaseContainer()
                      if not (c4d.gui.GetInputState(c4d.BFM_INPUT_KEYBOARD, c4d.BFM_INPUT_CHANNEL, bc)):
                          raise RuntimeError("Could not poll input event.")
                  
                      # Iterate over the container, there is nothing meaningful in there when you want to query a 
                      # specific key.
                      print ("\nThe result container:")
                      for key, value in bc:
                          print (f"key: {key}, value: {value}")
                      
                      # And here some selected values to be a bit more verbose.
                      print ("\nSelected values:")
                      print (f"{bc[c4d.BFM_INPUT_CHANNEL] = }") # Will return 1768973153, i.e., itself.
                      print (f"{bc[c4d.BFM_INPUT_VALUE] = }") # Will return False.
                      print (f"{bc[c4d.BFM_INPUT_ASC] = }") # Will return the empty string.
                  
                      # We can of course still use the result container to poll for things that are also evaluated as
                      # for example modifier keys (or the mouse position in case of the mouse device).
                      print ("\nThe container is still valid for other stuff, but BFM_INPUT_CHANNEL is" +
                             "meaningless in that context:")
                      print (f"BFM_INPUT_CHANNEL: {bc[c4d.BFM_INPUT_MODIFIERS] = }")
                  
                      # But we would not have to use BFM_INPUT_CHANNEL for that, we also just could do this here:
                      bc: c4d.BaseContainer = c4d.BaseContainer()
                      if not (c4d.gui.GetInputState(c4d.BFM_INPUT_KEYBOARD, 123456789, bc)):
                          raise RuntimeError("Could not poll input event.")
                      print (f"123456789: {bc[c4d.BFM_INPUT_MODIFIERS] = }")
                  
                      # And just for completeness, in case someone is just reading this snippet, here is how you
                      # should use these functions:
                      print ("\nHow it should be done:")
                  
                      bc: c4d.BaseContainer = c4d.BaseContainer()
                  
                      # Querying for a specific key with GetInputState.
                  
                      print ("\nQuerying a specific input state:")
                      # Note that you must poll for upper case characters, e.g., X instead of x.
                      if not c4d.gui.GetInputState(c4d.BFM_INPUT_KEYBOARD, ord ("X"), bc):
                          raise RuntimeError("Failed to query input events.")
                      # 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 = }")
                  
                      # Querying for the general state with key with GetInputEvent, i.e., capturing all inputs and
                      # not only a specific one.
                  
                      print ("\nQuerying the current input state:")
                      bc: c4d.BaseContainer = c4d.BaseContainer()
                      if not c4d.gui.GetInputEvent(c4d.BFM_INPUT_KEYBOARD, bc):
                          raise RuntimeError("Failed to query input events.")
                  
                      # Get the key which is currently be pressed as an ASCII value.
                      print (f"{bc[c4d.BFM_INPUT_CHANNEL] = } ({chr (bc[c4d.BFM_INPUT_CHANNEL])})")
                      # We can still read all the other things which are written into an input event container, as
                      # for example a modifier key state.
                      print (f"{bc[c4d.BFM_INPUT_QUALIFIER] == c4d.QUALIFIER_CTRL = }")
                  
                  
                  if __name__ == '__main__':
                      main()
                  

                  An example output for pressing CTRL + X while running the script:

                  
                  The result container:
                  key: 1768973430, value: 1801812322
                  key: 1768973153, value: 1768973153
                  key: 1768976737, value: 2
                  key: 1768975727, value: 258
                  key: 1768978017, value: 0
                  key: 1768977985, value: 0.0
                  key: 1801548643, value:
                  
                  Selected values:
                  bc[c4d.BFM_INPUT_CHANNEL] = 1768973153
                  bc[c4d.BFM_INPUT_VALUE] = 0
                  bc[c4d.BFM_INPUT_ASC] = ''
                  
                  The container is still valid for other stuff, but BFM_INPUT_CHANNEL ismeaningless in that context:
                  BFM_INPUT_CHANNEL: bc[c4d.BFM_INPUT_MODIFIERS] = 258
                  123456789: bc[c4d.BFM_INPUT_MODIFIERS] = 258
                  
                  How it should be done:
                  
                  Querying a specific input state:
                  bc[c4d.BFM_INPUT_VALUE] == 1 = True
                  bc[c4d.BFM_INPUT_QUALIFIER] == c4d.QUALIFIER_CTRL = True
                  
                  Querying the current input state:
                  bc[c4d.BFM_INPUT_CHANNEL] = 88 (X)
                  bc[c4d.BFM_INPUT_QUALIFIER] == c4d.QUALIFIER_CTRL = True
                  >>> 
                  

                  MAXON SDK Specialist
                  developers.maxon.net

                  1 Reply Last reply Reply Quote 1
                  • First post
                    Last post