From 6bb87174b9927d8348539944fc46c6680c37e590 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Thu, 9 Oct 2025 14:14:12 -0400 Subject: [PATCH 1/5] Revert "CarControlSP: live params (#943)" (#1345) * Revert "`CarControlSP`: live params (#943)" This reverts commit b5104377 * bump * bump --- opendbc_repo | 2 +- selfdrive/car/helpers.py | 2 +- .../selfdrive/controls/controlsd_ext.py | 5 --- .../selfdrive/controls/lib/param_store.py | 44 ------------------- 4 files changed, 2 insertions(+), 51 deletions(-) delete mode 100644 sunnypilot/selfdrive/controls/lib/param_store.py diff --git a/opendbc_repo b/opendbc_repo index 335e5da18..b592ecdd3 160000 --- a/opendbc_repo +++ b/opendbc_repo @@ -1 +1 @@ -Subproject commit 335e5da18219ebcb4d56ab81387b37f7adcae461 +Subproject commit b592ecdd3b571a1acee0c04726117a137cec5832 diff --git a/selfdrive/car/helpers.py b/selfdrive/car/helpers.py index a7abc1976..384152c71 100644 --- a/selfdrive/car/helpers.py +++ b/selfdrive/car/helpers.py @@ -57,7 +57,7 @@ def convert_carControlSP(struct: capnp.lib.capnp._DynamicStructReader) -> struct struct_dataclass = structs.CarControlSP(**remove_deprecated({k: v for k, v in struct_dict.items() if not isinstance(k, dict)})) struct_dataclass.mads = structs.ModularAssistiveDrivingSystem(**remove_deprecated(struct_dict.get('mads', {}))) - struct_dataclass.params = [structs.CarControlSP.Param(**remove_deprecated(p)) for p in struct_dict.get('params', [])] + # struct_dataclass.params = [structs.CarControlSP.Param(**remove_deprecated(p)) for p in struct_dict.get('params', [])] struct_dataclass.leadOne = structs.LeadData(**remove_deprecated(struct_dict.get('leadOne', {}))) struct_dataclass.leadTwo = structs.LeadData(**remove_deprecated(struct_dict.get('leadTwo', {}))) struct_dataclass.intelligentCruiseButtonManagement = structs.IntelligentCruiseButtonManagement( diff --git a/sunnypilot/selfdrive/controls/controlsd_ext.py b/sunnypilot/selfdrive/controls/controlsd_ext.py index e0f9326bf..8caeeaeab 100644 --- a/sunnypilot/selfdrive/controls/controlsd_ext.py +++ b/sunnypilot/selfdrive/controls/controlsd_ext.py @@ -10,7 +10,6 @@ from cereal import log, custom from opendbc.car import structs from openpilot.common.params import Params from openpilot.common.swaglog import cloudlog -from openpilot.sunnypilot.selfdrive.controls.lib.param_store import ParamStore from openpilot.sunnypilot.selfdrive.controls.lib.blinker_pause_lateral import BlinkerPauseLateral @@ -19,7 +18,6 @@ class ControlsExt: self.CP = CP self.params = params self.blinker_pause_lateral = BlinkerPauseLateral() - self.param_store = ParamStore(self.CP) self.get_params_sp() cloudlog.info("controlsd_ext is waiting for CarParamsSP") @@ -30,7 +28,6 @@ class ControlsExt: self.pm_services_ext = ['carControlSP'] def get_params_sp(self) -> None: - self.param_store.update(self.params) self.blinker_pause_lateral.get_params() def get_lat_active(self, sm: messaging.SubMaster) -> bool: @@ -73,8 +70,6 @@ class ControlsExt: # MADS state CC_SP.mads = sm['selfdriveStateSP'].mads - CC_SP.params = self.param_store.param_list - CC_SP.intelligentCruiseButtonManagement = sm['selfdriveStateSP'].intelligentCruiseButtonManagement return CC_SP diff --git a/sunnypilot/selfdrive/controls/lib/param_store.py b/sunnypilot/selfdrive/controls/lib/param_store.py deleted file mode 100644 index 785fe9c8f..000000000 --- a/sunnypilot/selfdrive/controls/lib/param_store.py +++ /dev/null @@ -1,44 +0,0 @@ -""" -Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. - -This file is part of sunnypilot and is licensed under the MIT License. -See the LICENSE.md file in the root directory for more details. -""" -from cereal import custom -from opendbc.car import structs -from openpilot.common.params import Params - -from sunnypilot.sunnylink.utils import get_param_as_byte - - -class ParamStore: - keys: list[str] - _params: dict[str, custom.CarControlSP.Param] - - def __init__(self, CP: structs.CarParams): - universal_params: list[str] = [] - brand_params: list[str] = [] - - self.keys = universal_params + brand_params - self._params = {} - - self.frame = -1 - - def update(self, params: Params) -> None: - self.frame += 1 - if self.frame % 300 != 0: - return - - for key in self.keys: - param_type = params.get_type(key).name.lower() # Using string instead of number because its "loose" dependency, and could change by OP at anytime. - - # Over engineering opportunity: It's possible this conversion is slow, we may check the value as params returns it for cache purposes. Not today. - param_value = get_param_as_byte(key, params) - if (existing_param := self._params.get(key)) is not None and existing_param.value == param_value: - continue - - self._params[key] = custom.CarControlSP.Param(key=key, value=param_value, type=param_type) - - @property - def param_list(self) -> list[custom.CarControlSP.Param]: - return [v for k,v in self._params.items()] From 3df1b53fab4341074ace53b2797eb16eb5ed3e09 Mon Sep 17 00:00:00 2001 From: Jason Wen Date: Thu, 9 Oct 2025 14:33:14 -0400 Subject: [PATCH 2/5] ui: update description of Speedometer: Hide from Onroad Screen (#1346) --- selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc index 8e26494a9..efd5e99f3 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc @@ -79,7 +79,7 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) { { "HideVEgoUI", tr("Speedometer: Hide from Onroad Screen"), - tr("Always display the true vehicle current speed from wheel speed sensors."), + tr("When enabled, the speedometer on the onroad screen is not displayed."), "", false, } From f429f3191f988d40e992a7d85a9b6f90c1ae037d Mon Sep 17 00:00:00 2001 From: Nayan Date: Thu, 9 Oct 2025 14:53:47 -0400 Subject: [PATCH 3/5] ui: Bug fix Search with special chars (#1344) Fix Search with special chars Co-authored-by: Jason Wen --- selfdrive/ui/sunnypilot/qt/util.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/selfdrive/ui/sunnypilot/qt/util.cc b/selfdrive/ui/sunnypilot/qt/util.cc index 2e066e88b..be39b297d 100644 --- a/selfdrive/ui/sunnypilot/qt/util.cc +++ b/selfdrive/ui/sunnypilot/qt/util.cc @@ -95,13 +95,12 @@ QStringList searchFromList(const QString &query, const QStringList &list) { return list; } - QStringList search_terms = query.simplified().toLower().split(" ", QString::SkipEmptyParts); + QStringList search_terms = query.simplified().toLower().replace(QRegularExpression("[^a-zA-Z0-9\\s]"), " ").split(" ", QString::SkipEmptyParts); QStringList search_results; for (const QString &element : list) { if (std::all_of(search_terms.begin(), search_terms.end(), [&](const QString &term) { QString normalized_term = term.normalized(QString::NormalizationForm_KD).toLower(); - normalized_term.remove(QRegularExpression("[^a-zA-Z0-9\\s]")); QString normalized_element = element.normalized(QString::NormalizationForm_KD).toLower(); return normalized_element.contains(normalized_term, Qt::CaseInsensitive); })) { From e9f054b7ee90d40d799c9f13fe26fd18d5ac793f Mon Sep 17 00:00:00 2001 From: Nayan Date: Thu, 9 Oct 2025 15:26:14 -0400 Subject: [PATCH 4/5] UI: Adjust Alert display with Developer UI (#1299) * Adjust Alert Rect for Dev UI * split out into SP classes * fix includes * formatting --------- Co-authored-by: Jason Wen --- selfdrive/ui/qt/onroad/alerts.cc | 9 -- selfdrive/ui/qt/onroad/onroad_home.h | 2 + selfdrive/ui/sunnypilot/SConscript | 1 + selfdrive/ui/sunnypilot/qt/onroad/alerts.cc | 102 ++++++++++++++++++++ selfdrive/ui/sunnypilot/qt/onroad/alerts.h | 22 +++++ 5 files changed, 127 insertions(+), 9 deletions(-) create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/alerts.cc create mode 100644 selfdrive/ui/sunnypilot/qt/onroad/alerts.h diff --git a/selfdrive/ui/qt/onroad/alerts.cc b/selfdrive/ui/qt/onroad/alerts.cc index 2e8f3612e..d6829c6b0 100644 --- a/selfdrive/ui/qt/onroad/alerts.cc +++ b/selfdrive/ui/qt/onroad/alerts.cc @@ -4,9 +4,6 @@ #include #include "selfdrive/ui/qt/util.h" -#ifdef SUNNYPILOT -#include "selfdrive/ui/sunnypilot/ui.h" -#endif void OnroadAlerts::updateState(const UIState &s) { Alert a = getAlert(*(s.sm), s.scene.started_frame); @@ -76,12 +73,6 @@ void OnroadAlerts::paintEvent(QPaintEvent *event) { } QRect r = QRect(0 + margin, height() - h + margin, width() - margin*2, h - margin*2); -#ifdef SUNNYPILOT - const int dev_ui_info = uiStateSP()->scene.dev_ui_info; - const int adjustment = dev_ui_info > 1 && alert.size != cereal::SelfdriveState::AlertSize::FULL ? 30 : 0; - r = QRect(0 + margin, height() - h + margin - adjustment, width() - margin*2, h - margin*2); -#endif - QPainter p(this); // draw background + gradient diff --git a/selfdrive/ui/qt/onroad/onroad_home.h b/selfdrive/ui/qt/onroad/onroad_home.h index 9b51bb62e..ad0603c09 100644 --- a/selfdrive/ui/qt/onroad/onroad_home.h +++ b/selfdrive/ui/qt/onroad/onroad_home.h @@ -4,8 +4,10 @@ #ifdef SUNNYPILOT #include "selfdrive/ui/sunnypilot/qt/onroad/annotated_camera.h" +#include "selfdrive/ui/sunnypilot/qt/onroad/alerts.h" #define UIState UIStateSP #define AnnotatedCameraWidget AnnotatedCameraWidgetSP +#define OnroadAlerts OnroadAlertsSP #else #include "selfdrive/ui/qt/onroad/annotated_camera.h" #endif diff --git a/selfdrive/ui/sunnypilot/SConscript b/selfdrive/ui/sunnypilot/SConscript index 12c09c53f..be92c1426 100644 --- a/selfdrive/ui/sunnypilot/SConscript +++ b/selfdrive/ui/sunnypilot/SConscript @@ -38,6 +38,7 @@ qt_src = [ "sunnypilot/qt/offroad/settings/trips_panel.cc", "sunnypilot/qt/offroad/settings/vehicle_panel.cc", "sunnypilot/qt/offroad/settings/visuals_panel.cc", + "sunnypilot/qt/onroad/alerts.cc", "sunnypilot/qt/onroad/annotated_camera.cc", "sunnypilot/qt/onroad/buttons.cc", "sunnypilot/qt/onroad/developer_ui/developer_ui.cc", diff --git a/selfdrive/ui/sunnypilot/qt/onroad/alerts.cc b/selfdrive/ui/sunnypilot/qt/onroad/alerts.cc new file mode 100644 index 000000000..695f6d2ce --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/alerts.cc @@ -0,0 +1,102 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#include "selfdrive/ui/sunnypilot/qt/onroad/alerts.h" + +#include +#include +#include +#include + +OnroadAlerts::Alert OnroadAlertsSP::getAlert(const SubMaster &sm, uint64_t started_frame) { + OnroadAlerts::Alert alert = OnroadAlerts::getAlert(sm, started_frame); + alert.text1.replace("openpilot", "sunnypilot"); + alert.text2.replace("openpilot", "sunnypilot"); + return alert; +} + +void OnroadAlertsSP::paintEvent(QPaintEvent *event) { + if (alert.size == cereal::SelfdriveState::AlertSize::NONE) { + return; + } else if (alert.size == cereal::SelfdriveState::AlertSize::FULL) { + OnroadAlerts::paintEvent(event); + return; + } + static std::map alert_heights = { + {cereal::SelfdriveState::AlertSize::SMALL, 271}, + {cereal::SelfdriveState::AlertSize::MID, 420} + }; + int h = alert_heights[alert.size]; + + QPainter p(this); + QFont topFont; + QFont bottomFont; + QRect topTextBoundingRect; + QRect bottomTextBoundingRect; + QRect rect; + + int margin = 40; + int radius = 30; + + const int dev_ui_info = uiStateSP()->scene.dev_ui_info; + const int v_adjustment = dev_ui_info > 1 && alert.size != cereal::SelfdriveState::AlertSize::FULL ? 40 : 0; + const int h_adjustment = dev_ui_info > 0 && alert.size != cereal::SelfdriveState::AlertSize::FULL ? 230 : 0; + + if (alert.size == cereal::SelfdriveState::AlertSize::SMALL) { + topFont = InterFont(74, QFont::DemiBold); + QFontMetrics fmTop(topFont); + topTextBoundingRect = fmTop.boundingRect( + QRect(0 + margin, height() - h + margin - v_adjustment, width() - margin * 2 - h_adjustment, 0), Qt::TextWordWrap, + alert.text1); + h = topTextBoundingRect.height(); + rect = QRect(0 + margin, height() - h - margin * 2 - v_adjustment, width() - margin * 2 - h_adjustment, h + margin); + } else if (alert.size == cereal::SelfdriveState::AlertSize::MID) { + topFont = InterFont(88, QFont::Bold); + bottomFont = InterFont(66); + QFontMetrics fmTop(topFont); + QFontMetrics fmBotton(bottomFont); + topTextBoundingRect = fmTop.boundingRect( + QRect(0 + margin, height() - h + margin - v_adjustment, width() - margin * 2 - h_adjustment, 0), Qt::TextWordWrap, + alert.text1); + bottomTextBoundingRect = fmBotton.boundingRect( + QRect(0 + margin, height() - h + margin - v_adjustment + topTextBoundingRect.height(), + width() - margin * 2 - h_adjustment, 0), Qt::TextWordWrap, alert.text2); + h = topTextBoundingRect.height() + bottomTextBoundingRect.height() + margin * 2; + rect = QRect(0 + margin, height() - h - margin * 2 - v_adjustment, width() - margin * 2 - h_adjustment, h + margin); + } + + + // draw background + gradient + // draw background + gradient + p.setPen(Qt::NoPen); + p.setCompositionMode(QPainter::CompositionMode_SourceOver); + p.setBrush(QBrush(alert_colors[alert.status])); + p.drawRoundedRect(rect, radius, radius); + + QLinearGradient g(0, rect.y(), 0, rect.bottom()); + g.setColorAt(0, QColor::fromRgbF(0, 0, 0, 0.05)); + g.setColorAt(1, QColor::fromRgbF(0, 0, 0, 0.35)); + + p.setCompositionMode(QPainter::CompositionMode_DestinationOver); + p.setBrush(QBrush(g)); + p.drawRoundedRect(rect, radius, radius); + p.setCompositionMode(QPainter::CompositionMode_SourceOver); + + // text + p.setPen(QColor(0xff, 0xff, 0xff)); + p.setRenderHint(QPainter::TextAntialiasing); + p.setFont(topFont); + if (alert.size == cereal::SelfdriveState::AlertSize::SMALL) { + p.drawText(rect, Qt::AlignCenter | Qt::TextWordWrap, alert.text1); + } else if (alert.size == cereal::SelfdriveState::AlertSize::MID) { + QRect topText = QRect(rect.x(), rect.top() + margin, rect.width(), topTextBoundingRect.height()); + p.drawText(topText, Qt::AlignHCenter | Qt::AlignTop | Qt::TextWordWrap | Qt::AlignCenter, alert.text1); + p.setFont(bottomFont); + p.drawText(QRect(rect.x(), topText.bottom() + margin, rect.width(), bottomTextBoundingRect.height()), + Qt::AlignHCenter | Qt::TextWordWrap | Qt::AlignCenter, alert.text2); + } +} diff --git a/selfdrive/ui/sunnypilot/qt/onroad/alerts.h b/selfdrive/ui/sunnypilot/qt/onroad/alerts.h new file mode 100644 index 000000000..8b65309f4 --- /dev/null +++ b/selfdrive/ui/sunnypilot/qt/onroad/alerts.h @@ -0,0 +1,22 @@ +/** + * Copyright (c) 2021-, Haibin Wen, sunnypilot, and a number of other contributors. + * + * This file is part of sunnypilot and is licensed under the MIT License. + * See the LICENSE.md file in the root directory for more details. + */ + +#pragma once + +#include "selfdrive/ui/qt/onroad/alerts.h" +#include "selfdrive/ui/sunnypilot/ui.h" + +class OnroadAlertsSP : public OnroadAlerts { + Q_OBJECT + +public: + OnroadAlertsSP(QWidget *parent = 0) : OnroadAlerts(parent) {} + +protected: + void paintEvent(QPaintEvent *) override; + Alert getAlert(const SubMaster &sm, uint64_t started_frame); +}; From 8864b79a6e29f4e9431977ef1472e30fa1b136c2 Mon Sep 17 00:00:00 2001 From: HazZelnutz <49021905+HazZelnutz@users.noreply.github.com> Date: Thu, 9 Oct 2025 21:59:28 +0200 Subject: [PATCH 5/5] Visuals: Turn signals on screen when blinker is used (#1291) * get blinker state from car_state * Draw turn signals when blinker is active * reloacted and resized turning signals * add turn signal display setting * Lowered Blink Frequency * Moved x and y Offsets to drawing Function Co-authored-by: Nayan * Grouped Settings * Moved blinking Logic to pulseElement Function Co-authored-by: Nayan * Improved Blinker Size and Position * Get laneChangeBlocked Event from onroadEvents * Draw Blinker red if laneChangeBlocked event is set * Revert "Get laneChangeBlocked Event from onroadEvents" This reverts commit 4310931b6cd59aed22119c2b4b558f09d3ad9c90. * Get left and right Blindspot * Replaced laneChangeBlocked by Blindspot checks * slight optimization * more refinement --------- Co-authored-by: Nayan Co-authored-by: Jason Wen --- common/params_keys.h | 1 + .../qt/offroad/settings/visuals_panel.cc | 9 ++- selfdrive/ui/sunnypilot/qt/onroad/hud.cc | 81 +++++++++++++++++++ selfdrive/ui/sunnypilot/qt/onroad/hud.h | 7 ++ selfdrive/ui/sunnypilot/ui.cc | 2 + selfdrive/ui/sunnypilot/ui_scene.h | 1 + 6 files changed, 100 insertions(+), 1 deletion(-) diff --git a/common/params_keys.h b/common/params_keys.h index 0ff0354b3..3b5d02a42 100644 --- a/common/params_keys.h +++ b/common/params_keys.h @@ -168,6 +168,7 @@ inline static std::unordered_map keys = { {"QuietMode", {PERSISTENT | BACKUP, BOOL, "0"}}, {"RainbowMode", {PERSISTENT | BACKUP, BOOL, "0"}}, {"ShowAdvancedControls", {PERSISTENT | BACKUP, BOOL, "0"}}, + {"ShowTurnSignals", {PERSISTENT | BACKUP, BOOL, "0"}}, {"StandstillTimer", {PERSISTENT | BACKUP, BOOL, "0"}}, {"TrueVEgoUI", {PERSISTENT | BACKUP, BOOL, "0"}}, diff --git a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc index efd5e99f3..0324cd70f 100644 --- a/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc +++ b/selfdrive/ui/sunnypilot/qt/offroad/settings/visuals_panel.cc @@ -82,7 +82,14 @@ VisualsPanel::VisualsPanel(QWidget *parent) : QWidget(parent) { tr("When enabled, the speedometer on the onroad screen is not displayed."), "", false, - } + }, + { + "ShowTurnSignals", + tr("Display Turn Signals"), + tr("When enabled, visual turn indicators are drawn on the HUD."), + "", + false, + }, }; // Add regular toggles first diff --git a/selfdrive/ui/sunnypilot/qt/onroad/hud.cc b/selfdrive/ui/sunnypilot/qt/onroad/hud.cc index 4bb2aca28..d98ae8914 100644 --- a/selfdrive/ui/sunnypilot/qt/onroad/hud.cc +++ b/selfdrive/ui/sunnypilot/qt/onroad/hud.cc @@ -122,6 +122,12 @@ void HudRendererSP::updateState(const UIState &s) { float v_ego = (v_ego_cluster_seen && !s.scene.trueVEgoUI) ? car_state.getVEgoCluster() : car_state.getVEgo(); speed = std::max(0.0f, v_ego * (is_metric ? MS_TO_KPH : MS_TO_MPH)); hideVEgoUI = s.scene.hideVEgoUI; + + leftBlinkerOn = car_state.getLeftBlinker(); + rightBlinkerOn = car_state.getRightBlinker(); + leftBlindspot = car_state.getLeftBlindspot(); + rightBlindspot = car_state.getRightBlindspot(); + showTurnSignals = s.scene.turn_signals; } void HudRendererSP::draw(QPainter &p, const QRect &surface_rect) { @@ -239,6 +245,11 @@ void HudRendererSP::draw(QPainter &p, const QRect &surface_rect) { } else { e2eAlertFrame = 0; } + + // Blinker + if (showTurnSignals) { + drawBlinker(p, surface_rect); + } } p.restore(); @@ -752,3 +763,73 @@ void HudRendererSP::drawCurrentSpeedSP(QPainter &p, const QRect &surface_rect) { p.setFont(InterFont(66)); HudRenderer::drawText(p, surface_rect.center().x(), 290, is_metric ? tr("km/h") : tr("mph"), 200); } + +void HudRendererSP::drawBlinker(QPainter &p, const QRect &surface_rect) { + if (!leftBlinkerOn && !rightBlinkerOn) { + blinkerFrameCounter = 0; + return; + } + ++blinkerFrameCounter; + + const int circleRadius = 44; + const int arrowLength = 44; + const int x_gap = 180; + const int y_offset = 272; + + const int centerX = surface_rect.center().x(); + const bool hazard = leftBlinkerOn && rightBlinkerOn; + + const QPen bgBorder(Qt::white, 5); + const QPen arrowPen(Qt::NoPen); + + p.save(); + + auto drawArrow = [&](int cx, int cy, int dir, const QBrush &arrowBrush) { + const int bodyLength = arrowLength / 2; + const int bodyWidth = arrowLength / 2; + const int headLength = arrowLength / 2; + const int headWidth = arrowLength; + + QPolygon arrow; + arrow.reserve(7); + arrow << QPoint(cx - dir * bodyLength, cy - bodyWidth / 2) + << QPoint(cx, cy - bodyWidth / 2) + << QPoint(cx, cy - headWidth / 2) + << QPoint(cx + dir * headLength, cy) + << QPoint(cx, cy + headWidth / 2) + << QPoint(cx, cy + bodyWidth / 2) + << QPoint(cx - dir * bodyLength, cy + bodyWidth / 2); + + p.setPen(arrowPen); + p.setBrush(arrowBrush); + p.drawPolygon(arrow); + }; + + auto drawCircle = [&](int cx, int cy, const QBrush &bgBrush) { + p.setPen(bgBorder); + p.setBrush(bgBrush); + p.drawEllipse(QPoint(cx, cy), circleRadius, circleRadius); + }; + + struct BlinkerSide { bool on; int dir; bool blocked; int cx; }; + const std::array sides = {{ + {leftBlinkerOn, -1, hazard ? true : (leftBlinkerOn && leftBlindspot), centerX - x_gap}, + {rightBlinkerOn, 1, hazard ? true : (rightBlinkerOn && rightBlindspot), centerX + x_gap}, + }}; + + for (const auto &s: sides) { + if (!s.on) continue; + + QColor bgColor = s.blocked ? QColor(135, 23, 23) : QColor(23, 134, 68); + QColor arrowColor = s.blocked ? QColor(66, 12, 12) : QColor(12, 67, 34); + if (pulseElement(blinkerFrameCounter)) arrowColor = Qt::white; + + const QBrush bgBrush(bgColor); + const QBrush arrowBrush(arrowColor); + + drawCircle(s.cx, y_offset, bgBrush); + drawArrow(s.cx, y_offset, s.dir, arrowBrush); + } + + p.restore(); +} diff --git a/selfdrive/ui/sunnypilot/qt/onroad/hud.h b/selfdrive/ui/sunnypilot/qt/onroad/hud.h index 7a4940a28..a5462fba9 100644 --- a/selfdrive/ui/sunnypilot/qt/onroad/hud.h +++ b/selfdrive/ui/sunnypilot/qt/onroad/hud.h @@ -38,6 +38,7 @@ private: void drawSetSpeedSP(QPainter &p, const QRect &surface_rect); void drawE2eAlert(QPainter &p, const QRect &surface_rect); void drawCurrentSpeedSP(QPainter &p, const QRect &surface_rect); + void drawBlinker(QPainter &p, const QRect &surface_rect); bool lead_status; float lead_d_rel; @@ -109,4 +110,10 @@ private: QString alert_text; QPixmap alert_img; bool hideVEgoUI; + bool leftBlinkerOn; + bool rightBlinkerOn; + bool leftBlindspot; + bool rightBlindspot; + int blinkerFrameCounter; + bool showTurnSignals; }; diff --git a/selfdrive/ui/sunnypilot/ui.cc b/selfdrive/ui/sunnypilot/ui.cc index e4d5b41ad..c021e0e95 100644 --- a/selfdrive/ui/sunnypilot/ui.cc +++ b/selfdrive/ui/sunnypilot/ui.cc @@ -72,6 +72,8 @@ void ui_update_params_sp(UIStateSP *s) { s->scene.onroadScreenOffControl = params.getBool("OnroadScreenOffControl"); s->scene.onroadScreenOffTimerParam = std::atoi(params.get("OnroadScreenOffTimer").c_str()); s->reset_onroad_sleep_timer(); + + s->scene.turn_signals = params.getBool("ShowTurnSignals"); } void UIStateSP::reset_onroad_sleep_timer() { diff --git a/selfdrive/ui/sunnypilot/ui_scene.h b/selfdrive/ui/sunnypilot/ui_scene.h index 5bf115664..233f1e15d 100644 --- a/selfdrive/ui/sunnypilot/ui_scene.h +++ b/selfdrive/ui/sunnypilot/ui_scene.h @@ -17,4 +17,5 @@ typedef struct UISceneSP : UIScene { int onroadScreenOffTimerParam; bool trueVEgoUI; bool hideVEgoUI; + bool turn_signals = false; } UISceneSP;