diff --git a/selfdrive/frogpilot/assets/custom_themes/frog_theme/images/turn_signal_1.png b/selfdrive/frogpilot/assets/custom_themes/frog_theme/images/turn_signal_1.png new file mode 100644 index 0000000..43e0b44 Binary files /dev/null and b/selfdrive/frogpilot/assets/custom_themes/frog_theme/images/turn_signal_1.png differ diff --git a/selfdrive/frogpilot/assets/custom_themes/frog_theme/images/turn_signal_1_red.png b/selfdrive/frogpilot/assets/custom_themes/frog_theme/images/turn_signal_1_red.png new file mode 100644 index 0000000..7c10245 Binary files /dev/null and b/selfdrive/frogpilot/assets/custom_themes/frog_theme/images/turn_signal_1_red.png differ diff --git a/selfdrive/frogpilot/assets/custom_themes/frog_theme/images/turn_signal_2.png b/selfdrive/frogpilot/assets/custom_themes/frog_theme/images/turn_signal_2.png new file mode 100644 index 0000000..e8e1479 Binary files /dev/null and b/selfdrive/frogpilot/assets/custom_themes/frog_theme/images/turn_signal_2.png differ diff --git a/selfdrive/frogpilot/assets/custom_themes/frog_theme/images/turn_signal_3.png b/selfdrive/frogpilot/assets/custom_themes/frog_theme/images/turn_signal_3.png new file mode 100644 index 0000000..b59b003 Binary files /dev/null and b/selfdrive/frogpilot/assets/custom_themes/frog_theme/images/turn_signal_3.png differ diff --git a/selfdrive/frogpilot/assets/custom_themes/frog_theme/images/turn_signal_4.png b/selfdrive/frogpilot/assets/custom_themes/frog_theme/images/turn_signal_4.png new file mode 100644 index 0000000..c3c1d20 Binary files /dev/null and b/selfdrive/frogpilot/assets/custom_themes/frog_theme/images/turn_signal_4.png differ diff --git a/selfdrive/frogpilot/assets/custom_themes/stalin_theme/images/turn_signal_1.png b/selfdrive/frogpilot/assets/custom_themes/stalin_theme/images/turn_signal_1.png new file mode 100644 index 0000000..f5116df Binary files /dev/null and b/selfdrive/frogpilot/assets/custom_themes/stalin_theme/images/turn_signal_1.png differ diff --git a/selfdrive/frogpilot/assets/custom_themes/stalin_theme/images/turn_signal_1_red.png b/selfdrive/frogpilot/assets/custom_themes/stalin_theme/images/turn_signal_1_red.png new file mode 100644 index 0000000..4c94813 Binary files /dev/null and b/selfdrive/frogpilot/assets/custom_themes/stalin_theme/images/turn_signal_1_red.png differ diff --git a/selfdrive/frogpilot/assets/custom_themes/stalin_theme/images/turn_signal_2.png b/selfdrive/frogpilot/assets/custom_themes/stalin_theme/images/turn_signal_2.png new file mode 100644 index 0000000..8ceed23 Binary files /dev/null and b/selfdrive/frogpilot/assets/custom_themes/stalin_theme/images/turn_signal_2.png differ diff --git a/selfdrive/frogpilot/assets/custom_themes/stalin_theme/images/turn_signal_3.png b/selfdrive/frogpilot/assets/custom_themes/stalin_theme/images/turn_signal_3.png new file mode 100644 index 0000000..48dbe09 Binary files /dev/null and b/selfdrive/frogpilot/assets/custom_themes/stalin_theme/images/turn_signal_3.png differ diff --git a/selfdrive/frogpilot/assets/custom_themes/stalin_theme/images/turn_signal_4.png b/selfdrive/frogpilot/assets/custom_themes/stalin_theme/images/turn_signal_4.png new file mode 100644 index 0000000..3e46279 Binary files /dev/null and b/selfdrive/frogpilot/assets/custom_themes/stalin_theme/images/turn_signal_4.png differ diff --git a/selfdrive/frogpilot/assets/custom_themes/tesla_theme/images/turn_signal_1.png b/selfdrive/frogpilot/assets/custom_themes/tesla_theme/images/turn_signal_1.png new file mode 100644 index 0000000..b635c57 Binary files /dev/null and b/selfdrive/frogpilot/assets/custom_themes/tesla_theme/images/turn_signal_1.png differ diff --git a/selfdrive/frogpilot/assets/custom_themes/tesla_theme/images/turn_signal_1_red.png b/selfdrive/frogpilot/assets/custom_themes/tesla_theme/images/turn_signal_1_red.png new file mode 100644 index 0000000..a2f508f Binary files /dev/null and b/selfdrive/frogpilot/assets/custom_themes/tesla_theme/images/turn_signal_1_red.png differ diff --git a/selfdrive/frogpilot/assets/custom_themes/tesla_theme/images/turn_signal_2.png b/selfdrive/frogpilot/assets/custom_themes/tesla_theme/images/turn_signal_2.png new file mode 100644 index 0000000..5158c0f Binary files /dev/null and b/selfdrive/frogpilot/assets/custom_themes/tesla_theme/images/turn_signal_2.png differ diff --git a/selfdrive/frogpilot/assets/custom_themes/tesla_theme/images/turn_signal_3.png b/selfdrive/frogpilot/assets/custom_themes/tesla_theme/images/turn_signal_3.png new file mode 100644 index 0000000..c0e2a87 Binary files /dev/null and b/selfdrive/frogpilot/assets/custom_themes/tesla_theme/images/turn_signal_3.png differ diff --git a/selfdrive/frogpilot/assets/custom_themes/tesla_theme/images/turn_signal_4.png b/selfdrive/frogpilot/assets/custom_themes/tesla_theme/images/turn_signal_4.png new file mode 100644 index 0000000..5932e83 Binary files /dev/null and b/selfdrive/frogpilot/assets/custom_themes/tesla_theme/images/turn_signal_4.png differ diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index 56da958..4143ae6 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -438,7 +438,7 @@ void AnnotatedCameraWidget::updateState(const UIState &s) { has_eu_speed_limit = (nav_alive && speed_limit_sign == cereal::NavInstruction::SpeedLimitSign::VIENNA) && !(speedLimitController && !useViennaSLCSign) || (speedLimitController && useViennaSLCSign); is_metric = s.scene.is_metric; speedUnit = s.scene.is_metric ? tr("km/h") : tr("mph"); - hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE); + hideBottomIcons = (cs.getAlertSize() != cereal::ControlsState::AlertSize::NONE || customSignals != 0 && (turnSignalLeft || turnSignalRight)); status = s.status; // update engageability/experimental mode button @@ -984,6 +984,11 @@ void AnnotatedCameraWidget::initializeFrogPilotWidgets() { {0.5, QBrush(QColor::fromHslF(0 / 360., 1.0, 0.5, 0.5))}, {1.0, QBrush(QColor::fromHslF(0 / 360., 1.0, 0.5, 0.1))}}}}, }; + + animationTimer = new QTimer(this); + connect(animationTimer, &QTimer::timeout, this, [this] { + animationFrameIndex = (animationFrameIndex + 1) % totalFrames; + }); } void AnnotatedCameraWidget::updateFrogPilotWidgets() { @@ -1043,6 +1048,39 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets() { useViennaSLCSign = scene.use_vienna_slc_sign; trafficModeActive = scene.traffic_mode_active; + + turnSignalLeft = scene.turn_signal_left; + turnSignalRight = scene.turn_signal_right; + + if (customSignals != scene.custom_signals) { + customSignals = scene.custom_signals; + + QString themePath; + + themePath = QString("../frogpilot/assets/custom_themes/%1/images").arg( + themeConfiguration.find(customSignals) != themeConfiguration.end() ? + std::get<0>(themeConfiguration[customSignals]) : ""); + + const QStringList imagePaths = { + themePath + "/turn_signal_1.png", + themePath + "/turn_signal_2.png", + themePath + "/turn_signal_3.png", + themePath + "/turn_signal_4.png" + }; + + signalImgVector.clear(); + signalImgVector.reserve(2 * imagePaths.size() + 2); + + for (const QString &imagePath : imagePaths) { + QPixmap pixmap(imagePath); + signalImgVector.push_back(pixmap); + signalImgVector.push_back(pixmap.transformed(QTransform().scale(-1, 1))); + } + + const QPixmap blindSpotPixmap(themePath + "/turn_signal_1_red.png"); + signalImgVector.push_back(blindSpotPixmap); + signalImgVector.push_back(blindSpotPixmap.transformed(QTransform().scale(-1, 1))); + } } void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) { @@ -1050,6 +1088,15 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) { drawStatusBar(p); } + if (customSignals != 0 && (turnSignalLeft || turnSignalRight)) { + if (!animationTimer->isActive()) { + animationTimer->start(totalFrames * 11); // 440 milliseconds per loop; syncs up perfectly with my 2019 Lexus ES 350 turn signal clicks + } + drawTurnSignals(p); + } else if (animationTimer->isActive()) { + animationTimer->stop(); + } + if (scene.speed_limit_changed) { drawSLCConfirmation(p); } @@ -1432,3 +1479,28 @@ void AnnotatedCameraWidget::drawStatusBar(QPainter &p) { p.restore(); } + +void AnnotatedCameraWidget::drawTurnSignals(QPainter &p) { + constexpr int signalHeight = 480; + constexpr int signalWidth = 360; + + p.setRenderHint(QPainter::Antialiasing); + + int baseYPosition = (height() - signalHeight) / 2 + (showAlwaysOnLateralStatusBar || showConditionalExperimentalStatusBar || roadNameUI ? 225 : 300) - alertSize; + int leftSignalXPosition = 75 + width() - signalWidth - 300 * (blindSpotLeft ? 0 : animationFrameIndex); + int rightSignalXPosition = -75 + 300 * (blindSpotRight ? 0 : animationFrameIndex); + + if (animationFrameIndex < signalImgVector.size()) { + auto drawSignal = [&](bool signalActivated, int xPosition, bool flip, bool blindspot) { + if (signalActivated) { + int uniqueImages = signalImgVector.size() / 4; + int index = (blindspot ? 2 * uniqueImages : 2 * animationFrameIndex % totalFrames) + (flip ? 1 : 0); + QPixmap &signal = signalImgVector[index]; + p.drawPixmap(xPosition, baseYPosition, signalWidth, signalHeight, signal); + } + }; + + drawSignal(turnSignalLeft, leftSignalXPosition, false, blindSpotLeft); + drawSignal(turnSignalRight, rightSignalXPosition, true, blindSpotRight); + } +} diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h index f427fb0..22dd785 100644 --- a/selfdrive/ui/qt/onroad.h +++ b/selfdrive/ui/qt/onroad.h @@ -190,6 +190,7 @@ private: void drawSLCConfirmation(QPainter &p); void drawStatusBar(QPainter &p); + void drawTurnSignals(QPainter &p); // FrogPilot variables Params paramsMemory{"/dev/shm/params"}; @@ -215,6 +216,8 @@ private: bool slcOverridden; bool speedLimitController; bool trafficModeActive; + bool turnSignalLeft; + bool turnSignalRight; bool useViennaSLCSign; bool vtscControllingCurve; @@ -230,12 +233,19 @@ private: int alertSize; int conditionalStatus; int customColors; + int customSignals; + int totalFrames = 8; QString accelerationUnit; QString leadDistanceUnit; QString leadSpeedUnit; + size_t animationFrameIndex; + std::unordered_map>> themeConfiguration; + std::vector signalImgVector; + + QTimer *animationTimer; inline QColor blueColor(int alpha = 255) { return QColor(0, 150, 255, alpha); } inline QColor greenColor(int alpha = 242) { return QColor(23, 134, 68, alpha); } diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 8bd8a25..57303e0 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -225,6 +225,8 @@ static void update_state(UIState *s) { scene.parked = carState.getGearShifter() == cereal::CarState::GearShifter::PARK; scene.standstill = carState.getStandstill(); scene.steering_angle_deg = -carState.getSteeringAngleDeg(); + scene.turn_signal_left = carState.getLeftBlinker(); + scene.turn_signal_right = carState.getRightBlinker(); } if (sm.updated("controlsState")) { auto controlsState = sm["controlsState"].getControlsState(); @@ -317,6 +319,7 @@ void ui_update_frogpilot_params(UIState *s) { bool custom_theme = params.getBool("CustomTheme"); scene.custom_colors = custom_theme ? params.getInt("CustomColors") : 0; scene.custom_icons = custom_theme ? params.getInt("CustomIcons") : 0; + scene.custom_signals = custom_theme ? params.getInt("CustomSignals") : 0; scene.disable_smoothing_mtsc = params.getBool("MTSCEnabled") && params.getBool("DisableMTSCSmoothing"); scene.disable_smoothing_vtsc = params.getBool("VisionTurnControl") && params.getBool("DisableVTSCSmoothing"); diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index 1992b74..31159d0 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -224,6 +224,8 @@ typedef struct UIScene { bool tethering_enabled; bool traffic_mode; bool traffic_mode_active; + bool turn_signal_left; + bool turn_signal_right; bool use_kaofui_icons; bool use_vienna_slc_sign; bool vtsc_controlling_curve; @@ -245,6 +247,7 @@ typedef struct UIScene { int conditional_status; int custom_colors; int custom_icons; + int custom_signals; int steering_angle_deg; int wheel_icon;