0.2.0 #23

Merged
Lerking merged 57 commits from 0.2.0 into main 2025-05-06 20:50:16 +02:00
21 changed files with 532 additions and 134 deletions

View File

@@ -30,7 +30,7 @@ class TextPrint:
def main():
# Set the width and height of the screen (width, height), and name the window.
screen = pygame.display.set_mode((500, 700))
screen = pygame.display.set_mode((800, 700))
pygame.display.set_caption("Joystick example")
# Used to manage how fast the screen updates.

55
joystick_debug.py Normal file
View File

@@ -0,0 +1,55 @@
import os
import pygame
from evdev import InputDevice, list_devices
def format_vidpid(info):
return f"{info.vendor:04x}:{info.product:04x}"
def get_evdev_devices():
evdev_devices = {}
for path in list_devices():
try:
dev = InputDevice(path)
evdev_devices[os.path.basename(path)] = {
'name': dev.name,
'phys': dev.phys,
'vidpid': format_vidpid(dev.info),
'path': path
}
except Exception:
pass
return evdev_devices
def match_evdev_to_pygame():
pygame.init()
pygame.joystick.init()
evdev_devices = get_evdev_devices()
for i in range(pygame.joystick.get_count()):
js = pygame.joystick.Joystick(i)
js.init()
name = js.get_name()
guid = js.get_guid()
# Try to guess the matching evdev device based on name
matched = None
for ev in evdev_devices.values():
if name.lower() in ev['name'].lower() or ev['name'].lower() in name.lower():
matched = ev
break
print(f"Joystick {i}:")
print(f" pygame name : {name}")
print(f" pygame guid : {guid}")
if matched:
print(f" evdev name : {matched['name']}")
print(f" path : {matched['path']}")
print(f" phys : {matched['phys']}")
print(f" VID:PID : {matched['vidpid']}")
else:
print(f" evdev info : [not matched]")
print("-" * 40)
if __name__ == "__main__":
match_evdev_to_pygame()

View File

@@ -1,8 +1,10 @@
import pygame
import pygameControls as PC
from pygameControls import globals
if __name__ == "__main__":
pygame.init()
globals.init()
done = False

View File

@@ -1,35 +1,21 @@
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 .sony_playstation3_controller import SonyPlayStation3Controller
from .playstation3_controller import PlayStation3Controller
from .generic_controller import GenericController
from .logitech_dual_action_controller import LogitechDualActionController
from . import globals
__version__ = "0.1.13"
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,
"Xbox Series X Controller": XboxSeriesXController,
"Sony PLAYSTATION(R)3 Controller": SonyPlayStation3Controller,
"PLAYSTATION(R)3 Controller": PlayStation3Controller
}
__version__ = "0.2.0"
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))
cont = self.detect_controller(joy.get_guid())
print(cont)
self.controllers.append(cont(joy))
def detect_controller(self, guid):
for gp in globals.GAMEPADS:
print(gp)
for p in globals.GAMEPADS[gp]:
print(p)
if p["guid"] != guid:
continue
return p["class"]
return globals.CONTROLLERS["Generic Controller"]

View File

