Source code for sdl2.ext.input

from ..stdinc import SDL_TRUE
from ..keyboard import (
    SDL_GetKeyFromName, SDL_GetKeyName, SDL_StartTextInput, SDL_StopTextInput,
    SDL_IsTextInputActive,
)
from ..keycode import KMOD_ALT, KMOD_CTRL, KMOD_GUI, KMOD_SHIFT
from ..mouse import (
    SDL_BUTTON_LEFT, SDL_BUTTON_RIGHT, SDL_BUTTON_MIDDLE,
    SDL_BUTTON_X1, SDL_BUTTON_X2,
)
from ..events import (
    SDL_KEYDOWN, SDL_KEYUP, SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP,
    SDL_TEXTINPUT,
)

from .compat import _is_text, byteify, isiterable

__all__ = [
    "key_pressed", "mouse_clicked", "get_clicks", "get_text_input",
    "start_text_input", "stop_text_input", "text_input_enabled",  
]


KEYMOD_MAP = {
    # main mappings
    'ctrl': KMOD_CTRL,
    'alt': KMOD_ALT,
    'gui': KMOD_GUI,
    'shift': KMOD_SHIFT,
    # additional aliases
    'control': KMOD_CTRL,
    'option': KMOD_ALT,
    'command': KMOD_GUI,
    'super': KMOD_GUI,
}

MOUSE_BUTTON_MAP = {
    'left': SDL_BUTTON_LEFT,
    'right': SDL_BUTTON_RIGHT,
    'middle': SDL_BUTTON_MIDDLE,
    'x1': SDL_BUTTON_X1,
    'x2': SDL_BUTTON_X2,
}



def _parse_keycode(key):

    keycode = 0
    if isinstance(key, int):
        if SDL_GetKeyName(key) == b"":
            raise ValueError("'{0}' is not a valid SDL_Keycode".format(key))
        keycode = key

    elif _is_text(key):
        keycode = SDL_GetKeyFromName(byteify(key))
        if keycode == 0:
            raise ValueError("'{0}' is not a valid SDL key name".format(key))
        
    else:
        e = "'key' must be a string or SDL_Keycode (got {0})"
        raise TypeError(e.format(str(type(key))))
    
    return keycode


def _mod_to_masks(mod):

    if not isiterable(mod):
        mod = [mod]
    
    masks = []
    for m in mod:
        # If mod is already an int, assume it's a valid bitmask
        if isinstance(m, int):
            masks.append(m)

        # If mod is a string, validate and convert it to a bitmask
        elif _is_text(m):
            m = m.lower()
            if not m in KEYMOD_MAP.keys():
                e = "'{0}' is not a valid modifer key name"
                raise ValueError(e.format(m))
            masks.append(KEYMOD_MAP[m])

        else:
            e = "'mod' must be a list of strings or SDL bitmasks (got {0})"
            raise TypeError(e.format(str(type(m))))
    
    return masks


def _get_sdl_mouse_button(button):
    
    # Check if valid SDL_BUTTON constant
    if isinstance(button, int):
        if not button in MOUSE_BUTTON_MAP.values():
            e = "'{0}' does not correspond to a valid SDL mouse button"
            raise ValueError(e.format(button))
    
    # Check if valid mouse button name
    elif _is_text(button):
        button = button.lower()
        if not button in MOUSE_BUTTON_MAP.keys():
            e = "'{0}' is not a valid mouse button name"
            raise ValueError(e.format(button))
        button = MOUSE_BUTTON_MAP[button]

    else:
        e = "'button' must be a string or SDL_BUTTON constant (got {0})"
        raise TypeError(e.format(str(type(button))))
    
    return button



