Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ynsta/steamcontroller.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md6
-rwxr-xr-xscripts/sc-desktop.py5
-rwxr-xr-xscripts/sc-mixed.py6
-rwxr-xr-xscripts/sc-xbox.py8
-rw-r--r--src/__init__.py32
-rw-r--r--src/daemon.py4
-rw-r--r--src/events.py18
-rw-r--r--src/uinput.c2
-rw-r--r--src/uinput.py65
9 files changed, 118 insertions, 28 deletions
diff --git a/README.md b/README.md
index 668918a..c0011a3 100644
--- a/README.md
+++ b/README.md
@@ -57,7 +57,7 @@ KERNEL=="uinput", MODE="0660", GROUP="games", OPTIONS+="static_node=uinput"
Other test tools are installed:
- `sc-dump.py` : Dump raw message from the controller.
- `sc-gyro-plot.py` : Plot curves from gyro data (require pyqtgraph and pyside installed).
- - `sc-test-cmsg.py` : Permit to send control message to the contoller. For example:
+ - `sc-test-cmsg.py` : Permit to send control message to the contoller. For example:
`echo 8f07005e 015e01f4 01000000 | sc-test-cmsg.py` will make the controller beep.
- `vdf2json.py` : Convert Steam VDF file to JSON.
- `json2vdf.py` : Convert back JSON to VDF file.
@@ -99,6 +99,10 @@ Other test tools are installed:
- `87153284 03180000 31020008 07000707 00300000 2f010000 00000000 00000000`
+### Stop Controller
+ - `9f046f66 66210000 ...`
+
+
## Control Messages formats
### Haptic feedback format:
diff --git a/scripts/sc-desktop.py b/scripts/sc-desktop.py
index fcbc51c..9eff430 100755
--- a/scripts/sc-desktop.py
+++ b/scripts/sc-desktop.py
@@ -30,6 +30,8 @@ from steamcontroller.uinput import Keys
from steamcontroller.daemon import Daemon
+import gc
+
def evminit():
evm = EventMapper()
evm.setPadMouse(Pos.RIGHT)
@@ -68,6 +70,9 @@ class SCDaemon(Daemon):
evm = evminit()
sc = SteamController(callback=evm.process)
sc.run()
+ del sc
+ del evm
+ gc.collect()
if __name__ == '__main__':
import argparse
diff --git a/scripts/sc-mixed.py b/scripts/sc-mixed.py
index 6cfebce..4a9c3c0 100755
--- a/scripts/sc-mixed.py
+++ b/scripts/sc-mixed.py
@@ -35,6 +35,7 @@ from steamcontroller.uinput import \
Axes
from steamcontroller.daemon import Daemon
+import gc
def set_evm_pad(evm):
evm.setStickAxes(Axes.ABS_X, Axes.ABS_Y)
@@ -87,8 +88,6 @@ def set_evm_desktop(evm):
evm.setButtonAction(SCButtons.LPAD, Keys.BTN_MIDDLE)
evm.setButtonAction(SCButtons.RPAD, Keys.KEY_SPACE)
-
-
pad = True
def toggle_callback(evm, btn, pressed):
global pad
@@ -110,6 +109,9 @@ class SCDaemon(Daemon):
evm = evminit()
sc = SteamController(callback=evm.process)
sc.run()
+ del sc
+ del evm
+ gc.collect()
if __name__ == '__main__':
import argparse
diff --git a/scripts/sc-xbox.py b/scripts/sc-xbox.py
index d25668b..814c8bc 100755
--- a/scripts/sc-xbox.py
+++ b/scripts/sc-xbox.py
@@ -35,6 +35,8 @@ from steamcontroller.uinput import \
Axes
from steamcontroller.daemon import Daemon
+import gc
+
def evminit():
evm = EventMapper()
@@ -67,6 +69,9 @@ class SCDaemon(Daemon):
evm = evminit()
sc = SteamController(callback=evm.process)
sc.run()
+ del sc
+ del evm
+ gc.collect()
if __name__ == '__main__':
import argparse
@@ -92,7 +97,8 @@ if __name__ == '__main__':
evm = evminit()
sc = SteamController(callback=evm.process)
sc.run()
+
except KeyboardInterrupt:
- return
+ pass
_main()
diff --git a/src/__init__.py b/src/__init__.py
index eebb073..1bf111e 100644
--- a/src/__init__.py
+++ b/src/__init__.py
@@ -24,7 +24,8 @@ import struct
from enum import IntEnum
from threading import Timer
-import time
+from time import time
+
VENDOR_ID = 0x28de
PRODUCT_ID = [0x1102, 0x1142, 0x1142, 0x1142, 0x1142]
@@ -65,6 +66,10 @@ STEAM_CONTROLER_FORMAT = [
_FORMATS, _NAMES = zip(*STEAM_CONTROLER_FORMAT)
+EXITCMD = struct.pack('>' + 'I' * 2,
+ 0x9f046f66,
+ 0x66210000)
+
SteamControllerInput = namedtuple('SteamControllerInput', ' '.join([x for x in _NAMES if not x.startswith('ukn_')]))
SCI_NULL = SteamControllerInput._make(struct.unpack('<' + ''.join(_FORMATS), b'\x00' * 64))
@@ -159,6 +164,7 @@ class SteamController(object):
setting.getProtocol() == 0 and
number == i+1):
self._handle.claimInterface(number)
+ self._number = number
claimed = True
except usb1.USBErrorBusy:
claimed = False
@@ -188,7 +194,7 @@ class SteamController(object):
self._timer = None
self._tup = None
- self._lastusb = time.time()
+ self._lastusb = time()
# Disable Haptic auto feedback
@@ -205,10 +211,16 @@ class SteamController(object):
0x2f010000))
self._ctx.handleEvents()
-
- def __del__(self):
+ def _close(self):
if self._handle:
+ self._sendControl(EXITCMD)
+ self._handle.releaseInterface(self._number)
+ self._handle.resetDevice()
self._handle.close()
+ self._handle = None
+
+ def __del__(self):
+ self._close()
def _sendControl(self, data, timeout=0):
@@ -221,6 +233,9 @@ class SteamController(object):
data=data + zeros,
timeout=timeout)
+ def addExit(self):
+ self._cmsg.insert(0, EXITCMD)
+
def addFeedback(self, position, amplitude=128, period=0, count=1):
"""
Add haptic feedback to be send on next usb tick
@@ -252,20 +267,18 @@ class SteamController(object):
if self._tup is None:
return
- self._lastusb = time.time()
+ self._lastusb = time()
if isinstance(self._cb_args, (list, tuple)):
self._cb(self, self._tup, *self._cb_args)
else:
self._cb(self, self._tup)
-
-
self._period = HPERIOD
def _callbackTimer(self):
- d = time.time() - self._lastusb
+ d = time() - self._lastusb
self._timer.cancel()
if d > DURATION:
@@ -295,7 +308,8 @@ class SteamController(object):
if len(self._cmsg) > 0:
cmsg = self._cmsg.pop()
self._sendControl(cmsg)
-
+ if cmsg == EXITCMD:
+ break
except usb1.USBErrorInterrupted:
pass
diff --git a/src/daemon.py b/src/daemon.py
index 029f360..964c765 100644
--- a/src/daemon.py
+++ b/src/daemon.py
@@ -10,6 +10,8 @@ import atexit
import signal
import syslog
import psutil
+import traceback
+import gc
class Daemon(object):
"""A generic daemon class.
@@ -96,6 +98,8 @@ class Daemon(object):
self.run()
except Exception as e: # pylint: disable=W0703
syslog.syslog(syslog.LOG_ERR, '{}: {!s}'.format(os.path.basename(sys.argv[0]), e))
+ syslog.syslog(syslog.LOG_ERR, traceback.format_exc())
+ gc.collect()
else:
syslog.syslog(syslog.LOG_INFO, '{}: steam client is runing'.format(os.path.basename(sys.argv[0])))
time.sleep(2)
diff --git a/src/events.py b/src/events.py
index bca43b3..ecfdb89 100644
--- a/src/events.py
+++ b/src/events.py
@@ -27,6 +27,7 @@ events
"""
+from time import time
from math import sqrt
from enum import IntEnum
@@ -40,6 +41,8 @@ import steamcontroller.uinput as sui
from collections import deque
+EXIT_PRESS_DURATION = 2.0
+
class Pos(IntEnum):
"""Specify witch pad or trig is used"""
RIGHT = 0
@@ -118,7 +121,13 @@ class EventMapper(object):
self._trig_axes_callbacks = [None, None]
self._moved = [0, 0]
+ self._steam_pressed_time = 0.0
+ def __del__(self):
+ if hasattr(self, '_uip') and self._uip:
+ for u in self._uip:
+ del u
+ self._uip = []
def process(self, sc, sci):
"""
@@ -188,17 +197,26 @@ class EventMapper(object):
else:
return False
+ # Manage long steam press to exit
+ if btn_add & SCButtons.STEAM == SCButtons.STEAM:
+ self._steam_pressed_time = time()
+ if (sci.buttons & SCButtons.STEAM == SCButtons.STEAM and
+ time() - self._steam_pressed_time > EXIT_PRESS_DURATION):
+ sc.addExit()
+
# Manage buttons
for btn, (mode, ev) in self._btn_map.items():
if mode is None:
continue
+
if btn & btn_add:
if mode is Modes.CALLBACK:
ev(self, btn, True)
else:
_keypressed(mode, ev)
elif btn & btn_rem:
+
if mode is Modes.CALLBACK:
ev(self, btn, False)
else:
diff --git a/src/uinput.c b/src/uinput.c
index a2d5e33..e28110a 100644
--- a/src/uinput.c
+++ b/src/uinput.c
@@ -208,6 +208,6 @@ void uinput_syn(int fd)
void uinput_destroy(int fd)
{
- ioctl(fd, UI_DEV_DESTROY);
+ ioctl(fd, UI_DEV_DESTROY, 0);
close(fd);
}
diff --git a/src/uinput.py b/src/uinput.py
index 537af2b..8ab8d25 100644
--- a/src/uinput.py
+++ b/src/uinput.py
@@ -22,8 +22,10 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
+
import os
import ctypes
+import _ctypes
import time
from math import pi, copysign, sqrt
from enum import IntEnum
@@ -191,6 +193,13 @@ class UInput(object):
self._a, self._amin, self._amax, self._afuzz, self._aflat = zip(*axes)
self._r = rels
+ self.vendor = vendor
+ self.product = product
+ self.name = name
+ self.keyboard = keyboard
+ self._fd = None
+
+ def createDevice(self):
possible_paths = []
for extension in get_so_extensions():
possible_paths.append(
@@ -214,6 +223,7 @@ class UInput(object):
'\n'.join(possible_paths)
)
)
+
self._lib = ctypes.CDLL(lib)
c_k = (ctypes.c_uint16 * len(self._k))(*self._k)
@@ -223,11 +233,11 @@ class UInput(object):
c_afuzz = (ctypes.c_int32 * len(self._afuzz))(*self._afuzz)
c_aflat = (ctypes.c_int32 * len(self._aflat))(*self._aflat)
c_r = (ctypes.c_uint16 * len(self._r))(*self._r)
- c_vendor = ctypes.c_uint16(vendor)
- c_product = ctypes.c_uint16(product)
- c_keyboard = ctypes.c_int(keyboard)
+ c_vendor = ctypes.c_uint16(self.vendor)
+ c_product = ctypes.c_uint16(self.product)
+ c_keyboard = ctypes.c_int(self.keyboard)
- c_name = ctypes.c_char_p(name)
+ c_name = ctypes.c_char_p(self.name)
self._fd = self._lib.uinput_init(ctypes.c_int(len(self._k)),
c_k,
ctypes.c_int(len(self._a)),
@@ -251,6 +261,10 @@ class UInput(object):
@param int axis key or btn event (KEY_* or BTN_*)
@param int val event value
"""
+
+ if self._fd == None:
+ self.createDevice()
+
self._lib.uinput_key(self._fd,
ctypes.c_uint16(key),
ctypes.c_int32(val))
@@ -263,6 +277,10 @@ class UInput(object):
@param int axis abs event (ABS_*)
@param int val event value
"""
+
+ if self._fd == None:
+ self.createDevice()
+
self._lib.uinput_abs(self._fd,
ctypes.c_uint16(axis),
ctypes.c_int32(val))
@@ -274,6 +292,10 @@ class UInput(object):
@param int rel rel event (REL_*)
@param int val event value
"""
+
+ if self._fd == None:
+ self.createDevice()
+
self._lib.uinput_rel(self._fd,
ctypes.c_uint16(rel),
ctypes.c_int32(val))
@@ -284,6 +306,10 @@ class UInput(object):
@param int val scan event value (scancode)
"""
+
+ if self._fd == None:
+ self.createDevice()
+
self._lib.uinput_scan(self._fd,
ctypes.c_int32(val))
@@ -291,6 +317,10 @@ class UInput(object):
"""
Generate a syn event
"""
+
+ if self._fd == None:
+ self.createDevice()
+
self._lib.uinput_syn(self._fd)
@@ -302,6 +332,9 @@ class UInput(object):
@param int period period is ms
"""
+ if self._fd == None:
+ self.createDevice()
+
self._lib.uinput_set_delay_period(self._fd,
ctypes.c_int32(delay),
ctypes.c_int32(period))
@@ -317,8 +350,12 @@ class UInput(object):
def __del__(self):
- if self._lib:
+
+ if self._lib and self._fd:
self._lib.uinput_destroy(self._fd)
+ self._fd = None
+ _ctypes.dlclose(self._lib._handle)
+ self._lib = None
class Gamepad(UInput):
@@ -430,9 +467,9 @@ class Mouse(UInput):
self._radscale = (degree * pi / 180) / ampli
self._mass = mass
self._friction = friction
- self._r = r
- self._I = (2 * self._mass * self._r**2) / 5.0
- self._a = self._r * self._friction / self._I
+ self._rad = r
+ self._I = (2 * self._mass * self._rad**2) / 5.0
+ self._acc = self._rad * self._friction / self._I
self._xvel_dq = deque(maxlen=mean_len)
self._yvel_dq = deque(maxlen=mean_len)
@@ -465,8 +502,8 @@ class Mouse(UInput):
self._scr_mass = mass
self._scr_friction = friction
self._scr_r = r
- self._scr_I = (2 * self._mass * self._r**2) / 5.0
- self._scr_a = self._r * self._friction / self._I
+ self._scr_I = (2 * self._mass * self._rad**2) / 5.0
+ self._scr_a = self._rad * self._friction / self._I
self._scr_xvel_dq = deque(maxlen=mean_len)
self._scr_yvel_dq = deque(maxlen=mean_len)
@@ -528,11 +565,11 @@ class Mouse(UInput):
_hyp = sqrt((self._xvel**2) + (self._yvel**2))
if _hyp != 0.0:
- _ax = self._a * (abs(self._xvel) / _hyp)
- _ay = self._a * (abs(self._yvel) / _hyp)
+ _ax = self._acc * (abs(self._xvel) / _hyp)
+ _ay = self._acc * (abs(self._yvel) / _hyp)
else:
- _ax = self._a
- _ay = self._a
+ _ax = self._acc
+ _ay = self._acc
# Cap friction desceleration
_dvx = min(abs(self._xvel), _ax * dt)