@@ -1,4 +1,5 @@
from pygameControls.controlsbase import ControlsBase
from .enums import ConnectionType
from pydualsense import *
BATTERY_STATE = {
@@ -15,6 +16,7 @@ class DualSenseController(ControlsBase):
self.device = pydualsense()
self.device.init()
self.name = self.device.device.get_product_string()
self.guid = self.device.get_guid()
self.powerlevel = self.device.battery.Level
self.batterystate = BATTERY_STATE[str(self.device.battery.State)]
self.set_player_id(PlayerID.PLAYER_1)

View File

@@ -1,4 +1,5 @@
from pygameControls.controlsbase import ControlsBase
from enums import ConnectionType
from pydualsense import *
@@ -7,6 +8,7 @@ class DualSenseEdgeController(ControlsBase):
self.device = pydualsense()
self.device.init()
self.name = self.device.device.get_product_string()
self.guid = self.device.get_guid()
self.powerlevel = self.device.battery.Level
self.batterystate = BATTERY_STATE[str(self.device.battery.State)]
self.set_player_id(PlayerID.PLAYER_1)

12
pygameControls/enums.py Normal file
View File

@@ -0,0 +1,12 @@
from enum import Enum
class ConnectionType(Enum):
USB = 1
BLUETOOTH = 2
WIRELESS = 3
Unknown = -1
class InputType(Enum):
DirectInput = 1
XInput = 2
Unknown = -1

View File

@@ -1,5 +1,6 @@
import pygame
from pygameControls.controlsbase import ControlsBase
from enums import ConnectionType
class GenericController(ControlsBase):
def __init__(self, joy):

207
pygameControls/globals.py Normal file
View File

@@ -0,0 +1,207 @@
from .enums import ConnectionType, InputType
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 .sony_playstation3_controller import SonyPlayStation3Controller
from .playstation3_controller import PlayStation3Controller
from .sony_playstation4_controller import SonyPlayStation4Controller
from .playstation4_controller import PlayStation4Controller
from .generic_controller import GenericController
from .logitech_dual_action_controller import LogitechDualActionController
def init():
global VID_PID
VID_PID = {
"046d:c216": "Logitech Gamepad F310",
"046d:c21d": "Microsoft X-Box 360 pad",
"046d:c21d": "Logitech Dual Action",
"045e:0b12": "Xbox Series X Controller",
"045e:0b13": "Xbox Series X Controller",
"045e:0b20": "Xbox Series X Controller",
"045e:0b21": "Xbox Series X Controller",
"054c:0ce6": "DualSense Wireless Controller",
"054c:0df2": "DualSense Wireless Controller",
}
global CONTROLLERS
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,
"Microsoft X-Box 360 pad": LogitechDualActionController,
"Xbox Series X Controller": XboxSeriesXController,
"Sony PLAYSTATION(R)3 Controller": SonyPlayStation3Controller,
"PLAYSTATION(R)3 Controller": PlayStation3Controller,
"Sony PLAYSTATION(R)4 Controller": SonyPlayStation4Controller,
"PLAYSTATION(R)4 Controller": PlayStation4Controller,
"Generic Controller": GenericController
}
global GAMEPADS
GAMEPADS = {
"Sony Controller": [
{
"vidpid": "054c:0ce6",
"guid": "0300fd574c050000e60c000011810000",
"connection": ConnectionType.USB,
"input": InputType.DirectInput,
"name": [
"Sony Interactive Entertainment DualSense Wireless Controller",
"Sony Corp. DualSense wireless controller (PS5)",
"DualSense Wireless Controller"
],
"class": CONTROLLERS["DualSense Wireless Controller"]
},
{
"vidpid": "054c:0df2",
"guid": "050057564c050000e60c000000810000",
"connection": ConnectionType.BLUETOOTH,
"input": InputType.DirectInput,
"name": [
"DualSense Wireless Controller"
],
"class": CONTROLLERS["DualSense Wireless Controller"]
},
{
"vidpid": "054c:0dfc",
"guid": "",
"connection": ConnectionType.USB,
"input": InputType.DirectInput,
"name": ["DualSense Edge Wireless Controller"],
"class": CONTROLLERS["DualSense Edge Wireless Controller"]
},
{
"vidpid": "054c:0dfc",
"guid": "",
"connection": ConnectionType.BLUETOOTH,
"input": InputType.DirectInput,
"name": ["DualSense Edge Wireless Controller"],
"class": CONTROLLERS["DualSense Edge Wireless Controller"]
},
{
"vidpid": "054c:0268",
"guid": "0300afd34c0500006802000011810000",
"connection": ConnectionType.USB,
"input": InputType.DirectInput,
"name": ["Sony PLAYSTATION(R)3 Controller"],
"class": CONTROLLERS["Sony PLAYSTATION(R)3 Controller"]
},
{
"vidpid": "",
"guid": "0500f9d24c0500006802000000800000",
"connection": ConnectionType.BLUETOOTH,
"input": InputType.DirectInput,
"name": ["PLAYSTATION(R)3 Controller"],
"class": CONTROLLERS["PLAYSTATION(R)3 Controller"]
},
{
"vidpid": "054c:05c4",
"guid": "",
"connection": ConnectionType.USB,
"input": InputType.DirectInput,
"name": ["DualShock 4 v1 Controller"],
"class": CONTROLLERS["PLAYSTATION(R)4 Controller"]
},
{
"vidpid": "054c:05c4",
"guid": "",
"connection": ConnectionType.BLUETOOTH,
"input": InputType.DirectInput,
"name": ["DualShock 4 v1 Controller"],
"class": CONTROLLERS["Sony PLAYSTATION(R)4 Controller"]
},
{
"vidpid": "054c:09cc",
"guid": "",
"connection": ConnectionType.USB,
"input": InputType.DirectInput,
"name": ["DualShock 4 v2 Controller"],
"class": CONTROLLERS["PLAYSTATION(R)4 Controller"]
},
{
"vidpid": "054c:09cc",
"guid": "",
"connection": ConnectionType.BLUETOOTH,
"input": InputType.DirectInput,
"name": ["DualShock 4 v2 Controller"],
"class": CONTROLLERS["Sony PLAYSTATION(R)4 Controller"]
}
],
"Microsoft Controller": [
{
"vidpid": "045e:0b12",
"guid": "0300509d5e040000120b000017050000",
"connection": ConnectionType.USB,
"input": InputType.XInput,
"name": [
"Xbox Series X Controller",
"Microsoft Corp. Xbox Controller"
],
"class": CONTROLLERS["Xbox Series X Controller"]
},
{
"vidpid": "045e:0b13",
"guid": "0500509d5e040000130b000023050000",
"connection": ConnectionType.BLUETOOTH,
"input": InputType.XInput,
"name": [
"Xbox Series X Controller",
"Xbox Wireless Controller"
],
"class": CONTROLLERS["Xbox Series X Controller"]
}
],
"Logitech Controller": [
{
"vidpid": "046d:c21d",
"guid": "030005ff6d0400001dc2000014400000",
"connection": ConnectionType.USB,
"input": InputType.XInput,
"name": [
"Logitech, Inc. F310 Gamepad [XInput Mode]",
"Logitech Gamepad F310"
],
"class": CONTROLLERS["Logitech Gamepad F310"]
},
{
"vidpid": "046d:c216",
"guid": "0300040e6d04000016c2000011010000",
"connection": ConnectionType.USB,
"input": InputType.DirectInput,
"name": [
"Logitech, Inc. F310 Gamepad [DirectInput Mode]",
"Logitech Dual Action",
"Logitech Logitech Dual Action"
],
"class": CONTROLLERS["Logitech Dual Action"]
},
{
"vidpid": "046d:c21d",
"guid": "",
"connection": ConnectionType.USB,
"input": InputType.XInput,
"name": [
"Logitech, Inc. F710 Gamepad [XInput Mode]",
"Logitech Gamepad F710"
],
"class": CONTROLLERS["Logitech Gamepad F710"]
},
{
"vidpid": "046d:c216",
"guid": "",
"connection": ConnectionType.USB,
"input": InputType.DirectInput,
"name": [
"Logitech, Inc. F710 Gamepad [DirectInput Mode]",
"Logitech Dual Action"
],
"class": CONTROLLERS["Logitech Dual Action"]
}
]
}

