Skip to content

Add support for xbox guide button #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 27 additions & 24 deletions XInput.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
BUTTON_DPAD_DOWN = 0x000002
BUTTON_DPAD_LEFT = 0x000004
BUTTON_DPAD_RIGHT = 0x000008
BUTTON_GUIDE = 0x000400
BUTTON_START = 0x000010
BUTTON_BACK = 0x000020
BUTTON_LEFT_THUMB = 0x000040
Expand Down Expand Up @@ -129,7 +130,7 @@ class XINPUT_BATTERY_INFORMATION(Structure):
libXInput.XInputGetState.restype = DWORD

def XInputGetState(dwUserIndex, state):
return libXInput.XInputGetState(dwUserIndex, ctypes.byref(state))
return libXInput[100](dwUserIndex, ctypes.byref(state))

libXInput.XInputSetState.argtypes = [DWORD, POINTER(XINPUT_VIBRATION)]
libXInput.XInputSetState.restype = DWORD
Expand Down Expand Up @@ -181,6 +182,7 @@ def XInputGetBatteryInformation(dwUserIndex, devType, batteryInformation):
0x0002 : "DPAD_DOWN",
0x0004 : "DPAD_LEFT",
0x0008 : "DPAD_RIGHT",
0x400 : "GUIDE",
0x0010 : "START",
0x0020 : "BACK",
0x0040 : "LEFT_THUMB",
Expand Down Expand Up @@ -210,9 +212,9 @@ def set_deadzone(dzone, value):
DEADZONE_LEFT_THUMB (default value is 7849, max is 32767)
DEADZONE_TRIGGER (default value is 30, max is 255 )"""
global XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE, XINPUT_GAMEPAD_TRIGGER_THRESHOLD

assert dzone >= 0 and dzone <= 2, "invalid deadzone"

if value == DEADZONE_DEFAULT:
value = 7849 if dzone == DEADZONE_LEFT_THUMB else \
8689 if dzone == DEADZONE_RIGHT_THUMB else \
Expand Down Expand Up @@ -255,7 +257,7 @@ def get_state(user_index):

if res == ERROR_BAD_ARGUMENTS:
raise XInputBadArgumentError("Controller [{}] doesn't exist. IDs range from 0 to 3.".format(user_index))

assert res == 0, "Couldn't get the state of controller [{}]. Is it disconnected?".format(user_index)

return state
Expand All @@ -277,9 +279,9 @@ def set_vibration(user_index, left_speed, right_speed):

if type(right_speed) == float and right_speed <= 1.0:
right_speed = (round(65535 * right_speed, 0))

vibration = XINPUT_VIBRATION()

vibration.wLeftMotorSpeed = int(left_speed)
vibration.wRightMotorSpeed = int(right_speed)

Expand All @@ -295,6 +297,7 @@ def get_button_values(state):
"DPAD_DOWN" : bool(wButtons & 0x0002),
"DPAD_LEFT" : bool(wButtons & 0x0004),
"DPAD_RIGHT" : bool(wButtons & 0x0008),
"GUIDE" : bool(wButtons & 0x0400),
"START" : bool(wButtons & 0x0010),
"BACK" : bool(wButtons & 0x0020),
"LEFT_THUMB" : bool(wButtons & 0x0040),
Expand Down Expand Up @@ -350,7 +353,7 @@ def get_thumb_values(state):
else: # if magL == 0 the stick is centered, there is no direction
normLX = 0
normLY = 0

if magR != 0:
normRX = RX / magR
normRY = RY / magR
Expand Down Expand Up @@ -394,7 +397,7 @@ def __init__(self, user_index, type_):

def __str__(self):
return str(self.__dict__)

def get_events():
"""get_events() -> generator
Returns a generator that yields events for each change that
Expand Down Expand Up @@ -550,13 +553,13 @@ def get_events():
_last_norm_values[3] = out

_last_states = these_states

class EventHandler:
def __init__(self, *controllers, filter = FILTER_NONE):
self.set_controllers(*controllers)

self.filter = filter

def process_button_event(self, event):
raise NotImplementedError("Method not implemented. Must be implemented in the child class")

Expand All @@ -580,7 +583,7 @@ def set_controllers(self, *controllers):
"""Sets the controllers that are processed"""
if not controllers:
raise ValueError("You need to specify at least one controller")

for user_index in controllers:
assert 0 <= user_index <= 3, "controllers must have a user_index between 0 and 3"

