Binding keymap dynamically: Missing the key code

Discuss how to use the Ren'Py engine to create visual novels and story-based games. New releases are announced in this section.
Forum rules
This is the right place for Ren'Py help. Please ask one question per thread, use a descriptive subject like 'NotFound error in option.rpy' , and include all the relevant information - especially any relevant code and traceback messages. Use the code tag to format scripts.
Post Reply
Message
Author
goldo
Regular
Posts: 129
Joined: Mon Jan 23, 2017 8:23 am
Contact:

Binding keymap dynamically: Missing the key code

#1 Post by goldo »

Hi,

So this is probably more of a pygame question, but I guess it belongs here because the keymap in Ren'Py is borrowed from it.

I am working on a way to let the user change some shortcuts dynamically, in the spirit of this question.

I am this close to making it work, but I am missing one final piece of the puzzle: How to turn the user input into a proper key code (the ones that start with 'K_').

Basically I'm using pygame to detect an event key which returns a number, and I can then turn it into a string using pygame.key.name.

However, this key is not a usable key code by itself, I can make it work for simple cases using a bit of string manipulation, but for complex cases such as keypad keys or symbols like * or ?, it breaks down.

Is there a way to detect the actual key_code so that I can hand it to Ren'Py?

Here is a complete example. You can try it for yourself and see which keys are failing:

Code: Select all

init python:
    import pygame
    
    def bind_key(target, old_key="", key=""):
        
        # Currently keys like +, ?, * fail
        
        if not getattr(pygame.locals, key, False):
            renpy.notify(key + " is not valid")
            return False

        for event, binding in config.keymap.items():
            remove_list = []
            for k in binding:
                if event == target:
                    if old_key and k.endswith(old_key):
                        remove_list.append(k)
                else:
                    if k.endswith(key):
                        remove_list.append(k)
                        renpy.notify(k + " no longer used for " + event)
            
            for k in remove_list:
                binding.remove(k)

        config.keymap[target].append(key)

        renpy.clear_keymap_cache()

        return True

    def catch_key():

        while True:
            for event in pygame.event.get():
                if event.type == pygame.KEYDOWN:
                    key = pygame.key.name(event.key).decode("utf-8")

                    # The following is an attempt to manually set up the pygame key code: Not ideal!
                    
                    if len(key) == 1:
                        return key.lower()

                    return key.upper()

# The game starts here.

label start:

    "'H' hides the UI."

    $ bind_key("hide_windows", "K_h", "K_f")

    "Now 'F' hides the UI!"

    $ bind_key("hide_windows", "K_f", "K_h")
    $ bind_key("toggle_fullscreen", key="K_f")

    "Back to normal."

label key_loop:

    "Now enter any key.{nw}"

    $ next = catch_key()
    
    # This lets you test the keys and see which ones fail

    if bind_key("hide_windows", "K_h", "K_" + next):
        "[next] now hides the window."
    else:
        "[next] doesn't work as a shortcut. This may be a problem."

    jump key_loop

User avatar
Ocelot
Lemma-Class Veteran
Posts: 2420
Joined: Tue Aug 23, 2016 10:35 am
Github: MiiNiPaa
Discord: MiiNiPaa#4384
Contact:

Re: Binding keymap dynamically: Missing the key code

#2 Post by Ocelot »

Not an answer, but collections of funny facts.
1) Look where clear_keymap_cache is documented. That's right, nowhere. It was undocumented sometime ago, since it was broken and didn't actually work in all cases.
2) Config variables are not saved. If you reload the game after changing keymap, it will be reverted to the default values. You need to chnage default values and restart the game to get correct results.
3) There are two ways to describe key event, by keycode and generated symbol (which depends on keyboard layout and user language). It looks like ypou want the first one, but you mention symbol "?", for which keycode does not exist, it is forward slash with "shift" modifier. If you want to detect if shift was pressed diring button press, you need to do that.
< < insert Rick Cook quote here > >

goldo
Regular
Posts: 129
Joined: Mon Jan 23, 2017 8:23 am
Contact:

Re: Binding keymap dynamically: Missing the key code

#3 Post by goldo »

Thanks!

Here are a few answers:
1) I am not aware of an alternative to clear_keymap_cache, so for now that will have to do. A feature that works most of the time is still better for my purposes than a feature that works none of the time.

2) I guess that can be handled with a persistent variable as a buffer.

3) Sorry I misspoke, I do not really care which symbol or which keyboard the player is using. If they choose the key on their keyboard that does '?', whatever that is, I would like the game to detect it and register it as a shortcut. Same if he chooses ü, ç, ñ, カ or any other key they might have on their keyboard.
Right now my implementation only works reliably for letters, non-keypad numbers, and a few other keys like Space or Enter. I guess that's good enough, but I would like it better if it could map the user's whole keyboard (regardless of what it may be).

To clarify, detecting key combinations with 'alt', 'shift' and 'ctrl', or mouse keys, is outside of the scope of my question.

Post Reply

Who is online

Users browsing this forum: No registered users