Skip to content

Commit

Permalink
Made it easier to trace what the saitek driver is doing
Browse files Browse the repository at this point in the history
  • Loading branch information
kfsone committed Aug 20, 2014
1 parent 9241a72 commit 7c4967f
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 39 deletions.
66 changes: 55 additions & 11 deletions saitek/DirectOutput.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,18 @@
SOFTBUTTON_UP = 0x00000002
SOFTBUTTON_DOWN = 0x00000004


class DirectOutput():
class TaggedSpan(object):
indentLevel = ""
def __init__(self, name, enabled):
self.name = name
self.enabled = enabled
if self.enabled: print("%s-> %s" % (TaggedSpan.indentLevel, self.name))
TaggedSpan.indentLevel += '--'
def __del__(self, *args, **kwargs):
TaggedSpan.indentLevel = TaggedSpan.indentLevel[:-2]
if self.enabled: print("%s<- %s" % (TaggedSpan.indentLevel, self.name))

class DirectOutput(object):

def __init__(self, dll_path):
"""
Expand Down Expand Up @@ -244,9 +254,9 @@ def SetString(self, device_handle, page, line, string):
return self.DirectOutputDLL.DirectOutput_SetString(device_handle, page, line, len(string), ctypes.wintypes.LPWSTR(string))


class DirectOutputDevice():
class DirectOutputDevice(object):

class Buttons():
class Buttons(object):

select, up, down = False, False, False

Expand Down Expand Up @@ -277,62 +287,86 @@ def __repr__(self):

application_name = "GenericDevice"
device_handle = None
direct_output = None
debug_level = 0

def __init__(self, dll_path="C:\\Program Files (x86)\\Saitek\\DirectOutput\\DirectOutput.dll", debug_level=0):
def __init__(self, dll_path="C:\\Program Files (x86)\\Saitek\\DirectOutput\\DirectOutput.dll", debug_level=0, name=None):

"""
Initialises device, creates internal state (device_handle) and registers callbacks.
"""

self.application_name = name or DirectOutputDevice.application_name

self.debug_level = debug_level
outerSpan = TaggedSpan("Initializing SaitekX52Pro", debug_level >= 1)

try:
innerSpan = TaggedSpan("Creating DirectOutput instance", debug_level >= 2)
self.direct_output = DirectOutput(dll_path)
except WindowsError as e:
raise DLLError(e.winerror)

innerSpan = TaggedSpan("Initializing DirectOutput(%s)" % self.application_name, debug_level >= 2)
result = self.direct_output.Initialize(self.application_name)
if result != S_OK:
raise DirectOutputError(result)
del innerSpan

innerSpan = TaggedSpan("Creating callback closures", debug_level >= 2)
self.device_callback_instance = self._DeviceCallbackClosure()
self.enumerate_callback_instance = self._EnumerateCallbackClosure()
self.register_page_callback_instance = self._RegisterPageCallbackClosure()
self.register_soft_button_callback_instance = self._RegisterSoftButtonCallbackClosure()
del innerSpan

innerSpan = TaggedSpan("Registering device callback", debug_level >= 2)
result = self.direct_output.RegisterDeviceCallback(self.device_callback_instance)
if result != S_OK:
self.finish()
raise DirectOutputError(result)
del innerSpan

innerSpan = TaggedSpan("Enumerating devices", debug_level >= 2)
result = self.direct_output.Enumerate(self.enumerate_callback_instance)
if result != S_OK:
self.finish()
raise DirectOutputError(result)
del innerSpan

if not self.device_handle:
self.finish()
raise DirectOutputError(result)

innerSpan = TaggedSpan("Registering page callback", debug_level >= 2)
result = self.direct_output.RegisterPageCallback(self.device_handle, self.register_page_callback_instance)
if result != S_OK:
self.finish()
raise DirectOutputError(result)
del innerSpan

innerSpan = TaggedSpan("Registering soft button callback", debug_level >= 2)
result = self.direct_output.RegisterSoftButtonCallback(self.device_handle, self.register_soft_button_callback_instance)
if result != S_OK:
self.finish()
raise DirectOutputError(result)
del innerSpan

self.DebugLevel = debug_level
def __del__(self, *args, **kwargs):
span = TaggedSpan("~DirectOutputDevice", self.debug_level >= 2)
self.finish()

def debug(self, level, *args, **kwargs):
if self.DebugLevel >= level:
if self.debug_level >= level:
print(*args, **kwargs)

def finish(self):
"""
De-initializes DLL. Must be called before program exit
"""
span = TaggedSpan("DirectOutputDevice.finish()", self.debug_level >= 2)
if self.direct_output:
self.direct_output.Deinitialize()
self.direct_output = None
Expand All @@ -346,6 +380,7 @@ def _DeviceCallbackClosure(self):
"""
DeviceCallback_Proto = ctypes.WINFUNCTYPE(None, ctypes.c_void_p, ctypes.c_bool, ctypes.c_void_p)
def func(hDevice, bAdded, pvContext):
span = TaggedSpan("devicecallback closure", debug_level >= 2)
self._DeviceCallback(hDevice, bAdded, pvContext)
return DeviceCallback_Proto(func)