Expand All @@ -591,7 +594,7 @@ def remove_controller(self, user_index):
assert 0 <= user_index <= 3, "controllers must have a user_index between 0 and 3"

assert len(self.controllers) >= 2, "you have to keep at least one controller"

try:
self.controllers.remove(user_index)
return True
Expand All @@ -603,14 +606,14 @@ def has_controller(self, user_index):
assert 0 <= user_index <= 3, "controllers must have a user_index between 0 and 3"

return user_index in self.controllers

def set_filter(self, filter_):
"""Applies a new filter mask to this handler.
A filter can be any combination of filters, such as
(BUTTON_A | BUTTON_B) to only get events for buttons A and B or
(FILTER_RELEASED_ONLY | BUTTON_Y) to get an event when Y is released."""
self.filter = filter_

# remove any filter
# the "controller" attribute remove the filter only for the selected controller. By default will remove every filter
def clear_filter(self):
Expand All @@ -623,14 +626,14 @@ def __init__(self, *event_handlers, auto_start=True):
for event_handler in event_handlers:
if (event_handler is None or not issubclass(type(event_handler), EventHandler)):
raise TypeError("The event handler must be a subclass of XInput.EventHandler")

self.handlers = set(event_handlers)

self.lock = Lock()

self.queued_new_handlers = []
self.queued_removed_handlers = []

if auto_start:
self.start()

Expand All @@ -639,21 +642,21 @@ def __tfun(self): # thread function
self.lock.acquire()
for new_handler in self.queued_new_handlers:
self.handlers.add(new_handler)

for removed_handler in self.queued_removed_handlers:
if removed_handler in self.handlers:
self.handlers.remove(removed_handler)
self.queued_new_handlers.clear()
self.queued_removed_handlers.clear()
self.lock.release()

events = get_events()
for event in events: # filtering events
if event.type == EVENT_CONNECTED or event.type == EVENT_DISCONNECTED:
for handler in self.handlers:
if handler.has_controller(event.user_index):
handler.process_connection_event(event)

elif event.type == EVENT_BUTTON_PRESSED or event.type == EVENT_BUTTON_RELEASED:
for handler in self.handlers:
if handler.has_controller(event.user_index):
Expand All @@ -670,9 +673,9 @@ def __tfun(self): # thread function
if handler.has_controller(event.user_index):
if (STICK_LEFT << event.stick) & handler.filter:
handler.process_stick_event(event)
else:
else:
raise ValueError("Event type not recognized")


def start(self): # starts the thread
self.running = True
Expand All @@ -684,7 +687,7 @@ def start(self): # starts the thread
def stop(self): # stops the thread
self.running = False
self.__thread.join()

def add_event_handler(self, event_handler):
if (event_handler is None or not issubclass(type(event_handler), EventHandler)):
raise TypeError("The event handler must be a subclass of XInput.EventHandler")
Expand All @@ -703,4 +706,4 @@ def __del__(self):
if hasattr(self, "__thread"):
self.stop()
#/defining custom classes and methods #

14 changes: 9 additions & 5 deletions XInputTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def __init__(self, center):
self.on_indicator_pos = (self.center[0], self.center[1] - 50)

self.on_indicator = canvas.create_oval(((self.on_indicator_pos[0] - 10, self.on_indicator_pos[1] - 10), (self.on_indicator_pos[0] + 10, self.on_indicator_pos[1] + 10)))

self.r_thumb_pos = (self.center[0] + 50, self.center[1] + 20)

r_thumb_outline = canvas.create_oval(((self.r_thumb_pos[0] - 25, self.r_thumb_pos[1] - 25), (self.r_thumb_pos[0] + 25, self.r_thumb_pos[1] + 25)))
Expand Down Expand Up @@ -112,15 +112,15 @@ def __init__(self, center):
controller = controllers[event.user_index]
if event.type == EVENT_CONNECTED:
canvas.itemconfig(controller.on_indicator, fill="light green")

elif event.type == EVENT_DISCONNECTED:
canvas.itemconfig(controller.on_indicator, fill="")

elif event.type == EVENT_STICK_MOVED:
if event.stick == LEFT:
l_thumb_stick_pos = (int(round(controller.l_thumb_pos[0] + 25 * event.x,0)), int(round(controller.l_thumb_pos[1] - 25 * event.y,0)))
canvas.coords(controller.l_thumb_stick, (l_thumb_stick_pos[0] - 10, l_thumb_stick_pos[1] - 10, l_thumb_stick_pos[0] + 10, l_thumb_stick_pos[1] + 10))