View File

@@ -14,12 +14,8 @@ This controller is a usb controller, with the following features.
import pygame
from pygameControls.controlsbase import ControlsBase
from enum import Enum
from enums import ConnectionType
class InputMode(Enum):
DirectInput = 1
XInput = 2
class LogitechDualActionController(ControlsBase):
def __init__(self, joy):
self.device = joy
@@ -33,7 +29,6 @@ class LogitechDualActionController(ControlsBase):
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
self.mapping = {
"r2 trigger": 7,
"l2 trigger": 6,
@@ -85,12 +80,4 @@ class LogitechDualActionController(ControlsBase):
@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

View File

@@ -14,12 +14,8 @@ This controller is a usb controller, with the following features.
import pygame
from pygameControls.controlsbase import ControlsBase
from enum import Enum
from enums import ConnectionType
class InputMode(Enum):
DirectInput = 1
XInput = 2
class LogitechF310Controller(ControlsBase):
def __init__(self, joy):
self.device = joy
@@ -33,7 +29,6 @@ class LogitechF310Controller(ControlsBase):
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
self.mapping = {
"l1 button": 4,
"r1 button": 5,
@@ -83,12 +78,4 @@ class LogitechF310Controller(ControlsBase):
@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

View File

@@ -1,41 +1,26 @@
"""
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
Logitech F510 Controller class.
This controller should have the same setup as the F310, but with the addition of rumble effect.
I don't have this controller, so I haven't been able to verify the workings!
Subsequent updates will be done, based on updates for the F310.
"""
import pygame
from pygameControls.controlsbase import ControlsBase
from enum import Enum
from enums import ConnectionType
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.guid = self.device.get_guid()
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.mapping = {
"l1 button": 4,
"r1 button": 5,
@@ -109,19 +94,4 @@ class LogitechF510Controller(ControlsBase):
@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

View File

@@ -1,41 +1,24 @@
"""
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
Logitech F710 Controller class.
This controller is a usb/wireless controller.
"""
import pygame
from pygameControls.controlsbase import ControlsBase
from enum import Enum
from enums import ConnectionType
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.guid = self.device.get_guid()
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.mapping = {
"l1 button": 4,
"r1 button": 5,
@@ -109,19 +92,4 @@ class LogitechF710Controller(ControlsBase):
@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

View File

@@ -1,4 +1,6 @@
from pygameControls.controlsbase import ControlsBase
from enums import ConnectionType
import pygame
class PlayStation3Controller(ControlsBase):
def __init__(self, joy):

View File

@@ -0,0 +1,72 @@
from pygameControls.controlsbase import ControlsBase
from enums import ConnectionType
import pygame
class PlayStation4Controller(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)]
self.mapping = {
"l1 button": 4,
"r1 button": 5,
"cross button": 0,
"triangle button": 2,
"circle button": 1,
"square button": 3,
"left stick button": 11,
"right stick button": 12,
"logo button": 10,
"select button": 8,
"start button": 9,
"down button": 14,
"up button": 13,
"left button": 15,
"right button": 16
}
print(f"{self.name} connected.")
def close(self):
self.device.quit()
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, left, right, duration=0):
if not left in range(256) or not right in range(256):
raise ValueError("left and right values must be in the range 0 - 255")
self.device.rumble(left / 255, right / 255, duration)
def stop_rumble(self):
self.device.stop_rumble()
@property
def name(self) -> str:
return self._name
@name.setter
def name(self, name: str) -> None:
self._name = name

View File

@@ -1,4 +1,6 @@
from pygameControls.controlsbase import ControlsBase
from enums import ConnectionType
import pygame
class SonyPlayStation3Controller(ControlsBase):
def __init__(self, joy):

View File

@@ -0,0 +1,72 @@
from pygameControls.controlsbase import ControlsBase
from enums import ConnectionType
import pygame
class SonyPlayStation4Controller(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)]
self.mapping = {
"l1 button": 4,
"r1 button": 5,
"cross button": 0,
"triangle button": 2,
"circle button": 1,
"square button": 3,
"left stick button": 11,
"right stick button": 12,
"logo button": 10,
"select button": 8,
"start button": 9,
"down button": 14,
"up button": 13,
"left button": 15,
"right button": 16
}
print(f"{self.name} connected.")
def close(self):
self.device.quit()
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, left, right, duration=0):
if not left in range(256) or not right in range(256):
raise ValueError("left and right values must be in the range 0 - 255")
self.device.rumble(left / 255, right / 255, duration)
def stop_rumble(self):
self.device.stop_rumble()
@property
def name(self) -> str:
return self._name
@name.setter
def name(self, name: str) -> None:
self._name = name