Expand All @@ -358,6 +393,7 @@ def _EnumerateCallbackClosure(self):
"""
EnumerateCallback_Proto = ctypes.WINFUNCTYPE(None, ctypes.c_void_p, ctypes.c_void_p)
def func(hDevice, pvContext):
span = TaggedSpan("enumeratecallback closure", self.debug_level >= 2)
self._EnumerateCallback(hDevice, pvContext)
return EnumerateCallback_Proto(func)

Expand All @@ -370,6 +406,7 @@ def _RegisterPageCallbackClosure(self):
"""
PageCallback_Proto = ctypes.WINFUNCTYPE(None, ctypes.c_void_p, ctypes.wintypes.DWORD, ctypes.c_bool, ctypes.c_void_p)
def func(hDevice, dwPage, bActivated, pvContext):
span = TaggedSpan("registerpage closure", self.debug_level >= 2)
self._RegisterPageCallback(hDevice, dwPage, bActivated, pvContext)
return PageCallback_Proto(func)

Expand All @@ -382,6 +419,7 @@ def _RegisterSoftButtonCallbackClosure(self):
"""
SoftButtonCallback_Proto = ctypes.WINFUNCTYPE(None, ctypes.c_void_p, ctypes.wintypes.DWORD, ctypes.c_void_p)
def func(hDevice, dwButtons, pvContext):
span = TaggedSpan("softbutton closure", self.debug_level >= 2)
self._RegisterSoftButtonCallback(hDevice, dwButtons, pvContext)
return SoftButtonCallback_Proto(func)

Expand All @@ -390,6 +428,7 @@ def _DeviceCallback(self, hDevice, bAdded, pvContext):
Internal function to register device handle
"""
span = TaggedSpan("_devicecallback", self.debug_level >= 2)
if not bAdded:
raise NotImplementedError("Received a message that a device went away.")
if self.device_handle and self.device_handle != hDevice:
Expand All @@ -401,20 +440,23 @@ def _EnumerateCallback(self, hDevice, pvContext):
Internal function to process a device enumeration
"""
span = TaggedSpan("_enumeratecallback", self.debug_level >= 2)
self._DeviceCallback(hDevice, True, pvContext)

def _RegisterPageCallback(self, hDevice, dwPage, bActivated, pvContext):
"""
Method called when page changes. Calls self.RegisterPageCallback to hide hDevice and pvContext from end-user
"""
span = TaggedSpan("_registerpagecallback", self.debug_level >= 2)
self.RegisterPageCallback(dwPage, bActivated)

def _RegisterSoftButtonCallback(self, hDevice, dwButtons, pvContext):
"""
Method called when soft button changes. Calls self.RegisterSoftButtonCallback to hide hDevice and pvContext from end-user. Also hides change of page softbutton and press-up.
"""
span = TaggedSpan("_registersoftbuttoncallback", self.debug_level >= 2)
self.RegisterSoftButtonCallback(self.Buttons(dwButtons))

def RegisterPageCallback(self, page, activated):
Expand All @@ -426,7 +468,7 @@ def RegisterPageCallback(self, page, activated):
activated -- true if this page has become the active page, false if this page was the active page
"""
self.debug(1, "Base RegisterPageCallback called")
span = TaggedSpan("registerpagecallback", self.debug_level >= 2)

def RegisterSoftButtonCallback(self, buttons):
"""
Expand All @@ -436,7 +478,7 @@ def RegisterSoftButtonCallback(self, buttons):
buttons - Buttons object representing button state
"""
self.debug(1, "Base RegisterSoftButtonCallback called")
span = TaggedSpan("registersoftbuttoncallback", self.debug_level >= 2)

def AddPage(self, page, name, active):
"""
Expand All @@ -448,7 +490,7 @@ def AddPage(self, page, name, active):
active -- True if page is to become the active page, if False this will not change the active page
"""
self.debug(1, "*AddPage(%s, %s, %s)" % (str(page), str(name), str(active)))
span = TaggedSpan("AddPage(%s, %s, %s)" % (str(page), str(name), str(active)), self.debug_level >= 1)
self.direct_output.AddPage(self.device_handle, page, name, active)

