示例#1
0
    def match(self, sequence):
        # The ANSI sequence for a mouse event in mode 1006 is '<ESC>[B;Col;RowM' (last char is 'm' if button-release)
        m = re.match(
            r"\x1b\[\<(?P<button>\d+);(?P<column>\d+);(?P<row>\d+)(?P<press>[Mm])",
            sequence)
        if not m:
            return None
        params = m.groupdict()
        pressed = params["press"] == "M"
        button = _button_map.get(int(params["button"]) & (~0x20), None)
        moving = bool(int(params["button"]) & 0x20)

        col = int(params["column"]) - 1
        row = int(params["row"]) - 1

        click_event = event = None

        # TBD: check for different buttons in press events and send combined button events
        if moving:
            event = Event(EventTypes.MouseMove,
                          pos=V2(col, row),
                          buttons=button)
        elif pressed:
            ts = time.time()
            event = Event(EventTypes.MousePress,
                          pos=V2(col, row),
                          buttons=button,
                          time=ts)
            self.last_press = (
                ts,
                button,
            )
        else:
            ts = time.time()
            event = Event(EventTypes.MouseRelease,
                          pos=V2(col, row),
                          buttons=button)
            if ts - self.last_press[
                    0] < self.CLICK_THRESHOLD and button == self.last_press[1]:
                Event(EventTypes.MouseClick,
                      pos=V2(col, row),
                      buttons=button,
                      time=ts)
                if ts - self.last_click[
                        0] < self.DOUBLE_CLICK_THRESHOLD and button == self.last_click[
                            1]:
                    Event(EventTypes.MouseDoubleClick,
                          pos=V2(col, row),
                          buttons=button,
                          time=ts)
                self.last_click = (
                    ts,
                    button,
                )

        return event
示例#2
0
def test_event_system_select_events():
    subscription = Subscription([0, 1])
    Event(2)
    process()
    assert not subscription.queue
    Event(0)
    process()
    assert subscription.queue
    subscription.queue.clear()
    Event(1)
    process()
    assert subscription.queue
示例#3
0
def _posix_inkey(break_=True, clear=True):
    """Return currently pressed key as a string

    This is the implemenation of old 8-bit basic "inkey" and "inkey$" builtins,
    and also the heart of the keybard input system.

    Args:
      - break\_ (bool): Boolean parameter specifying whether "CTRL + C"
        (\x03) should raise KeyboardInterrupt or be returned as a
        keycode. Defaults to True.
      -clear (bool): clears the keyboard buffer contents.
            If False, queued keyboard codes are returned in order, for each call
            Otherwise queued codes are discarded and only the last-pressed character
            if returned. Even when "clear" is True, one keyboard event
            is generated for each token.
            Also, note that calling Screen.update will cause this to be called
            with clear=True, to flush all keypress events. Applications using
            'inkey' to read all input should make all the calls between 'updates'
            defaults to True

    *Important*: This function only works inside a
    :any:`keyboard` managed context. (Posix)

    Code values or code sequences for non-character keys,
    like ESC, direction arrows, fkeys are kept as constants
    in the "KeyCodes" class.

    Unfortunatelly, due to the nature of console streaming,
    this can't receive "keypress" or "keyup" events, and repeat-rate
    is not configurable, nor can it detect modifier keys, or
    simultaneous key-presses.

    """

    # In this context, 'token' is either a single-char string, representing an
    # 'ordinary' keypress or an escape sequence representing a special key or mouse event.
    old_keycode = ""

    if not clear:
        buffer = sys.stdin
    else:
        # read all buffer, generate events for all tokens
        # but return only the last event
        # (so, the caller to "inkey" have info on the last pressed key)
        buffer = io.StringIO(sys.stdin.read(10000))
        buffer.seek(0)

    while True:
        keycode, stream_eof = _posix_scan_code(buffer)
        if stream_eof and not keycode:
            keycode = old_keycode
        if keycode == '\x03' and break_:
            raise KeyboardInterrupt()
        if list_subscriptions(EventTypes.KeyPress):
            Event(EventTypes.KeyPress, key=keycode)
        if not clear or stream_eof:
            # next characters will be consumed in next calls
            break
        old_keycode = keycode
    return keycode
示例#4
0
    def inkey(self, break_=True, clear=True, consume=True):
        """Return currently pressed key as a string

        Args:
        - break\_ (bool): Boolean parameter specifying whether "CTRL + C"
            (\x03) should raise KeyboardInterrupt or be returned as a
            keycode. Defaults to True.
        - clear (bool): clears the keyboard buffer contents


        [WIP: latest updates adding support for the event system
        on the posix side where not reflected here]
        [TODO: Dispatch mouse event handling from here]

        """

        if not msvcrt.kbhit():
            return ""

        code = msvcrt.getwch()
        if code in "\x00à":  # and msvcrt.kbhit():
            code += msvcrt.getwch()

        if list_subscriptions(EventTypes.KeyPress):
            Event(EventTypes.KeyPress, key=keycode)

        return code
示例#5
0
 def flush_clicks(self):
     to_kill = []
     for i, click in enumerate(self.on_hold_clicks):
         if click["time"] - time.time() > 0.1:
             Event(EventTypes.MouseClick, **click)
             to_kill.append(i)
     for index in reversed(to_kill):
         self.on_hold_clicks.pop(index)