[docs]def key_pressed(events, key=None, mod=None, released=False): """Checks for key press events in a given event queue. By default, this function will return True if any key has been pressed. However, you can also check a specific key by providing its name (e.g. 'up') or SDL keycode (e.g. ``sdl2.SDLK_up``) to the 'key' argument. This function is meant to be used with :func:`~sdl2.ext.get_events`:: response = None while not response: q = get_events() # Fetch latest SDL input events if key_pressed(q, 'z'): response = 'left' elif key_pressed(q, '/'): response = 'right' Additionally, you can check if the key has been pressed while holding one or more modifier keys (e.g. control + q to quit the program) by providing the name(s) (e.g. 'ctrl') or SDL bitmask(s) (e.g. ``sdl2.KMOD_LCTRL``) of the modifiers to the 'mod' argument:: q = get_events() if key_pressed(q, 'q', mod='ctrl'): exit_app() elif key_pressed(q, 'd', mod=['ctrl', 'shift']): debug_mode = True Valid modifier names include 'ctrl' and 'control' for the Control keys, 'alt' and 'option' for the Alt keys, 'gui', 'command', and 'super' for the Command/Win/Super keys, and 'shift' for the shift keys. A full list of SDL modifier bitmasks can be found here: https://wiki.libsdl.org/SDL2/SDL_Keymod For a comprehensive list of valid key names, see the 'Name' column of the following table: https://wiki.libsdl.org/SDL2/SDL_Scancode For a comprehensive list of valid SDL keycodes, consult the following table: https://wiki.libsdl.org/SDL_Keycode Args: events (list of :obj:`sdl2.SDL_Event`): A list of SDL events to check for matching key presses (or releases). key (str or :obj:`sdl2.SDL_Keycode`, optional): The name or SDL keycode of the key to check. If ``None``, will return True on any keypress. Defaults to ``None``. mod (str or list, optional): The key modifiers (if any) to require for the key press (e.g. 'ctrl' for Control-Q). Has no effect if ``key`` is not specified. Defaults to ``None``. released (bool, optional): If True, will check for key release events instead of key presses. Defaults to False. Returns: bool: True if key has been pressed, otherwise False. """ # If key specified, validate and coerce to SDL_Keycode keycode = None if key: keycode = _parse_keycode(key) # If modifier key(s) specified, validate and coerce to list of bitmasks if mod: mod = _mod_to_masks(mod) # Ensure 'events' is iterable if not isiterable(events): events = [events] # Check for any key events matching the criteria in the given event queue pressed = False for e in events: if e.type == (SDL_KEYUP if released else SDL_KEYDOWN): if not keycode: pressed = True break elif e.key.keysym.sym == keycode: if not mod or all([e.key.keysym.mod & m for m in mod]): pressed = True break return pressed
[docs]def mouse_clicked(events, button=None, released=False): """Checks for any mouse clicks in a given event queue. This function is meant to be used with :func:`~sdl2.ext.get_events`:: response = None while not response: q = get_events() # Fetch latest SDL input events if mouse_clicked(q, 'left'): response = 'left' elif mouse_clicked(q, 'right'): response = 'right' By default, this function checks for clicks from any button. However, you can also check for clicks from a specific button by specifying one of the following strings or SDL constants for the ``button`` argument: ===================== ============= SDL Constant String ===================== ============= ``SDL_BUTTON_LEFT`` ``'left'`` ``SDL_BUTTON_RIGHT`` ``'right'`` ``SDL_BUTTON_MIDDLE`` ``'middle'`` ``SDL_BUTTON_X1`` ``'x1'`` ``SDL_BUTTON_X2`` ``'x2'`` ===================== ============= Args: events (list of :obj:`sdl2.SDL_Event`): A list of SDL events to check for mouse click events. button (str or int, optional): The name or SDL constant of the mouse button to listen for. If ``None``, all mouse buttons will . Defaults to ``None``. released (bool, optional): If True, will check the queue for mouse button release events instead of mouse button down events. Defaults to False. Returns: bool: True if the mouse has been clicked, otherwise False. """ # If button specified, validate and coerce to SDL_BUTTON constant if button: button = _get_sdl_mouse_button(button) # Ensure 'events' is iterable if not isiterable(events): events = [events] # Check for any click events matching the criteria in the given event queue clicked = False for e in events: if e.type == (SDL_MOUSEBUTTONUP if released else SDL_MOUSEBUTTONDOWN): if not button or e.button.button == button: clicked = True break return clicked
[docs]def get_clicks(events, button=None, released=False): """Returns the (x, y) coordinates of the mouse clicks in an event queue. By default, this function returns clicks from any button. However, you can also return clicks from a specific button only by specifying a string or SDL button constant (see :func:`mouse_clicked` for details). Args: events (list of :obj:`sdl2.SDL_Event`): A list of SDL events to check for mouse click events. button (str or int, optional): The name or SDL constant of the mouse button to listen for. If ``None``, will return clicks from any mouse button. Defaults to ``None``. released (bool, optional): If True, will return the coordinates for mouse button release events instead of mouse button click events. Defaults to False. Returns: list: A list of the (x, y) coordinates for each matching click event in the queue. """ # If button specified, validate and coerce to SDL_BUTTON constant if button: button = _get_sdl_mouse_button(button) # Ensure 'events' is iterable if not isiterable(events): events = [events] # Gather and return any matching mouse clicks in the given queue clicks = [] for e in events: if e.type == (SDL_MOUSEBUTTONUP if released else SDL_MOUSEBUTTONDOWN): if not button or e.button.button == button: clicks.append((e.button.x, e.button.y)) return clicks
[docs]def start_text_input(): """Enables SDL unicode text input events. """ SDL_StartTextInput()
[docs]def stop_text_input(): """Disables SDL unicode text input events. """ SDL_StopTextInput()
[docs]def text_input_enabled(): """Checks whether SDL text input events are currently enabled. Returns: bool: True if text input events are enabled, otherwise False. """ return SDL_IsTextInputActive() == SDL_TRUE
[docs]def get_text_input(events): """Returns the text input events from a queue as a unicode string. Note that SDL text input events need to be enabled for this function to work. This can be toggled with :func:`start_text_input` / :func:`stop_text_input` and queried with :func:`text_input_enabled`:: start_text_input() response = u"" while True: q = get_events() if key_pressed(q, 'return'): break response += get_text_input(q) draw_text(response) stop_text_input() If there are no text input events in the given event queue, an empty unicode string will be returned. Args: events (list of :obj:`sdl2.SDL_Event`): A list of SDL events to check for unicode text input (``SDL_TEXTINPUT``) events. Returns: str: A UTF8-encoded unicode string containing all text input from the queue. """ # Make sure text input events are enabled if not text_input_enabled(): e = "Text input events must be enabled before using get_text()" raise RuntimeError(e) # Ensure 'events' is iterable if not isiterable(events): events = [events] # Check for any key events matching the criteria in the given event queue text = u"" for e in events: if e.type == SDL_TEXTINPUT: text += e.text.text.decode('utf-8') return text