Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package sc-controller for openSUSE:Factory 
checked in at 2021-07-17 23:36:41
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/sc-controller (Old)
 and      /work/SRC/openSUSE:Factory/.sc-controller.new.2632 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "sc-controller"

Sat Jul 17 23:36:41 2021 rev:10 rq:906745 version:0.4.8.5

Changes:
--------
--- /work/SRC/openSUSE:Factory/sc-controller/sc-controller.changes      
2021-04-29 01:40:00.182714636 +0200
+++ /work/SRC/openSUSE:Factory/.sc-controller.new.2632/sc-controller.changes    
2021-07-17 23:37:20.981640882 +0200
@@ -1,0 +2,16 @@
+Fri Jul 16 22:07:40 UTC 2021 - Jannik Seiler <se...@mosad.xyz>
+
+- Update to 0.4.8.5
+  * Fixed AttributeError problems in sccdaemon.py. Contribution by Alastor27
+  * Fixed using two Steam Controllers in daemon. Contribution by berarma
+  * Added toggled handler for 'Minimize to tray on start' checkbox 
+    in Settings. Contribution by Alastor27
+  * Fixed 'Edit Bindings' and 'Autoswitcher Options' OSD options. 
+    Contribution by Supreeeme
+  * Fixed importing VDFs from Steam. Contribution by Supreeeme
+  * Changes to adjust mouse cursor movement based on gamepad poll rate. 
+    Allows more uniform mouse cursor movement between wired and 
+    wireless Steam Controllers
+  * Now clear mouse cursor distance remainders when changing directions
+
+-------------------------------------------------------------------

Old:
----
  sc-controller-0.4.8.4.tar.gz

New:
----
  sc-controller-0.4.8.5.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ sc-controller.spec ++++++
--- /var/tmp/diff_new_pack.Hp99xL/_old  2021-07-17 23:37:21.405637613 +0200
+++ /var/tmp/diff_new_pack.Hp99xL/_new  2021-07-17 23:37:21.409637583 +0200
@@ -17,7 +17,7 @@
 
 
 Name:           sc-controller
-Version:        0.4.8.4
+Version:        0.4.8.5
 Release:        0
 Summary:        User-mode driver and GTK3-based GUI for the Steam Controller
 License:        GPL-2.0-only

