<reponame>jacoblb64/pico_rgb_keypad_hid<filename>adafruit_circuitpython_libs/adafruit-circuitpython-bundle-py-20210214/lib/adafruit_cursorcontrol/cursorcontrol_cursormanager.py
# SPDX-FileCopyrightText: 2019 <NAME> for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
`adafruit_cursorcontrol_cursormanager`
================================================================================
Simple interaction user interface interaction for Adafruit_CursorControl.
* Author(s): <NAME>
"""
import board
import digitalio
from micropython import const
import analogio
from gamepadshift import GamePadShift
from adafruit_debouncer import Debouncer

# PyBadge
PYBADGE_BUTTON_LEFT = const(128)
PYBADGE_BUTTON_UP = const(64)
PYBADGE_BUTTON_DOWN = const(32)
PYBADGE_BUTTON_RIGHT = const(16)
# PyBadge & PyGamer
PYBADGE_BUTTON_A = const(2)


class CursorManager:
    """Simple interaction user interface interaction for Adafruit_CursorControl.

    :param adafruit_cursorcontrol cursor: The cursor object we are using.
    """

    def __init__(self, cursor):
        self._cursor = cursor
        self._is_clicked = False
        self._init_hardware()

    def __enter__(self):
        return self

    def __exit__(self, exception_type, exception_value, traceback):
        self.deinit()

    def deinit(self):
        """Deinitializes a CursorManager object."""
        self._is_deinited()
        self._pad.deinit()
        self._cursor.deinit()
        self._cursor = None

    def _is_deinited(self):
        """Checks if CursorManager object has been deinitd."""
        if self._cursor is None:
            raise ValueError(
                "CursorManager object has been deinitialized and can no longer "
                "be used. Create a new CursorManager object."
            )

    def _init_hardware(self):
        """Initializes PyBadge or PyGamer hardware."""
        if hasattr(board, "BUTTON_CLOCK") and not hasattr(board, "JOYSTICK_X"):
            self._pad_btns = {
                "btn_left": PYBADGE_BUTTON_LEFT,
                "btn_right": PYBADGE_BUTTON_RIGHT,
                "btn_up": PYBADGE_BUTTON_UP,
                "btn_down": PYBADGE_BUTTON_DOWN,
                "btn_a": PYBADGE_BUTTON_A,
            }
        elif hasattr(board, "JOYSTICK_X"):
            self._joystick_x = analogio.AnalogIn(board.JOYSTICK_X)
            self._joystick_y = analogio.AnalogIn(board.JOYSTICK_Y)
            self._pad_btns = {"btn_a": PYBADGE_BUTTON_A}
            # Sample the center points of the joystick
            self._center_x = self._joystick_x.value
            self._center_y = self._joystick_y.value
        else:
            raise AttributeError(
                "Board must have a D-Pad or Joystick for use with CursorManager!"
            )
        self._pad = GamePadShift(
            digitalio.DigitalInOut(board.BUTTON_CLOCK),
            digitalio.DigitalInOut(board.BUTTON_OUT),
            digitalio.DigitalInOut(board.BUTTON_LATCH),
        )

    @property
    def is_clicked(self):
        """Returns True if the cursor button was pressed
        during previous call to update()
        """
        return self._is_clicked

    def update(self):
        """Updates the cursor object."""
        pressed = self._pad.get_pressed()
        self._check_cursor_movement(pressed)
        if self._is_clicked:
            self._is_clicked = False
        elif pressed & self._pad_btns["btn_a"]:
            self._is_clicked = True

    def _read_joystick_x(self, samples=3):
        """Read the X analog joystick on the PyGamer.
        :param int samples: How many samples to read and average.
        """
        reading = 0
        # pylint: disable=unused-variable
        if hasattr(board, "JOYSTICK_X"):
            for sample in range(0, samples):
                reading += self._joystick_x.value
            reading /= samples
        return reading

    def _read_joystick_y(self, samples=3):
        """Read the Y analog joystick on the PyGamer.
        :param int samples: How many samples to read and average.
        """
        reading = 0
        # pylint: disable=unused-variable
        if hasattr(board, "JOYSTICK_Y"):
            for sample in range(0, samples):
                reading += self._joystick_y.value
            reading /= samples
        return reading

    def _check_cursor_movement(self, pressed=None):
        """Checks the PyBadge D-Pad or the PyGamer's Joystick for movement.
        :param int pressed: 8-bit number with bits that correspond to buttons
            which have been pressed down since the last call to get_pressed().
        """
        if hasattr(board, "BUTTON_CLOCK") and not hasattr(board, "JOYSTICK_X"):
            if pressed & self._pad_btns["btn_right"]:
                self._cursor.x += self._cursor.speed
            elif pressed & self._pad_btns["btn_left"]:
                self._cursor.x -= self._cursor.speed
            if pressed & self._pad_btns["btn_up"]:
                self._cursor.y -= self._cursor.speed
            elif pressed & self._pad_btns["btn_down"]:
                self._cursor.y += self._cursor.speed
        elif hasattr(board, "JOYSTICK_X"):
            joy_x = self._read_joystick_x()
            joy_y = self._read_joystick_y()
            if joy_x > self._center_x + 1000:
                self._cursor.x += self._cursor.speed
            elif joy_x < self._center_x - 1000:
                self._cursor.x -= self._cursor.speed
            if joy_y > self._center_y + 1000:
                self._cursor.y += self._cursor.speed
            elif joy_y < self._center_y - 1000:
                self._cursor.y -= self._cursor.speed
        else:
            raise AttributeError(
                "Board must have a D-Pad or Joystick for use with CursorManager!"
            )


class DebouncedCursorManager(CursorManager):
    """Simple interaction user interface interaction for Adafruit_CursorControl.
    This subclass provide a debounced version on the A button and provides queries for when
    the button is just pressed, and just released, as well it's current state. "Just" in this
    context means "since the previous call to update."

    :param adafruit_cursorcontrol cursor: The cursor object we are using.
    """

    def __init__(self, cursor, debounce_interval=0.01):
        CursorManager.__init__(self, cursor)
        self._pressed = 0
        self._debouncer = Debouncer(
            lambda: bool(self._pressed & self._pad_btns["btn_a"]),
            interval=debounce_interval,
        )

    @property
    def is_clicked(self):
        """Returns True if the cursor button was pressed
        during previous call to update()
        """
        return self._debouncer.rose

    pressed = is_clicked

    @property
    def released(self):
        """Returns True if the cursor button was released
        during previous call to update()
        """
        return self._debouncer.fell

    @property
    def held(self):
        """Returns True if the cursor button is currently being held"""
        return self._debouncer.value

    def update(self):
        """Updates the cursor object."""
        self._pressed = self._pad.get_pressed()
        self._check_cursor_movement(self._pressed)
        self._debouncer.update()

# SPDX-FileCopyrightText: 2019 <NAME> for Adafruit Industries
#
# SPDX-License-Identifier: MIT

"""
`adafruit_cursorcontrol_cursormanager`
================================================================================
Simple interaction user interface interaction for Adafruit_CursorControl.
* Author(s): <NAME>
"""
import board
import digitalio
from micropython import const
import analogio
from gamepadshift import GamePadShift
from adafruit_debouncer import Debouncer

# PyBadge
PYBADGE_BUTTON_LEFT = const(128)
PYBADGE_BUTTON_UP = const(64)
PYBADGE_BUTTON_DOWN = const(32)
PYBADGE_BUTTON_RIGHT = const(16)
# PyBadge & PyGamer
PYBADGE_BUTTON_A = const(2)


class CursorManager:
    """Simple interaction user interface interaction for Adafruit_CursorControl.

    :param adafruit_cursorcontrol cursor: The cursor object we are using.
    """

    def __init__(self, cursor):
        self._cursor = cursor
        self._is_clicked = False
        self._init_hardware()

    def __enter__(self):
        return self

    def __exit__(self, exception_type, exception_value, traceback):
        self.deinit()

    def deinit(self):
        """Deinitializes a CursorManager object."""
        self._is_deinited()
        self._pad.deinit()
        self._cursor.deinit()
        self._cursor = None

    def _is_deinited(self):
        """Checks if CursorManager object has been deinitd."""
        if self._cursor is None:
            raise ValueError(
                "CursorManager object has been deinitialized and can no longer "
                "be used. Create a new CursorManager object."
            )

    def _init_hardware(self):
        """Initializes PyBadge or PyGamer hardware."""
        if hasattr(board, "BUTTON_CLOCK") and not hasattr(board, "JOYSTICK_X"):
            self._pad_btns = {
                "btn_left": PYBADGE_BUTTON_LEFT,
                "btn_right": PYBADGE_BUTTON_RIGHT,
                "btn_up": PYBADGE_BUTTON_UP,
                "btn_down": PYBADGE_BUTTON_DOWN,
                "btn_a": PYBADGE_BUTTON_A,
            }
        elif hasattr(board, "JOYSTICK_X"):
            self._joystick_x = analogio.AnalogIn(board.JOYSTICK_X)
            self._joystick_y = analogio.AnalogIn(board.JOYSTICK_Y)
            self._pad_btns = {"btn_a": PYBADGE_BUTTON_A}
            # Sample the center points of the joystick
            self._center_x = self._joystick_x.value
            self._center_y = self._joystick_y.value
        else:
            raise AttributeError(
                "Board must have a D-Pad or Joystick for use with CursorManager!"
            )
        self._pad = GamePadShift(
            digitalio.DigitalInOut(board.BUTTON_CLOCK),
            digitalio.DigitalInOut(board.BUTTON_OUT),
            digitalio.DigitalInOut(board.BUTTON_LATCH),
        )

    @property
    def is_clicked(self):
        """Returns True if the cursor button was pressed
        during previous call to update()
        """
        return self._is_clicked

    def update(self):
        """Updates the cursor object."""
        pressed = self._pad.get_pressed()
        self._check_cursor_movement(pressed)
        if self._is_clicked:
            self._is_clicked = False
        elif pressed & self._pad_btns["btn_a"]:
            self._is_clicked = True

    def _read_joystick_x(self, samples=3):
        """Read the X analog joystick on the PyGamer.
        :param int samples: How many samples to read and average.
        """
        reading = 0
        # pylint: disable=unused-variable
        if hasattr(board, "JOYSTICK_X"):
            for sample in range(0, samples):
                reading += self._joystick_x.value
            reading /= samples
        return reading

    def _read_joystick_y(self, samples=3):
        """Read the Y analog joystick on the PyGamer.
        :param int samples: How many samples to read and average.
        """
        reading = 0
        # pylint: disable=unused-variable
        if hasattr(board, "JOYSTICK_Y"):
            for sample in range(0, samples):
                reading += self._joystick_y.value
            reading /= samples
        return reading

    def _check_cursor_movement(self, pressed=None):
        """Checks the PyBadge D-Pad or the PyGamer's Joystick for movement.
        :param int pressed: 8-bit number with bits that correspond to buttons
            which have been pressed down since the last call to get_pressed().
        """
        if hasattr(board, "BUTTON_CLOCK") and not hasattr(board, "JOYSTICK_X"):
            if pressed & self._pad_btns["btn_right"]:
                self._cursor.x += self._cursor.speed
            elif pressed & self._pad_btns["btn_left"]:
                self._cursor.x -= self._cursor.speed
            if pressed & self._pad_btns["btn_up"]:
                self._cursor.y -= self._cursor.speed
            elif pressed & self._pad_btns["btn_down"]:
                self._cursor.y += self._cursor.speed
        elif hasattr(board, "JOYSTICK_X"):
            joy_x = self._read_joystick_x()
            joy_y = self._read_joystick_y()
            if joy_x > self._center_x + 1000:
                self._cursor.x += self._cursor.speed
            elif joy_x < self._center_x - 1000:
                self._cursor.x -= self._cursor.speed
            if joy_y > self._center_y + 1000:
                self._cursor.y += self._cursor.speed
            elif joy_y < self._center_y - 1000:
                self._cursor.y -= self._cursor.speed
        else:
            raise AttributeError(
                "Board must have a D-Pad or Joystick for use with CursorManager!"
            )


class DebouncedCursorManager(CursorManager):
    """Simple interaction user interface interaction for Adafruit_CursorControl.
    This subclass provide a debounced version on the A button and provides queries for when
    the button is just pressed, and just released, as well it's current state. "Just" in this
    context means "since the previous call to update."

    :param adafruit_cursorcontrol cursor: The cursor object we are using.
    """

    def __init__(self, cursor, debounce_interval=0.01):
        CursorManager.__init__(self, cursor)
        self._pressed = 0
        self._debouncer = Debouncer(
            lambda: bool(self._pressed & self._pad_btns["btn_a"]),
            interval=debounce_interval,
        )

    @property
    def is_clicked(self):
        """Returns True if the cursor button was pressed
        during previous call to update()
        """
        return self._debouncer.rose

    pressed = is_clicked

    @property
    def released(self):
        """Returns True if the cursor button was released
        during previous call to update()
        """
        return self._debouncer.fell

    @property
    def held(self):
        """Returns True if the cursor button is currently being held"""
        return self._debouncer.value

    def update(self):
        """Updates the cursor object."""
        self._pressed = self._pad.get_pressed()
        self._check_cursor_movement(self._pressed)
        self._debouncer.update()
# SPDX-FileCopyrightText: 2019 <NAME> for Adafruit Industries # # SPDX-License-Identifier: MIT `adafruit_cursorcontrol_cursormanager` ================================================================================ Simple interaction user interface interaction for Adafruit_CursorControl. * Author(s): <NAME> # PyBadge # PyBadge & PyGamer Simple interaction user interface interaction for Adafruit_CursorControl.      :param adafruit_cursorcontrol cursor: The cursor object we are using. Deinitializes a CursorManager object. Checks if CursorManager object has been deinitd. Initializes PyBadge or PyGamer hardware. # Sample the center points of the joystick Returns True if the cursor button was pressed         during previous call to update() Updates the cursor object. Read the X analog joystick on the PyGamer.         :param int samples: How many samples to read and average. # pylint: disable=unused-variable Read the Y analog joystick on the PyGamer.         :param int samples: How many samples to read and average. # pylint: disable=unused-variable Checks the PyBadge D-Pad or the PyGamer's Joystick for movement.         :param int pressed: 8-bit number with bits that correspond to buttons             which have been pressed down since the last call to get_pressed(). Simple interaction user interface interaction for Adafruit_CursorControl.     This subclass provide a debounced version on the A button and provides queries for when     the button is just pressed, and just released, as well it's current state. "Just" in this     context means "since the previous call to update."      :param adafruit_cursorcontrol cursor: The cursor object we are using. Returns True if the cursor button was pressed         during previous call to update() Returns True if the cursor button was released         during previous call to update() Returns True if the cursor button is currently being held Updates the cursor object.