diff --git a/.gitignore b/.gitignore
index 0dbf2f2..3a9e9e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -168,3 +168,4 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
+testing/
diff --git a/README.md b/README.md
index 27b72f7..8fc5cac 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-
+
# Snake
diff --git a/controls/Gamepad/AsyncAndEventExample.py b/controls/Gamepad/AsyncAndEventExample.py
deleted file mode 100755
index 49c5511..0000000
--- a/controls/Gamepad/AsyncAndEventExample.py
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-
-# Load the gamepad and time libraries
-import Gamepad
-import time
-
-# Gamepad settings
-gamepadType = Gamepad.PS4
-buttonHappy = 'CROSS'
-buttonBeep = 'CIRCLE'
-buttonExit = 'PS'
-joystickSpeed = 'LEFT-Y'
-joystickSteering = 'RIGHT-X'
-pollInterval = 0.1
-
-# Wait for a connection
-if not Gamepad.available():
- print('Please connect your gamepad...')
- while not Gamepad.available():
- time.sleep(1.0)
-gamepad = gamepadType()
-print('Gamepad connected')
-
-# Set some initial state
-global running
-running = True
-speed = 0.0
-steering = 0.0
-
-# Create some callback functions for single events
-def happyButtonPressed():
- print(':)')
-
-def happyButtonReleased():
- print(':(')
-
-def exitButtonPressed():
- global running
- print('EXIT')
- running = False
-
-# Start the background updating
-gamepad.startBackgroundUpdates()
-
-# Register the callback functions
-gamepad.addButtonPressedHandler(buttonHappy, happyButtonPressed)
-gamepad.addButtonReleasedHandler(buttonHappy, happyButtonReleased)
-gamepad.addButtonPressedHandler(buttonExit, exitButtonPressed)
-
-# Joystick events handled in the background
-try:
- while running and gamepad.isConnected():
- # Check if the beep button is held
- if gamepad.isPressed(buttonBeep):
- print('BEEP')
-
- # Update the joystick positions
- # Speed control (inverted)
- speed = -gamepad.axis(joystickSpeed)
- # Steering control (not inverted)
- steering = gamepad.axis(joystickSteering)
- print('%+.1f %% speed, %+.1f %% steering' % (speed * 100, steering * 100))
-
- # Sleep for our polling interval
- time.sleep(pollInterval)
-finally:
- # Ensure the background thread is always terminated when we are done
- gamepad.disconnect()
diff --git a/controls/Gamepad/AsyncExample.py b/controls/Gamepad/AsyncExample.py
deleted file mode 100755
index 78bf1d9..0000000
--- a/controls/Gamepad/AsyncExample.py
+++ /dev/null
@@ -1,61 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-
-# Load the gamepad and time libraries
-import Gamepad
-import time
-
-# Gamepad settings
-gamepadType = Gamepad.PS4
-buttonHappy = 'CROSS'
-buttonBeep = 'CIRCLE'
-buttonExit = 'PS'
-joystickSpeed = 'LEFT-Y'
-joystickSteering = 'RIGHT-X'
-pollInterval = 0.1
-
-# Wait for a connection
-if not Gamepad.available():
- print('Please connect your gamepad...')
- while not Gamepad.available():
- time.sleep(1.0)
-gamepad = gamepadType()
-print('Gamepad connected')
-
-# Set some initial state
-speed = 0.0
-steering = 0.0
-
-# Start the background updating
-gamepad.startBackgroundUpdates()
-
-# Joystick events handled in the background
-try:
- while gamepad.isConnected():
- # Check for the exit button
- if gamepad.beenPressed(buttonExit):
- print('EXIT')
- break
-
- # Check for happy button changes
- if gamepad.beenPressed(buttonHappy):
- print(':)')
- if gamepad.beenReleased(buttonHappy):
- print(':(')
-
- # Check if the beep button is held
- if gamepad.isPressed(buttonBeep):
- print('BEEP')
-
- # Update the joystick positions
- # Speed control (inverted)
- speed = -gamepad.axis(joystickSpeed)
- # Steering control (not inverted)
- steering = gamepad.axis(joystickSteering)
- print('%+.1f %% speed, %+.1f %% steering' % (speed * 100, steering * 100))
-
- # Sleep for our polling interval
- time.sleep(pollInterval)
-finally:
- # Ensure the background thread is always terminated when we are done
- gamepad.disconnect()
diff --git a/controls/Gamepad/Controllers.py b/controls/Gamepad/Controllers.py
deleted file mode 100644
index d7a0a57..0000000
--- a/controls/Gamepad/Controllers.py
+++ /dev/null
@@ -1,428 +0,0 @@
-# coding: utf-8
-"""
-Standard gamepad mappings.
-
-Pulled in to Gamepad.py directly.
-"""
-
-
-class PS3(Gamepad):
- fullName = 'PlayStation 3 controller'
-
- def __init__(self, joystickNumber = 0):
- Gamepad.__init__(self, joystickNumber)
- self.axisNames = {
- 0: 'LEFT-X',
- 1: 'LEFT-Y',
- 2: 'L2',
- 3: 'RIGHT-X',
- 4: 'RIGHT-Y',
- 5: 'R2'
- }
- self.buttonNames = {
- 0: 'CROSS',
- 1: 'CIRCLE',
- 2: 'TRIANGLE',
- 3: 'SQUARE',
- 4: 'L1',
- 5: 'R1',
- 6: 'L2',
- 7: 'R2',
- 8: 'SELECT',
- 9: 'START',
- 10: 'PS',
- 11: 'L3',
- 12: 'R3',
- 13: 'DPAD-UP',
- 14: 'DPAD-DOWN',
- 15: 'DPAD-LEFT',
- 16: 'DPAD-RIGHT'
- }
- self._setupReverseMaps()
-
-
-# PS3 controller settings for older Raspbian versions
-#class PS3(Gamepad):
-# fullName = 'PlayStation 3 controller'
-#
-# def __init__(self, joystickNumber = 0):
-# Gamepad.__init__(self, joystickNumber)
-# self.axisNames = {
-# 0: 'LEFT-X',
-# 1: 'LEFT-Y',
-# 2: 'RIGHT-X',
-# 3: 'RIGHT-Y',
-# 4: 'roll-1',
-# 5: 'pitch',
-# 6: 'roll-2',
-# 8: 'DPAD-UP',
-# 9: 'DPAD-RIGHT',
-# 10: 'DPAD-DOWN',
-# 11: 'DPAD-LEFT',
-# 12: 'L2',
-# 13: 'R2',
-# 14: 'L1',
-# 15: 'R1',
-# 16: 'TRIANGLE',
-# 17: 'CIRCLE',
-# 18: 'CROSS',
-# 19: 'SQUARE'
-# }
-# self.buttonNames = {
-# 0: 'SELECT',
-# 1: 'L3',
-# 2: 'R3',
-# 3: 'START',
-# 4: 'DPAD-UP',
-# 5: 'DPAD-RIGHT',
-# 6: 'DPAD-DOWN',
-# 7: 'DPAD-LEFT',
-# 8: 'L2',
-# 9: 'R2',
-# 10: 'L1',
-# 11: 'R1',
-# 12: 'TRIANGLE',
-# 13: 'CIRCLE',
-# 14: 'CROSS',
-# 15: 'SQUARE',
-# 16: 'PS'
-# }
-# self._setupReverseMaps()
-
-
-class PS4(Gamepad):
- fullName = 'PlayStation 4 controller'
-
- def __init__(self, joystickNumber = 0):
- Gamepad.__init__(self, joystickNumber)
- self.axisNames = {
- 0: 'LEFT-X',
- 1: 'LEFT-Y',
- 2: 'L2',
- 3: 'RIGHT-X',
- 4: 'RIGHT-Y',
- 5: 'R2',
- 6: 'DPAD-X',
- 7: 'DPAD-Y'
- }
- self.buttonNames = {
- 0: 'CROSS',
- 1: 'CIRCLE',
- 2: 'TRIANGLE',
- 3: 'SQUARE',
- 4: 'L1',
- 5: 'R1',
- 6: 'L2',
- 7: 'R2',
- 8: 'SHARE',
- 9: 'OPTIONS',
- 10: 'PS',
- 11: 'L3',
- 12: 'R3'
- }
- self._setupReverseMaps()
-
-
-# PS4 controller settings for older Raspbian versions
-#class PS4(Gamepad):
-# fullName = 'PlayStation 4 controller'
-#
-# def __init__(self, joystickNumber = 0):
-# Gamepad.__init__(self, joystickNumber)
-# self.axisNames = {
-# 0: 'LEFT-X',
-# 1: 'LEFT-Y',
-# 2: 'RIGHT-X',
-# 3: 'L2',
-# 4: 'R2',
-# 5: 'RIGHT-Y',
-# 6: 'DPAD-X',
-# 7: 'DPAD-Y'
-# }
-# self.buttonNames = {
-# 0: 'SQUARE',
-# 1: 'CROSS',
-# 2: 'CIRCLE',
-# 3: 'TRIANGLE',
-# 4: 'L1',
-# 5: 'R1',
-# 6: 'L2',
-# 7: 'R2',
-# 8: 'SHARE',
-# 9: 'OPTIONS',
-# 10: 'L3',
-# 11: 'R3',
-# 12: 'PS',
-# 13: 'PAD'
-# }
-# self._setupReverseMaps()
-
-class PS5(Gamepad):
- fullName = 'PlayStation 5 controller'
-
- def __init__(self, joystickNumber = 0):
- Gamepad.__init__(self, joystickNumber)
- self.axisNames = {
- 0: 'LEFT-X',
- 1: 'LEFT-Y',
- 2: 'L2',
- 3: 'RIGHT-X',
- 4: 'RIGHT-Y',
- 5: 'R2',
- 6: 'DPAD-X',
- 7: 'DPAD-Y'
- }
- self.buttonNames = {
- 0: 'CROSS',
- 1: 'CIRCLE',
- 2: 'TRIANGLE',
- 3: 'SQUARE',
- 4: 'L1',
- 5: 'R1',
- 6: 'L2',
- 7: 'R2',
- 8: 'SHARE',
- 9: 'OPTIONS',
- 10: 'PS',
- 11: 'L3',
- 12: 'R3'
- }
- self._setupReverseMaps()
-
-
-# PS4 controller settings for older Raspbian versions
-#class PS5(Gamepad):
-# fullName = 'PlayStation 5 controller'
-#
-# def __init__(self, joystickNumber = 0):
-# Gamepad.__init__(self, joystickNumber)
-# self.axisNames = {
-# 0: 'LEFT-X',
-# 1: 'LEFT-Y',
-# 2: 'RIGHT-X',
-# 3: 'L2',
-# 4: 'R2',
-# 5: 'RIGHT-Y',
-# 6: 'DPAD-X',
-# 7: 'DPAD-Y'
-# }
-# self.buttonNames = {
-# 0: 'SQUARE',
-# 1: 'CROSS',
-# 2: 'CIRCLE',
-# 3: 'TRIANGLE',
-# 4: 'L1',
-# 5: 'R1',
-# 6: 'L2',
-# 7: 'R2',
-# 8: 'SHARE',
-# 9: 'OPTIONS',
-# 10: 'L3',
-# 11: 'R3',
-# 12: 'PS',
-# 13: 'PAD'
-# }
-# self._setupReverseMaps()
-
-class Xbox360(Gamepad):
- fullName = 'Xbox 360 controller'
-
- def __init__(self, joystickNumber = 0):
- Gamepad.__init__(self, joystickNumber)
- self.axisNames = {
- 0: 'LEFT-X',
- 1: 'LEFT-Y',
- 2: 'LT',
- 3: 'RIGHT-X',
- 4: 'RIGHT-Y',
- 5: 'RT'
- }
- self.buttonNames = {
- 0: 'A',
- 1: 'B',
- 2: 'X',
- 3: 'Y',
- 4: 'LB',
- 5: 'RB',
- 6: 'BACK',
- 7: 'START',
- 8: 'XBOX',
- 9: 'LA',
- 10: 'RA'
- }
- self._setupReverseMaps()
-
-class XboxONE(Gamepad):
- fullName = 'Xbox ONE controller'
-
- def __init__(self, joystickNumber = 0):
- Gamepad.__init__(self, joystickNumber)
- self.axisNames = {
- 0: 'LAS -X', #Left Analog Stick Left/Right
- 1: 'LAS -Y', #Left Analog Stick Up/Down
- 2: 'RAS -X', #Right Analog Stick Left/Right
- 3: 'RAS -Y', #Right Analog Stick Up/Down
- 4: 'RT', #Right Trigger
- 5: 'LT', #Left Trigger
- 6: 'DPAD -X', #D-Pad Left/Right
- 7: 'DPAD -Y' #D-Pad Up/Down
- }
- self.buttonNames = {
- 0: 'A', #A Button
- 1: 'B', #B Button
- 3: 'X', #X Button
- 4: 'Y', #Y Button
- 6: 'LB', #Left Bumper
- 7: 'RB', #Right Bumper
- 11: 'START', #Hamburger Button
- 12: 'HOME', #XBOX Button
- 13: 'LASB', #Left Analog Stick button
- 14: 'RASB' #Right Analog Stick button
-
- }
- self._setupReverseMaps()
-
-class Steam(Gamepad):
- fullName = 'Steam controller'
-
- def __init__(self, joystickNumber = 0):
- Gamepad.__init__(self, joystickNumber)
- self.axisNames = {
- 0: 'AS -X', #Analog Stick Left/Right
- 1: 'AS -Y', #Analog Stick Up/Down
- 2: 'RTP -X', #Right Track Pad Left/Right
- 3: 'RTP -Y', #Right Track Pad Up/Down
- 4: 'LTP -Y', #Left Track Pad Up/Down
- 5: 'LTP -X', #Left Track Pad Left/Right
- 6: 'RTA', #Right Trigger Axis
- 7: 'LTA' #Left Trigger Axis
- }
- self.buttonNames = {
- 0: 'LPTBUTTON', #Left TrackPad button
- 1: 'RTPBUTTON', #Right TrackPad button
- 2: 'A', #A Button
- 3: 'B', #B Button
- 4: 'X', #X Button
- 5: 'Y', #Y Button
- 6: 'LB', #Left Bumper
- 7: 'RB', #Right Bumper
- 8: 'LT', #Left Trigger
- 9: 'RT', #Right Trigger
- 10: 'SELECT', #Select Button <
- 11: 'START', #Start button >
- 12: 'HOME', #Steam Button
- 13: 'STICKBUTTON', #Analog Stick button
- 15: 'LG', #Left Grip
- 16: 'RG', #Right Grip
- 17: 'LTP -DUP', #Left TrackPad D-PAD Up
- 18: 'LTP -DDOWN', #Left TrackPad D-PAD Down
- 19: 'LTP -DLEFT', #Left TrackPad D-PAD Left
- 20: 'LTP -DRIGHT', #Left TrackPad D-PAD Right
- }
- self._setupReverseMaps()
-
-class MMP1251(Gamepad):
- fullName = "ModMyPi Raspberry Pi Wireless USB Gamepad"
-
- def __init__(self, joystickNumber = 0):
- Gamepad.__init__(self, joystickNumber)
- self.axisNames = {
- 0: 'LEFT-X',
- 1: 'LEFT-Y',
- 2: 'L2',
- 3: 'RIGHT-X',
- 4: 'RIGHT-Y',
- 5: 'R2',
- 6: 'DPAD-X',
- 7: 'DPAD-Y'
- }
- self.buttonNames = {
- 0: 'A',
- 1: 'B',
- 2: 'X',
- 3: 'Y',
- 4: 'L1',
- 5: 'R1',
- 6: 'SELECT',
- 7: 'START',
- 8: 'HOME',
- 9: 'L3',
- 10: 'R3'
- }
- self._setupReverseMaps()
-
-class GameHat(Gamepad):
- fullName = "WaveShare rpi GameHat "
-
- def __init__(self, joystickNumber = 0):
- Gamepad.__init__(self, joystickNumber)
- self.axisNames = {
- 0: 'LEFT-X',
- 1: 'LEFT-Y'
- }
- self.buttonNames = {
- 0: 'A',
- 1: 'B',
- 2: 'X',
- 3: 'Y',
- 4: 'TR',
- 5: 'TL',
- 6: 'SELECT',
- 7: 'START'
- }
- self._setupReverseMaps()
-
-class PG9099(Gamepad):
- fullName = 'ipega PG-9099 Bluetooth Controller'
-
- def __init__(self, joystickNumber = 0):
- Gamepad.__init__(self, joystickNumber)
- self.axisNames = {
- 0: 'LAS -X', #Left Analog Stick Left/Right
- 1: 'LAS -Y', #Left Analog Stick Up/Down
- 2: 'RAS -X', #Right Analog Stick Left/Right
- 3: 'RAS -Y', #Right Analog Stick Up/Down
- 4: 'RT', #Right Trigger
- 5: 'LT', #Left Trigger
- 6: 'DPAD -X', #D-Pad Left/Right
- 7: 'DPAD -Y' #D-Pad Up/Down
- }
- self.buttonNames = {
- 0: 'A', #A Button
- 1: 'B', #B Button
- 3: 'X', #X Button
- 4: 'Y', #Y Button
- 6: 'LB', #Left Bumper
- 7: 'RB', #Right Bumper
- 10: 'SELECT', #Select Button
- 11: 'START', #Hamburger Button
- 13: 'LASB', #Left Analog Stick button
- 14: 'RASB' #Right Analog Stick button
-
- }
- self._setupReverseMaps()
-
-
-class example(Gamepad):
- # This class must have self.axisNames with a map
- # of numbers to capitalised strings. Follow the
- # conventions the other classes use for generic
- # axes, make up your own names for axes unique
- # to your device.
- # self.buttonNames needs the same treatment.
- # Use python Gamepad.py to get the event mappings.
- fullName = 'Enter the human readable name of the device here'
-
- def __init__(self, joystickNumber = 0):
- Gamepad.__init__(self, joystickNumber)
- self.axisNames = {
- 0: 'AXIS0',
- 1: 'AXIS1',
- 2: 'AXIS2'
- }
- self.buttonNames = {
- 0: 'BUTTON0',
- 1: 'BUTTON1',
- 2: 'BUTTON2'
- }
- self._setupReverseMaps()
diff --git a/controls/Gamepad/CustomGamepadExample.py b/controls/Gamepad/CustomGamepadExample.py
deleted file mode 100755
index f28e121..0000000
--- a/controls/Gamepad/CustomGamepadExample.py
+++ /dev/null
@@ -1,91 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-
-# Load the gamepad and time libraries
-import Gamepad
-import time
-
-# Make our own custom gamepad
-# The numbers can be figured out by running the Gamepad script:
-# ./Gamepad.py
-# Press ENTER without typing a name to get raw numbers for each
-# button press or axis movement, press CTRL+C when done
-class CustomGamepad(Gamepad.Gamepad):
- def __init__(self, joystickNumber = 0):
- Gamepad.Gamepad.__init__(self, joystickNumber)
- self.axisNames = {
- 0: 'LEFT-X',
- 1: 'LEFT-Y',
- 2: 'RIGHT-Y',
- 3: 'RIGHT-X',
- 4: 'DPAD-X',
- 5: 'DPAD-Y'
- }
- self.buttonNames = {
- 0: '1',
- 1: '2',
- 2: '3',
- 3: '4',
- 4: 'L1',
- 5: 'L2',
- 6: 'R1',
- 7: 'R2',
- 8: 'SELECT',
- 9: 'START',
- 10: 'L3',
- 11: 'R3'
- }
- self._setupReverseMaps()
-
-# Gamepad settings
-gamepadType = CustomGamepad
-buttonHappy = '3'
-buttonBeep = 'L3'
-buttonExit = 'START'
-joystickSpeed = 'LEFT-Y'
-joystickSteering = 'RIGHT-X'
-
-# Wait for a connection
-if not Gamepad.available():
- print('Please connect your gamepad...')
- while not Gamepad.available():
- time.sleep(1.0)
-gamepad = gamepadType()
-print('Gamepad connected')
-
-# Set some initial state
-speed = 0.0
-steering = 0.0
-
-# Handle joystick updates one at a time
-while gamepad.isConnected():
- # Wait for the next event
- eventType, control, value = gamepad.getNextEvent()
-
- # Determine the type
- if eventType == 'BUTTON':
- # Button changed
- if control == buttonHappy:
- # Happy button (event on press and release)
- if value:
- print(':)')
- else:
- print(':(')
- elif control == buttonBeep:
- # Beep button (event on press)
- if value:
- print('BEEP')
- elif control == buttonExit:
- # Exit button (event on press)
- if value:
- print('EXIT')
- break
- elif eventType == 'AXIS':
- # Joystick changed
- if control == joystickSpeed:
- # Speed control (inverted)
- speed = -value
- elif control == joystickSteering:
- # Steering control (not inverted)
- steering = value
- print('%+.1f %% speed, %+.1f %% steering' % (speed * 100, steering * 100))
diff --git a/controls/Gamepad/Diagrams/Asynchronous-and-event.svg b/controls/Gamepad/Diagrams/Asynchronous-and-event.svg
deleted file mode 100644
index ec725fe..0000000
--- a/controls/Gamepad/Diagrams/Asynchronous-and-event.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/controls/Gamepad/Diagrams/Asynchronous.svg b/controls/Gamepad/Diagrams/Asynchronous.svg
deleted file mode 100644
index e0bb59a..0000000
--- a/controls/Gamepad/Diagrams/Asynchronous.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/controls/Gamepad/Diagrams/Event.svg b/controls/Gamepad/Diagrams/Event.svg
deleted file mode 100644
index 59a6bb7..0000000
--- a/controls/Gamepad/Diagrams/Event.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/controls/Gamepad/Diagrams/Polling.svg b/controls/Gamepad/Diagrams/Polling.svg
deleted file mode 100644
index 5957114..0000000
--- a/controls/Gamepad/Diagrams/Polling.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/controls/Gamepad/Diagrams/gamepad-logo.svg b/controls/Gamepad/Diagrams/gamepad-logo.svg
deleted file mode 100644
index 4779c23..0000000
--- a/controls/Gamepad/Diagrams/gamepad-logo.svg
+++ /dev/null
@@ -1,2139 +0,0 @@
-
-
diff --git a/controls/Gamepad/EventExample.py b/controls/Gamepad/EventExample.py
deleted file mode 100755
index d2d7b55..0000000
--- a/controls/Gamepad/EventExample.py
+++ /dev/null
@@ -1,120 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-
-# Load the gamepad and time libraries
-import Gamepad
-import time
-
-# Gamepad settings
-gamepadType = Gamepad.PS5
-buttonHappy = 'CROSS'
-buttonBeep = 'CIRCLE'
-buttonExit = 'PS'
-joystickSpeed = 'LEFT-Y'
-joystickSteering = 'RIGHT-X'
-dpadX = 'DPAD-X'
-dpadY = 'DPAD-Y'
-pollInterval = 0.2
-
-# Wait for a connection
-if not Gamepad.available():
- print('Please connect your gamepad...')
- while not Gamepad.available():
- time.sleep(1.0)
-gamepad = gamepadType()
-print('Gamepad connected')
-
-# Set some initial state
-global running
-global beepOn
-global speed
-global steering
-global direction
-global dirChange
-running = True
-beepOn = False
-speed = 0.0
-steering = 0.0
-direction = ""
-dirChange = False
-
-# Create some callback functions
-def happyButtonPressed():
- print(':)')
-
-def happyButtonReleased():
- print(':(')
-
-def beepButtonChanged(isPressed):
- global beepOn
- beepOn = isPressed
-
-def exitButtonPressed():
- global running
- print('EXIT')
- running = False
-
-def speedAxisMoved(position):
- global speed
- speed = -position # Inverted
-
-def steeringAxisMoved(position):
- global steering
- steering = position # Non-inverted
-
-def dpadXMoved(dir):
- global direction
- global dirChange
- print("X direction: ", dir)
- match dir:
- case -1.0:
- direction = 'LEFT'
- dirChange = True
- case 1.0:
- direction = 'RIGHT'
- dirChange = True
-
-def dpadYMoved(dir):
- global direction
- global dirChange
- print("Y direction: ", dir)
- match dir:
- case -1.0:
- direction = 'UP'
- dirChange = True
- case 1.0:
- direction = 'DOWN'
- dirChange = True
-
-# Start the background updating
-gamepad.startBackgroundUpdates()
-
-# Register the callback functions
-gamepad.addButtonPressedHandler(buttonHappy, happyButtonPressed)
-gamepad.addButtonReleasedHandler(buttonHappy, happyButtonReleased)
-gamepad.addButtonChangedHandler(buttonBeep, beepButtonChanged)
-gamepad.addButtonPressedHandler(buttonExit, exitButtonPressed)
-gamepad.addAxisMovedHandler(joystickSpeed, speedAxisMoved)
-gamepad.addAxisMovedHandler(joystickSteering, steeringAxisMoved)
-gamepad.addAxisMovedHandler(dpadX, dpadXMoved)
-gamepad.addAxisMovedHandler(dpadY, dpadYMoved)
-
-# Keep running while joystick updates are handled by the callbacks
-try:
- while running and gamepad.isConnected():
- # Show the current speed and steering
- # print('%+.1f %% speed, %+.1f %% steering' % (speed * 100, steering * 100))
-
- # Display the beep if held
- if beepOn:
- print('BEEP')
-
- if dirChange:
- print(direction)
- dirChange = False
-
- # Sleep for our polling interval
- time.sleep(pollInterval)
-finally:
- # Ensure the background thread is always terminated when we are done
- gamepad.disconnect()
diff --git a/controls/Gamepad/Gamepad.py b/controls/Gamepad/Gamepad.py
deleted file mode 100755
index b3c82a4..0000000
--- a/controls/Gamepad/Gamepad.py
+++ /dev/null
@@ -1,617 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-"""
-This module is designed to read inputs from a gamepad or joystick.
-See Controllers.py the names which can be used with specific gamepad types.
-
-For basic use see the following examples:
- AsyncExample.py - Updates read in the background.
- EventExample.py - Updates passed to callback functions.
- PollingExample.py - Reading updates one at a time.
- AsyncAndEventExample.py - Mixing callbacks and background updates.
-"""
-
-import os
-import sys
-import struct
-import time
-import threading
-import inspect
-
-def available(joystickNumber = 0):
- """Check if a joystick is connected and ready to use."""
- joystickPath = '/dev/input/js' + str(joystickNumber)
- return os.path.exists(joystickPath)
-
-class Gamepad:
- EVENT_CODE_BUTTON = 0x01
- EVENT_CODE_AXIS = 0x02
- EVENT_CODE_INIT_BUTTON = 0x80 | EVENT_CODE_BUTTON
- EVENT_CODE_INIT_AXIS = 0x80 | EVENT_CODE_AXIS
- MIN_AXIS = -32767.0
- MAX_AXIS = +32767.0
- EVENT_BUTTON = 'BUTTON'
- EVENT_AXIS = 'AXIS'
- fullName = 'Generic (numbers only)'
-
- class UpdateThread(threading.Thread):
- """Thread used to continually run the updateState function on a Gamepad in the background
-
- One of these is created by the Gamepad startBackgroundUpdates function and closed by stopBackgroundUpdates"""
- def __init__(self, gamepad):
- threading.Thread.__init__(self)
- if isinstance(gamepad, Gamepad):
- self.gamepad = gamepad
- else:
- raise ValueError('Gamepad update thread was not created with a valid Gamepad object')
- self.running = True
-
- def run(self):
- try:
- while self.running:
- self.gamepad.updateState()
- self.gamepad = None
- except:
- self.running = False
- self.gamepad = None
- raise
-
- def __init__(self, joystickNumber = 0):
- self.joystickNumber = str(joystickNumber)
- self.joystickPath = '/dev/input/js' + self.joystickNumber
- retryCount = 5
- while True:
- try:
- self.joystickFile = open(self.joystickPath, 'rb')
- break
- except IOError as e:
- retryCount -= 1
- if retryCount > 0:
- time.sleep(0.5)
- else:
- raise IOError('Could not open gamepad %s: %s' % (self.joystickNumber, str(e)))
- self.eventSize = struct.calcsize('IhBB')
- self.pressedMap = {}
- self.wasPressedMap = {}
- self.wasReleasedMap = {}
- self.axisMap = {}
- self.buttonNames = {}
- self.buttonIndex = {}
- self.axisNames = {}
- self.axisIndex = {}
- self.lastTimestamp = 0
- self.updateThread = None
- self.connected = True
- self.pressedEventMap = {}
- self.releasedEventMap = {}
- self.changedEventMap = {}
- self.movedEventMap = {}
-
- def __del__(self):
- try:
- self.joystickFile.close()
- except AttributeError:
- pass
-
- def _setupReverseMaps(self):
- for index in self.buttonNames:
- self.buttonIndex[self.buttonNames[index]] = index
- for index in self.axisNames:
- self.axisIndex[self.axisNames[index]] = index
-
- def _getNextEventRaw(self):
- """Returns the next raw event from the gamepad.
-
- The return format is:
- timestamp (ms), value, event type code, axis / button number
- Throws an IOError if the gamepad is disconnected"""
- if self.connected:
- try:
- rawEvent = self.joystickFile.read(self.eventSize)
- except IOError as e:
- self.connected = False
- raise IOError('Gamepad %s disconnected: %s' % (self.joystickNumber, str(e)))
- if rawEvent is None:
- self.connected = False
- raise IOError('Gamepad %s disconnected' % self.joystickNumber)
- else:
- return struct.unpack('IhBB', rawEvent)
- else:
- raise IOError('Gamepad has been disconnected')
-
- def _rawEventToDescription(self, event):
- """Decodes the raw event from getNextEventRaw into a formatted string."""
- timestamp, value, eventType, index = event
- if eventType == Gamepad.EVENT_CODE_BUTTON:
- if index in self.buttonNames:
- button = self.buttonNames[index]
- else:
- button = str(index)
- if value == 0:
- return '%010u: Button %s released' % (timestamp, button)
- elif value == 1:
- return '%010u: button %s pressed' % (timestamp, button)
- else:
- return '%010u: button %s state %i' % (timestamp, button, value)
- elif eventType == Gamepad.EVENT_CODE_AXIS:
- if index in self.axisNames:
- axis = self.axisNames[index]
- else:
- axis = str(index)
- position = value / Gamepad.MAX_AXIS
- return '%010u: Axis %s at %+06.1f %%' % (timestamp, axis, position * 100)
- elif eventType == Gamepad.EVENT_CODE_INIT_BUTTON:
- if index in self.buttonNames:
- button = self.buttonNames[index]
- else:
- button = str(index)
- if value == 0:
- return '%010u: Button %s initially released' % (timestamp, button)
- elif value == 1:
- return '%010u: button %s initially pressed' % (timestamp, button)
- else:
- return '%010u: button %s initially state %i' % (timestamp, button, value)
- elif eventType == Gamepad.EVENT_CODE_INIT_AXIS:
- if index in self.axisNames:
- axis = self.axisNames[index]
- else:
- axis = str(index)
- position = value / Gamepad.MAX_AXIS
- return '%010u: Axis %s initially at %+06.1f %%' % (timestamp, axis, position * 100)
- else:
- return '%010u: Unknown event %u, Index %u, Value %i' % (timestamp, eventType, index, value)
-
- def getNextEvent(self, skipInit = True):
- """Returns the next event from the gamepad.
-
- The return format is:
- event name, entity name, value
-
- For button events the event name is BUTTON and value is either True or False.
- For axis events the event name is AXIS and value is between -1.0 and +1.0.
-
- Names are string based when found in the button / axis decode map.
- When not available the raw index is returned as an integer instead.
-
- After each call the internal state used by getPressed and getAxis is updated.
-
- Throws an IOError if the gamepad is disconnected"""
- self.lastTimestamp, value, eventType, index = self._getNextEventRaw()
- skip = False
- eventName = None
- entityName = None
- finalValue = None
- if eventType == Gamepad.EVENT_CODE_BUTTON:
- eventName = Gamepad.EVENT_BUTTON
- if index in self.buttonNames:
- entityName = self.buttonNames[index]
- else:
- entityName = index
- if value == 0:
- finalValue = False
- self.wasReleasedMap[index] = True
- for callback in self.releasedEventMap[index]:
- callback()
- else:
- finalValue = True
- self.wasPressedMap[index] = True
- for callback in self.pressedEventMap[index]:
- callback()
- self.pressedMap[index] = finalValue
- for callback in self.changedEventMap[index]:
- callback(finalValue)
- elif eventType == Gamepad.EVENT_CODE_AXIS:
- eventName = Gamepad.EVENT_AXIS
- if index in self.axisNames:
- entityName = self.axisNames[index]
- else:
- entityName = index
- finalValue = value / Gamepad.MAX_AXIS
- self.axisMap[index] = finalValue
- for callback in self.movedEventMap[index]:
- callback(finalValue)
- elif eventType == Gamepad.EVENT_CODE_INIT_BUTTON:
- eventName = Gamepad.EVENT_BUTTON
- if index in self.buttonNames:
- entityName = self.buttonNames[index]
- else:
- entityName = index
- if value == 0:
- finalValue = False
- else:
- finalValue = True
- self.pressedMap[index] = finalValue
- self.wasPressedMap[index] = False
- self.wasReleasedMap[index] = False
- self.pressedEventMap[index] = []
- self.releasedEventMap[index] = []
- self.changedEventMap[index] = []
- skip = skipInit
- elif eventType == Gamepad.EVENT_CODE_INIT_AXIS:
- eventName = Gamepad.EVENT_AXIS
- if index in self.axisNames:
- entityName = self.axisNames[index]
- else:
- entityName = index
- finalValue = value / Gamepad.MAX_AXIS
- self.axisMap[index] = finalValue
- self.movedEventMap[index] = []
- skip = skipInit
- else:
- skip = True
-
- if skip:
- return self.getNextEvent()
- else:
- return eventName, entityName, finalValue
-
- def updateState(self):
- """Updates the internal button and axis states with the next pending event.
-
- This call waits for a new event if there are not any waiting to be processed."""
- self.lastTimestamp, value, eventType, index = self._getNextEventRaw()
- if eventType == Gamepad.EVENT_CODE_BUTTON:
- if value == 0:
- finalValue = False
- self.wasReleasedMap[index] = True
- for callback in self.releasedEventMap[index]:
- callback()
- else:
- finalValue = True
- self.wasPressedMap[index] = True
- for callback in self.pressedEventMap[index]:
- callback()
- self.pressedMap[index] = finalValue
- for callback in self.changedEventMap[index]:
- callback(finalValue)
- elif eventType == Gamepad.EVENT_CODE_AXIS:
- finalValue = value / Gamepad.MAX_AXIS
- self.axisMap[index] = finalValue
- for callback in self.movedEventMap[index]:
- callback(finalValue)
- elif eventType == Gamepad.EVENT_CODE_INIT_BUTTON:
- if value == 0:
- finalValue = False
- else:
- finalValue = True
- self.pressedMap[index] = finalValue
- self.wasPressedMap[index] = False
- self.wasReleasedMap[index] = False
- self.pressedEventMap[index] = []
- self.releasedEventMap[index] = []
- self.changedEventMap[index] = []
- elif eventType == Gamepad.EVENT_CODE_INIT_AXIS:
- finalValue = value / Gamepad.MAX_AXIS
- self.axisMap[index] = finalValue
- self.movedEventMap[index] = []
-
- def startBackgroundUpdates(self, waitForReady = True):
- """Starts a background thread which keeps the gamepad state updated automatically.
- This allows for asynchronous gamepad updates and event callback code.
-
- Do not use with getNextEvent"""
- if self.updateThread is not None:
- if self.updateThread.running:
- raise RuntimeError('Called startBackgroundUpdates when the update thread is already running')
- self.updateThread = Gamepad.UpdateThread(self)
- self.updateThread.start()
- if waitForReady:
- while not self.isReady() and self.connected:
- time.sleep(1.0)
-
- def stopBackgroundUpdates(self):
- """Stops the background thread which keeps the gamepad state updated automatically.
- This may be called even if the background thread was never started.
-
- The thread will stop on the next event after this call was made."""
- if self.updateThread is not None:
- self.updateThread.running = False
-
- def isReady(self):
- """Used with updateState to indicate that the gamepad is now ready for use.
-
- This is usually after the first button press or stick movement."""
- return len(self.axisMap) + len(self.pressedMap) > 1
-
- def waitReady(self):
- """Convenience function which waits until the isReady call is True."""
- self.updateState()
- while not self.isReady() and self.connected:
- time.sleep(1.0)
- self.updateState()
-
- def isPressed(self, buttonName):
- """Returns the last observed state of a gamepad button specified by name or index.
- True if pressed, False if not pressed.
-
- Status is updated by getNextEvent calls.
-
- Throws ValueError if the button name or index cannot be found."""
- try:
- if buttonName in self.buttonIndex:
- buttonIndex = self.buttonIndex[buttonName]
- else:
- buttonIndex = int(buttonName)
- return self.pressedMap[buttonIndex]
- except KeyError:
- raise ValueError('Button %i was not found' % buttonIndex)
- except ValueError:
- raise ValueError('Button name %s was not found' % buttonName)
-
- def beenPressed(self, buttonName):
- """Returns True if the button specified by name or index has been pressed since the last beenPressed call.
- Used in conjunction with updateState.
-
- Throws ValueError if the button name or index cannot be found."""
- try:
- if buttonName in self.buttonIndex:
- buttonIndex = self.buttonIndex[buttonName]
- else:
- buttonIndex = int(buttonName)
- if self.wasPressedMap[buttonIndex]:
- self.wasPressedMap[buttonIndex] = False
- return True
- else:
- return False
- except KeyError:
- raise ValueError('Button %i was not found' % buttonIndex)
- except ValueError:
- raise ValueError('Button name %s was not found' % buttonName)
-
- def beenReleased(self, buttonName):
- """Returns True if the button specified by name or index has been released since the last beenReleased call.
- Used in conjunction with updateState.
-
- Throws ValueError if the button name or index cannot be found."""
- try:
- if buttonName in self.buttonIndex:
- buttonIndex = self.buttonIndex[buttonName]
- else:
- buttonIndex = int(buttonName)
- if self.wasReleasedMap[buttonIndex]:
- self.wasReleasedMap[buttonIndex] = False
- return True
- else:
- return False
- except KeyError:
- raise ValueError('Button %i was not found' % buttonIndex)
- except ValueError:
- raise ValueError('Button name %s was not found' % buttonName)
-
- def axis(self, axisName):
- """Returns the last observed state of a gamepad axis specified by name or index.
- Throws a ValueError if the axis index is unavailable.
-
- Status is updated by getNextEvent calls.
-
- Throws ValueError if the button name or index cannot be found."""
- try:
- if axisName in self.axisIndex:
- axisIndex = self.axisIndex[axisName]
- else:
- axisIndex = int(axisName)
- return self.axisMap[axisIndex]
- except KeyError:
- raise ValueError('Axis %i was not found' % axisIndex)
- except ValueError:
- raise ValueError('Axis name %s was not found' % axisName)
-
- def availableButtonNames(self):
- """Returns a list of available button names for this gamepad.
- An empty list means that no button mapping has been provided."""
- return self.buttonIndex.keys()
-
- def availableAxisNames(self):
- """Returns a list of available axis names for this gamepad.
- An empty list means that no axis mapping has been provided."""
- return self.axisIndex.keys()
-
- def isConnected(self):
- """Returns True until reading from the device fails."""
- return self.connected
-
- def addButtonPressedHandler(self, buttonName, callback):
- """Adds a callback for when a specific button specified by name or index is pressed.
- This callback gets no parameters passed."""
- try:
- if buttonName in self.buttonIndex:
- buttonIndex = self.buttonIndex[buttonName]
- else:
- buttonIndex = int(buttonName)
- if callback not in self.pressedEventMap[buttonIndex]:
- self.pressedEventMap[buttonIndex].append(callback)
- except KeyError:
- raise ValueError('Button %i was not found' % buttonIndex)
- except ValueError:
- raise ValueError('Button name %s was not found' % buttonName)
-
- def removeButtonPressedHandler(self, buttonName, callback):
- """Removes a callback for when a specific button specified by name or index is pressed."""
- try:
- if buttonName in self.buttonIndex:
- buttonIndex = self.buttonIndex[buttonName]
- else:
- buttonIndex = int(buttonName)
- if callback in self.pressedEventMap[buttonIndex]:
- self.pressedEventMap[buttonIndex].remove(callback)
- except KeyError:
- raise ValueError('Button %i was not found' % buttonIndex)
- except ValueError:
- raise ValueError('Button name %s was not found' % buttonName)
-
- def addButtonReleasedHandler(self, buttonName, callback):
- """Adds a callback for when a specific button specified by name or index is released.
- This callback gets no parameters passed."""
- try:
- if buttonName in self.buttonIndex:
- buttonIndex = self.buttonIndex[buttonName]
- else:
- buttonIndex = int(buttonName)
- if callback not in self.releasedEventMap[buttonIndex]:
- self.releasedEventMap[buttonIndex].append(callback)
- except KeyError:
- raise ValueError('Button %i was not found' % buttonIndex)
- except ValueError:
- raise ValueError('Button name %s was not found' % buttonName)
-
- def removeButtonReleasedHandler(self, buttonName, callback):
- """Removes a callback for when a specific button specified by name or index is released."""
- try:
- if buttonName in self.buttonIndex:
- buttonIndex = self.buttonIndex[buttonName]
- else:
- buttonIndex = int(buttonName)
- if callback in self.releasedEventMap[buttonIndex]:
- self.releasedEventMap[buttonIndex].remove(callback)
- except KeyError:
- raise ValueError('Button %i was not found' % buttonIndex)
- except ValueError:
- raise ValueError('Button name %s was not found' % buttonName)
-
- def addButtonChangedHandler(self, buttonName, callback):
- """Adds a callback for when a specific button specified by name or index changes.
- This callback gets a boolean for the button pressed state."""
- try:
- if buttonName in self.buttonIndex:
- buttonIndex = self.buttonIndex[buttonName]
- else:
- buttonIndex = int(buttonName)
- if callback not in self.changedEventMap[buttonIndex]:
- self.changedEventMap[buttonIndex].append(callback)
- except KeyError:
- raise ValueError('Button %i was not found' % buttonIndex)
- except ValueError:
- raise ValueError('Button name %s was not found' % buttonName)
-
- def removeButtonChangedHandler(self, buttonName, callback):
- """Removes a callback for when a specific button specified by name or index changes."""
- try:
- if buttonName in self.buttonIndex:
- buttonIndex = self.buttonIndex[buttonName]
- else:
- buttonIndex = int(buttonName)
- if callback in self.changedEventMap[buttonIndex]:
- self.changedEventMap[buttonIndex].remove(callback)
- except KeyError:
- raise ValueError('Button %i was not found' % buttonIndex)
- except ValueError:
- raise ValueError('Button name %s was not found' % buttonName)
-
- def addAxisMovedHandler(self, axisName, callback):
- """Adds a callback for when a specific axis specified by name or index changes.
- This callback gets the updated position of the axis."""
- try:
- if axisName in self.axisIndex:
- axisIndex = self.axisIndex[axisName]
- else:
- axisIndex = int(axisName)
- if callback not in self.movedEventMap[axisIndex]:
- self.movedEventMap[axisIndex].append(callback)
- except KeyError:
- raise ValueError('Button %i was not found' % axisIndex)
- except ValueError:
- raise ValueError('Button name %s was not found' % axisName)
-
- def removeAxisMovedHandler(self, axisName, callback):
- """Removes a callback for when a specific axis specified by name or index changes."""
- try:
- if axisName in self.axisIndex:
- axisIndex = self.axisIndex[axisName]
- else:
- axisIndex = int(axisName)
- if callback in self.movedEventMap[axisIndex]:
- self.movedEventMap[axisIndex].remove(callback)
- except KeyError:
- raise ValueError('Button %i was not found' % axisIndex)
- except ValueError:
- raise ValueError('Button name %s was not found' % axisName)
-
- def removeAllEventHandlers(self):
- """Removes all event handlers from all axes and buttons."""
- for index in self.pressedEventMap.keys():
- self.pressedEventMap[index] = []
- self.releasedEventMap[index] = []
- self.changedEventMap[index] = []
- self.movedEventMap[index] = []
-
- def disconnect(self):
- """Cleanly disconnect and remove any threads and event handlers."""
- self.connected = False
- self.removeAllEventHandlers()
- self.stopBackgroundUpdates()
- del self.joystickFile
-
-###########################
-# Import gamepad mappings #
-###########################
-scriptDir = os.path.dirname(os.path.realpath(__file__))
-controllerScript = os.path.join(scriptDir, "Controllers.py")
-exec(open(controllerScript).read())
-
-# Generate a list of available gamepad types
-moduleDict = globals()
-classList = [moduleDict[a] for a in moduleDict.keys() if inspect.isclass(moduleDict[a])]
-controllerDict = {}
-deviceNames = []
-for gamepad in classList:
- controllerDict[gamepad.__name__.upper()] = gamepad
- deviceNames.append(gamepad.__name__)
-deviceNames.sort()
-
-##################################################################
-# When this script is run it provides testing code for a gamepad #
-##################################################################
-
-if __name__ == "__main__":
- # Python 2/3 compatibility
- try:
- input = raw_input
- except NameError:
- pass
-
- # ANSI colour code sequences
- GREEN = '\033[0;32m'
- CYAN = '\033[0;36m'
- BLUE = '\033[1;34m'
- RESET = '\033[0m'
-
- # Ask for the gamepad to use
- print('Gamepad axis and button events...')
- print('Press CTRL+C to exit')
- print('')
- print('Available device names:')
- formatString = ' ' + GREEN + '%s' + RESET + ' - ' + CYAN + '%s' + RESET
- for device in deviceNames:
- print(formatString % (device, controllerDict[device.upper()].fullName))
- print('')
- print('What device name are you using (leave blank if not in the list)')
- device = input('? ' + GREEN).strip().upper()
- print(RESET)
-
- # Wait for a connection
- if not available():
- print('Please connect your gamepad...')
- while not available():
- time.sleep(1.0)
- print('Gamepad connected')
-
- # Pick the correct class
- if device in controllerDict:
- print(controllerDict[device].fullName)
- gamepad = controllerDict[device]()
- elif device == '':
- print('Unspecified gamepad')
- print('')
- gamepad = Gamepad()
- else:
- print('Unknown gamepad')
- print('')
- sys.exit()
-
- # Display the event messages as they arrive
- while True:
- eventType, index, value = gamepad.getNextEvent()
- print(BLUE + eventType + RESET + ',\t ' +
- GREEN + str(index) + RESET + ',\t' +
- CYAN + str(value) + RESET)
- time.sleep(1.0)
diff --git a/controls/Gamepad/LICENSE b/controls/Gamepad/LICENSE
deleted file mode 100644
index 2e337ad..0000000
--- a/controls/Gamepad/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2019 Arron Churchill
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/controls/Gamepad/ListNames.py b/controls/Gamepad/ListNames.py
deleted file mode 100755
index 358cb05..0000000
--- a/controls/Gamepad/ListNames.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-
-# Load the gamepad and time libraries
-import Gamepad
-import time
-
-# Wait for a connection
-if not Gamepad.available():
- print('Please connect your gamepad...')
- while not Gamepad.available():
- time.sleep(1.0)
-print('Gamepad connected')
-
-# Pick the gamepad type
-#gamepad = Gamepad.Gamepad() #Generic unnamed controls
-#gamepad = Gamepad.PS3()
-gamepad = Gamepad.PS4()
-
-# Show the selected gamepad type
-print('Gamepad type: ' + gamepad.__class__.__name__)
-
-# Display axis names
-axisNames = gamepad.availableAxisNames()
-if axisNames:
- print('Available axis names:')
- for axis in axisNames:
- print(' ' + str(axis))
-else:
- print('No axis name mappings configured')
-print('')
-
-# Display button names
-buttonNames = gamepad.availableButtonNames()
-if buttonNames:
- print('Available button names:')
- for button in buttonNames:
- print(' ' + str(button))
-else:
- print('No button name mappings configured')
diff --git a/controls/Gamepad/PollingExample.py b/controls/Gamepad/PollingExample.py
deleted file mode 100755
index 79e2d32..0000000
--- a/controls/Gamepad/PollingExample.py
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-
-# Load the gamepad and time libraries
-import Gamepad
-import time
-
-# Gamepad settings
-gamepadType = Gamepad.PS4
-buttonHappy = 'CROSS'
-buttonBeep = 'CIRCLE'
-buttonExit = 'PS'
-joystickSpeed = 'LEFT-Y'
-joystickSteering = 'RIGHT-X'
-
-# Wait for a connection
-if not Gamepad.available():
- print('Please connect your gamepad...')
- while not Gamepad.available():
- time.sleep(1.0)
-gamepad = gamepadType()
-print('Gamepad connected')
-
-# Set some initial state
-speed = 0.0
-steering = 0.0
-
-# Handle joystick updates one at a time
-while gamepad.isConnected():
- # Wait for the next event
- eventType, control, value = gamepad.getNextEvent()
-
- # Determine the type
- if eventType == 'BUTTON':
- # Button changed
- if control == buttonHappy:
- # Happy button (event on press and release)
- if value:
- print(':)')
- else:
- print(':(')
- elif control == buttonBeep:
- # Beep button (event on press)
- if value:
- print('BEEP')
- elif control == buttonExit:
- # Exit button (event on press)
- if value:
- print('EXIT')
- break
- elif eventType == 'AXIS':
- # Joystick changed
- if control == joystickSpeed:
- # Speed control (inverted)
- speed = -value
- elif control == joystickSteering:
- # Steering control (not inverted)
- steering = value
- print('%+.1f %% speed, %+.1f %% steering' % (speed * 100, steering * 100))
diff --git a/controls/Gamepad/README.md b/controls/Gamepad/README.md
deleted file mode 100644
index 2bcafe9..0000000
--- a/controls/Gamepad/README.md
+++ /dev/null
@@ -1,168 +0,0 @@
-# PiBorg Gamepad Library
-
-
-
-The Gamepad library provides a simple way of getting inputs from joysticks, gamepads, and other game controllers.
-
-Both buttons and axes / joysticks can be referenced by names as well as their raw index numbers. These can be easily customised and new controllers can easily be added as well.
-
-It is designed and tested for the Raspberry Pi, but it should work with an Linux install and any device which shows up as a ```/dev/input/js?``` device.
-
-Gamepad only uses imports which are part of the standard Python install and works with both Python 2 and 3.
-
-Multiple controllers are also supported, just create more than one ```Gamepad``` instance with different joystick IDs.
-
-See our other projects on GitHub [here](https://github.com/piborg) :)
-
-# Installing the Library
-```
-cd ~
-git clone https://github.com/piborg/Gamepad
-```
-That's it. The library does not need installing, it just needs to be downloaded.
-
-# The Gamepad Library
-The library provides three basic modes for getting updates from your controller:
-
-1. Polling - we ask the library for the next controller update one at a time.
-2. Asynchronous - the controller state is updated in the background.
-3. Event - callbacks are made when the controller state changes.
-
-See the examples further down for an explanation of how each mode is used.
-
-The library itself is contained in just two scripts:
-
-## ```Gamepad.py```
-The main library, this script contains all of the code which reads input from the controller. It contains the ```Gamepad``` class which has all of the logic used to decode the joystick events and store the current state.
-
-It works with both the raw axis / joystick and button numbers and easy to read names. It also contains the threading code needed to handle the background updating used in the asynchronous and event modes.
-
-This script can be run directly using ```./Gamepad.py``` to check your controller mappings are correct or work out the mapping numbers for your own controller.
-
-## ```Controllers.py```
-This script contains the layouts for the different controllers. Each controller is represented by its own class inheriting from the main ```Gamepad``` class.
-
-If the mapping is not right for you the layout for both the axis / joystick names and the button names by editing these classes. Adding your own controller is also simple, just make your own class based on the example at the bottom :)
-
-Any button or axis without a name can still be used by the raw number if needed. This also means the ```Gamepad``` class can be used directly if you are only using the raw numbers.
-
-This script is not run directly, instead it is read by ```Gamepad.py``` so that all of the devices are available when Gamepad is imported.
-
-# Examples
-
-## Polling mode - ```PollingExample.py```
-The polling mode is probably the simplest and uses the Gamepad library to decode controller events one at a time.
-
-
-
-It works by repeatedly calling the ```getNextEvent()``` function to get the next update from the controller in the order they occurred. Each call returns three things:
-
-1. The event type. This is either ```'BUTTON'``` for button presses or ```'AXIS'``` for axis / joystick movements.
-2. The control which changed. This is a string with the name if available, or the index number if no name was available.
-3. The new value for the control. For buttons this is ```True``` or ```False```, for axis / joystick movement it is a position between -1.0 and +1.0.
-
-For example if the circle button on your controller was just pressed you would get ```'BUTTON', 'CIRCLE', True``` as the result.
-
-Polling mode cannot be used at the same time as the asynchronous or event modes as they read the controller events for you.
-
-## Asynchronous mode - ```AsyncExample.py```
-Asynchronous mode works by reading the controller events in a background thread and updating the objects state to match the controller.
-
-
-
-It is started by calling the ```startBackgroundUpdates()``` function and should be stopped at the end of your script by calling the ```disconnect()``` function.
-
-The current controller state can be queried using the following functions:
-
-* ```isConnected()``` - check if the controller is still connected.
-* ```isPressed(X)``` - check if a button is currently pressed.
-* ```beenPressed(X)``` - see if a button was pressed since the last check. Only returns ```True``` once per press.
-* ```beenReleased(X)``` - see if a button was released since the last check. Only returns ```True``` once per release.
-* ```axis(X)``` - read the latest axis / joystick position. This is a float number between -1.0 and +1.0.
-
-In all cases ```X``` can either be the string based name or the raw index number (e.g. ```'CIRCLE'``` or ```1```).
-
-Asynchronous mode cannot be used at the same time as the polling mode as it reads the controller events for you, but it can be used with event mode.
-
-## Event mode - ```EventExample.py```
-Event mode works by reading the controller events in a background thread and calling functions in your script when changes occur.
-
-
-
-It is started by calling the ```startBackgroundUpdates()``` function and should be stopped at the end of your script by calling the ```disconnect()``` function. The ```isConnected()``` function will return ```False``` if the controller is disconnected while running.
-
-Once started you can register your functions to controller events using these calls:
-
-* ```addButtonPressedHandler(X, F)``` - called when a button is pressed, no values passed.
-* ```addButtonReleasedHandler(X, F)``` - called when a button is released, no values passed.
-* ```addButtonChangedHandler(X, F)``` - called when a button changes state, boolean passed with ```True``` for pressed or ```False``` for released.
-* ```addAxisMovedHandler(X, F)``` - called when an axis / joystick is moved, a float number between -1.0 and +1.0 is passed.
-
-In all cases ```X``` can either be the string based name or the raw index number (e.g. ```'CIRCLE'``` or ```1```). ```F``` is the function which gets called when the event occurs.
-
-The same function can be registered with multiple events. You can also register multiple functions with the same event.
-
-You can also remove an already registered event using these calls if needed:
-
-* ```removeButtonPressedHandler(X, F)``` - removes a callback added by ```addButtonPressedHandler```.
-* ```removeButtonReleasedHandler(X, F)``` - removes a callback added by ```addButtonReleasedHandler```.
-* ```removeButtonChangedHandler(X, F)``` - removes a callback added by ```addButtonChangedHandler```.
-* ```removeAxisMovedHandler(X, F)``` - removes a callback added by ```addAxisMovedHandler```.
-* ```removeAllEventHandlers()``` - removes all added callbacks for this controller.
-
-Event mode cannot be used at the same time as the polling mode as it reads the controller events for you, but it can be used with asynchronous mode.
-
-## Asynchronous and event mode - ```AsyncAndEventExample.py```
-This is not really a mode, but an example of how the asynchronous and event modes can be used at the same time. This is generally my preferred option as event mode is often easier to understand for button presses and asynchronous mode works well with axis / joystick movements.
-
-
-
-The example script here is really a hybrid between the ```AsyncExample.py``` and ```EventExample.py``` examples. Button callbacks are registered in the event style, then the loop only checks one button and the joystick positions.
-
-In this style you are free to mix and match what you see as events and what you read the state of directly.
-
-## Getting the available names - ```ListNames.py```
-This example is just a helpful utility to print out all of the axis and button names for a controller type. You can change the controller type by looking for this line:
-```
-gamepad = Gamepad.PS4()
-```
-and changing ```PS4``` to any available device name.
-
-The list of device names is shown when you run ```./Gamepad.py``` directly.
-
-## Custom controller in your own script - ```CustomGamepadExample.py```
-This example shows how you can create a controller mapping in your own script without changing ```Controllers.py```. This can be useful if you need to use different names in just one script, or if you want to keep all of your changes in your own code.
-
-In this case you make your own class inheriting from ```Gamepad.Gamepad``` in the same way as they are written in ```Controllers.py```. You do not have to set the ```fullName``` value.
-
-## RockyBorg example - ```rockyJoy.py```
-Here we have an actual use of the Gamepad library, controlling a [RockyBorg](https://www.piborg.org/rockyborg-white) robot :)
-
-It works exactly like the old Pygame version of the ```rbJoystick.py``` example script, but with the addition of a button to end the script.
-
-The controller and button layout is all specified towards the top of the script and the standard [RockyBorg library](https://github.com/piborg/rockyborg) is used to control the motors and servo.
-
-## MonsterBorg example - ```monsterJoy.py```
-Here we have another real-world use of the Gamepad library, controlling a [MonsterBorg](https://www.piborg.org/monsterborg) robot :)
-
-This provides the same control scheme we usually use for our robots, plus buttons to toggle the LEDs and end the script.
-
-The controller and button layout is all specified towards the top of the script and the standard [ThunderBorg](https://www.piborg.org/thunderborg) library is used to control the motors.
-
-# Using Gamepad in your own project
-If you are using ```Gamepad``` in your own script it will need access to both the ```Gamepad.py``` and ```Controllers.py``` scripts. This can be done in a few ways:
-
-1. Write your script in the Gamepad directory.
-2. Copy the two scripts into your project's directory.
-3. Add the Gamepad directory to your PYTHONPATH environment variable.
-4. Add these lines to your script before importing the library: ```import sys``` and ```sys.path.insert(0, "/home/pi/Gamepad")```, where ```/home/pi/Gamepad``` is the Gamepad directory.
-
-# Troubleshooting
-For troubleshooting and further help please post questions on our [forum](http://forum.piborg.org/forum).
-
-# Reporting Issues
-
-If you find a bug with the RockyBorg code, please feel free to report it as an issue. When reporting issues with this library please fill in the issue report with as much information as you can to help us help you.
-
-Please supply as much information as you can so we can replicate the bug locally. :)
-
diff --git a/controls/Gamepad/monsterJoy.py b/controls/Gamepad/monsterJoy.py
deleted file mode 100755
index d670121..0000000
--- a/controls/Gamepad/monsterJoy.py
+++ /dev/null
@@ -1,166 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-
-# Load the libraries
-import sys
-import Gamepad
-import time
-sys.path.insert(0, "/home/pi/thunderborg")
-import ThunderBorg
-
-# Settings for the gamepad
-gamepadType = Gamepad.PS4 # Class for the gamepad (e.g. Gamepad.PS3)
-joystickSpeed = 'LEFT-Y' # Joystick axis to read for up / down position
-joystickSpeedInverted = True # Set this to True if up and down appear to be swapped
-joystickSteering = 'RIGHT-X' # Joystick axis to read for left / right position
-joystickSteeringInverted = False # Set this to True if left and right appear to be swapped
-buttonSlow = 'L2' # Joystick button for driving slowly whilst held
-slowFactor = 0.5 # Speed to slow to when the drive slowly button is held, e.g. 0.5 would be half speed
-buttonFastTurn = 'R2' # Joystick button for turning fast
-buttonExit = 'PS' # Joystick button to end the program
-buttonFullBeamToggle = 'CROSS' # Joystick button to toggle the LEDs between battery mode and fully white
-interval = 0.05 # Time between motor updates in seconds, smaller responds faster but uses more processor time
-
-# Power settings
-voltageIn = 1.2 * 10 # Total battery voltage to the ThunderBorg
-voltageOut = 12.0 * 0.95 # Maximum motor voltage, we limit it to 95% to allow the RPi to get uninterrupted power
-
-# Setup the power limits
-if voltageOut > voltageIn:
- maxPower = 1.0
-else:
- maxPower = voltageOut / float(voltageIn)
-
-# Setup the ThunderBorg
-TB = ThunderBorg.ThunderBorg()
-#TB.i2cAddress = 0x15 # Uncomment and change the value if you have changed the board address
-TB.Init()
-if not TB.foundChip:
- boards = ThunderBorg.ScanForThunderBorg()
- if len(boards) == 0:
- print('No ThunderBorg found, check you are attached :)')
- else:
- print('No ThunderBorg at address %02X, but we did find boards:' % (TB.i2cAddress))
- for board in boards:
- print(' %02X (%d)' % (board, board))
- print('If you need to change the I²C address change the setup line so it is correct, e.g.')
- print('TB.i2cAddress = 0x%02X' % (boards[0]))
- sys.exit()
-# Ensure the communications failsafe has been enabled!
-failsafe = False
-for i in range(5):
- TB.SetCommsFailsafe(True)
- failsafe = TB.GetCommsFailsafe()
- if failsafe:
- break
-if not failsafe:
- print('Board %02X failed to report in failsafe mode!' % (TB.i2cAddress))
- sys.exit()
-
-# Show battery monitoring settings
-battMin, battMax = TB.GetBatteryMonitoringLimits()
-battCurrent = TB.GetBatteryReading()
-print('Battery monitoring settings:')
-print(' Minimum (red) %02.2f V' % (battMin))
-print(' Half-way (yellow) %02.2f V' % ((battMin + battMax) / 2))
-print(' Maximum (green) %02.2f V' % (battMax))
-print('')
-print(' Current voltage %02.2f V' % (battCurrent))
-print('')
-
-# Setup the state shared with callbacks
-global running
-global fullBeam
-running = True
-fullBeam = False
-
-# Create the callback functions
-def exitButtonPressed():
- global running
- print('EXIT')
- running = False
-
-def fullBeamButtonPressed():
- global fullBeam
- fullBeam = not fullBeam
- if fullBeam:
- TB.SetLedShowBattery(False)
- TB.SetLeds(1, 1, 1)
- else:
- TB.SetLedShowBattery(True)
-
-# Wait for a connection
-TB.MotorsOff()
-TB.SetLedShowBattery(False)
-TB.SetLeds(0, 0, 1)
-waitingToggle = True
-if not Gamepad.available():
- print('Please connect your gamepad...')
- while not Gamepad.available():
- time.sleep(interval * 4)
- waitingToggle = not waitingToggle
- if waitingToggle:
- TB.SetLeds(0, 0, 1)
- else:
- TB.SetLeds(0, 0, 0)
-print('Gamepad connected')
-gamepad = gamepadType()
-
-# Start the background updating
-gamepad.startBackgroundUpdates()
-TB.SetLedShowBattery(True)
-
-# Register the callback functions
-gamepad.addButtonPressedHandler(buttonExit, exitButtonPressed)
-gamepad.addButtonPressedHandler(buttonFullBeamToggle, fullBeamButtonPressed)
-
-# Keep running while joystick updates are handled by the callbacks
-try:
- while running and gamepad.isConnected():
- # Read the latest speed and steering
- if joystickSpeedInverted:
- speed = -gamepad.axis(joystickSpeed)
- else:
- speed = +gamepad.axis(joystickSpeed)
- if joystickSteeringInverted:
- steering = -gamepad.axis(joystickSteering)
- else:
- steering = +gamepad.axis(joystickSteering)
-
- # Work out the adjusted speed and steering
- if gamepad.isPressed(buttonSlow):
- finalSpeed = speed * slowFactor
- else:
- finalSpeed = speed
- if gamepad.isPressed(buttonFastTurn):
- finalSteering = steering * 2.0
- else:
- finalSteering = steering
-
- # Determine the drive power levels
- driveLeft = finalSpeed
- driveRight = finalSpeed
- if finalSteering < -0.05:
- # Turning left
- driveLeft *= 1.0 + finalSteering
- elif finalSteering > +0.05:
- # Turning right
- driveRight *= 1.0 - finalSteering
-
- # Set the motors to the new speeds
- TB.SetMotor1(driveRight * maxPower)
- TB.SetMotor2(driveLeft * maxPower)
-
- # Sleep for our motor change interval
- time.sleep(interval)
-finally:
- # Ensure the background thread is always terminated when we are done
- gamepad.disconnect()
-
- # Turn the motors off
- TB.MotorsOff()
-
- # Set the LED to a dim red to indicate we have finished
- TB.SetCommsFailsafe(False)
- TB.SetLedShowBattery(False)
- TB.SetLeds(0.2, 0, 0)
diff --git a/controls/Gamepad/rockyJoy.py b/controls/Gamepad/rockyJoy.py
deleted file mode 100755
index bcf252a..0000000
--- a/controls/Gamepad/rockyJoy.py
+++ /dev/null
@@ -1,131 +0,0 @@
-#!/usr/bin/env python
-# coding: utf-8
-
-# Load the libraries
-import sys
-import Gamepad
-import time
-sys.path.insert(0, "/home/pi/rockyborg")
-import RockyBorg
-
-# Settings for the gamepad
-gamepadType = Gamepad.PS4 # Class for the gamepad (e.g. Gamepad.PS3)
-joystickSpeed = 'LEFT-Y' # Joystick axis to read for up / down position
-joystickSpeedInverted = True # Set this to True if up and down appear to be swapped
-joystickSteering = 'RIGHT-X' # Joystick axis to read for left / right position
-joystickSteeringInverted = False # Set this to True if left and right appear to be swapped
-buttonSlow = 'L2' # Joystick button for driving slowly whilst held
-slowFactor = 0.5 # Speed to slow to when the drive slowly button is held, e.g. 0.5 would be half speed
-buttonExit = 'PS' # Joystick button to end the program
-interval = 0.05 # Time between motor updates in seconds, smaller responds faster but uses more processor time
-
-# Power settings
-voltageIn = 1.2 * 8 # Total battery voltage to the RockyBorg
-voltageOut = 6.0 # Maximum motor voltage
-
-# Setup the power limits
-if voltageOut > voltageIn:
- maxPower = 1.0
-else:
- maxPower = voltageOut / float(voltageIn)
-
-# Setup the RockyBorg
-RB = RockyBorg.RockyBorg()
-#RB.i2cAddress = 0x21 # Uncomment and change the value if you have changed the board address
-RB.Init()
-if not RB.foundChip:
- boards = RockyBorg.ScanForRockyBorg()
- if len(boards) == 0:
- print('No RockyBorg found, check you are attached :)')
- else:
- print('No RockyBorg at address %02X, but we did find boards:' % (RB.i2cAddress))
- for board in boards:
- print(' %02X (%d)' % (board, board))
- print('If you need to change the I²C address change the setup line so it is correct, e.g.')
- print('RB.i2cAddress = 0x%02X' % (boards[0]))
- sys.exit()
-
-# Enable the motors and disable the failsafe
-RB.SetCommsFailsafe(False)
-RB.MotorsOff()
-RB.SetMotorsEnabled(True)
-
-# Setup the state shared with callbacks
-global running
-running = True
-
-# Create the callback functions
-def exitButtonPressed():
- global running
- print('EXIT')
- running = False
-
-# Wait for a connection
-RB.MotorsOff()
-RB.SetLed(True)
-waitingToggle = True
-if not Gamepad.available():
- print('Please connect your gamepad...')
- while not Gamepad.available():
- time.sleep(interval * 4)
- waitingToggle = not waitingToggle
- if waitingToggle:
- RB.SetLed(False)
- else:
- RB.SetLed(True)
-print('Gamepad connected')
-gamepad = gamepadType()
-
-# Start the background updating
-gamepad.startBackgroundUpdates()
-RB.SetLed(True)
-
-# Register the callback functions
-gamepad.addButtonPressedHandler(buttonExit, exitButtonPressed)
-
-# Keep running while joystick updates are handled by the callbacks
-try:
- while running and gamepad.isConnected():
- # Read the latest speed and steering
- if joystickSpeedInverted:
- speed = -gamepad.axis(joystickSpeed)
- else:
- speed = +gamepad.axis(joystickSpeed)
- if joystickSteeringInverted:
- steering = -gamepad.axis(joystickSteering)
- else:
- steering = +gamepad.axis(joystickSteering)
-
- # Work out the adjusted speed
- if gamepad.isPressed(buttonSlow):
- finalSpeed = speed * slowFactor
- else:
- finalSpeed = speed
-
- # Determine the drive power levels based on steering angle
- servoPosition = steering
- driveLeft = finalSpeed
- driveRight = finalSpeed
- if steering < -0.05:
- # Turning left
- driveLeft *= 1.0 + (0.5 * steering)
- elif steering > +0.05:
- # Turning right
- driveRight *= 1.0 - (0.5 * steering)
-
- # Set the motors to the new speeds and tilt the servo to steer
- RB.SetMotor1(-driveLeft * maxPower)
- RB.SetMotor2(driveRight * maxPower)
- RB.SetServoPosition(servoPosition)
-
- # Sleep for our motor change interval
- time.sleep(interval)
-finally:
- # Ensure the background thread is always terminated when we are done
- gamepad.disconnect()
-
- # Turn the motors off
- RB.MotorsOff()
-
- # Turn the LED off indicate we have finished
- RB.SetLed(False)
diff --git a/controls/__init__.py b/controls/__init__.py
index bf2d4fe..030f1c5 100644
--- a/controls/__init__.py
+++ b/controls/__init__.py
@@ -1,3 +1,2 @@
-from . import gamepad
-from . import joystick
+from . import controller
from . import keyboard
\ No newline at end of file
diff --git a/controls/controller.py b/controls/controller.py
new file mode 100644
index 0000000..a1fddee
--- /dev/null
+++ b/controls/controller.py
@@ -0,0 +1,76 @@
+import pygame
+from controls.controlsbase import ControlsBase
+
+class ControllerControls(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.power_level: str = ""
+
+ 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 power_level(self) -> str:
+ return self._power_level
+
+ @power_level.setter
+ def power_level(self) -> None:
+ self._power_level = self.device.get_power_level()
\ No newline at end of file
diff --git a/controls/controlsbase.py b/controls/controlsbase.py
index 452964d..d6cd6ca 100644
--- a/controls/controlsbase.py
+++ b/controls/controlsbase.py
@@ -5,17 +5,5 @@ from abc import ABC, abstractmethod
class ControlsBase(ABC):
@abstractmethod
- def left(self):
- pass
-
- def right(self):
- pass
-
- def up(self):
- pass
-
- def down(self):
- pass
-
- def pause(self):
+ def handle_input(self, event):
pass
\ No newline at end of file
diff --git a/controls/gamepad.py b/controls/gamepad.py
deleted file mode 100644
index 203015a..0000000
--- a/controls/gamepad.py
+++ /dev/null
@@ -1,25 +0,0 @@
-"""
-Class for grabbing gamepad signals
-"""
-from controls.controlsbase import ControlsBase
-import pygame
-import gamepad
-
-class GamePadControls(ControlsBase):
- def __init__(self):
- gamapadType = gamepad.ps5
-
- def left(self):
- pass
-
- def right(self):
- pass
-
- def up(self):
- pass
-
- def down(self):
- pass
-
- def pause(self):
- pass
\ No newline at end of file
diff --git a/controls/joystick.py b/controls/joystick.py
deleted file mode 100644
index e7268f6..0000000
--- a/controls/joystick.py
+++ /dev/null
@@ -1,44 +0,0 @@
-"""
-Class for grabbing joystick signals
-"""
-from controls.controlsbase import ControlsBase
-import pygame
-import pyjoystick
-from pyjoystick.pygame import Key, Joystick, run_event_loop
-
-class JoystickControls(ControlsBase):
- def __init__(self):
- mngr = pyjoystick.ThreadEventManager(event_loop=run_event_loop,
- handle_key_event=self.handle_key_event)
- mngr.start()
-
- def handle_key_event(self, key):
- if key.keytype != Key.HAT:
- if key.value != pygame.K_P:
- return
-
- if key.value == Key.HAT_UP:
- self.up()
- elif key.value == Key.HAT_DOWN:
- self.down()
- if key.value == Key.HAT_LEFT:
- self.left()
- elif key.value == Key.HAT_RIGHT:
- self.right()
- elif key.value == Key.KEY_P:
- self.pause()
-
- def left(self):
- print("Joystick moved left")
-
- def right(self):
- print("Joystick moved right")
-
- def up(self):
- print("Joystick moved up")
-
- def down(self):
- print("Joystick moved down")
-
- def pause(self):
- print("Paused")
\ No newline at end of file
diff --git a/controls/keyboard.py b/controls/keyboard.py
index 7e03b85..af8f860 100644
--- a/controls/keyboard.py
+++ b/controls/keyboard.py
@@ -7,6 +7,9 @@ class KeyboardControls(ControlsBase):
def __init__(self):
pass
+ def handle_input(self, event):
+ pass
+
def left(self):
pass
@@ -20,4 +23,7 @@ class KeyboardControls(ControlsBase):
pass
def pause(self):
- pass
\ No newline at end of file
+ pass
+
+ def rumble(self):
+ return
\ No newline at end of file
diff --git a/gamepad.png b/gamepad.png
new file mode 100644
index 0000000..cefbea0
Binary files /dev/null and b/gamepad.png differ
diff --git a/joystick.png b/joystick.png
new file mode 100644
index 0000000..e644d5d
Binary files /dev/null and b/joystick.png differ
diff --git a/keyboard.png b/keyboard.png
new file mode 100644
index 0000000..7c98c60
Binary files /dev/null and b/keyboard.png differ
diff --git a/logitech-t310.png b/logitech-t310.png
new file mode 100644
index 0000000..b8f27e5
Binary files /dev/null and b/logitech-t310.png differ
diff --git a/main.py b/main.py
index 074cd22..3a57fa3 100644
--- a/main.py
+++ b/main.py
@@ -1,8 +1,80 @@
import pygame
+import time
import controls
+from screens.startscreen import StartScreen
from tilemap.playground import PlayGround
-if __name__ == "__main__":
- pg = PlayGround(geometry = (1, 3))
+class Snake:
+ def __init__(self):
+ self.STATES = {"starting": 0, "playing": 1, "pausing": 2, "ending": 3, "setting": 4, "highscore": 5}
+ self.running = True
+ self.numplayers: int = 1
+ self.playground: PlayGround = None
+ self.controls = None
+ self.players: list = []
+ self.windowed: bool = True
+ self.width: int = 800
+ self.height: int =600
+ self.startscreen = None
+ self.icon = pygame.image.load("snake.webp")
+ pygame.display.set_icon(self.icon)
+
+ self.set_state("starting")
+ self.set_windowed(self.width, self.height)
+ # self.set_fullscreen(self.width, self.height)
+
+ self.mainloop()
+
+ def mainloop(self):
+ while self.running:
+ match self.state:
+ case 0:
+ self.startscreen = StartScreen(self)
+ case 1:
+ pass
+ case 2:
+ pass
+ case 3:
+ pass
+ case 4:
+ pass
+ case 5:
+ pass
+ pygame.display.flip()
+ time.sleep(5)
+ self.running = False
+ pygame.quit()
+
+ def set_state(self, state: str) -> None:
+ self.state = self.STATES[state]
+
+ def set_windowed(self, width:int, height: int) -> None:
+ self.screen = pygame.display.set_mode((width, height))
+ self.windowed = True
+ pygame.display.set_caption("Snake")
+
+ def set_fullscreen(self, width: int, height: int) -> None:
+ self.screen = pygame.display.set_mode((width, height), flags=pygame.FULLSCREEN)
+ self.windowed = False
+
+ def get_width(self) -> int:
+ return self.width
+
+ @property
+ def numplayers(self) -> int:
+ return self._numplayers
+
+ @numplayers.setter
+ def numplayers(self, pl: int):
+ if pl <= 0 or pl >= 3:
+ raise ValueError("Numplayers must be a positive value of 1 or 2")
+ else:
+ self._numplayers = pl
+
+def main():
pygame.init()
- pass
\ No newline at end of file
+ snake = Snake()
+
+if __name__ == "__main__":
+ main()
+
\ No newline at end of file
diff --git a/player/player.py b/player/player.py
new file mode 100644
index 0000000..6a158db
--- /dev/null
+++ b/player/player.py
@@ -0,0 +1,5 @@
+class Player:
+ def __init__(self):
+ self.controls = None
+
+
\ No newline at end of file
diff --git a/ps5-dualsense.png b/ps5-dualsense.png
new file mode 100644
index 0000000..88b0617
Binary files /dev/null and b/ps5-dualsense.png differ
diff --git a/ps5-dualsense.webp b/ps5-dualsense.webp
new file mode 100644
index 0000000..380c55f
Binary files /dev/null and b/ps5-dualsense.webp differ
diff --git a/resources/fonts/1001fonts-fun-blob-eula.txt b/resources/fonts/1001fonts-fun-blob-eula.txt
new file mode 100644
index 0000000..2bd98e4
--- /dev/null
+++ b/resources/fonts/1001fonts-fun-blob-eula.txt
@@ -0,0 +1,38 @@
+1001Fonts Free For Personal Use License (FFP)
+
+Preamble
+In this license, 'Fun Blob' refers to the given .zip file, which may contain one or numerous fonts. These fonts can be of any type (.ttf, .otf, ...) and together they form a 'font family' or in short a 'typeface'.
+
+1. Copyright
+Fun Blob is the intellectual property of its respective author, provided it is original, and is protected by copyright laws in many parts of the world.
+
+2. Personal Use
+Fun Blob may be downloaded and used free of charge for personal use, as long as the usage is not racist or illegal. Personal use refers to all usage that does not generate financial income in a business manner, for instance:
+
+ - personal scrapbooking for yourself
+ - recreational websites and blogs for friends and family
+ - prints such as flyers, posters, t-shirts for churches, charities, and non-profit organizations
+
+3. Commercial Use
+Commercial use is not allowed without prior written permission from the respective author. Please contact the author to ask for commercial licensing. Commercial use refers to usage in a business environment, including:
+
+ - business cards, logos, advertising, websites, mobile apps for companies
+ - t-shirts, books, apparel that will be sold for money
+ - flyers, posters for events that charge admission
+ - freelance graphic design work
+ - anything that will generate direct or indirect income
+
+4. Modification
+Fun Blob may not be modified, altered, adapted or built upon without written permission by its respective author. This pertains all files within the downloadable font zip-file.
+
+5. Conversion
+Fun Blob may be converted to other formats such as WOFF, SVG or EOT webfonts, as long as the font is not modified in any other way, such as changing names or altering individual glyphs.
+
+6. Distribution
+While Fun Blob may freely be copied and passed along to other individuals for private use as its original downloadable zip-file, it may not be sold or published without written permission by its respective author.
+
+7. Embedding
+Fun Blob may be embedded into an application such as a web- or mobile app, as long as the application is of personal use and does not distribute Fun Blob, such as offering it as a download.
+
+8. Disclaimer
+Fun Blob is offered 'as is' without any warranty. 1001fonts.com and the respective author of Fun Blob shall not be liable for any damage derived from using this typeface. By using Fun Blob you agree to the terms of this license.
\ No newline at end of file
diff --git a/resources/fonts/Chewy Noodles.otf b/resources/fonts/Chewy Noodles.otf
new file mode 100644
index 0000000..32a7d6a
Binary files /dev/null and b/resources/fonts/Chewy Noodles.otf differ
diff --git a/resources/fonts/FunBlob.otf b/resources/fonts/FunBlob.otf
new file mode 100644
index 0000000..2a5cd1a
Binary files /dev/null and b/resources/fonts/FunBlob.otf differ
diff --git a/resources/fonts/FunBlob.ttf b/resources/fonts/FunBlob.ttf
new file mode 100644
index 0000000..71ba6f5
Binary files /dev/null and b/resources/fonts/FunBlob.ttf differ
diff --git a/resources/fonts/Ghostmeat Back.otf b/resources/fonts/Ghostmeat Back.otf
new file mode 100644
index 0000000..3fb3d7e
Binary files /dev/null and b/resources/fonts/Ghostmeat Back.otf differ
diff --git a/resources/fonts/Ghostmeat Front.otf b/resources/fonts/Ghostmeat Front.otf
new file mode 100644
index 0000000..dbd81ab
Binary files /dev/null and b/resources/fonts/Ghostmeat Front.otf differ
diff --git a/resources/fonts/Ghostmeat.otf b/resources/fonts/Ghostmeat.otf
new file mode 100644
index 0000000..a10d5e6
Binary files /dev/null and b/resources/fonts/Ghostmeat.otf differ
diff --git a/resources/fonts/Read me!.txt b/resources/fonts/Read me!.txt
new file mode 100644
index 0000000..3abf3c2
--- /dev/null
+++ b/resources/fonts/Read me!.txt
@@ -0,0 +1,34 @@
+This font is a DEMO version of the font and the characters in it contain the standard version. Read the description below for more details.
+
+By installing or using this font, you agree to the Product Use Agreement:
+
+1. This font is for PERSONAL USE. No commercial use allowed!
+2. If you want to use it for commercial purposes, you can download it via the following link and you will get the full version and complete characters:
+
+https://teenagefoundry.com/product/teenage-popular/
+
+3. If you don't mind, give me a cup of coffee to get even more excited!
+
+Donate click here
+
+paypal.me/teenagefoundry
+
+4. For inquiries, you can contact me at: teenagefoundry@gmail.com
+
+Thank you
+Teenage Foundry
+
+INDONESIAN VERSION:
+
+1. Font demo ini hanya untuk penggunaan pribadi, tidak untuk komersial/yang menghasilkan profit
+atau keuntungan dari hasil menggunakan font ini. Baik itu untuk Pribadi, Agensi Desain Grafis, Youtube, Tv, Percetakan, Perusahaan dll.
+(APABILA ANDA MELANGGAR DAN MENGGUNAKAN TANPA MEMBELI LISENSI TERLEBIH DAHULU AKAN DIKENAKAN DENDA SEBESAR 10X LIPAT HARGA LISENSI SESUAI PENGGUNAAN).
+
+2. Jika ingin menggunakan secara komersial silahkan membeli lisensi melalui link di bawah ini:
+
+https://teenagefoundry.com/product/teenage-popular/
+
+3. Untuk pertanyaan, Anda dapat menghubungi saya di: teenagefoundry@gmail.com
+
+Terimakasih
+Teenage Foundry
\ No newline at end of file
diff --git a/resources/fonts/Readme.txt b/resources/fonts/Readme.txt
new file mode 100644
index 0000000..5eb0408
--- /dev/null
+++ b/resources/fonts/Readme.txt
@@ -0,0 +1,37 @@
+This font is a DEMO version of the font and the characters in it contain the standard version. Read the description below for more details.
+
+By installing or using this font, you agree to the Product Use Agreement:
+
+1. This font is for PERSONAL USE. No commercial use allowed!
+2. If you want to use it for commercial purposes, you can download it via the following link and you will get the full version and complete characters:
+
+https://timurtype.com/product/chewy-noodles/
+
+3. If you don't mind, give me a cup of coffee to get even more excited!
+
+Donate click here
+
+https://paypal.me/timurtypestd
+
+4. Please visit our store for more amazing fonts :
+
+https://timurtype.com
+
+5. For inquiries, you can contact me at: timurtype.studio@gmail.com
+
+Thank you
+Timurtype Studio
+
+INDONESIAN VERSION:
+
+1. Font demo ini hanya untuk penggunaan pribadi, tidak untuk komersial/yang menghasilkan profit atau keuntungan dari hasil menggunakan font ini. Baik itu untuk Pribadi, Agensi Desain Grafis, Youtube, Tv, Percetakan, Perusahaan dll.
+(APABILA ANDA MELANGGAR DAN MENGGUNAKAN TANPA MEMBELI LISENSI TERLEBIH DAHULU AKAN DIKENAKAN DENDA SEBESAR 10X LIPAT HARGA LISENSI SESUAI PENGGUNAAN).
+
+2. Jika ingin menggunakan secara komersial silahkan membeli lisensi melalui link di bawah ini:
+
+https://timurtype.com/product/chewy-noodles/
+
+3. Untuk pertanyaan, Anda dapat menghubungi saya di: timurtype.studio@gmail.com
+
+Terimakasih
+Timurtype Studio
\ No newline at end of file
diff --git a/resources/fonts/Teenage Popular.otf b/resources/fonts/Teenage Popular.otf
new file mode 100644
index 0000000..04caf45
Binary files /dev/null and b/resources/fonts/Teenage Popular.otf differ
diff --git a/resources/fonts/Teenage Popular.png b/resources/fonts/Teenage Popular.png
new file mode 100644
index 0000000..0fa4176
Binary files /dev/null and b/resources/fonts/Teenage Popular.png differ
diff --git a/resources/fonts/license.txt b/resources/fonts/license.txt
new file mode 100644
index 0000000..08b9936
--- /dev/null
+++ b/resources/fonts/license.txt
@@ -0,0 +1,5 @@
+The fonts included in this archive are released under a “no rights reserved” Creative Commons Zero license. Please do not ask permission to do anything with these fonts. Whatever you want to do with this font, the answer will be yes. Please read about the CC0 Public Domain license before contacting me.
+
+https://creativecommons.org/publicdomain/zero/1.0/
+
+To the extent possible under law, Raymond Larabie has waived all copyright and related or neighboring rights to the fonts in this archive. This work is published from: Japan.
\ No newline at end of file
diff --git a/gfx/LoseScreen.png b/resources/gfx/LoseScreen.png
similarity index 100%
rename from gfx/LoseScreen.png
rename to resources/gfx/LoseScreen.png
diff --git a/gfx/StartScreen.png b/resources/gfx/StartScreen.png
similarity index 100%
rename from gfx/StartScreen.png
rename to resources/gfx/StartScreen.png
diff --git a/gfx/Textures.png b/resources/gfx/Textures.png
similarity index 100%
rename from gfx/Textures.png
rename to resources/gfx/Textures.png
diff --git a/gfx/Vignette.png b/resources/gfx/Vignette.png
similarity index 100%
rename from gfx/Vignette.png
rename to resources/gfx/Vignette.png
diff --git a/gfx/WinScreen.png b/resources/gfx/WinScreen.png
similarity index 100%
rename from gfx/WinScreen.png
rename to resources/gfx/WinScreen.png
diff --git a/resources/gfx/snake_banner.png b/resources/gfx/snake_banner.png
new file mode 100644
index 0000000..a2c82f1
Binary files /dev/null and b/resources/gfx/snake_banner.png differ
diff --git a/resources/gfx/snake_banner.webp b/resources/gfx/snake_banner.webp
new file mode 100644
index 0000000..4d0a5e3
Binary files /dev/null and b/resources/gfx/snake_banner.webp differ
diff --git a/resources/gfx/snake_intro_1.webp b/resources/gfx/snake_intro_1.webp
new file mode 100644
index 0000000..eca050c
Binary files /dev/null and b/resources/gfx/snake_intro_1.webp differ
diff --git a/resources/gfx/snake_intro_2.webp b/resources/gfx/snake_intro_2.webp
new file mode 100644
index 0000000..a44bb4e
Binary files /dev/null and b/resources/gfx/snake_intro_2.webp differ
diff --git a/screens/gameoverscreen.py b/screens/gameoverscreen.py
new file mode 100644
index 0000000..e69de29
diff --git a/screens/gamescreen.py b/screens/gamescreen.py
new file mode 100644
index 0000000..e69de29
diff --git a/screens/highscorescreen.py b/screens/highscorescreen.py
new file mode 100644
index 0000000..e69de29
diff --git a/screens/settingsscreen.py b/screens/settingsscreen.py
new file mode 100644
index 0000000..e69de29
diff --git a/screens/splitscreen.py b/screens/splitscreen.py
new file mode 100644
index 0000000..e69de29
diff --git a/screens/startscreen.py b/screens/startscreen.py
new file mode 100644
index 0000000..ce3e3c5
--- /dev/null
+++ b/screens/startscreen.py
@@ -0,0 +1,11 @@
+import pygame
+
+class StartScreen:
+ def __init__(self, parent):
+ self.parent = parent
+ self.banner = pygame.image.load("resources/gfx/snake_banner.png")
+ self.banner_width = int(self.parent.get_width())
+ self.banner = pygame.transform.smoothscale(self.banner, (self.banner_width, int(self.banner_width*0.4)))
+
+ self.parent.screen.blit(self.banner, (0, 0))
+
\ No newline at end of file
diff --git a/snake.webp b/snake.webp
new file mode 100644
index 0000000..3e390fd
Binary files /dev/null and b/snake.webp differ
diff --git a/startscreen/startscreen.py b/startscreen/startscreen.py
deleted file mode 100644
index fa7ac6a..0000000
--- a/startscreen/startscreen.py
+++ /dev/null
@@ -1,5 +0,0 @@
-import pygame
-
-class StartScreen:
- def __init__(self):
- pass
\ No newline at end of file
diff --git a/testing/controls.py b/testing/controls.py
new file mode 100644
index 0000000..5f50919
--- /dev/null
+++ b/testing/controls.py
@@ -0,0 +1,119 @@
+import pygame
+from abc import ABC, abstractmethod
+
+# Initialize pygame
+pygame.init()
+
+# Screen settings
+WIDTH, HEIGHT = 600, 400
+screen = pygame.display.set_mode((WIDTH, HEIGHT))
+pygame.display.set_caption("Select Control Method")
+
+# Colors
+WHITE = (255, 255, 255)
+BLACK = (0, 0, 0)
+BLUE = (0, 0, 255)
+RED = (255, 0, 0)
+
+# Font
+font = pygame.font.Font(None, 36)
+
+# Load images (replace with actual image paths)
+def load_image(path, max_size):
+ image = pygame.image.load(path).convert_alpha() # Use convert_alpha for transparency
+ image_rect = image.get_rect()
+ scale_factor = min(max_size[0] / image_rect.width, max_size[1] / image_rect.height)
+ new_size = (int(image_rect.width * scale_factor), int(image_rect.height * scale_factor))
+ return pygame.transform.smoothscale(image, new_size)
+
+keyboard_image = load_image("keyboard.png", (100, 100))
+#joystick_image = load_image("ps5-dualsense.png", (100, 100))
+joystick_image = load_image("gamepad.png", (100, 100))
+
+# Abstract Control Class
+class Control(ABC):
+ @abstractmethod
+ def handle_input(self, event):
+ pass
+
+class KeyboardControl(Control):
+ def handle_input(self, event):
+ if event.type == pygame.KEYDOWN:
+ print("Keyboard key pressed")
+
+class JoystickControl(Control):
+ def __init__(self, joystick_id):
+ self.joystick = pygame.joystick.Joystick(joystick_id)
+ self.joystick.init()
+
+ def handle_input(self, event):
+ if event.type == pygame.JOYBUTTONDOWN and event.joy == self.joystick.get_id():
+ print(f"Joystick {self.joystick.get_id()} button pressed")
+
+# Options
+options = ["Keyboard"]
+option_images = {"Keyboard": keyboard_image}
+pygame.joystick.init()
+joystick_count = pygame.joystick.get_count()
+joystick_controls = {}
+
+for i in range(joystick_count):
+ joystick = pygame.joystick.Joystick(i)
+ joystick.init()
+ joystick_name = joystick.get_name()
+ options.append(joystick_name)
+ joystick_controls[joystick_name] = JoystickControl(i)
+ option_images[joystick_name] = joystick_image
+
+selected_index = 0
+
+# Control mapping
+control_classes = {"Keyboard": KeyboardControl}
+control_classes.update(joystick_controls)
+selected_control = KeyboardControl()
+
+running = True
+while running:
+ screen.fill(WHITE)
+
+ for event in pygame.event.get():
+ if event.type == pygame.QUIT:
+ running = False
+ elif event.type == pygame.KEYDOWN:
+ if event.key == pygame.K_DOWN and selected_index < len(options) - 1:
+ selected_index += 1
+ elif event.key == pygame.K_UP and selected_index > 0:
+ selected_index -= 1
+ elif event.key == pygame.K_RETURN:
+ selected_control = control_classes[options[selected_index]]() if options[selected_index] == "Keyboard" else joystick_controls[options[selected_index]]
+ elif event.type == pygame.MOUSEBUTTONDOWN:
+ x, y = event.pos
+ for i in range(len(options)):
+ if 100 <= x <= 300 and 100 + i * 40 <= y <= 140 + i * 40:
+ selected_index = i
+ selected_control = control_classes[options[selected_index]]() if options[selected_index] == "Keyboard" else joystick_controls[options[selected_index]]
+ break
+
+ # Pass input to selected control method
+ selected_control.handle_input(event)
+
+ # Render label
+ label = font.render("Controls:", True, BLACK)
+ screen.blit(label, (50, 50))
+
+ # Render list
+ for i, option in enumerate(options):
+ color = RED if i == selected_index else BLACK
+ text = font.render(option, True, color)
+ screen.blit(text, (100, 100 + i * 40))
+
+ # Render selected item image
+ selected_option = options[selected_index]
+ if selected_option in option_images:
+ image = option_images[selected_option]
+ image_rect = image.get_rect(center=(500, 200)) # Keep aspect ratio
+ screen.blit(image, image_rect.topleft)
+
+ pygame.display.flip()
+
+pygame.quit()
diff --git a/testing/joystick.py b/testing/joystick.py
new file mode 100644
index 0000000..7874915
--- /dev/null
+++ b/testing/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, 0.7, 500):
+ 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