++++++ sc-controller-0.4.8.4.tar.gz -> sc-controller-0.4.8.5.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sc-controller-0.4.8.4/README.md 
new/sc-controller-0.4.8.5/README.md
--- old/sc-controller-0.4.8.4/README.md 2021-03-18 06:13:09.000000000 +0100
+++ new/sc-controller-0.4.8.5/README.md 2021-07-08 20:19:28.000000000 +0200
@@ -42,6 +42,7 @@
   - [setuptools](https://pypi.python.org/pypi/setuptools)
   - [python-pylibacl](http://pylibacl.k1024.org/) is recommended
   - [python-evdev](https://python-evdev.readthedocs.io/en/latest/) is strongly 
recommended
+  - [python-vdf](https://pypi.org/project/vdf/)
 
 ### Installing
   - Download and extract  [latest 
release](https://github.com/kozec/sc-controller/releases/latest)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sc-controller-0.4.8.4/glade/global_settings.glade 
new/sc-controller-0.4.8.5/glade/global_settings.glade
--- old/sc-controller-0.4.8.4/glade/global_settings.glade       2021-03-18 
06:13:09.000000000 +0100
+++ new/sc-controller-0.4.8.5/glade/global_settings.glade       2021-07-08 
20:19:28.000000000 +0200
@@ -2242,6 +2242,7 @@
                     <property name="margin_left">30</property>
                     <property name="margin_top">5</property>
                     <property name="draw_indicator">True</property>
+                    <signal name="toggled" 
handler="on_random_checkbox_toggled" swapped="no"/>
                     <child>
                       <object class="GtkLabel" id="label15">
                         <property name="visible">True</property>
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sc-controller-0.4.8.4/scc/actions.py 
new/sc-controller-0.4.8.5/scc/actions.py
--- old/sc-controller-0.4.8.4/scc/actions.py    2021-03-18 06:13:09.000000000 
+0100
+++ new/sc-controller-0.4.8.5/scc/actions.py    2021-07-08 20:19:28.000000000 
+0200
@@ -877,6 +877,10 @@
                
        def pad(self, mapper, position, what):
                if mapper.is_touched(what):
+                       # Initial pad touch
+                       if not mapper.was_touched(what):
+                               mapper.mouse.clearRemainders()
+
                        if self._old_pos and mapper.was_touched(what):
                                d = position - self._old_pos[0]
                                self.change(mapper, d, 0, what)
@@ -897,7 +901,7 @@
                
                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:
@@ -1004,7 +1008,7 @@
        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):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sc-controller-0.4.8.4/scc/constants.py 
new/sc-controller-0.4.8.5/scc/constants.py
--- old/sc-controller-0.4.8.4/scc/constants.py  2021-03-18 06:13:09.000000000 
+0100
+++ new/sc-controller-0.4.8.5/scc/constants.py  2021-07-08 20:19:28.000000000 
+0200
@@ -28,7 +28,7 @@
 If SC-Controller is updated while daemon is running, DAEMON_VERSION send by
 daemon will differ one one expected by UI and daemon will be forcefully 
restarted.
 """
-DAEMON_VERSION = "0.4.8.4"
+DAEMON_VERSION = "0.4.8.5"
 
 HPERIOD  = 0.02
 LPERIOD  = 0.5
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sc-controller-0.4.8.4/scc/controller.py 
new/sc-controller-0.4.8.5/scc/controller.py
--- old/sc-controller-0.4.8.4/scc/controller.py 2021-03-18 06:13:09.000000000 
+0100
+++ new/sc-controller-0.4.8.5/scc/controller.py 2021-07-08 20:19:28.000000000 
+0200
@@ -1,5 +1,6 @@
 #!/usr/bin/env python2
 from scc.constants import HapticPos
+import time
 import logging
 
 log = logging.getLogger("SCController")
@@ -20,6 +21,8 @@
                self.mapper = None
                self._id = next_id
                next_id += 1
+               self.lastTime = time.time()
+               self.time_elapsed = 0.0
        
        
        def get_type(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sc-controller-0.4.8.4/scc/foreign/vdf.py 
new/sc-controller-0.4.8.5/scc/foreign/vdf.py
--- old/sc-controller-0.4.8.4/scc/foreign/vdf.py        2021-03-18 
06:13:09.000000000 +0100
+++ new/sc-controller-0.4.8.5/scc/foreign/vdf.py        2021-07-08 
20:19:28.000000000 +0200
@@ -7,7 +7,7 @@
 from scc.actions import HatRightAction, TriggerAction, MouseAction
 from scc.actions import HatUpAction, HatDownAction, HatLeftAction
 from scc.actions import AxisAction, RelAreaAction, MultiAction
-from scc.special_actions import ChangeProfileAction, GridMenuAction, MenuAction
+from scc.special_actions import ChangeProfileAction, GridMenuAction, 
RadialMenuAction, MenuAction
 from scc.modifiers import SensitivityModifier, ClickModifier, FeedbackModifier
 from scc.constants import SCButtons, HapticPos, TRIGGER_CLICK, YAW, ROLL
 from scc.modifiers import BallModifier, DoubleclickModifier
@@ -239,7 +239,7 @@
                elif "activators" in bdef:
                        # V3
                        act_actions = []
-                       for k in ("full_press", "double_press", "long_press"):
+                       for k in ("Full_Press", "Double_Press", "Long_Press"):
                                a = NoAction()
                                if k in bdef["activators"]:
                                        # TODO: Handle multiple bindings
@@ -277,7 +277,7 @@
        @staticmethod
        def find_group(data, id):
                """ Returns group with specified ID or None """
-               for g in ensure_list(data["group"]):
+               for g in data.get_all_for('group'):
                        if "id" in g and g["id"] == id:
                                return g
                return None
@@ -360,6 +360,26 @@
                                'LEFT' if side == Profile.LEFT else 'RIGHT',
                                SCButtons.LPAD if side == Profile.LEFT else 
SCButtons.RPAD
                        )
+               elif mode == "radial_menu":
+                       items = []
+                       next_item_id = 1
+                       for k in inputs:
+                               action = self.parse_button(inputs[k])
+                               items.append(MenuItem(
+                                       "item_%s" % (next_item_id,),
+                                       action.describe(Action.AC_BUTTON),
+                                       action
+                               ))
+                               next_item_id += 1
+                       # Menu is stored in profile, with generated ID
+                       menu_id = "menu_%s" % (self.next_menu_id,)
+                       self.next_menu_id += 1
+                       self.menus[menu_id] = MenuData(*items)
+                       
+                       action = RadialMenuAction(menu_id,
+                               'LEFT' if side == Profile.LEFT else 'RIGHT',
+                               SCButtons.LPAD if side == Profile.LEFT else 
SCButtons.RPAD
+                       )
                elif mode == "absolute_mouse":
                        if "click" in inputs:
                                if side == Profile.LEFT:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sc-controller-0.4.8.4/scc/gui/app.py 
new/sc-controller-0.4.8.5/scc/gui/app.py
--- old/sc-controller-0.4.8.4/scc/gui/app.py    2021-03-18 06:13:09.000000000 
+0100
+++ new/sc-controller-0.4.8.5/scc/gui/app.py    2021-07-08 20:19:28.000000000 
+0200
@@ -1608,7 +1608,7 @@
                                        # Failed. Just do nothing
                                        return
                        if giofile.get_path():
-                               path = giofile.get_path().decode("utf-8")
+                               path = giofile.get_path()
                                filetype = Dialog.determine_type(path)
                                if filetype:
                                        log.info("Importing '%s'..." % 
(filetype))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/sc-controller-0.4.8.4/scc/gui/importexport/import_vdf.py 
new/sc-controller-0.4.8.5/scc/gui/importexport/import_vdf.py
--- old/sc-controller-0.4.8.4/scc/gui/importexport/import_vdf.py        
2021-03-18 06:13:09.000000000 +0100
+++ new/sc-controller-0.4.8.5/scc/gui/importexport/import_vdf.py        
2021-07-08 20:19:28.000000000 +0200
@@ -20,7 +20,7 @@
 log = logging.getLogger("IE.ImportVdf")
 
 class ImportVdf(object):
-       PROFILE_LIST = "7/remote/sharedconfig.vdf"
+       PROFILE_LIST = "config/localconfig.vdf"
        STEAMPATH = '~/.steam/steam/'
        
        def __init__(self):
@@ -34,7 +34,6 @@
                self.__profile_load_started = False
                self._on_preload_finished = None
        
-       
        def on_grVdfImport_activated(self, *a):
                if not self.__profile_load_started:
                        self.__profile_load_started = True
@@ -55,22 +54,20 @@
                i = 0
                if os.path.exists(p):
                        for user in os.listdir(p):
-                               sharedconfig = os.path.join(p, user, 
self.PROFILE_LIST)
-                               if os.path.isfile(sharedconfig):
+                               profilelist = os.path.join(p, user, 
self.PROFILE_LIST)
+                               if os.path.isfile(profilelist):
                                        self._lock.acquire()
-                                       log.debug("Loading sharedconfig from 
'%s'", sharedconfig)
+                                       log.debug("Loading profile list from 
'%s'", profilelist)
                                        try:
-                                               i = self._parse_profile_list(i, 
sharedconfig)
+                                               i = self._parse_profile_list(i, 
profilelist, user)
                                        except Exception as e:
                                                log.exception(e)
                                        self._lock.release()
-               
                GLib.idle_add(self._load_finished)
        
-       
-       def _parse_profile_list(self, i, filename):
+       def _parse_profile_list(self, i, filename, userid):
                """
-               Parses sharedconfig.vdf and loads game and profile IDs. That is 
later
+               Parses localconfig.vdf and loads game and profile IDs. That is 
later
                decoded into name of game and profile name.
                
                Called from _load_profiles, in thread. Exceptions are catched 
and logged
@@ -79,24 +76,43 @@
                """
                data = parse_vdf(open(filename, "r"))
                # Sanity check
-               if "userroamingconfigstore" not in data: return
-               if "controller_config" not in data["userroamingconfigstore"]: 
return
-               # Grab config
-               cc = data["userroamingconfigstore"]["controller_config"]
+               if "UserLocalConfigStore" not in data: return
+               if "controller_config" not in data["UserLocalConfigStore"]: 
return
+               
+               # Grab config - currently only grabs SC configs!
+               cc = 
data["UserLocalConfigStore"]["controller_config"][userid]["controller_steamcontroller_gordon"]["DEFAULT_FOR_TYPE"]
                # Go through all games
                listitems = []
+               i = 0
                for gameid in cc:
-                       if "selected" in cc[gameid] and 
cc[gameid]["selected"].startswith("workshop"):
-                               profile_id = 
cc[gameid]["selected"].split("/")[-1]
-                               listitems.append(( i, gameid, profile_id, None 
))
-                               i += 1
-                               if len(listitems) > 10:
-                                       GLib.idle_add(self.fill_list, listitems)
-                                       listitems = []
+                       # skip templates
+                       if 'selected' not in cc[gameid]:
+                               continue
+                       
+                       if not self._check_for_app_manifest(gameid):
+                               continue
+                               
+                       profile_id = cc[gameid]["selected"]
+                       listitems.append(( i, gameid, profile_id, None ))
+                       i += 1
+                       if len(listitems) > 10:
+                               GLib.idle_add(self.fill_list, listitems)
+                               listitems = []
                
                GLib.idle_add(self.fill_list, listitems)
                return i
        
+       def _check_for_app_manifest(self, gameid):
+               """
+               Checks if an app manifest exists for a game.
+               It seems like a better idea to only worry about importing 
configs for games the user                    already has installed.
+               """
+               sa_path = self._find_steamapps()
+               if gameid.isdigit():
+                       filename = os.path.join(sa_path, "appmanifest_%s.acf" % 
(gameid))
+                       if os.path.exists(filename):
+                               return True
+                       
        
        def _load_game_names(self):
                """
@@ -108,6 +124,7 @@
                
                Calls GLib.idle_add to send loaded data into UI.
                """
+               
                sa_path = self._find_steamapps()
                while True:
                        self._s_games.acquire(True)     # Wait until something 
is added to the queue
@@ -122,7 +139,7 @@
                                if os.path.exists(filename):
                                        try:
                                                data = parse_vdf(open(filename, 
"r"))
-                                               name = data['appstate']['name']
+                                               name = data['AppState']['name']
                                        except Exception as e:
                                                log.error("Failed to load app 
manifest for '%s'", gameid)
                                                log.exception(e)
@@ -188,6 +205,7 @@
                                name = _("(not found)")
                                GLib.idle_add(self._set_profile_name, index, 
name, None)
                        self._lock.release()
+                       
        
        
        def _load_finished(self):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sc-controller-0.4.8.4/scc/lib/vdf.py 
new/sc-controller-0.4.8.5/scc/lib/vdf.py
--- old/sc-controller-0.4.8.4/scc/lib/vdf.py    2021-03-18 06:13:09.000000000 
+0100
+++ new/sc-controller-0.4.8.5/scc/lib/vdf.py    2021-07-08 20:19:28.000000000 
+0200
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 """
 VDF file reader
 Copyright (C) 2017 Kozec
@@ -16,59 +16,28 @@
 with this program; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 """
-import shlex
+import os, sys, importlib
 
 
-def parse_vdf(fileobj):
-       """
-       Converts VDF file or file-like object into python dict
-       
-       Throws ValueError if profile cannot be parsed.
-       """
-       rv = {}
-       stack = [ rv ]
-       lexer = shlex.shlex(fileobj)
-       key = None
+def parse_vdf(file):
+       # employ arcane rituals to import a better vdf parser
        
-       t = lexer.get_token()
-       while t:
-               if t == "{":
-                       # Set value to dict and add it on top of stack
-                       if key is None:
-                               raise ValueError("Dict without key")
-                       value = {}
-                       if key in stack[-1]:
-                               lst = ensure_list(stack[-1][key])
-                               lst.append(value)
-                               stack[-1][key] = lst
-                       else:
-                               stack[-1][key] = value
-                       
-                       stack.append(value)
-                       key = None
-               elif t == "}":
-                       # Pop last dict from stack
-                       if len(stack) < 2:
-                               raise ValueError("'}' without '{'")
-                       stack = stack[0:-1]
-               elif key is None:
-                       key = t.strip('"').lower()
-               elif key in stack[-1]:
-                       lst = ensure_list(stack[-1][key])
-                       lst.append(t.strip('"'))
-                       stack[-1][key] = lst
-                       key = None
-               else:
-                       stack[-1][key] = t.strip('"')
-                       key = None
-               
-               t = lexer.get_token()
-       
-       if len(stack) > 1:
-               raise ValueError("'{' without '}'")
-       
-       return rv
-
+       # find system installed vdf package
+       locs = sys.path[1:]
+       for d in locs:
+               if os.path.isdir(d):
+                       if os.path.isdir(os.path.join(d, 'vdf')):
+                               vdfpath = os.path.join(d, 'vdf/__init__.py')
+                               
+       if not vdfpath:
+               raise ModuleNotFoundError("vdf package not found!")
+
+       # import it
+       spec = importlib.util.spec_from_file_location('vdf', vdfpath)
+       vdf = importlib.util.module_from_spec(spec)
+       sys.modules[spec.name] = vdf
+       spec.loader.exec_module(vdf)
+       return vdf.parse(file, mapper=vdf.VDFDict, merge_duplicate_keys=False)
 
 def ensure_list(value):
        """
@@ -79,5 +48,5 @@
 
 
 if __name__ == "__main__":
-       print(parse_vdf(open('app_generic.vdf', "r")))
+       print(parse_vdf(open('test.vdf', "r")))
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sc-controller-0.4.8.4/scc/lib/xwrappers.py 
new/sc-controller-0.4.8.5/scc/lib/xwrappers.py
--- old/sc-controller-0.4.8.4/scc/lib/xwrappers.py      2021-03-18 
06:13:09.000000000 +0100
+++ new/sc-controller-0.4.8.5/scc/lib/xwrappers.py      2021-07-08 
20:19:28.000000000 +0200
@@ -331,7 +331,7 @@
        Returns (nitems, property) of specified window or (-1, None) if 
anything fails.
        Returned 'property' is POINTER(c_void_p) and has to be freed using 
X.free().
        """
-       prop_atom = intern_atom(dpy, prop_name, False)
+       prop_atom = intern_atom(dpy, bytes(prop_name, "utf-8"), False)
        type_return, format_return = Atom(), Atom()
        nitems, bytes_after = c_ulong(), c_ulong()
        prop = c_void_p()
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sc-controller-0.4.8.4/scc/mapper.py 
new/sc-controller-0.4.8.5/scc/mapper.py
--- old/sc-controller-0.4.8.4/scc/mapper.py     2021-03-18 06:13:09.000000000 
+0100
+++ new/sc-controller-0.4.8.5/scc/mapper.py     2021-07-08 20:19:28.000000000 
+0200
@@ -58,6 +58,7 @@
                self.lpad_touched = False
                self.state, self.old_state = None, None
                self.force_event = set()
+               self.time_elapsed = 0.0
        
        
        def create_gamepad(self, enabled, poller):
@@ -359,6 +360,10 @@
                self.state = state
                self.buttons = state.buttons
                
+               t = time.time()
+               controller.time_elapsed = self.time_elapsed = t - 
controller.lastTime
+               controller.lastTime = t
+
                if self.buttons & SCButtons.LPAD and not self.buttons & 
(SCButtons.LPADTOUCH | STICKTILT):
                        self.buttons = (self.buttons & ~SCButtons.LPAD) | 
SCButtons.STICKPRESS
                
@@ -463,7 +468,7 @@
                # 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(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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sc-controller-0.4.8.4/scc/modifiers.py 
new/sc-controller-0.4.8.5/scc/modifiers.py
--- old/sc-controller-0.4.8.4/scc/modifiers.py  2021-03-18 06:13:09.000000000 
+0100
+++ new/sc-controller-0.4.8.5/scc/modifiers.py  2021-07-08 20:19:28.000000000 
+0200
@@ -423,6 +423,9 @@
        
        def set_speed(self, x, y, *a):
                self.speed = (x, y)
+               # Reset calculated x and y velocities
+               self._xvel = 0.0
+               self._yvel = 0.0
        
        
        def get_speed(self):
@@ -563,6 +566,9 @@
                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
@@ -1512,7 +1518,7 @@
                """ 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 int(x / self._w_sum), int(y / self._w_sum)
        
        
        def whole(self, mapper, x, y, what):
@@ -1639,8 +1645,10 @@
                                                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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sc-controller-0.4.8.4/scc/parser.py 
new/sc-controller-0.4.8.5/scc/parser.py
--- old/sc-controller-0.4.8.4/scc/parser.py     2021-03-18 06:13:09.000000000 
+0100
+++ new/sc-controller-0.4.8.5/scc/parser.py     2021-07-08 20:19:28.000000000 
+0200
@@ -1,4 +1,4 @@
-#!/usr/bin/env python2
+#!/usr/bin/env python3
 """
 SC Controller - ActionParser
 
@@ -96,17 +96,19 @@
                return a
        
        
-       def restart(self, string):
+       def restart(self, s):
                """
                Restarts parsing with new string
                Returns self for chaining.
                """
-               
+               if type(s) == bytes:
+                       s = s.decode("utf-8")
+                       
                try:
                        self.tokens = [
                                ActionParser.Token(type, string)
-                               for (type, string, trash, trash, trash)
-                               in generate_tokens( iter([string]).__next__ )
+                               for type, string, *_
+                               in generate_tokens( iter([s]).__next__ )
                                if type != TokenType.ENDMARKER
                        ]
                except TokenError:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sc-controller-0.4.8.4/scc/sccdaemon.py 
new/sc-controller-0.4.8.5/scc/sccdaemon.py
--- old/sc-controller-0.4.8.4/scc/sccdaemon.py  2021-03-18 06:13:09.000000000 
+0100
+++ new/sc-controller-0.4.8.5/scc/sccdaemon.py  2021-07-08 20:19:28.000000000 
+0200
@@ -786,8 +786,7 @@
                                        client.wfile.write(b"Fail: cannot 
display OSD\n")
                elif message.startswith(b"Feedback:"):
                        try:
-                               message = message.decode("utf-8")
-                               position, amplitude = 
message[9:].strip().split(" ", 2)
+                               position, amplitude = 
message[9:].strip().split(b" ", 2)
                                data = HapticData(
                                        getattr(HapticPos, position.strip(" 
\t\r")),
                                        int(amplitude)
@@ -805,7 +804,7 @@
                elif message.startswith(b"Controller:"):
                        with self.lock:
                                try:
-                                       controller_id = message[11:].strip()
+                                       controller_id = 
message[11:].strip().decode('utf-8')
                                        for c in self.controllers:
                                                if c.get_id() == controller_id:
                                                        client.mapper = 
c.get_mapper()
@@ -832,8 +831,7 @@
                                
client.mapper.get_controller().set_led_level(number)
                elif message.startswith(b"Observe:"):
                        if Config()["enable_sniffing"]:
-                               message = message.decode("utf-8")
-                               to_observe = [ x for x in message.split(":", 
1)[1].strip(" \t\r").split(" ") ]
+                               to_observe = [ x for x in message.split(b":", 
1)[1].strip(b" \t\r").split(b" ") ]
                                with self.lock:
                                        for l in to_observe:
                                                client.observe_action(self, 
SCCDaemon.source_to_constant(l))
@@ -843,8 +841,7 @@
                                client.wfile.write(b"Fail: Sniffing 
disabled.\n")
                elif message.startswith(b"Replace:"):
                        try:
-                               message = message.decode("utf-8")
-                               l, actionstr = message.split(":", 1)[1].strip(" 
\t\r").split(" ", 1)
+                               l, actionstr = message.split(b":", 
1)[1].strip(b" \t\r").split(b" ", 1)
                                action = 
TalkingActionParser().restart(actionstr).parse().compress()
                        except Exception as e:
                                e = 
str(e).encode("utf-8").decode('unicode_escape').encode("latin1")
@@ -936,8 +933,7 @@
                        client.wfile.write(b"OK.\n")
                elif message.startswith(b"Gesture:"):
                        try:
-                               message = message.decode("utf-8")
-                               what, up_angle = message[8:].strip().split(" ", 
2)
+                               what, up_angle = message[8:].strip().split(b" 
", 2)
                                up_angle = int(up_angle)
                        except Exception as  e:
                                tb = 
str(traceback.format_exc()).encode("utf-8").decode('unicode_escape').encode("latin1")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/sc-controller-0.4.8.4/scc/uinput.py 
new/sc-controller-0.4.8.5/scc/uinput.py
--- old/sc-controller-0.4.8.4/scc/uinput.py     2021-03-18 06:13:09.000000000 
+0100
+++ new/sc-controller-0.4.8.5/scc/uinput.py     2021-07-08 20:19:28.000000000 
+0200
@@ -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
@@ -468,7 +468,7 @@
                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 +476,119 @@
                @param int dy              delta movement from last call on y 
axis
 
                """
-               self._dx += dx * self._xscale
-               self._dy += dy * self._yscale
                _syn = False
+
+               # Clear mouse axis remainders if axis direction has changed
+               if (dx == 0 or ((dx > 0) != (self._dx > 0))):
+                       self._dx = 0
+
+               # Clear mouse axis remainders if axis direction has changed
+               if (dy == 0 or ((dy > 0) != (self._dy > 0))):
+                       self._dy = 0
+
+               # Base speed around 8 ms standard
+               # (base USB poll rate for Steam Controller)
+               baseFactor = (time_elapsed * 125.0)
+               self._dx += dx * self._xscale * baseFactor
+               self._dy += dy * self._yscale * baseFactor
+               #self._factorDeadzone(dx, dy, time_elapsed)
                if 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)
                        _syn = True
                if 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)
                        _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
+               @param double time_elapsed              time elapsed in sec.
+               """
+
+               #print("COMING IN {} {}".format(dx, dy))
+               #deadzonetmp = 15
+               deadzonetmp = 22
+               #offset = 0.297477440456902
+               offset = 0.375 #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
+               # Throttle low end of X axis movement
+               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
+
+               # Throttle low end of Y axis movement
+               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

Reply via email to