elif event.stick == RIGHT:
r_thumb_stick_pos = (int(round(controller.r_thumb_pos[0] + 25 * event.x,0)), int(round(controller.r_thumb_pos[1] - 25 * event.y,0)))
canvas.coords(controller.r_thumb_stick, (r_thumb_stick_pos[0] - 10, r_thumb_stick_pos[1] - 10, r_thumb_stick_pos[0] + 10, r_thumb_stick_pos[1] + 10))
Expand Down Expand Up @@ -148,6 +148,8 @@ def __init__(self, center):
canvas.itemconfig(controller.back_button, fill="red")
elif event.button == "START":
canvas.itemconfig(controller.start_button, fill="red")
elif event.button == "GUIDE":
canvas.itemconfig(controller.on_indicator, fill="red")

elif event.button == "DPAD_LEFT":
canvas.itemconfig(controller.dpad_left, fill="red")
Expand Down Expand Up @@ -182,6 +184,8 @@ def __init__(self, center):
canvas.itemconfig(controller.back_button, fill="")
elif event.button == "START":
canvas.itemconfig(controller.start_button, fill="")
elif event.button == "GUIDE":
canvas.itemconfig(controller.on_indicator, fill="light green")

elif event.button == "DPAD_LEFT":
canvas.itemconfig(controller.dpad_left, fill="")
Expand All @@ -201,7 +205,7 @@ def __init__(self, center):
elif event.button == "X":
canvas.itemconfig(controller.X_button, fill="")

try:
try:
root.update()
except tk.TclError:
break
16 changes: 10 additions & 6 deletions XInputThreadTest.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
root.title("XInput")
canvas = tk.Canvas(root, width= 600, height = 400, bg="white")
canvas.pack()

set_deadzone(DEADZONE_TRIGGER,10)

class Controller:
Expand All @@ -24,7 +24,7 @@ def __init__(self, center):
self.on_indicator_pos = (self.center[0], self.center[1] - 50)

self.on_indicator = canvas.create_oval(((self.on_indicator_pos[0] - 10, self.on_indicator_pos[1] - 10), (self.on_indicator_pos[0] + 10, self.on_indicator_pos[1] + 10)))

self.r_thumb_pos = (self.center[0] + 50, self.center[1] + 20)

r_thumb_outline = canvas.create_oval(((self.r_thumb_pos[0] - 25, self.r_thumb_pos[1] - 25), (self.r_thumb_pos[0] + 25, self.r_thumb_pos[1] + 25)))
Expand Down Expand Up @@ -135,6 +135,10 @@ def process_button_event(self,event):
canvas.itemconfig(controller.back_button, fill=fill_color)
elif event.button == "START":
canvas.itemconfig(controller.start_button, fill=fill_color)
elif event.button == "GUIDE":
if fill_color == "":
fill_color = "light green"
canvas.itemconfig(controller.on_indicator, fill=fill_color)

elif event.button == "DPAD_LEFT":
canvas.itemconfig(controller.dpad_left, fill=fill_color)
Expand All @@ -153,13 +157,13 @@ def process_button_event(self,event):
canvas.itemconfig(controller.Y_button, fill=fill_color)
elif event.button == "X":
canvas.itemconfig(controller.X_button, fill=fill_color)

def process_stick_event(self, event):
controller = controllers[event.user_index]
if event.stick == LEFT:
l_thumb_stick_pos = (int(round(controller.l_thumb_pos[0] + 25 * event.x,0)), int(round(controller.l_thumb_pos[1] - 25 * event.y,0)))
canvas.coords(controller.l_thumb_stick, (l_thumb_stick_pos[0] - 10, l_thumb_stick_pos[1] - 10, l_thumb_stick_pos[0] + 10, l_thumb_stick_pos[1] + 10))

elif event.stick == RIGHT:
r_thumb_stick_pos = (int(round(controller.r_thumb_pos[0] + 25 * event.x,0)), int(round(controller.r_thumb_pos[1] - 25 * event.y,0)))
canvas.coords(controller.r_thumb_stick, (r_thumb_stick_pos[0] - 10, r_thumb_stick_pos[1] - 10, r_thumb_stick_pos[0] + 10, r_thumb_stick_pos[1] + 10))
Expand Down Expand Up @@ -197,8 +201,8 @@ def process_trigger_event(self, event):

def process_connection_event(self, event):
pass


handler = MyHandler(0, 1, 2, 3) # initialize handler object
thread = GamepadThread(handler) # initialize controller thread

Expand Down