brintos

brintos / linux-shallow public Read only

0
0
Text · 7.1 KiB · ec74d75 Raw
239 lines · python
1# SPDX-License-Identifier: GPL-2.02import libevdev3 4from .base_device import BaseDevice5from hidtools.util import BusType6 7 8class InvalidHIDCommunication(Exception):9    pass10 11 12class GamepadData(object):13    pass14 15 16class AxisMapping(object):17    """Represents a mapping between a HID type18    and an evdev event"""19 20    def __init__(self, hid, evdev=None):21        self.hid = hid.lower()22 23        if evdev is None:24            evdev = f"ABS_{hid.upper()}"25 26        self.evdev = libevdev.evbit("EV_ABS", evdev)27 28 29class BaseGamepad(BaseDevice):30    buttons_map = {31        1: "BTN_SOUTH",32        2: "BTN_EAST",33        3: "BTN_C",34        4: "BTN_NORTH",35        5: "BTN_WEST",36        6: "BTN_Z",37        7: "BTN_TL",38        8: "BTN_TR",39        9: "BTN_TL2",40        10: "BTN_TR2",41        11: "BTN_SELECT",42        12: "BTN_START",43        13: "BTN_MODE",44        14: "BTN_THUMBL",45        15: "BTN_THUMBR",46    }47 48    axes_map = {49        "left_stick": {50            "x": AxisMapping("x"),51            "y": AxisMapping("y"),52        },53        "right_stick": {54            "x": AxisMapping("z"),55            "y": AxisMapping("Rz"),56        },57    }58 59    def __init__(self, rdesc, application="Game Pad", name=None, input_info=None):60        assert rdesc is not None61        super().__init__(name, application, input_info=input_info, rdesc=rdesc)62        self.buttons = (1, 2, 3)63        self._buttons = {}64        self.left = (127, 127)65        self.right = (127, 127)66        self.hat_switch = 1567        assert self.parsed_rdesc is not None68 69        self.fields = []70        for r in self.parsed_rdesc.input_reports.values():71            if r.application_name == self.application:72                self.fields.extend([f.usage_name for f in r])73 74    def store_axes(self, which, gamepad, data):75        amap = self.axes_map[which]76        x, y = data77        setattr(gamepad, amap["x"].hid, x)78        setattr(gamepad, amap["y"].hid, y)79 80    def create_report(81        self,82        *,83        left=(None, None),84        right=(None, None),85        hat_switch=None,86        buttons=None,87        reportID=None,88        application="Game Pad",89    ):90        """91        Return an input report for this device.92 93        :param left: a tuple of absolute (x, y) value of the left joypad94            where ``None`` is "leave unchanged"95        :param right: a tuple of absolute (x, y) value of the right joypad96            where ``None`` is "leave unchanged"97        :param hat_switch: an absolute angular value of the hat switch98            (expressed in 1/8 of circle, 0 being North, 2 East)99            where ``None`` is "leave unchanged"100        :param buttons: a dict of index/bool for the button states,101            where ``None`` is "leave unchanged"102        :param reportID: the numeric report ID for this report, if needed103        :param application: the application used to report the values104        """105        if buttons is not None:106            for i, b in buttons.items():107                if i not in self.buttons:108                    raise InvalidHIDCommunication(109                        f"button {i} is not part of this {self.application}"110                    )111                if b is not None:112                    self._buttons[i] = b113 114        def replace_none_in_tuple(item, default):115            if item is None:116                item = (None, None)117 118            if None in item:119                if item[0] is None:120                    item = (default[0], item[1])121                if item[1] is None:122                    item = (item[0], default[1])123 124            return item125 126        right = replace_none_in_tuple(right, self.right)127        self.right = right128        left = replace_none_in_tuple(left, self.left)129        self.left = left130 131        if hat_switch is None:132            hat_switch = self.hat_switch133        else:134            self.hat_switch = hat_switch135 136        reportID = reportID or self.default_reportID137 138        gamepad = GamepadData()139        for i, b in self._buttons.items():140            gamepad.__setattr__(f"b{i}", int(b) if b is not None else 0)141 142        self.store_axes("left_stick", gamepad, left)143        self.store_axes("right_stick", gamepad, right)144        gamepad.hatswitch = hat_switch  # type: ignore  ### gamepad is by default empty145        return super().create_report(146            gamepad, reportID=reportID, application=application147        )148 149    def event(150        self, *, left=(None, None), right=(None, None), hat_switch=None, buttons=None151    ):152        """153        Send an input event on the default report ID.154 155        :param left: a tuple of absolute (x, y) value of the left joypad156            where ``None`` is "leave unchanged"157        :param right: a tuple of absolute (x, y) value of the right joypad158            where ``None`` is "leave unchanged"159        :param hat_switch: an absolute angular value of the hat switch160            where ``None`` is "leave unchanged"161        :param buttons: a dict of index/bool for the button states,162            where ``None`` is "leave unchanged"163        """164        r = self.create_report(165            left=left, right=right, hat_switch=hat_switch, buttons=buttons166        )167        self.call_input_event(r)168        return [r]169 170 171class JoystickGamepad(BaseGamepad):172    buttons_map = {173        1: "BTN_TRIGGER",174        2: "BTN_THUMB",175        3: "BTN_THUMB2",176        4: "BTN_TOP",177        5: "BTN_TOP2",178        6: "BTN_PINKIE",179        7: "BTN_BASE",180        8: "BTN_BASE2",181        9: "BTN_BASE3",182        10: "BTN_BASE4",183        11: "BTN_BASE5",184        12: "BTN_BASE6",185        13: "BTN_DEAD",186    }187 188    axes_map = {189        "left_stick": {190            "x": AxisMapping("x"),191            "y": AxisMapping("y"),192        },193        "right_stick": {194            "x": AxisMapping("rudder"),195            "y": AxisMapping("throttle"),196        },197    }198 199    def __init__(self, rdesc, application="Joystick", name=None, input_info=None):200        super().__init__(rdesc, application, name, input_info)201 202    def create_report(203        self,204        *,205        left=(None, None),206        right=(None, None),207        hat_switch=None,208        buttons=None,209        reportID=None,210        application=None,211    ):212        """213        Return an input report for this device.214 215        :param left: a tuple of absolute (x, y) value of the left joypad216            where ``None`` is "leave unchanged"217        :param right: a tuple of absolute (x, y) value of the right joypad218            where ``None`` is "leave unchanged"219        :param hat_switch: an absolute angular value of the hat switch220            where ``None`` is "leave unchanged"221        :param buttons: a dict of index/bool for the button states,222            where ``None`` is "leave unchanged"223        :param reportID: the numeric report ID for this report, if needed224        :param application: the application for this report, if needed225        """226        if application is None:227            application = "Joystick"228        return super().create_report(229            left=left,230            right=right,231            hat_switch=hat_switch,232            buttons=buttons,233            reportID=reportID,234            application=application,235        )236 237    def store_right_joystick(self, gamepad, data):238        gamepad.rudder, gamepad.throttle = data239