diff --git a/pygameController/controller.py b/pygameController/controller.py index cdb833f..5167912 100644 --- a/pygameController/controller.py +++ b/pygameController/controller.py @@ -1,13 +1,13 @@ import pygame -from pygameController.controlsbase import ControlsBase -from pygameController.dualsense_controller import DualSenseController -from pygameController.dualsense_edge_controller import DualSenseEdgeController -from pygameController.logitech_f310_controller import LogitechF310Controller -from pygameController.logitech_f510_controller import LogitechF510Controller -from pygameController.logitech_f710_controller import LogitechF710Controller -from pygameController.xbox_controller import XboxController -from pygameController.generic_controller import GenericController -from pygameController.logitech_dual_action_controller import LogitechDualActionController +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 CONTROLLERS = { "DualSense Wireless Controller": DualSenseController, @@ -16,7 +16,7 @@ CONTROLLERS = { "Logitech Gamepad F510": LogitechF510Controller, "Logitech Gamepad F710": LogitechF710Controller, "Logitech Dual Action": LogitechDualActionController, - "Xbox": XboxController + "X box Series X Controller": XboxSeriesXController } class Controllers: diff --git a/pygameController/xbox_controller.py b/pygameController/xbox_controller.py deleted file mode 100644 index 86c97af..0000000 --- a/pygameController/xbox_controller.py +++ /dev/null @@ -1,199 +0,0 @@ -import time -import threading -import numpy as np -import sounddevice as sd -import alsaaudio -import pulsectl -from pydualsense import * - - -class XboxController: - 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/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