def RemovePage(self, page):
Expand All @@ -459,6 +501,7 @@ def RemovePage(self, page):
page -- page ID to remove
"""
span = TaggedSpan("RemovePage(%s)" % (str(page)), self.debug_level >= 1)
result = self.direct_output.RemovePage(self.device_handle, page)
if result != S_OK:
self.finish()
Expand All @@ -473,7 +516,7 @@ def SetString(self, page, line, string):
line -- the line to display the string on (0 = top, 1 = middle, 2 = bottom)
string -- the string to display
"""
self.debug(1, "*SetString(%s, %s, %s)" % (str(page), str(line), str(string)))
span = TaggedSpan("SetString(%s, %s, %s)" % (str(page), str(line), str(string)), self.debug_level >= 1)
result = self.direct_output.SetString(self.device_handle, page, line, string)
if result != S_OK:
self.finish()
Expand All @@ -489,6 +532,7 @@ def SetLed(self, page, led, value):
value -- value to set LED (1 = on, 0 = off)
"""
span = TaggedSpan("SetLed(%s, %s, %s)" % (str(page), str(led), str(value)), self.debug_level >= 1)
result = self.direct_output.SetLed(self.device_handle, page, led, value)
if result != S_OK:
self.finish()
Expand Down
56 changes: 28 additions & 28 deletions saitek/X52Pro.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@
* Error handling and exceptions
"""

from .DirectOutput import DirectOutputDevice
from . DirectOutput import DirectOutputDevice, TaggedSpan


class SaitekX52Pro(DirectOutputDevice):
class Page():
class Page(object):
_lines = [ str(), str(), str() ]
_leds = dict()

Expand All @@ -28,7 +28,7 @@ def __init__(self, device, page_id, name, active):
self.device.AddPage(self.page_id, name, 1 if active else 0)
self.active = active

def __del__(self):
def __del__(self, *args, **kwargs):
try:
self.device.RemovePage(self.page_id)
except AttributeError:
Expand All @@ -54,57 +54,57 @@ def refresh(self):
for led, value in self._leds:
self.device.SetLed(self.page_id, led, 1 if value else 0)

def _set_led(self, led, value):
def set_led(self, led, value):
self._leds[led] = value
if self.active:
self.device.SetLed(self.page_id, led, 1 if value else 0)

def _set_led_colour(self, value, led_red, led_green):
def set_led_colour(self, value, led_red, led_green):
if value == "red":
self._set_led(led_red, 1)
self._set_led(led_green, 0)
self.set_led(led_red, 1)
self.set_led(led_green, 0)
elif value == "green":
self._set_led(led_red, 0)
self._set_led(led_green, 1)
self.set_led(led_red, 0)
self.set_led(led_green, 1)
elif value == "orange":
self._set_led(led_red, 1)
self._set_led(led_green, 1)
self.set_led(led_red, 1)
self.set_led(led_green, 1)
elif value == "off":
self._set_led(led_red, 0)
self._set_led(led_green, 0)
self.set_led(led_red, 0)
self.set_led(led_green, 0)

def fire(self, value):
self._set_led(0, value)
self.set_led(0, value)

def fire_a(self, value):
self._set_led_colour(value, 1, 2)
self.set_led_colour(value, 1, 2)

def fire_b(self, value):
self._set_led_colour(value, 3, 4)
self.set_led_colour(value, 3, 4)

def fire_d(self, value):
self._set_led_colour(value, 5, 6)
self.set_led_colour(value, 5, 6)

def fire_e(self, value):
self._set_led_colour(value, 7, 8)
self.set_led_colour(value, 7, 8)

def toggle_1_2(self, value):
self._set_led_colour(value, 9, 10)
self.set_led_colour(value, 9, 10)

def toggle_3_4(self, value):
self._set_led_colour(value, 11, 12)
self.set_led_colour(value, 11, 12)

def toggle_5_6(self, value):
self._set_led_colour(value, 13, 14)
self.set_led_colour(value, 13, 14)

def pov_2(self, value):
self._set_led_colour(value, 15, 16)
self.set_led_colour(value, 15, 16)

def clutch(self, value):
self._set_led_colour(value, 17, 18)
self.set_led_colour(value, 17, 18)

def throttle_axis(self, value):
self._set_led(19, value)
self.set_led(19, value)

pages = {}
_page_counter = 0
Expand All @@ -118,7 +118,7 @@ def remove_page(self, name):
del self.pages[name]

def RegisterPageCallback(self, page_id, activated):
self.debug(1, "Page Callback:", page_id, activated)
span = TaggedSpan("RegisterPageCallback(%s, %s)" % (str(page_id), str(activated)), self.debug_level >= 2)
for page in self.pages.values():
if page.page_id == page_id:
print("Found the page", page_id, activated)
Expand All @@ -128,15 +128,15 @@ def RegisterPageCallback(self, page_id, activated):
page.active = False
return

def RegisterSoftButtonCallback(self, buttons):
self.debug(1, "soft button callback", buttons)
def RegisterSoftButtonCallback(self, *args, **kwargs):
span = TaggedSpan("RegisterSoftButtonCallback()")

def finish(self):
span = TaggedSpan("SaitekX52Pro.finish()", self.debug_level >= 2)
for page in self.pages:
del page
super().finish()


if __name__ == '__main__':
x52 = SaitekX52Pro(debug_level=1)
print("X52 Connected")
Expand Down

0 comments on commit 7c4967f

Please sign in to comment.