diff --git a/.gitignore b/.gitignore index 2a83b8307..0e9b29694 100644 --- a/.gitignore +++ b/.gitignore @@ -17,6 +17,7 @@ appimage/ *.cpp *.vdf .kateconfig +*.kdev4 dist/ sccontroller.egg-info/ diff --git a/images/controller-images/deck.svg b/images/controller-images/deck.svg index fdf7910e0..f00a586c4 100644 --- a/images/controller-images/deck.svg +++ b/images/controller-images/deck.svg @@ -7,7 +7,7 @@ viewBox="0 0 445.99999 345" id="svg2" version="1.1" - inkscape:version="1.2 (dc2aedaf03, 2022-05-15)" + inkscape:version="1.2.2 (b0a8486541, 2022-12-01)" sodipodi:docname="deck.svg" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" @@ -25,16 +25,16 @@ borderopacity="1.0" inkscape:pageopacity="0.0" inkscape:pageshadow="2" - inkscape:zoom="14.833317" - inkscape:cx="213.33731" - inkscape:cy="265.7868" + inkscape:zoom="7.4166584" + inkscape:cx="40.853978" + inkscape:cy="99.101234" inkscape:document-units="px" - inkscape:current-layer="controller" + inkscape:current-layer="layer2" showgrid="false" units="px" - inkscape:window-width="1855" - inkscape:window-height="1032" - inkscape:window-x="65" + inkscape:window-width="1920" + inkscape:window-height="1005" + inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" fit-margin-top="0" @@ -656,6 +656,74 @@ + + + + + + + + + + diff --git a/scc/drivers/steamdeck.py b/scc/drivers/steamdeck.py index 84f94c589..2d381bf41 100644 --- a/scc/drivers/steamdeck.py +++ b/scc/drivers/steamdeck.py @@ -192,9 +192,8 @@ def _on_input(self, endpoint, data): self.daemon.add_controller(self) self.configure() self._ready = True - - # Don't like this version - #self._old_state, self._input = self._input, self._old_state + + self._old_state, self._input = self._input, self._old_state ctypes.memmove(ctypes.addressof(self._input), data, len(data)) if self._input.seq % UNLIZARD_INTERVAL == 0: # Keeps lizard mode from happening @@ -222,16 +221,13 @@ def _on_input(self, endpoint, data): self._input.stick_y = apply_deadzone(self._input.stick_y, STICK_DEADZONE) self._input.rstick_x = apply_deadzone(self._input.rstick_x, STICK_DEADZONE) self._input.rstick_y = apply_deadzone(self._input.rstick_y, STICK_DEADZONE) - + # Invert Gyro Roll to match Steam Controller coordinate system self._input.groll = -self._input.groll - + m = self.get_mapper() if m: self.mapper.input(self, self._old_state, self._input) - - # Preserve current state into previous state object - self._old_state = self._input def close(self): if self._ready: diff --git a/scc/gui/app.py b/scc/gui/app.py index 99b70b995..900f536c4 100644 --- a/scc/gui/app.py +++ b/scc/gui/app.py @@ -20,7 +20,7 @@ from scc.gui.ribar import RIBar from scc.tools import check_access, find_gksudo, profile_is_override, nameof from scc.tools import get_profile_name, profile_is_default, find_profile -from scc.constants import SCButtons, STICK, STICK_PAD_MAX +from scc.constants import SCButtons, STICK, RSTICK, STICK_PAD_MAX from scc.constants import DAEMON_VERSION, LEFT, RIGHT from scc.paths import get_config_path, get_profiles_path from scc.custom import load_custom_module @@ -135,10 +135,14 @@ def setup_widgets(self): self.lpad_test = Gtk.Image.new_from_file(os.path.join(self.imagepath, "test-cursor.svg")) self.rpad_test = Gtk.Image.new_from_file(os.path.join(self.imagepath, "test-cursor.svg")) self.stick_test = Gtk.Image.new_from_file(os.path.join(self.imagepath, "test-cursor.svg")) + self.rstick_test = Gtk.Image.new_from_file(os.path.join(self.imagepath, "test-cursor.svg")) + self.dpad_test = Gtk.Image.new_from_file(os.path.join(self.imagepath, "test-cursor.svg")) self.main_area.put(self.lpad_test, 40, 40) self.main_area.put(self.rpad_test, 290, 90) self.main_area.put(self.stick_test, 150, 40) - + self.main_area.put(self.rstick_test, 290, 40) + self.main_area.put(self.dpad_test, 38, 78) + # OSD mode (if used) if self.osd_mode: self.builder.get_object("btDaemon").set_sensitive(False) @@ -992,16 +996,16 @@ def enable_test_mode(self): if self.test_mode_controller: self.test_mode_controller.unlock_all() try: - c = self.dm.get_controllers()[0] + c = self.profile_switchers[0].get_controller() except IndexError: # Zero controllers return if c: c.unlock_all() c.observe(DaemonManager.nocallback, self.on_observe_failed, - 'A', 'B', 'C', 'X', 'Y', 'START', 'BACK', 'LB', 'RB', - 'LPAD', 'RPAD', 'LGRIP', 'RGRIP', 'LT', 'RT', 'LEFT', - 'RIGHT', 'STICK', 'STICKPRESS') + 'A', 'B', 'C', 'X', 'Y', 'START', 'BACK', 'DOTS', 'LB', 'RB', + 'LPAD', 'RPAD', 'LGRIP', 'LGRIP2', 'RGRIP', 'RGRIP2', 'LT', 'RT', 'LEFT', + 'RIGHT', 'STICK', 'STICKPRESS', 'RSTICK', 'RSTICKPRESS', 'DPAD') self.test_mode_controller = c @@ -1108,11 +1112,12 @@ def on_daemon_error(self, daemon, error): def on_daemon_event_observer(self, daemon, c, what, data): if self.osd_mode_mapper: self.osd_mode_mapper.handle_event(daemon, what, data) - elif what in (LEFT, RIGHT, STICK): + elif what in (LEFT, RIGHT, STICK, RSTICK): widget, area = { LEFT : (self.lpad_test, "LPADTEST"), RIGHT : (self.rpad_test, "RPADTEST"), STICK : (self.stick_test, "STICKTEST"), + RSTICK : (self.rstick_test, "RSTICKTEST"), }[what] # Check if stick or pad is released if data[0] == data[1] == 0: @@ -1130,12 +1135,36 @@ def on_daemon_event_observer(self, daemon, c, what, data): y -= data[1] * aw / STICK_PAD_MAX * 0.5 # Move circle self.main_area.move(widget, x, y) - elif what in ("LT", "RT", "STICKPRESS"): + elif what in ("LT", "RT", "STICKPRESS", "RSTICKPRESS"): if data[0]: self.hilights[App.OBSERVE_COLOR].add(what) else: self.hilights[App.OBSERVE_COLOR].remove(what) self._update_background() + elif what == "DPAD": + if data[0] == 0 and data[1] == 0: + self.dpad_test.hide() + else: + ax, ay, aw, ah = self.background.get_area_position("DPADTEST") + cw = self.dpad_test.get_allocation().width + ch = self.dpad_test.get_allocation().height + + pos_left = [ax , ay + ah/2 - ch/2] + pos_right = [ax + aw - cw , ay + ah/2 - ch/2] + pos_top = [ax + aw/2 - cw/2, ay] + pos_bottom = [ax + aw/2 - cw/2, ay + ah - ch] + + if data[0] < 0: + pos = pos_left + elif data[0] > 0: + pos = pos_right + elif data[1] < 0: + pos = pos_bottom + elif data[1] > 0: + pos = pos_top + + self.main_area.move(self.dpad_test, pos[0], pos[1]) + self.dpad_test.show() elif hasattr(SCButtons, what): try: if data[0]: diff --git a/scc/gui/controller_image.py b/scc/gui/controller_image.py index 7c6f3578a..849ffcbfa 100644 --- a/scc/gui/controller_image.py +++ b/scc/gui/controller_image.py @@ -157,6 +157,8 @@ def _fill_button_images(self, buttons): for i in range(len(ControllerImage.BUTTONS_WITH_IMAGES)): b = nameof(ControllerImage.BUTTONS_WITH_IMAGES[i]) if b == "DOTS": + if self.current["gui"]["background"] != "deck": + continue # How did I managed to create this kind of special case? -_- i = 16 path = None diff --git a/scc/gui/daemon_manager.py b/scc/gui/daemon_manager.py index d68c67c6a..f2f8d7b2b 100644 --- a/scc/gui/daemon_manager.py +++ b/scc/gui/daemon_manager.py @@ -348,7 +348,7 @@ class ControllerManager(GObject.GObject): } DEFAULT_ICONS = [ "A", "B", "X", "Y", "BACK", "C", "START", - "LB", "RB", "LT", "RT", "STICK", "LPAD", "RPAD", "RGRIP", "LGRIP", "DOTS" ] + "LB", "RB", "LT", "RT", "STICK", "RSTICK", "LPAD", "RPAD", "RGRIP", "LGRIP", "DOTS" ] # ^^ those are icon names def __init__(self, daemon_manager, controller_id, controller_type): diff --git a/scc/gui/statusicon.py b/scc/gui/statusicon.py index 836004927..5f6405dd5 100644 --- a/scc/gui/statusicon.py +++ b/scc/gui/statusicon.py @@ -263,6 +263,8 @@ def __init__(self, *args, **kwargs): StatusIcon.__init__(self, *args, **kwargs) try: + import gi + gi.require_version('AppIndicator3', '0.1') from gi.repository import AppIndicator3 as appindicator self._status_active = appindicator.IndicatorStatus.ACTIVE @@ -350,8 +352,8 @@ def _load_fallback(self): for StatusIconBackend in status_icon_backends: try: self._status_fb = StatusIconBackend(*self._arguments[0], **self._arguments[1]) - self._status_fb.connect(b"clicked", self._on_click) - self._status_fb.connect(b"notify::active", self._on_notify_active_fb) + self._status_fb.connect("clicked", self._on_click) + self._status_fb.connect("notify::active", self._on_notify_active_fb) self._on_notify_active_fb() log.warning("StatusIcon: Using backend %s (fallback)" % StatusIconBackend.__name__) diff --git a/scc/gui/svg_widget.py b/scc/gui/svg_widget.py index 7cfdd32d0..b66ab2d62 100644 --- a/scc/gui/svg_widget.py +++ b/scc/gui/svg_widget.py @@ -49,6 +49,7 @@ def __init__(self, filename, init_hilighted=True): self.size_override = None self.image_width = 1 self.image_height = 1 + self.filename = filename self.set_image(filename) self.image = Gtk.Image() if init_hilighted: @@ -58,6 +59,7 @@ def __init__(self, filename, init_hilighted=True): def set_image(self, filename): + self.filename = filename self.current_svg = open(filename, "r").read() self.cache = OrderedDict() self.areas = [] @@ -139,7 +141,7 @@ def get_area_position(self, area_id): a = self.get_area(area_id) if a: return a.x, a.y, a.w, a.h - raise ValueError("Area '%s' not found" % (area_id, )) + raise ValueError("Area '%s' not found in '%s'" % (area_id, self.filename)) @staticmethod diff --git a/scc/osd/inputdisplay.py b/scc/osd/inputdisplay.py index eb9ab1e14..ba8b19ddd 100644 --- a/scc/osd/inputdisplay.py +++ b/scc/osd/inputdisplay.py @@ -6,7 +6,7 @@ from scc.tools import _, set_logging_level from gi.repository import Gtk, GLib -from scc.constants import SCButtons, STICK, LEFT, RIGHT, STICK_PAD_MAX +from scc.constants import SCButtons, STICK, RSTICK, LEFT, RIGHT, STICK_PAD_MAX from scc.gui.daemon_manager import DaemonManager from scc.gui.svg_widget import SVGWidget from scc.osd import OSDWindow @@ -36,7 +36,8 @@ def show(self): self.lpadTest = Gtk.Image.new_from_file(os.path.join(self.imagepath, "inputdisplay-cursor.svg")) self.rpadTest = Gtk.Image.new_from_file(os.path.join(self.imagepath, "inputdisplay-cursor.svg")) self.stickTest = Gtk.Image.new_from_file(os.path.join(self.imagepath, "inputdisplay-cursor.svg")) - + self.rStickTest = Gtk.Image.new_from_file(os.path.join(self.imagepath, "inputdisplay-cursor.svg")) + self.main_area.set_property("margin-left", 10) self.main_area.set_property("margin-right", 10) self.main_area.set_property("margin-top", 10) @@ -46,14 +47,15 @@ def show(self): self.main_area.put(self.lpadTest, 40, 40) self.main_area.put(self.rpadTest, 290, 90) self.main_area.put(self.stickTest, 150, 40) - + self.main_area.put(self.rStickTest, 290, 40) + self.add(self.main_area) OSDWindow.show(self) self.lpadTest.hide() self.rpadTest.hide() self.stickTest.hide() - + self.rStickTest.hide() def run(self): self.daemon = DaemonManager() @@ -85,7 +87,7 @@ def on_daemon_connected(self, *a): c.observe(DaemonManager.nocallback, self.on_observe_failed, 'A', 'B', 'C', 'X', 'Y', 'START', 'BACK', 'LB', 'RB', 'LPAD', 'RPAD', 'LGRIP', 'RGRIP', 'LT', 'RT', 'LEFT', - 'RIGHT', 'STICK', 'STICKPRESS') + 'RIGHT', 'STICK', 'STICKPRESS', 'RSTICK', 'RSTICKPRESS') c.connect('event', self.on_daemon_event_observer) c.connect('lost', self.on_controller_lost) @@ -101,11 +103,12 @@ def on_observe_failed(self, error): def on_daemon_event_observer(self, daemon, what, data): - if what in (LEFT, RIGHT, STICK): + if what in (LEFT, RIGHT, STICK, RSTICK): widget, area = { - LEFT : (self.lpadTest, "LPADTEST"), - RIGHT : (self.rpadTest, "RPADTEST"), - STICK : (self.stickTest, "STICKTEST"), + LEFT : (self.lpadTest, "LPADTEST"), + RIGHT : (self.rpadTest, "RPADTEST"), + STICK : (self.stickTest, "STICKTEST"), + RSTICK : (self.rStickTest, "RSTICKTEST"), }[what] # Check if stick or pad is released if data[0] == data[1] == 0: @@ -123,11 +126,12 @@ def on_daemon_event_observer(self, daemon, what, data): y -= data[1] * aw / STICK_PAD_MAX * 0.5 # Move circle self.main_area.move(widget, x, y) - elif what in ("LT", "RT", "STICKPRESS"): + elif what in ("LT", "RT", "STICKPRESS", "RSTICKPRESS"): what = { "LT" : "LEFT", "RT" : "RIGHT", - "STICKPRESS" : "STICK" + "STICKPRESS" : "STICK", + "RSTICKPRESS" : "RSTICK" }[what] if data[0]: self.hilights[self.OBSERVE_COLOR].add(what) diff --git a/scc/sccdaemon.py b/scc/sccdaemon.py index 4ef65a3f5..13df87eb4 100644 --- a/scc/sccdaemon.py +++ b/scc/sccdaemon.py @@ -1099,11 +1099,14 @@ def source_to_constant(s): Used when parsing `Lock: ...` message """ s = s.decode("utf-8").strip(" \t\r\n") - if s in (STICK, LEFT, RIGHT, CPAD): + if s in (STICK, RSTICK, LEFT, RIGHT, CPAD, DPAD): return s if s == "STICKPRESS": # Special case, as that button is actually named STICK :( return SCButtons.STICKPRESS + if s == "RSTICKPRESS": + # Special case, as that button is actually named STICK :( + return SCButtons.RSTICKPRESS if hasattr(SCButtons, s): return getattr(SCButtons, s) raise ValueError("Unknown source: %s" % (s,))