View File

@@ -0,0 +1,69 @@
from pygameControls.controlsbase import ControlsBase
from enums import ConnectionType, InputType
import pygame
class Xbox360Controller(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)]
self.mapping = {
"l1 button": 6,
"r1 button": 7,
"X button": 3,
"Y button": 4,
"A button": 0,
"B button": 1,
"left stick button": 13,
"right stick button": 14,
"logo button": 12,
"share button": 15,
"list button": 11,
"copy button": 10
}
print(f"{self.name} connected.")
def close(self):
self.device.quit()
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, left, right, duration=0):
if not left in range(256) or not right in range(256):
raise ValueError("left and right values must be in the range 0 - 255")
self.device.rumble(left / 255, right / 255, duration)
def stop_rumble(self):
self.device.stop_rumble()
@property
def name(self) -> str:
return self._name
@name.setter
def name(self, name: str) -> None:
self._name = name

View File

@@ -1,4 +1,6 @@
from pygameControls.controlsbase import ControlsBase
from enums import ConnectionType
import pygame
class XboxSeriesXController(ControlsBase):
def __init__(self, joy):

View File

@@ -3,4 +3,4 @@ pulsectl==24.12.0
pydualsense==0.7.3
pygame==2.6.1
setuptools==68.2.2
sounddevice==0.5.1
sounddevice==0.5.1

View File

@@ -3,7 +3,7 @@ if __name__ == "__main__":
setup(
name='pygameControls',
version='0.1.13',
version='0.2.0',
packages=find_packages(),
install_requires=[],
author='Jan Lerking',