示例#6
0
def test_event_system_subscripton_queue_works():
    subscription = Subscription(EventTypes.Tick)

    assert subscription.queue is not None and not subscription.queue

    e = Event(EventTypes.Tick)
    process()
    assert subscription.queue and subscription.queue.popleft() is e
示例#7
0
def tick_forward():
    global root_context, Event, EventTypes

    # Lazy imports:
    if not root_context:
        from terminedia import context as root_context
    if not Event:
        from terminedia.events import Event, EventTypes

    current = root_context.ticks = get_current_tick() + 1
    # All events have "tick" automatically, but passing it explicitly avoids "get_current_tick" to be called again.
    Event(EventTypes.Tick, tick=current)
示例#8
0
def test_event_system_subscripton_callback_works():
    callback_called = False
    def callback(event):
        nonlocal callback_called
        callback_called = True

    subscription = Subscription(EventTypes.Tick, callback=callback)

    assert subscription.queue is None and subscription.callback

    e = Event(EventTypes.Tick)
    process()
    assert callback_called
    assert subscription.queue is None
示例#9
0
def _win32_inkey(break_=True, clear=True):
    """Return currently pressed key as a string

    Args:
      - break\_ (bool): Boolean parameter specifying whether "CTRL + C"
        (\x03) should raise KeyboardInterrupt or be returned as a
        keycode. Defaults to True.
      - clear (bool): clears the keyboard buffer contents


    *Important*: This function only works inside a
    :any:`keyboard` managed context. (Posix)

    Code values or code sequences for non-character keys,
    like ESC, direction arrows, fkeys are kept as constants
    in the "KeyCodes" class.

    Unfortunatelly, due to the nature of console streaming,
    this can't receive "keypress" or "keyup" events, and repeat-rate
    is not configurable, nor can it detect modifier keys, or
    simultaneous key-presses.

    [WIP: latest updates adding support for the event system
    on the posix side where not reflected here]

    """

    if not msvcrt.kbhit():
        return ""

    code = msvcrt.getwch()
    if code in "\x00à":  # and msvcrt.kbhit():
        code += msvcrt.getwch()

    if list_subscriptions(EventTypes.KeyPress):
        Event(EventTypes.KeyPress, key=keycode)

    return code
示例#10
0
    def inkey(self, break_=True, clear=True, consume=True):
        """Return currently pressed key as a string

        This is the implemenation of old 8-bit basic "inkey" and "inkey$" builtins,
        and also the heart of the keybard input system.

        Args:
        - break\_ (bool): Boolean parameter specifying whether "CTRL + C"
            (\x03) should raise KeyboardInterrupt or be returned as a
            keycode. Defaults to True.
        - clear (bool): clears the keyboard buffer contents.
                If False, queued keyboard codes are returned in order, for each call
                Otherwise queued codes are discarded and only the last-pressed character
                if returned. Even when "clear" is True, one keyboard event
                is generated for each token.
                Also, note that calling Screen.update will cause this to be called
                with clear=True, to flush all keypress events. Applications using
                'inkey' to read all input should make all the calls between 'updates'
                defaults to True
        - consume: remove the received key from keypresses. When using an event-based
                approach, this function is responsible for dispatching the events, and
                have to be called. The default behavior, however, will make keypresses
                go missing when "inkey" is called to read the keyboard with the even system on.
                TL;DR: the calls to inkey made by the inner event system should pass
                False to this parameter, otherwise leave it as is.

        *Important*: This function only works inside a
        :any:`keyboard` managed context. (Posix)

        Code values or code sequences for non-character keys,
        like ESC, direction arrows, fkeys are kept as constants
        in the "KeyCodes" class.

        Unfortunatelly, due to the nature of console streaming,
        this can't receive "keypress" or "keyup" events, and repeat-rate
        is not configurable, nor can it detect modifier keys, or
        simultaneous key-presses.

        """
        # In this context, 'token' is either a single-char string, representing an
        # 'ordinary' keypress or an escape sequence representing a special key or mouse event.

        if not self.enabled:
            raise RuntimeError(
                "keyboard context manager must be entered to enable non-blocking keyboard reads"
            )

        if self.not_consumed and consume:
            return self.not_consumed.popleft()

        last_emitted = old_keycode = ""

        if not clear:
            buffer = sys.stdin
        else:
            # read all buffer, generate events for all tokens
            # but return only the last event
            # (so, the caller to "inkey" have info on the last pressed key)
            buffer = io.StringIO(sys.stdin.read(10000))
            buffer.seek(0)

        while True:
            keycode, stream_eof = self._scan_code(buffer)
            if stream_eof and not keycode:
                keycode = old_keycode
            if keycode == '\x03' and break_:
                raise KeyboardInterrupt()
            if keycode and list_subscriptions(EventTypes.KeyPress):
                if not (last_emitted == keycode and old_keycode == keycode):
                    Event(EventTypes.KeyPress, key=keycode)
                last_emitted = keycode
            if not clear or stream_eof:
                # next characters will be consumed in next calls
                break
            old_keycode = keycode
        if not consume:
            self.not_consumed.append(keycode)
        if mouse.enabled and mouse.on_hold_clicks:
            mouse.flush_clicks()
        return keycode
示例#11
0
def keys(event):
    global char
    if event.key == "\x1b":
        Event(EventTypes.QuitLoop)
        return