From 032f0855004ab8378f70035c3e0e4e0c2ef1da02 Mon Sep 17 00:00:00 2001 From: Prabhaav Pillai Date: Thu, 27 Aug 2026 11:36:58 -0500 Subject: [PATCH] ui: add PiP side-camera orientation toggle --- common/params_keys.h | 1 + selfdrive/ui/onroad/starpilot/pip_sidecam.py | 1 + selfdrive/ui/tests/test_pip_sidecam.py | 30 +++++++++++++++++++ .../common/assets/device_settings_layout.json | 14 +++++++-- .../tests/test_device_settings_layout.py | 5 +++- starpilot/system/the_galaxy/the_galaxy.py | 2 +- 6 files changed, 49 insertions(+), 4 deletions(-) diff --git a/common/params_keys.h b/common/params_keys.h index 2704a6c87..8b8e5528c 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -522,6 +522,7 @@ inline static std::unordered_map keys = { {"PIPPreviewMask", {PERSISTENT, JSON, "{\"width\":1928,\"height\":1208,\"center_left\":[315,548],\"center_right\":[1571,539],\"crop_size\":580}", "{\"width\":1928,\"height\":1208,\"center_left\":[315,548],\"center_right\":[1571,539],\"crop_size\":580}", 2}}, {"PIPPreviewShowOnBlinker", {PERSISTENT, BOOL, "0", "0", 1}}, {"PIPPreviewShowOnBSM", {PERSISTENT, BOOL, "0", "0", 1}}, + {"PIPPreviewInvert", {PERSISTENT, BOOL, "0", "0", 1}}, {"GalaxyPaired", {PERSISTENT, BOOL, "0", "0", 0}}, {"GalaxyUploadPending", {PERSISTENT, BOOL, "0", "0", 0}}, {"PreferredSchedule", {PERSISTENT, INT, "2", "0", 0}}, diff --git a/selfdrive/ui/onroad/starpilot/pip_sidecam.py b/selfdrive/ui/onroad/starpilot/pip_sidecam.py index 1bc47a125..ce1adf180 100644 --- a/selfdrive/ui/onroad/starpilot/pip_sidecam.py +++ b/selfdrive/ui/onroad/starpilot/pip_sidecam.py @@ -266,6 +266,7 @@ class PipSideCamera(Widget): self._enabled = self._params.get_bool("PIPPreviewEnabled") and self._params.get_bool("GalaxyDeveloperMode") self._show_on_blinker = self._params.get_bool("PIPPreviewShowOnBlinker") self._show_on_bsm = self._params.get_bool("PIPPreviewShowOnBSM") + self._flip_x_value[0] = 1 if self._params.get_bool("PIPPreviewInvert") else 0 try: raw = self._params.get("PIPPreviewMask") if isinstance(raw, (bytes, str)): diff --git a/selfdrive/ui/tests/test_pip_sidecam.py b/selfdrive/ui/tests/test_pip_sidecam.py index dcdd1855a..38169cdbc 100644 --- a/selfdrive/ui/tests/test_pip_sidecam.py +++ b/selfdrive/ui/tests/test_pip_sidecam.py @@ -29,6 +29,36 @@ def test_pip_driver_camera_shader_mirrors_the_crop(): assert "cropCoord.x = 1.0 - cropCoord.x" in PIP_FRAGMENT_SHADER +class _FakeParams: + def __init__(self, invert: bool = False): + self._invert = invert + + def get_bool(self, key: str) -> bool: + if key == "PIPPreviewInvert": + return self._invert + if key == "GalaxyDeveloperMode": + return True + return False + + def get(self, key: str): + return None + + +def test_pip_flip_value_follows_invert_param(): + camera = PipSideCamera.__new__(PipSideCamera) + camera._closed = True + camera._last_param_refresh = 0.0 + camera._flip_x_value = __import__("pyray").ffi.new("int[1]", [1]) + camera._params = _FakeParams(invert=False) + camera._mask = {} + camera._refresh_config(force=True) + assert camera._flip_x_value[0] == 0 + + camera._params = _FakeParams(invert=True) + camera._refresh_config(force=True) + assert camera._flip_x_value[0] == 1 + + def test_pip_driver_camera_shader_masks_before_sampling_and_keeps_two_texture_reads(): assert "fwidth(radius)" in PIP_FRAGMENT_SHADER assert "if (radius > 1.0 + aa)" in PIP_FRAGMENT_SHADER diff --git a/starpilot/common/assets/device_settings_layout.json b/starpilot/common/assets/device_settings_layout.json index a24bfa421..4f70400bd 100644 --- a/starpilot/common/assets/device_settings_layout.json +++ b/starpilot/common/assets/device_settings_layout.json @@ -2511,7 +2511,7 @@ { "key": "PIPPreviewShowOnBlinker", "label": "Show on Turn Signal", - "description": "Display the side preview bubble while the corresponding turn signal is engaged.", + "description": "Display the side preview while the corresponding turn signal is engaged.", "picker_description": "Shows the side preview while a turn signal is on.", "data_type": "bool", "ui_type": "toggle", @@ -2521,13 +2521,23 @@ { "key": "PIPPreviewShowOnBSM", "label": "Show on Blind Spot Detection", - "description": "Display the side preview bubble when a vehicle is detected in the blind spot, whether from factory BSM, V-ASM, or both.", + "description": "Display the side preview when a vehicle is detected in the blind spot, whether from factory BSM, V-ASM, or both.", "picker_description": "Shows the side preview when a blind spot is detected.", "data_type": "bool", "ui_type": "toggle", "parent_key": "PIPPreviewEnabled", "settings_tier": "advanced" }, + { + "key": "PIPPreviewInvert", + "label": "Invert Side View Orientation", + "description": "Flips the camera horizontally to switch whether your car's body appears on the inner/left or outer/right edge of the preview.", + "picker_description": "Keep off for the default view with the car body along the inside edge.", + "data_type": "bool", + "ui_type": "toggle", + "parent_key": "PIPPreviewEnabled", + "settings_tier": "advanced" + }, { "key": "Compass", "label": "Compass", diff --git a/starpilot/system/the_galaxy/tests/test_device_settings_layout.py b/starpilot/system/the_galaxy/tests/test_device_settings_layout.py index 22a6ee8ce..969dda9e0 100644 --- a/starpilot/system/the_galaxy/tests/test_device_settings_layout.py +++ b/starpilot/system/the_galaxy/tests/test_device_settings_layout.py @@ -331,17 +331,20 @@ def test_pip_preview_is_under_driving_screen_widgets_and_configured_only_in_gala sections = _params_by_section(_layout()) visual = sections["Visual (Display & UI)"] - assert {"PIPPreviewEnabled", "PIPPreviewShowOnBlinker", "PIPPreviewShowOnBSM"} <= visual.keys() + assert {"PIPPreviewEnabled", "PIPPreviewShowOnBlinker", "PIPPreviewShowOnBSM", "PIPPreviewInvert"} <= visual.keys() assert visual["PIPPreviewEnabled"]["parent_key"] == "CustomUI" assert visual["PIPPreviewShowOnBlinker"]["parent_key"] == "PIPPreviewEnabled" assert visual["PIPPreviewShowOnBSM"]["parent_key"] == "PIPPreviewEnabled" + assert visual["PIPPreviewInvert"]["parent_key"] == "PIPPreviewEnabled" assert visual["PIPPreviewEnabled"]["settings_tier"] == "advanced" assert visual["PIPPreviewShowOnBlinker"]["settings_tier"] == "advanced" assert visual["PIPPreviewShowOnBSM"]["settings_tier"] == "advanced" + assert visual["PIPPreviewInvert"]["settings_tier"] == "advanced" assert _declared_default("PIPPreviewEnabled") == "0" assert _declared_default("PIPPreviewShowOnBlinker") == "0" assert _declared_default("PIPPreviewShowOnBSM") == "0" + assert _declared_default("PIPPreviewInvert") == "0" annotation_default = ( '"{\\"width\\":1928,\\"height\\":1208,\\"center_left\\":[315,548],' + '\\"center_right\\":[1571,539],\\"crop_size\\":580}"' diff --git a/starpilot/system/the_galaxy/the_galaxy.py b/starpilot/system/the_galaxy/the_galaxy.py index 5891ada7c..276acc71d 100644 --- a/starpilot/system/the_galaxy/the_galaxy.py +++ b/starpilot/system/the_galaxy/the_galaxy.py @@ -111,7 +111,7 @@ GITLAB_SUBMISSIONS_PROJECT_ID = "71992109" GITLAB_TOKEN = os.environ.get("GITLAB_TOKEN", "") LEGACY_LATERAL_METHOD_API_PREFIX = "/api/" + "".join(("f", "t", "m")) VASM_CONFIGURATION_KEYS = {"VASMEnabled", "VASMConfidenceThreshold", "VASMSmoothSeconds", "VASMAnnotationConfig"} -PIP_PREVIEW_CONFIGURATION_KEYS = {"PIPPreviewEnabled", "PIPPreviewMask", "PIPPreviewShowOnBlinker", "PIPPreviewShowOnBSM"} +PIP_PREVIEW_CONFIGURATION_KEYS = {"PIPPreviewEnabled", "PIPPreviewMask", "PIPPreviewShowOnBlinker", "PIPPreviewShowOnBSM", "PIPPreviewInvert"} MODEL_SMOOTHING_KEYS = {"LatSmoothSeconds", "LongSmoothSeconds"} GALAXY_DEVELOPER_ONLY_KEYS = {"TurnSteeringLimitMuteSpeed"} PULSE_GLIDE_BUTTON_KEYS = {