diff --git a/dualsense.py b/dualsense.py new file mode 100644 index 0000000..9aae921 --- /dev/null +++ b/dualsense.py @@ -0,0 +1,78 @@ +import pygame +from pydualsense import * + +BATTERY_STATE = { + "0": "Discharging", + "1": "Charging", + "2": "Full", + "11": "Not charging", + "15": "Error", + "10": "Temp or voltage out of range" + } + +# Initialize pygame +pygame.init() +screen = pygame.display.set_mode((800, 600)) +pygame.display.set_caption("DualSense Demo with Pygame") +font = pygame.font.SysFont(None, 48) +clock = pygame.time.Clock() + +# Initialize DualSense +ds = pydualsense() +ds.init() + +# Set initial LED color +ds.light.setColorI(0, 100, 255) + +# Configure adaptive trigger +ds.triggerL.setMode(TriggerModes.Rigid) +ds.triggerL.setForce(1, 255) + +running = True +while running: + screen.fill((30, 30, 30)) + + # Read inputs from DualSense + text_lines = [ + f"LX: {ds.state.LX:.2f}, LY: {ds.state.LY:.2f}", + f"RX: {ds.state.RX:.2f}, RY: {ds.state.RY:.2f}", + f"L2: {ds.state.L2:.2f}, R2: {ds.state.R2:.2f}", + f"Accel: {ds.state.accelerometer.X:.2f}, {ds.state.accelerometer.Y:.2f}, {ds.state.accelerometer.Z:.2f}", + f"Gyro: {ds.state.gyro.Roll:.2f}, {ds.state.gyro.Pitch:.2f}, {ds.state.gyro.Yaw:.2f}", + f"Power: {ds.battery.Level:4}%, State: {BATTERY_STATE[str(ds.battery.State)]}" + ] + + for i, line in enumerate(text_lines): + text = font.render(line, True, (255, 255, 255)) + screen.blit(text, (20, 40 + i * 50)) + + # Button-based actions + if ds.state.square: + ds.light.setColorI(255, 0, 0) # Red + elif ds.state.triangle: + ds.light.setColorI(0, 255, 0) # Green + elif ds.state.circle: + ds.light.setColorI(0, 0, 255) # Blue + elif ds.state.cross: + ds.light.setColorI(255, 255, 255) # White + + #ds.setLeftMotor(200) + #ds.setRightMotor(200) # Heavy rumble + + # Handle pygame events + for event in pygame.event.get(): + if event.type == pygame.QUIT: # Exit on window close or PS button + running = False + + if ds.state.ps: + running = False + + pygame.display.flip() + clock.tick(60) + +# Clean up +ds.setLeftMotor(0) +ds.setRightMotor(0) +ds.light.setColorI(0, 0, 255) +ds.close() +pygame.quit() diff --git a/joystick.py b/joystick.py new file mode 100644 index 0000000..7e31275 --- /dev/null +++ b/joystick.py @@ -0,0 +1,151 @@ +import pygame + +pygame.init() + + +# This is a simple class that will help us print to the screen. +# It has nothing to do with the joysticks, just outputting the +# information. +class TextPrint: + def __init__(self): + self.reset() + self.font = pygame.font.Font(None, 25) + + def tprint(self, screen, text): + text_bitmap = self.font.render(text, True, (0, 0, 0)) + screen.blit(text_bitmap, (self.x, self.y)) + self.y += self.line_height + + def reset(self): + self.x = 10 + self.y = 10 + self.line_height = 15 + + def indent(self): + self.x += 10 + + def unindent(self): + self.x -= 10 + + +def main(): + # Set the width and height of the screen (width, height), and name the window. + screen = pygame.display.set_mode((500, 700)) + pygame.display.set_caption("Joystick example") + + # Used to manage how fast the screen updates. + clock = pygame.time.Clock() + + # Get ready to print. + text_print = TextPrint() + + # This dict can be left as-is, since pygame will generate a + # pygame.JOYDEVICEADDED event for every joystick connected + # at the start of the program. + joysticks = {} + + done = False + while not done: + # Event processing step. + # Possible joystick events: JOYAXISMOTION, JOYBALLMOTION, JOYBUTTONDOWN, + # JOYBUTTONUP, JOYHATMOTION, JOYDEVICEADDED, JOYDEVICEREMOVED + for event in pygame.event.get(): + if event.type == pygame.QUIT: + done = True # Flag that we are done so we exit this loop. + + if event.type == pygame.JOYBUTTONDOWN: + print("Joystick button pressed.") + if event.button == 0: + joystick = joysticks[event.instance_id] + if joystick.rumble(0.7, 1.0, 1000): + print(f"Rumble effect played on joystick {event.instance_id}") + + if event.type == pygame.JOYBUTTONUP: + print("Joystick button released.") + + # Handle hotplugging + if event.type == pygame.JOYDEVICEADDED: + # This event will be generated when the program starts for every + # joystick, filling up the list without needing to create them manually. + joy = pygame.joystick.Joystick(event.device_index) + joysticks[joy.get_instance_id()] = joy + print(f"Joystick {joy.get_instance_id()} connencted") + + if event.type == pygame.JOYDEVICEREMOVED: + del joysticks[event.instance_id] + print(f"Joystick {event.instance_id} disconnected") + + # Drawing step + # First, clear the screen to white. Don't put other drawing commands + # above this, or they will be erased with this command. + screen.fill((255, 255, 255)) + text_print.reset() + + # Get count of joysticks. + joystick_count = pygame.joystick.get_count() + + text_print.tprint(screen, f"Number of joysticks: {joystick_count}") + text_print.indent() + + # For each joystick: + for joystick in joysticks.values(): + jid = joystick.get_instance_id() + + text_print.tprint(screen, f"Joystick {jid}") + text_print.indent() + + # Get the name from the OS for the controller/joystick. + name = joystick.get_name() + text_print.tprint(screen, f"Joystick name: {name}") + + guid = joystick.get_guid() + text_print.tprint(screen, f"GUID: {guid}") + + power_level = joystick.get_power_level() + text_print.tprint(screen, f"Joystick's power level: {power_level}") + + # Usually axis run in pairs, up/down for one, and left/right for + # the other. Triggers count as axes. + axes = joystick.get_numaxes() + text_print.tprint(screen, f"Number of axes: {axes}") + text_print.indent() + + for i in range(axes): + axis = joystick.get_axis(i) + text_print.tprint(screen, f"Axis {i} value: {axis:>6.3f}") + text_print.unindent() + + buttons = joystick.get_numbuttons() + text_print.tprint(screen, f"Number of buttons: {buttons}") + text_print.indent() + + for i in range(buttons): + button = joystick.get_button(i) + text_print.tprint(screen, f"Button {i:>2} value: {button}") + text_print.unindent() + + hats = joystick.get_numhats() + text_print.tprint(screen, f"Number of hats: {hats}") + text_print.indent() + + # Hat position. All or nothing for direction, not a float like + # get_axis(). Position is a tuple of int values (x, y). + for i in range(hats): + hat = joystick.get_hat(i) + text_print.tprint(screen, f"Hat {i} value: {str(hat)}") + text_print.unindent() + + text_print.unindent() + + # Go ahead and update the screen with what we've drawn. + pygame.display.flip() + + # Limit to 30 frames per second. + clock.tick(30) + + +if __name__ == "__main__": + main() + # If you forget this line, the program will 'hang' + # on exit if running from IDLE. + pygame.quit() \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..3f3cddb --- /dev/null +++ b/main.py @@ -0,0 +1,49 @@ +import pygame +import pygameController as PC + +if __name__ == "__main__": + pygame.init() + + done = False + + joystick_count = pygame.joystick.get_count() + + joysticks = {} + if joystick_count == 0: + print("No controllers found...Exiting!") + done = True + + while not done: + # Event processing step. + # Possible joystick events: JOYAXISMOTION, JOYBALLMOTION, JOYBUTTONDOWN, + # JOYBUTTONUP, JOYHATMOTION, JOYDEVICEADDED, JOYDEVICEREMOVED + for event in pygame.event.get(): + if event.type == pygame.QUIT: + done = True # Flag that we are done so we exit this loop. + + if event.type == pygame.JOYBUTTONDOWN: + print("Joystick button pressed.") + if event.button == 0: + joystick = joysticks[event.instance_id] + try: + joystick.rumble(0.7, 1.0, 1000) + except AttributeError: + print("Rumble is not supported on this joystick.") + except Exception as e: + print(f"Rumble call failed: {e}") + + if event.type == pygame.JOYBUTTONUP: + print("Joystick button released.") + + # Handle hotplugging + if event.type == pygame.JOYDEVICEADDED: + # This event will be generated when the program starts for every + # joystick, filling up the list without needing to create them manually. + joy = pygame.joystick.Joystick(event.device_index) + joysticks[joy.get_instance_id()] = PC.controller.Controllers(joy) + + if event.type == pygame.JOYDEVICEREMOVED: + del joysticks[event.instance_id] + print(f"Joystick {event.instance_id} disconnected") + + \ No newline at end of file diff --git a/pygameController/__init__.py b/pygameController/__init__.py new file mode 100644 index 0000000..5397a56 --- /dev/null +++ b/pygameController/__init__.py @@ -0,0 +1 @@ +from . import controller diff --git a/pygameController/controller.py b/pygameController/controller.py new file mode 100644 index 0000000..5a13a1c --- /dev/null +++ b/pygameController/controller.py @@ -0,0 +1,31 @@ +import pygame +from .controlsbase import ControlsBase +from .dualsense_controller import DualSenseController +from .dualsense_edge_controller import DualSenseEdgeController +from .logitech_f310_controller import LogitechF310Controller +from .logitech_f510_controller import LogitechF510Controller +from .logitech_f710_controller import LogitechF710Controller +from .xbox_series_x_controller import XboxSeriesXController +from .generic_controller import GenericController +from .logitech_dual_action_controller import LogitechDualActionController + +__version__ = "0.0.1" + +CONTROLLERS = { + "DualSense Wireless Controller": DualSenseController, + "DualSense Edge Wireless Controller": DualSenseEdgeController, + "Logitech Gamepad F310": LogitechF310Controller, + "Logitech Gamepad F510": LogitechF510Controller, + "Logitech Gamepad F710": LogitechF710Controller, + "Logitech Dual Action": LogitechDualActionController, + "X box Series X Controller": XboxSeriesXController + } + +class Controllers: + def __init__(self, joy): + self.controllers = [] + if not joy.get_name() in CONTROLLERS: + self.controllers.append(GenericController(joy)) + else: + self.controllers.append(CONTROLLERS[joy.get_name()](joy)) + \ No newline at end of file diff --git a/pygameController/controlsbase.py b/pygameController/controlsbase.py new file mode 100644 index 0000000..8285cae --- /dev/null +++ b/pygameController/controlsbase.py @@ -0,0 +1,33 @@ +""" +This is an abstract baseclass for the controls of snake. +""" +from abc import ABC, abstractmethod + +class ControlsBase(ABC): + @abstractmethod + def handle_input(self, event): + pass + + @abstractmethod + def left(self): + pass + + @abstractmethod + def right(self): + pass + + @abstractmethod + def up(self): + pass + + @abstractmethod + def down(self): + pass + + @abstractmethod + def pause(self): + pass + + @abstractmethod + def rumble(self): + pass \ No newline at end of file diff --git a/pygameController/dualsense_audio.py b/pygameController/dualsense_audio.py new file mode 100644 index 0000000..430ca5e --- /dev/null +++ b/pygameController/dualsense_audio.py @@ -0,0 +1,70 @@ +import os +import time +import numpy as np +import sounddevice as sd +import alsaaudio +import pulsectl + +class DualSenseAudio: + def __init__(self): + self.alsa_devices = self._get_alsa_devices() + self.pulse_devices = self._get_pulseaudio_devices() + self.dualsense_device = self._detect_dualsense() + + def _get_alsa_devices(self): + try: + cards = alsaaudio.cards() + return cards + except Exception as e: + print("ALSA detection failed:", e) + return [] + + def _get_pulseaudio_devices(self): + try: + pulse = pulsectl.Pulse("dualsense-audio") + sinks = pulse.sink_list() + return sinks + except Exception as e: + print("PulseAudio detection failed:", e) + return [] + + def _detect_dualsense(self): + # Check ALSA names + for card in self.alsa_devices: + if "DualSense" in card: + return {'type': 'alsa', 'name': card} + + # Check PulseAudio sinks + for sink in self.pulse_devices: + if "dualsense" in sink.description.lower(): + return {'type': 'pulse', 'name': sink.name} + + return None + + def play_tone(self, frequency=440.0, duration=2.0, volume=0.5): + if not self.dualsense_device: + print("DualSense speaker not found.") + return + + print(f"Playing tone on DualSense ({self.dualsense_device['type']})...") + + fs = 48000 # Sample rate + t = np.linspace(0, duration, int(fs * duration), False) + tone = np.sin(frequency * 2 * np.pi * t) * volume + audio = tone.astype(np.float32) + + if self.dualsense_device['type'] == 'pulse': + sd.play(audio, samplerate=fs, device=self.dualsense_device['name']) + elif self.dualsense_device['type'] == 'alsa': + device_index = self.alsa_devices.index(self.dualsense_device['name']) + sd.play(audio, samplerate=fs, device=device_index) + sd.wait() + + def list_devices(self): + print("ALSA Devices:") + for card in self.alsa_devices: + print(f" - {card}") + print("\nPulseAudio Devices:") + for sink in self.pulse_devices: + print(f" - {sink.name} ({sink.description})") + diff --git a/pygameController/dualsense_controller.py b/pygameController/dualsense_controller.py new file mode 100644 index 0000000..075fda6 --- /dev/null +++ b/pygameController/dualsense_controller.py @@ -0,0 +1,74 @@ +from pygameController.controlsbase import ControlsBase +from pydualsense import * + +BATTERY_STATE = { + "0": "Discharging", + "1": "Charging", + "2": "Full", + "11": "Not charging", + "15": "Error", + "10": "Temp or voltage out of range" + } + +class DualSenseController(ControlsBase): + def __init__(self, joy): + self.device = pydualsense() + self.device.init() + self.name = self.device.device.get_product_string() + self.powerlevel = self.device.battery.Level + self.batterystate = BATTERY_STATE[str(self.device.battery.State)] + self.set_player_id(PlayerID.PLAYER_1) + print(f"{self.name} connected") + print(f"Power level: {self.powerlevel}") + print(f"Battery state: {self.batterystate}") + + def handle_input(self, event): + pass + + def set_led(self, red: int, green: int, blue: int): + self.device.light.setColorI(red, green, blue) + + def set_player_id(self, playerid: PlayerID): + self.device.light.setPlayerID(playerid) + + def left(self): + pass + + def right(self): + pass + + def up(self): + pass + + def down(self): + pass + + def pause(self): + pass + + def rumble(self): + pass + + @property + def name(self) -> str: + return self._name + + @name.setter + def name(self, name: str) -> None: + self._name = name + + @property + def powerlevel(self) -> str: + return self._powerlevel + + @powerlevel.setter + def powerlevel(self, lvl: str) -> None: + self._powerlevel = lvl + + @property + def batterystate(self) -> int: + return self._batterystate + + @batterystate.setter + def batterystate(self, state) -> None: + self._batterystate = state \ No newline at end of file diff --git a/pygameController/dualsense_edge_controller.py b/pygameController/dualsense_edge_controller.py new file mode 100644 index 0000000..46bfb50 --- /dev/null +++ b/pygameController/dualsense_edge_controller.py @@ -0,0 +1,199 @@ +import time +import threading +import numpy as np +import sounddevice as sd +import alsaaudio +import pulsectl +from pydualsense import * + + +class DualSenseEdgeController: + def __init__(self): + # DualSense input/output interface + self.ds = pydualsense() + self.ds.init() + self._listening = False + self._bindings = {} + + # Audio detection + self.alsa_devices = self._get_alsa_devices() + self.pulse_devices = self._get_pulseaudio_devices() + self.dualsense_audio_device = self._detect_dualsense_audio() + + print("DualSense initialized.") + + # ---------------------- Device Controls ---------------------- + + def set_rumble(self, small_motor: int, big_motor: int): + self.ds.setRumble(small_motor, big_motor) + + def stop_rumble(self): + self.set_rumble(0, 0) + + def set_led_color(self, r: int, g: int, b: int): + self.ds.setLightBarColor(r, g, b) + + def set_trigger_effects(self, left_mode='Off', right_mode='Off', force=0): + left = getattr(TriggerModes, left_mode.upper(), TriggerModes.Off) + right = getattr(TriggerModes, right_mode.upper(), TriggerModes.Off) + self.ds.triggerL.setMode(left) + self.ds.triggerR.setMode(right) + if force > 0: + self.ds.triggerL.setForce(force) + self.ds.triggerR.setForce(force) + + # ---------------------- Predefined Rumble Patterns ---------------------- + + def rumble_pattern(self, pattern: str, duration: float = 1.0): + patterns = { + "pulse": self._pulse_rumble, + "heartbeat": self._heartbeat_rumble, + "buzz": self._buzz_rumble, + "wave": self._wave_rumble, + "alarm": self._alarm_rumble, + } + if pattern in patterns: + threading.Thread(target=patterns[pattern], args=(duration,), daemon=True).start() + else: + print(f"Unknown rumble pattern: {pattern}") + + def _pulse_rumble(self, duration): + end = time.time() + duration + while time.time() < end: + self.set_rumble(50, 150) + time.sleep(0.2) + self.stop_rumble() + time.sleep(0.2) + + def _heartbeat_rumble(self, duration): + end = time.time() + duration + while time.time() < end: + self.set_rumble(200, 200) + time.sleep(0.1) + self.stop_rumble() + time.sleep(0.1) + self.set_rumble(100, 100) + time.sleep(0.1) + self.stop_rumble() + time.sleep(0.4) + + def _buzz_rumble(self, duration): + self.set_rumble(80, 255) + time.sleep(duration) + self.stop_rumble() + + def _wave_rumble(self, duration): + start = time.time() + while time.time() - start < duration: + for i in range(0, 256, 25): + self.set_rumble(i, 255 - i) + time.sleep(0.05) + for i in reversed(range(0, 256, 25)): + self.set_rumble(i, 255 - i) + time.sleep(0.05) + self.stop_rumble() + + def _alarm_rumble(self, duration): + end = time.time() + duration + while time.time() < end: + self.set_rumble(255, 0) + time.sleep(0.1) + self.set_rumble(0, 255) + time.sleep(0.1) + self.stop_rumble() + + # ---------------------- Input Listener + Bindings ---------------------- + + def bind(self, button: str, action: callable): + """Bind a button to a callable. Ex: controller.bind('cross', lambda: rumble_pattern('buzz'))""" + self._bindings[button] = action + + def start_input_listener(self): + def listen(): + while self._listening: + #self.ds.update() + for button, action in self._bindings.items(): + if getattr(self.ds, button, False): + action() + self._listening = True + thread = threading.Thread(target=listen, daemon=True) + thread.start() + + def stop_input_listener(self): + self._listening = False + + # ---------------------- Audio Output ---------------------- + + def _get_alsa_devices(self): + try: + return alsaaudio.cards() + except Exception: + return [] + + def _get_pulseaudio_devices(self): + try: + pulse = pulsectl.Pulse("dualsense-audio") + return pulse.sink_list() + except Exception: + return [] + + def _detect_dualsense_audio(self): + # Check ALSA names + for card in self.alsa_devices: + if "DualSense" in card: + return {'type': 'alsa', 'name': card} + + # Check PulseAudio sinks + for sink in self.pulse_devices: + if "dualsense" in sink.description.lower(): + return {'type': 'pulse', 'name': sink.name} + + return None + + def play_tone(self, frequency=440.0, duration=2.0, volume=0.5): + if not self.dualsense_audio_device: + print("DualSense speaker not detected.") + return + + print(f"Playing tone on DualSense ({self.dualsense_audio_device['type']})...") + + fs = 48000 # Sample rate + t = np.linspace(0, duration, int(fs * duration), False) + tone = np.sin(frequency * 2 * np.pi * t) * volume + audio = tone.astype(np.float32) + + try: + if self.dualsense_audio_device['type'] == 'pulse': + sd.play(audio, samplerate=fs, device=self.dualsense_audio_device['name']) + elif self.dualsense_audio_device['type'] == 'alsa': + device_index = self.alsa_devices.index(self.dualsense_audio_device['name']) + sd.play(audio, samplerate=fs, device=device_index) + sd.wait() + except Exception as e: + print("Failed to play tone:", e) + + def list_audio_devices(self): + print("ALSA Devices:") + for card in self.alsa_devices: + print(f" - {card}") + print("\nPulseAudio Devices:") + for sink in self.pulse_devices: + print(f" - {sink.name} ({sink.description})") + + # ---------------------- Cleanup ---------------------- + + def close(self): + self.ds.close() + +if __name__ == "__main__": + + controller = DualSenseController() + + # Bind buttons to patterns + controller.bind("cross", lambda: controller.rumble_pattern("heartbeat", 1.5)) + controller.bind("circle", lambda: controller.rumble_pattern("buzz", 0.5)) + controller.bind("triangle", lambda: controller.rumble_pattern("pulse", 2)) + controller.bind("square", lambda: controller.set_led_color(255, 0, 0)) + + # Start listening + controller.start_input_listener() diff --git a/pygameController/generic_controller.py b/pygameController/generic_controller.py new file mode 100644 index 0000000..e6bb0b2 --- /dev/null +++ b/pygameController/generic_controller.py @@ -0,0 +1,50 @@ +import pygame +from pygameController.controlsbase import ControlsBase + +class GenericController(ControlsBase): + def __init__(self, joy): + self.device = joy + self.instance_id: int = self.device.get_instance_id() + self.name = self.device.get_name() + self.guid = self.device.get_guid() + self.numaxis: int = self.device.get_numaxes() + self.axis: list = [self.device.get_axis(a) for a in range(self.numaxis)] + self.numhats: int = self.device.get_numhats() + self.hats: list = [self.device.get_hat(h) for h in range(self.numhats)] + self.numbuttons: int = self.device.get_numbuttons() + self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)] + print(f"{self.name} connected") + print("GUID:", self.guid) + print("Axis:", self.numaxis, self.axis) + print("Hats:", self.numhats, self.hats) + print("Buttons:", self.numbuttons, self.buttons) + + def handle_input(self, event): + pass + + def left(self): + pass + + def right(self): + pass + + def up(self): + pass + + def down(self): + pass + + def pause(self): + pass + + def rumble(self): + pass + + @property + def name(self) -> str: + return self._name + + @name.setter + def name(self, name: str) -> None: + self._name = name + \ No newline at end of file diff --git a/pygameController/logitech_dual_action_controller.py b/pygameController/logitech_dual_action_controller.py new file mode 100644 index 0000000..e380a83 --- /dev/null +++ b/pygameController/logitech_dual_action_controller.py @@ -0,0 +1,80 @@ +""" +Logitech F310 Controller class. +This controller is a usb controller, with the following features. +(XInput mode) +6 axis +11 buttons +1 hat + +(DirectInput mode) +4 axis +12 buttons +1 hat +""" + +import pygame +from pygameController.controlsbase import ControlsBase +from enum import Enum + +class InputMode(Enum): + DirectInput = 1 + XInput = 2 + +class LogitechDualActionController(ControlsBase): + def __init__(self, joy): + self.device = joy + self.instance_id: int = self.device.get_instance_id() + self.name = self.device.get_name() + self.guid = self.device.get_guid() + self.powerlevel = self.device.get_power_level() + self.numaxis: int = self.device.get_numaxes() + self.axis: list = [self.device.get_axis(a) for a in range(self.numaxis)] + self.numhats: int = self.device.get_numhats() + self.hats: list = [self.device.get_hat(h) for h in range(self.numhats)] + self.numbuttons: int = self.device.get_numbuttons() + self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)] + self.input_mode = InputMode.DirectInput + print(f"{self.name} connected") + print("GUID:", self.guid) + print("Axis:", self.numaxis, self.axis) + print("Hats:", self.numhats, self.hats) + print("Buttons:", self.numbuttons, self.buttons) + print("Input mode:", self.input_mode) + + def handle_input(self, event): + pass + + def left(self): + pass + + def right(self): + pass + + def up(self): + pass + + def down(self): + pass + + def pause(self): + pass + + def rumble(self): + pass + + @property + def name(self) -> str: + return self._name + + @name.setter + def name(self, name: str) -> None: + self._name = name + + @property + def input_mode(self) -> int: + return self._inputmode + + @input_mode.setter + def input_mode(self, mode: int) -> None: + self._inputmode = mode + \ No newline at end of file diff --git a/pygameController/logitech_f310_controller.py b/pygameController/logitech_f310_controller.py new file mode 100644 index 0000000..be34092 --- /dev/null +++ b/pygameController/logitech_f310_controller.py @@ -0,0 +1,80 @@ +""" +Logitech F310 Controller class. +This controller is a usb controller, with the following features. +(XInput mode) +6 axis +11 buttons +1 hat + +(DirectInput mode) +4 axis +12 buttons +1 hat +""" + +import pygame +from pygameController.controlsbase import ControlsBase +from enum import Enum + +class InputMode(Enum): + DirectInput = 1 + XInput = 2 + +class LogitechF310Controller(ControlsBase): + def __init__(self, joy): + self.device = joy + self.instance_id: int = self.device.get_instance_id() + self.name = self.device.get_name() + self.guid = self.device.get_guid() + self.powerlevel = self.device.get_power_level() + self.numaxis: int = self.device.get_numaxes() + self.axis: list = [self.device.get_axis(a) for a in range(self.numaxis)] + self.numhats: int = self.device.get_numhats() + self.hats: list = [self.device.get_hat(h) for h in range(self.numhats)] + self.numbuttons: int = self.device.get_numbuttons() + self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)] + self.input_mode = InputMode.XInput + print(f"{self.name} connected") + print("GUID:", self.guid) + print("Axis:", self.numaxis, self.axis) + print("Hats:", self.numhats, self.hats) + print("Buttons:", self.numbuttons, self.buttons) + print("Input mode:", self.input_mode) + + def handle_input(self, event): + pass + + def left(self): + pass + + def right(self): + pass + + def up(self): + pass + + def down(self): + pass + + def pause(self): + pass + + def rumble(self): + pass + + @property + def name(self) -> str: + return self._name + + @name.setter + def name(self, name: str) -> None: + self._name = name + + @property + def input_mode(self) -> int: + return self._inputmode + + @input_mode.setter + def input_mode(self, mode: int) -> None: + self._inputmode = mode + \ No newline at end of file diff --git a/pygameController/logitech_f510_controller.py b/pygameController/logitech_f510_controller.py new file mode 100644 index 0000000..a3d8a09 --- /dev/null +++ b/pygameController/logitech_f510_controller.py @@ -0,0 +1,108 @@ +""" +Logitech F310 Controller class. +This controller is a usb controller, with the following features. +(XInput mode) +6 axis +11 buttons +1 hat + +(DirectInput mode) +4 axis +12 buttons +1 hat +""" + +import pygame +from pygameController.controlsbase import ControlsBase +from enum import Enum + +class InputMode(Enum): + DirectInput = 1 + XInput = 2 + +class ConnectionType(Enum): + WIRED = 1 + WIRELESS = 2 + +class LogitechF510Controller(ControlsBase): + def __init__(self, joy): + self.device = joy + self.instance_id: int = self.device.get_instance_id() + self.name = self.device.get_name() + self.numaxis: int = self.device.get_numaxis() + self.axis: list = [] + self.numhats: int = self.device.get_numhats() + self.hats: list = [] + self.numbuttons: int = self.device.get_numbuttons() + self.buttons: list = [] + self.input_mode: InputMode.DirectInput + self.input_connection: ConnectionType.WIRED + + def handle_input(self, event): + pass + + def left(self): + pass + + def right(self): + pass + + def up(self): + pass + + def down(self): + pass + + def pause(self): + pass + + def rumble(self): + pass + + @property + def name(self) -> str: + return self._name + + @name.setter + def name(self, name: str) -> None: + self._name = name + + @property + def axis(self) -> list: + return self._axis + + @axis.setter + def axis(self) -> None: + self._axis = [self.device.get_axis(a) for a in range(self.numaxis)] + + @property + def hats(self) -> list: + return self._hats + + @hats.setter + def hats(self) -> None: + self.hats = [self.device.get_hats(h) for h in range(self.numhats)] + + @property + def buttons(self) -> list: + return self._buttons + + @buttons.setter + def buttons(self) -> None: + self._buttons = [self.device.get_buttons(b) for b in range(self.numbuttons)] + + @property + def input_mode(self) -> int: + return self._inputmode + + @input_mode.setter + def input_mode(self, mode: int) -> None: + self._inputmode = mode + + @property + def input_connection(self) -> int: + return self._input_connection + + @input_connection.setter + def input_connection(self, conn: int) -> None: + self._input_connection = conn \ No newline at end of file diff --git a/pygameController/logitech_f710_controller.py b/pygameController/logitech_f710_controller.py new file mode 100644 index 0000000..1df528c --- /dev/null +++ b/pygameController/logitech_f710_controller.py @@ -0,0 +1,108 @@ +""" +Logitech F310 Controller class. +This controller is a usb controller, with the following features. +(XInput mode) +6 axis +11 buttons +1 hat + +(DirectInput mode) +4 axis +12 buttons +1 hat +""" + +import pygame +from pygameController.controlsbase import ControlsBase +from enum import Enum + +class InputMode(Enum): + DirectInput = 1 + XInput = 2 + +class ConnectionType(Enum): + WIRED = 1 + WIRELESS = 2 + +class LogitechF710Controller(ControlsBase): + def __init__(self, joy): + self.device = joy + self.instance_id: int = self.device.get_instance_id() + self.name = self.device.get_name() + self.numaxis: int = self.device.get_numaxis() + self.axis: list = [] + self.numhats: int = self.device.get_numhats() + self.hats: list = [] + self.numbuttons: int = self.device.get_numbuttons() + self.buttons: list = [] + self.input_mode: InputMode.DirectInput + self.input_connection: ConnectionType.WIRED + + def handle_input(self, event): + pass + + def left(self): + pass + + def right(self): + pass + + def up(self): + pass + + def down(self): + pass + + def pause(self): + pass + + def rumble(self): + pass + + @property + def name(self) -> str: + return self._name + + @name.setter + def name(self, name: str) -> None: + self._name = name + + @property + def axis(self) -> list: + return self._axis + + @axis.setter + def axis(self) -> None: + self._axis = [self.device.get_axis(a) for a in range(self.numaxis)] + + @property + def hats(self) -> list: + return self._hats + + @hats.setter + def hats(self) -> None: + self.hats = [self.device.get_hats(h) for h in range(self.numhats)] + + @property + def buttons(self) -> list: + return self._buttons + + @buttons.setter + def buttons(self) -> None: + self._buttons = [self.device.get_buttons(b) for b in range(self.numbuttons)] + + @property + def input_mode(self) -> int: + return self._inputmode + + @input_mode.setter + def input_mode(self, mode: int) -> None: + self._inputmode = mode + + @property + def input_connection(self) -> int: + return self._input_connection + + @input_connection.setter + def input_connection(self, conn: int) -> None: + self._input_connection = conn \ No newline at end of file diff --git a/pygameController/xbox_series_x_controller.py b/pygameController/xbox_series_x_controller.py new file mode 100644 index 0000000..7ed608f --- /dev/null +++ b/pygameController/xbox_series_x_controller.py @@ -0,0 +1,50 @@ +import time +import threading + +class XboxSeriesXController: + def __init__(self, joy): + self.device = joy + self.instance_id: int = self.device.get_instance_id() + self.name = self.device.get_name() + self.guid = self.device.get_guid() + self.numaxis: int = self.device.get_numaxes() + self.axis: list = [self.device.get_axis(a) for a in range(self.numaxis)] + self.numhats: int = self.device.get_numhats() + self.hats: list = [self.device.get_hat(h) for h in range(self.numhats)] + self.numbuttons: int = self.device.get_numbuttons() + self.buttons: list = [self.device.get_button(b) for b in range(self.numbuttons)] + print(f"{self.name} connected") + print("GUID:", self.guid) + print("Axis:", self.numaxis, self.axis) + print("Hats:", self.numhats, self.hats) + print("Buttons:", self.numbuttons, self.buttons) + + def handle_input(self, event): + pass + + def left(self): + pass + + def right(self): + pass + + def up(self): + pass + + def down(self): + pass + + def pause(self): + pass + + def rumble(self): + pass + + @property + def name(self) -> str: + return self._name + + @name.setter + def name(self, name: str) -> None: + self._name = name + \ No newline at end of file