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:
authorStany MARCEL <stanypub@gmail.com>2016-12-02 14:04:30 +0300
committerStany MARCEL <stanypub@gmail.com>2016-12-02 14:04:30 +0300
commit0c439095333dfed5aa36e594ef6840008128b36f (patch)
tree82ce7dba68d8a987b595327be73ee49059c5fbe9
parent82e84c5cd1773f3236f09cd1a22c7ab363b72008 (diff)
Close issue #47 close uinput and turn off controller
A long press (>2s) to the steam button close and deleate the uinput device The device is created on first event to prevent it to be created at daemon restart When the daeomon is stopped the controller should also be powered off Signed-off-by: Stany MARCEL <stanypub@gmail.com>
-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)