Skip to content

Instantly share code, notes, and snippets.

@Ryochan7
Last active July 7, 2020 22:59
Show Gist options
  • Save Ryochan7/316a644935f33e4f51ee19058c79777c to your computer and use it in GitHub Desktop.
Save Ryochan7/316a644935f33e4f51ee19058c79777c to your computer and use it in GitHub Desktop.
Some action changes for sc-controller. Based on Python 3 fork of 0.4.7
diff --git a/glade/app.glade b/glade/app.glade
index 4cd57dc6..db6cb750 100644
--- a/glade/app.glade
+++ b/glade/app.glade
@@ -565,7 +565,6 @@
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="orientation">vertical</property>
- <signal name="drag-data-received" handler="on_drag_data_received" swapped="no"/>
<child>
<object class="GtkBox" id="vbSwitchers">
<property name="visible">True</property>
diff --git a/scc/actions.py b/scc/actions.py
index d52da6ef..b43326b6 100644
--- a/scc/actions.py
+++ b/scc/actions.py
@@ -24,6 +24,7 @@ from scc.aliases import ALL_BUTTONS as GAMEPAD_BUTTONS
from math import sqrt, sin, cos, atan2, pi as PI
import sys, time, logging, inspect
+from collections import deque
log = logging.getLogger("Actions")
# Default delay after action, if used in macro. May be overriden using sleep() action.
@@ -896,7 +897,7 @@ class MouseAction(WholeHapticAction, Action):
dx, dy = dx * self.speed[0], dy * self.speed[1]
if self._mouse_axis is None:
- mapper.mouse.moveEvent(dx, dy)
+ mapper.mouse.moveEvent(dx, dy, mapper.time_elapsed)
elif self._mouse_axis == Rels.REL_X:
mapper.mouse_move(dx, 0)
elif self._mouse_axis == Rels.REL_Y:
@@ -1003,7 +1004,7 @@ class MouseAbsAction(Action):
def whole(self, mapper, x, y, what):
dx = x * self.speed[0] * MouseAbsAction.MOUSE_FACTOR
dy = y * self.speed[0] * MouseAbsAction.MOUSE_FACTOR
- mapper.mouse.moveEvent(dx, dy)
+ mapper.mouse.moveEvent(dx, dy, mapper.time_elapsed)
class AreaAction(Action, SpecialAction, OSDEnabledAction):
@@ -1904,7 +1905,24 @@ class DPadAction(MultichildAction, HapticEnabledAction):
( 3, 1 ), # Index 7, down-right
( None, 1 ), # Index 8, same as 0
)
-
+
+
+ DIAG_RANGE = 60.0
+ CARD_RANGE = 90.0 - DIAG_RANGE
+ SLICE_ANGLES = []
+ SLICE_ANGLES.append((360.0 - (CARD_RANGE / 2.0), (CARD_RANGE / 2.0),)) # Up
+ SLICE_ANGLES.append((SLICE_ANGLES[0][1], SLICE_ANGLES[0][1] + DIAG_RANGE,)) # Up-Right
+ SLICE_ANGLES.append((SLICE_ANGLES[1][1], SLICE_ANGLES[1][1] + CARD_RANGE,)) # Right
+ SLICE_ANGLES.append((SLICE_ANGLES[2][1], SLICE_ANGLES[2][1] + DIAG_RANGE,)) # Down-Right
+ SLICE_ANGLES.append((SLICE_ANGLES[3][1], SLICE_ANGLES[3][1] + CARD_RANGE,)) # Down
+ SLICE_ANGLES.append((SLICE_ANGLES[4][1], SLICE_ANGLES[4][1] + DIAG_RANGE,)) # Down-Left
+ SLICE_ANGLES.append((SLICE_ANGLES[5][1], SLICE_ANGLES[5][1] + CARD_RANGE,)) # Left
+ SLICE_ANGLES.append((SLICE_ANGLES[6][1], SLICE_ANGLES[6][1] + DIAG_RANGE,)) # Up-Left
+ print(SLICE_ANGLES)
+ DEBUG = False
+ DEQUELEN = 10
+ WEIGHT_MODIFIER = 0.7125
+
def __init__(self, *actions):
MultichildAction.__init__(self, *actions)
HapticEnabledAction.__init__(self)
@@ -1915,6 +1933,8 @@ class DPadAction(MultichildAction, HapticEnabledAction):
self.actions = self._ensure_size(actions)
self.dpad_state = [ None, None ] # X, Y
self.side_before = None
+ self._mouse_dq = [ deque(maxlen=self.DEQUELEN), deque(maxlen=self.DEQUELEN), deque(maxlen=8), deque(maxlen=8) ] # x, y, wheel, hwheel
+ self._smoothed_pos = [0, 0, 0, 0]
# Generate mapping of angle range -> index
self.ranges = []
normal_range = 90 - self.diagonal_rage
@@ -1964,30 +1984,58 @@ class DPadAction(MultichildAction, HapticEnabledAction):
return "DPad"
- def compute_side(self, x, y):
+ #def compute_side(self, x, y):
+ def compute_side(self, mapper, x, y, what):
""" Computes which sides of dpad are supposed to be active """
## dpad(up, down, left, right)
## dpad8(up, down, left, right, upleft, upright, downleft, downright)
side = self.SIDE_NONE
if x*x + y*y > self.MIN_DISTANCE_P2:
+ tempangle = atan2(x, y)
+ tempangle = (tempangle if tempangle >= 0 else (2 * PI + tempangle)) * 180 / PI;
+ #print("JKLSDKLJDKJ {}".format(tempangle))
+ index = 4
+ if tempangle >= self.SLICE_ANGLES[0][0] or tempangle <= self.SLICE_ANGLES[0][1]:
+ index = 4 # Up
+ elif tempangle > self.SLICE_ANGLES[1][0] and tempangle <= self.SLICE_ANGLES[1][1]:
+ index = 5 # Up-Right
+ elif tempangle > self.SLICE_ANGLES[2][0] and tempangle <= self.SLICE_ANGLES[2][1]:
+ index = 6 # Right
+ elif tempangle > self.SLICE_ANGLES[3][0] and tempangle <= self.SLICE_ANGLES[3][1]:
+ index = 7 # Down-Right
+ elif tempangle > self.SLICE_ANGLES[4][0] and tempangle <= self.SLICE_ANGLES[4][1]:
+ index = 0 # Down
+ elif tempangle > self.SLICE_ANGLES[5][0] and tempangle <= self.SLICE_ANGLES[5][1]:
+ index = 1 # Down-Left
+ elif tempangle > self.SLICE_ANGLES[6][0] and tempangle <= self.SLICE_ANGLES[6][1]:
+ index = 2 # Left
+ elif tempangle > self.SLICE_ANGLES[7][0] and tempangle <= self.SLICE_ANGLES[7][1]:
+ index = 3 # Up-Left
+
# Compute angle from center of pad to finger position
- angle = (atan2(x, y) * 180.0 / PI) + 180
+ #angle = (atan2(x, y) * 180.0 / PI) + 180
# Translate it to index
- index = 0
- for a1, a2, i in self.ranges:
- if angle >= a1 and angle < a2:
- index = i
- break
+ #index = 0
+ #for a1, a2, i in self.ranges:
+ # if angle >= a1 and angle < a2:
+ # index = i
+ # break
+
side = self.SIDES[index]
return side
def whole(self, mapper, x, y, what):
+ if not mapper.is_touched(what):
+ self._mouse_dq_clear(0)
+ self._mouse_dq_clear(1)
+
if self.haptic:
# Called like this just so there is not same code on two places
side = self.whole_blocked(mapper, x, y, what)
else:
- side = self.compute_side(x, y)
+ #side = self.compute_side(x, y)
+ side = self.compute_side(mapper, x, y, what)
for i in (0, 1):
if side[i] != self.dpad_state[i] and self.dpad_state[i] is not None:
@@ -2002,7 +2050,8 @@ class DPadAction(MultichildAction, HapticEnabledAction):
def whole_blocked(self, mapper, x, y, what):
if self.haptic:
- side = self.compute_side(x, y)
+ #side = self.compute_side(x, y)
+ side = self.compute_side(x, y, mapper, what)
if self.side_before != side:
self.side_before = side
mapper.send_feedback(self.haptic)
@@ -2013,6 +2062,34 @@ class DPadAction(MultichildAction, HapticEnabledAction):
def change(self, mapper, dx, dy, what):
self.whole(mapper, dx, -dy, what)
+ def _mouse_dq_clear(self, axis):
+ """ Used by trackpad, trackball and mouse wheel emulation """
+ self._mouse_dq[axis].clear()
+
+ def _mouse_dq_add(self, axis, position):
+ #print(self.__class__.DEQUELEN)
+ if len(self._mouse_dq[axis]) == 0:
+ # Mark new position as zero point and populate queue
+ for i in range(self.__class__.DEQUELEN):
+ self._mouse_dq[axis].appendleft(position)
+ else:
+ self._mouse_dq[axis].appendleft(position)
+
+ smoothed_pos = 0
+ if len(self._mouse_dq[axis]) != 0:
+ currentSum = 0.0
+ currentWeight = 1.0
+ finalWeight = 0.0
+ weightModifier = self.__class__.WEIGHT_MODIFIER
+ for entry in self._mouse_dq[axis]:
+ currentSum += entry * currentWeight
+ finalWeight += currentWeight
+ currentWeight *= weightModifier
+
+ smoothed_pos = int(currentSum / finalWeight)
+
+ self._smoothed_pos[axis] = smoothed_pos
+
class DPad8Action(DPadAction):
COMMAND = "dpad8"
diff --git a/scc/gui/app.py b/scc/gui/app.py
index d999ead6..796921d0 100644
--- a/scc/gui/app.py
+++ b/scc/gui/app.py
@@ -110,11 +110,11 @@ class App(Gtk.Application, UserDataManager, BindingEditor):
ps.connect('save-clicked', self.on_save_clicked)
# Drag&drop target
- self.builder.get_object("content").drag_dest_set(Gtk.DestDefaults.ALL, [
- Gtk.TargetEntry.new("text/uri-list", Gtk.TargetFlags.OTHER_APP, 0),
- Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.OTHER_APP, 0)
- ], Gdk.DragAction.COPY
- )
+ #self.builder.get_object("content").drag_dest_set(Gtk.DestDefaults.ALL, [
+ # Gtk.TargetEntry.new("text/uri-list", Gtk.TargetFlags.OTHER_APP, 0),
+ # Gtk.TargetEntry.new("text/plain", Gtk.TargetFlags.OTHER_APP, 0)
+ # ], Gdk.DragAction.COPY
+ #)
# 'C' and 'CPAD' buttons
vbc = self.builder.get_object("vbC")
diff --git a/scc/mapper.py b/scc/mapper.py
index 577a5347..e9db9e14 100644
--- a/scc/mapper.py
+++ b/scc/mapper.py
@@ -58,6 +58,8 @@ class Mapper(object):
self.lpad_touched = False
self.state, self.old_state = None, None
self.force_event = set()
+ self._lastTime = time.time()
+ self.time_elapsed = 0.0
def create_gamepad(self, enabled, poller):
@@ -359,6 +361,9 @@ class Mapper(object):
self.state = state
self.buttons = state.buttons
+ t = time.time()
+ self.time_elapsed, self._lastTime = t - self._lastTime, t
+
if self.buttons & SCButtons.LPAD and not self.buttons & SCButtons.LPADTOUCH:
self.buttons = (self.buttons & ~SCButtons.LPAD) | SCButtons.STICKPRESS
@@ -463,7 +468,8 @@ class Mapper(object):
# Generate events - mouse
mx, my, wx, wy = self.mouse_movements
if mx != 0 or my != 0:
- self.mouse.moveEvent(mx, my * -1)
+ #self.mouse.moveEvent(mx, my * -1)
+ self.mouse.moveEvent(int(mx), int(my * -1), self.time_elapsed)
self.syn_list.add(self.mouse)
if wx != 0 or wy != 0:
self.mouse.scrollEvent(wx, wy)
diff --git a/scc/modifiers.py b/scc/modifiers.py
index cd4c0dfc..32a2a41b 100644
--- a/scc/modifiers.py
+++ b/scc/modifiers.py
@@ -393,6 +393,12 @@ class BallModifier(Modifier, WholeHapticAction):
MIN_LIFT_VELOCITY = 0.2 # If finger is lifter after movement slower than
# this, roll doesn't happens
+ DEBUG = False
+ DEQUELEN = 10
+ WEIGHT_MODIFIER = 0.71
+ #DEQUELEN = 11
+ #WEIGHT_MODIFIER = 0.71
+
def __init__(self, *params):
Modifier.__init__(self, *params)
WholeHapticAction.__init__(self)
@@ -406,7 +412,8 @@ class BallModifier(Modifier, WholeHapticAction):
self._yvel = 0.0
self._ampli = ampli
self._degree = degree
- self._radscale = (degree * PI / 180) / ampli
+ #self._radscale = (degree * PI / 180) / ampli
+ self._radscale = (degree * PI / 180) / 30000
self._mass = mass
self._roll_task = None
self._r = r
@@ -416,10 +423,14 @@ class BallModifier(Modifier, WholeHapticAction):
self._yvel_dq = deque(maxlen=mean_len)
self._lastTime = time.time()
self._old_pos = None
+ self._mouse_dq = [ deque(maxlen=self.DEQUELEN), deque(maxlen=self.DEQUELEN), deque(maxlen=8), deque(maxlen=8) ] # x, y, wheel, hwheel
+ self._smoothed_pos = [0, 0, 0, 0]
def set_speed(self, x, y, *a):
self.speed = (x, y)
+ self._xvel = 0.0
+ self._yvel = 0.0
def get_speed(self):
@@ -560,22 +571,42 @@ class BallModifier(Modifier, WholeHapticAction):
if mapper.controller_flags() & ControllerFlags.HAS_RSTICK and what == RIGHT:
return self.action.whole(mapper, x, y, what)
if mapper.is_touched(what):
+ if mapper.is_touched(what) and not mapper.was_touched(what):
+ mapper.mouse.clearRemainders()
+
if self._old_pos and mapper.was_touched(what):
t = time.time()
dt = t - self._lastTime
if dt < 0.0075: return
self._lastTime = t
+
+ self._mouse_dq_add(0, x)
+ self._mouse_dq_add(1, y)
+ x = self._smoothed_pos[0]
+ y = self._smoothed_pos[1]
+
dx, dy = x - self._old_pos[0], self._old_pos[1] - y
self._add(dx / dt, dy / dt)
self.action.add(mapper, dx * self.speed[0], dy * self.speed[1])
else:
self._stop()
+ self._mouse_dq_clear(0)
+ self._mouse_dq_clear(1)
+ self._mouse_dq_add(0, x)
+ self._mouse_dq_add(1, y)
+ x = self._smoothed_pos[0]
+ y = self._smoothed_pos[1]
+
self._old_pos = x, y
elif mapper.was_touched(what):
self._old_pos = None
velocity = sqrt(self._xvel * self._xvel + self._yvel * self._yvel)
if velocity > BallModifier.MIN_LIFT_VELOCITY:
self._roll(mapper)
+
+ self._mouse_dq_clear(0)
+ self._mouse_dq_clear(1)
+ self._old_pos = None
elif what == STICK:
return self.action.whole(mapper, x, y, what)
@@ -604,6 +635,35 @@ class BallModifier(Modifier, WholeHapticAction):
return self
+ def _mouse_dq_clear(self, axis):
+ """ Used by trackpad, trackball and mouse wheel emulation """
+ self._mouse_dq[axis].clear()
+
+ def _mouse_dq_add(self, axis, position):
+ #print(self.__class__.DEQUELEN)
+ if len(self._mouse_dq[axis]) == 0:
+ # Mark new position as zero point and populate queue
+ for i in range(self.__class__.DEQUELEN):
+ self._mouse_dq[axis].appendleft(position)
+ else:
+ self._mouse_dq[axis].appendleft(position)
+
+ smoothed_pos = 0
+ if len(self._mouse_dq[axis]) != 0:
+ currentSum = 0.0
+ currentWeight = 1.0
+ finalWeight = 0.0
+ weightModifier = self.__class__.WEIGHT_MODIFIER
+ for entry in self._mouse_dq[axis]:
+ currentSum += entry * currentWeight
+ finalWeight += currentWeight
+ currentWeight *= weightModifier
+
+ smoothed_pos = int(currentSum / finalWeight)
+
+ self._smoothed_pos[axis] = smoothed_pos
+
+
class DeadzoneModifier(Modifier):
COMMAND = "deadzone"
JUMP_HARDCODED_LIMIT = 5
@@ -1509,7 +1569,8 @@ class SmoothModifier(Modifier):
""" Computes average x,y from all accumulated positions """
x = sum(( self._deq_x[i] * self._weights[i] for i in self._range ))
y = sum(( self._deq_y[i] * self._weights[i] for i in self._range ))
- return x / self._w_sum, y / self._w_sum
+ #return x / self._w_sum, y / self._w_sum
+ return int(x / self._w_sum), int(y / self._w_sum)
def whole(self, mapper, x, y, what):
@@ -1528,8 +1589,9 @@ class SmoothModifier(Modifier):
self._deq_x.append(x)
self._deq_y.append(y)
x, y = self._get_pos()
- if abs(x + y - self._last_pos) > self.filter:
- self.action.whole(mapper, x, y, what)
+ #if abs(x + y - self._last_pos) > self.filter:
+ # self.action.whole(mapper, x, y, what)
+ self.action.whole(mapper, x, y, what)
self._last_pos = x + y
elif what == STICK:
return self.action.whole(mapper, x, y, what)
@@ -1625,7 +1687,8 @@ class CircularModifier(Modifier, HapticEnabledAction):
# Add a full rotation to counter the wrapping
angle += 2 * PI
# Apply bulgarian constant
- angle *= 10000.0
+ #angle *= 10000.0
+ angle *= 7000.0 # Raise sensitivity
# Generate feedback, if enabled
if self.haptic:
self._haptic_counter += angle * self.speed / self.haptic.frequency
@@ -1636,8 +1699,10 @@ class CircularModifier(Modifier, HapticEnabledAction):
self._haptic_counter += 0.5
mapper.send_feedback(self.haptic)
# Apply movement to child action
- self.action.change(mapper, -angle * self.speed, 0, what)
- mapper.force_event.add(FE_PAD)
+ # Keep event from activating if no angle change
+ if angle != 0.0:
+ self.action.change(mapper, -angle * self.speed, 0, what)
+ mapper.force_event.add(FE_PAD)
class CircularAbsModifier(Modifier, WholeHapticAction):
diff --git a/scc/uinput.py b/scc/uinput.py
index 88b17c93..14eaa541 100644
--- a/scc/uinput.py
+++ b/scc/uinput.py
@@ -24,7 +24,7 @@
import os, ctypes, time
from ctypes import Structure, POINTER, c_bool, c_int16, c_uint16, c_int32, byref
-from math import pi, copysign, sqrt
+from math import pi, copysign, sqrt, fmod
from scc.lib.libusb1 import timeval
from scc.tools import find_library
from scc.cheader import defines
@@ -399,8 +399,10 @@ class Mouse(UInput):
updateParams permit to upgrade ball model and move scale
"""
- DEFAULT_XSCALE = 0.006
- DEFAULT_YSCALE = 0.006
+ #DEFAULT_XSCALE = 0.006
+ #DEFAULT_YSCALE = 0.006
+ DEFAULT_XSCALE = 0.006 * 1.5
+ DEFAULT_YSCALE = 0.006 * 1.5
DEFAULT_SCR_XSCALE = 0.0005
DEFAULT_SCR_YSCALE = 0.0005
@@ -468,7 +470,7 @@ class Mouse(UInput):
self._scr_xscale = xscale
self._scr_yscale = yscale
- def moveEvent(self, dx=0, dy=0):
+ def moveEvent(self, dx=0, dy=0, time_elapsed=0.0):
"""
Generate move events from parametters and displacement
@@ -476,20 +478,109 @@ class Mouse(UInput):
@param int dy delta movement from last call on y axis
"""
- self._dx += dx * self._xscale
- self._dy += dy * self._yscale
+ #self._dx += dx * self._xscale
+ #self._dy += dy * self._yscale
+ self._factorDeadzone(dx, dy, time_elapsed)
_syn = False
- if int(self._dx):
+ #if int(self._dx):
+ if abs(self._dx) >= 1.0:#int(self._dx):
+ self._dx = self._dx - (fmod(self._dx * 100.0, 1.0) / 100.0)
self.relEvent(rel=Rels.REL_X, val=int(self._dx))
+ #self._dx -= int(self._dx / 1.05) * 1.05
self._dx -= int(self._dx)
_syn = True
- if int(self._dy):
+ #if int(self._dy):
+ if abs(self._dy) >= 1.0:#int(self._dy):
+ self._dy = self._dy - (fmod(self._dy * 100.0, 1.0) / 100.0)
self.relEvent(rel=Rels.REL_Y, val=int(self._dy))
+ #self._dy -= int(self._dy / 1.05) * 1.05
self._dy -= int(self._dy)
_syn = True
if _syn:
self.synEvent()
+ def clearRemainders(self):
+ self._dx = 0
+ self._dy = 0
+
+ def _factorDeadzone(self, dx, dy, time_elapsed):
+ """
+ Take raw event and adjust based on assigned dead zone setting.
+
+ @param int dx delta movement from last call on x axis
+ @param int dy delta movement from last call on y axis
+ """
+
+ #print("COMING IN {} {}".format(dx, dy))
+ #deadzonetmp = 15
+ deadzonetmp = 22
+ #offset = 0.297477440456902
+ offset = 0.8 #0.6 #0.45
+
+ if (dx == 0 or ((dx > 0) != (self._dx > 0))):
+ self._dx = 0
+
+ if (dy == 0 or ((dy > 0) != (self._dy > 0))):
+ self._dy = 0
+
+ _hyp = sqrt((dx**2) + (dy**2))
+ unitx = 0
+ unity = 0
+ deadzoneX = deadzonetmp
+ deadzoneY = deadzonetmp
+ if _hyp != 0.0:
+ unitx = (dx / _hyp)
+ unity = (dy / _hyp)
+ deadzoneX = int(deadzonetmp * unitx)
+ deadzoneY = int(deadzonetmp * unity)
+
+ if (abs(dx) > abs(deadzoneX)):
+ beforedx = dx
+ dx -= copysign(deadzoneX, dx)
+ #print("DX: {} {} {} {}".format(beforedx, dx, deadzoneX, unitx))
+ else:
+ dx = 0
+
+ if (abs(dy) > abs(deadzoneY)):
+ beforedy = dy
+ dy -= copysign(deadzoneY, dy)
+ #print("DY: {} {} {} {}".format(beforedy, dy, deadzoneY, unity))
+ else:
+ dy = 0
+
+ #throttla = 1.43
+ throttla = 1.428
+ offman = 28 #30
+ tempx = 0.0
+ tempy = 0.0
+ if (dx != 0.0):
+ if abs(dx) < (abs(unitx) * offman):
+ signx = copysign(1.0, dx)
+ ratioX = abs(dx) / offman
+ #print("OLD {} | NEW {}".format(tempx, tempx ** 1.5))
+ dx = ratioX ** throttla * signx * offman
+
+ if (dx != 0.0):
+ tempx = dx * (time_elapsed * 125.0) * self._xscale + (abs(unitx) * copysign(offset, dx))
+ self._dx += tempx
+ else:
+ #print("UP IN HERE {}".format(self._dx))
+ self._dx = 0
+
+ if (dy != 0.0):
+ if abs(dy) < (abs(unity) * offman):
+ signy = copysign(1.0, dy)
+ ratioY = abs(dy) / offman
+ #print("OLD {} | NEW {}".format(tempy, tempy ** 1.5))
+ dy = ratioY ** throttla * signy * offman
+
+ if (dy != 0.0):
+ tempy = dy * (time_elapsed * 125.0) * self._yscale + (abs(unity) * copysign(offset, dy))
+ self._dy += tempy
+ else:
+ self._dy = 0
+
+
def scrollEvent(self, dx=0, dy=0):
"""
Generate scroll events from parametters and displacement
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment