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