diff --git a/common/params.cc b/common/params.cc index 296069571..2dfb24130 100644 --- a/common/params.cc +++ b/common/params.cc @@ -298,6 +298,7 @@ std::unordered_map keys = { {"HigherBitrate", PERSISTENT}, {"HolidayThemes", PERSISTENT}, {"IncreaseThermalLimits", PERSISTENT}, + {"KaofuiIcons", PERSISTENT}, {"LaneChangeTime", PERSISTENT}, {"LaneDetectionWidth", PERSISTENT}, {"LaneLinesWidth", PERSISTENT}, @@ -332,6 +333,8 @@ std::unordered_map keys = { {"NumericalTemp", PERSISTENT}, {"OfflineMode", PERSISTENT}, {"OneLaneChange", PERSISTENT}, + {"OnroadDistanceButton", PERSISTENT}, + {"OnroadDistanceButtonPressed", PERSISTENT}, {"PathEdgeWidth", PERSISTENT}, {"PathWidth", PERSISTENT}, {"PauseAOLOnBrake", PERSISTENT}, diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index ec8fdc822..a6bf29e74 100644 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -190,6 +190,7 @@ class Controls: self.always_on_lateral_main = self.always_on_lateral and self.params.get_bool("AlwaysOnLateralMain") self.holiday_theme_alerted = False + self.onroad_distance_pressed = False self.previously_enabled = False self.previous_lead_distance = 0 @@ -703,10 +704,11 @@ class Controls: # decrement personality on distance button press if self.CP.openpilotLongitudinalControl: - if any(not be.pressed and be.type == ButtonType.gapAdjustCruise for be in CS.buttonEvents): - if not self.params_memory.get_bool("DistanceLongPressed"): + if any(not be.pressed and be.type == ButtonType.gapAdjustCruise for be in CS.buttonEvents) or self.onroad_distance_pressed: + if not (self.params_memory.get_bool("DistanceLongPressed") or self.params_memory.get_bool("OnroadDistanceButtonPressed")): self.personality = (self.personality - 1) % 3 self.params.put_nonblocking('LongitudinalPersonality', str(self.personality)) + self.onroad_distance_pressed = self.params_memory.get_bool("OnroadDistanceButtonPressed") return CC, lac_log diff --git a/selfdrive/frogpilot/assets/other_images/aggressive.png b/selfdrive/frogpilot/assets/other_images/aggressive.png new file mode 100644 index 000000000..42d005538 Binary files /dev/null and b/selfdrive/frogpilot/assets/other_images/aggressive.png differ diff --git a/selfdrive/frogpilot/assets/other_images/aggressive_kaofui.png b/selfdrive/frogpilot/assets/other_images/aggressive_kaofui.png new file mode 100644 index 000000000..52da0a46a Binary files /dev/null and b/selfdrive/frogpilot/assets/other_images/aggressive_kaofui.png differ diff --git a/selfdrive/frogpilot/assets/other_images/relaxed.png b/selfdrive/frogpilot/assets/other_images/relaxed.png new file mode 100644 index 000000000..fb77ec3ca Binary files /dev/null and b/selfdrive/frogpilot/assets/other_images/relaxed.png differ diff --git a/selfdrive/frogpilot/assets/other_images/relaxed_kaofui.png b/selfdrive/frogpilot/assets/other_images/relaxed_kaofui.png new file mode 100644 index 000000000..897ad3d2a Binary files /dev/null and b/selfdrive/frogpilot/assets/other_images/relaxed_kaofui.png differ diff --git a/selfdrive/frogpilot/assets/other_images/standard.png b/selfdrive/frogpilot/assets/other_images/standard.png new file mode 100644 index 000000000..57c5a67e7 Binary files /dev/null and b/selfdrive/frogpilot/assets/other_images/standard.png differ diff --git a/selfdrive/frogpilot/assets/other_images/standard_kaofui.png b/selfdrive/frogpilot/assets/other_images/standard_kaofui.png new file mode 100644 index 000000000..481e25772 Binary files /dev/null and b/selfdrive/frogpilot/assets/other_images/standard_kaofui.png differ diff --git a/selfdrive/frogpilot/ui/qt/offroad/control_settings.cc b/selfdrive/frogpilot/ui/qt/offroad/control_settings.cc index aceb56d9e..8b6c13194 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/control_settings.cc +++ b/selfdrive/frogpilot/ui/qt/offroad/control_settings.cc @@ -103,6 +103,7 @@ FrogPilotControlsPanel::FrogPilotControlsPanel(SettingsWindow *parent) : FrogPil {"CustomCruise", tr("Cruise Increase Interval"), tr("Set a custom interval to increase the max set speed by."), ""}, {"CustomCruiseLong", tr("Cruise Increase Interval (Long Press)"), tr("Set a custom interval to increase the max set speed by when holding down the cruise increase button."), ""}, {"DisableOnroadUploads", tr("Disable Onroad Uploads"), tr("Prevent uploads to comma connect unless you're offroad and connected to Wi-Fi."), ""}, + {"OnroadDistanceButton", tr("Onroad Distance Button"), tr("Simulate a distance button via the onroad UI to control personalities, 'Experimental Mode', and 'Traffic Mode'."), ""}, {"ReverseCruise", tr("Reverse Cruise Increase"), tr("Reverses the 'long press' functionality logic to increase the max set speed by 5 instead of 1. Useful to increase the max speed quickly."), ""}, }; @@ -503,6 +504,10 @@ FrogPilotControlsPanel::FrogPilotControlsPanel(SettingsWindow *parent) : FrogPil for (auto &[key, toggle] : toggles) { std::set modifiedQolKeys = qolKeys; + if (!hasOpenpilotLongitudinal) { + modifiedQolKeys.erase("OnroadDistanceButton"); + } + if (!hasPCMCruise) { modifiedQolKeys.erase("ReverseCruise"); } else { @@ -518,6 +523,10 @@ FrogPilotControlsPanel::FrogPilotControlsPanel(SettingsWindow *parent) : FrogPil toggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 99, std::map(), this, false, tr(" mph")); } else if (param == "CustomCruiseLong") { toggle = new FrogPilotParamValueControl(param, title, desc, icon, 1, 99, std::map(), this, false, tr(" mph")); + } else if (param == "OnroadDistanceButton") { + std::vector onroadDistanceToggles{"KaofuiIcons"}; + std::vector onroadDistanceToggleNames{tr("Kaofui's Icons")}; + toggle = new FrogPilotParamToggleControl(param, title, desc, icon, onroadDistanceToggles, onroadDistanceToggleNames); } else if (param == "ReverseCruise") { std::vector reverseCruiseToggles{"ReverseCruiseUI"}; std::vector reverseCruiseNames{tr("Control Via UI")}; diff --git a/selfdrive/frogpilot/ui/qt/offroad/control_settings.h b/selfdrive/frogpilot/ui/qt/offroad/control_settings.h index f341317b2..bc0947285 100644 --- a/selfdrive/frogpilot/ui/qt/offroad/control_settings.h +++ b/selfdrive/frogpilot/ui/qt/offroad/control_settings.h @@ -45,7 +45,7 @@ private: std::set lateralTuneKeys = {"ForceAutoTune", "NNFF", "NNFFLite"}; std::set longitudinalTuneKeys = {"AccelerationProfile", "AggressiveAcceleration", "DecelerationProfile", "StoppingDistance"}; std::set mtscKeys = {"DisableMTSCSmoothing", "MTSCAggressiveness", "MTSCCurvatureCheck"}; - std::set qolKeys = {"CustomCruise", "CustomCruiseLong", "DisableOnroadUploads", "ReverseCruise"}; + std::set qolKeys = {"CustomCruise", "CustomCruiseLong", "DisableOnroadUploads", "OnroadDistanceButton", "ReverseCruise"}; std::set speedLimitControllerKeys = {}; std::set speedLimitControllerControlsKeys = {}; std::set speedLimitControllerQOLKeys = {}; diff --git a/selfdrive/ui/qt/onroad.cc b/selfdrive/ui/qt/onroad.cc index fb53589ec..7cfed75ce 100644 --- a/selfdrive/ui/qt/onroad.cc +++ b/selfdrive/ui/qt/onroad.cc @@ -854,6 +854,7 @@ void AnnotatedCameraWidget::drawDriverState(QPainter &painter, const UIState *s) // base icon int offset = UI_BORDER_SIZE + btn_size / 2; int x = rightHandDM ? width() - offset : offset; + x += onroadDistanceButton ? 250 : 0; offset += showAlwaysOnLateralStatusBar || showConditionalExperimentalStatusBar ? 25 : 0; int y = height() - offset; float opacity = dmActive ? 0.65 : 0.2; @@ -1055,6 +1056,9 @@ void AnnotatedCameraWidget::showEvent(QShowEvent *event) { void AnnotatedCameraWidget::initializeFrogPilotWidgets() { bottom_layout = new QHBoxLayout(); + distance_btn = new DistanceButton(this); + bottom_layout->addWidget(distance_btn); + QSpacerItem *spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); bottom_layout->addItem(spacer); @@ -1154,6 +1158,8 @@ void AnnotatedCameraWidget::updateFrogPilotWidgets() { mapOpen = scene.map_open; bigMapOpen = mapOpen && scene.big_map; + onroadDistanceButton = scene.onroad_distance_button; + turnSignalLeft = scene.turn_signal_left; turnSignalRight = scene.turn_signal_right; @@ -1220,6 +1226,13 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &p) { bottom_layout->setAlignment(compass_img, (rightHandDM ? Qt::AlignLeft : Qt::AlignRight)); } + bool enableDistanceButton = onroadDistanceButton && !hideBottomIcons; + distance_btn->setVisible(enableDistanceButton); + if (enableDistanceButton) { + distance_btn->updateState(); + bottom_layout->setAlignment(distance_btn, (rightHandDM ? Qt::AlignRight : Qt::AlignLeft)); + } + map_settings_btn_bottom->setEnabled(map_settings_btn->isEnabled()); if (map_settings_btn_bottom->isEnabled()) { map_settings_btn_bottom->setVisible(!hideBottomIcons && !compass); @@ -1333,6 +1346,69 @@ void Compass::paintEvent(QPaintEvent *event) { } } +DistanceButton::DistanceButton(QWidget *parent) : QPushButton(parent), scene(uiState()->scene) { + setFixedSize(btn_size * 1.5, btn_size * 1.5); + + profile_data = { + {QPixmap("../frogpilot/assets/other_images/aggressive.png"), "Aggressive"}, + {QPixmap("../frogpilot/assets/other_images/standard.png"), "Standard"}, + {QPixmap("../frogpilot/assets/other_images/relaxed.png"), "Relaxed"} + }; + + profile_data_kaofui = { + {QPixmap("../frogpilot/assets/other_images/aggressive_kaofui.png"), "Aggressive"}, + {QPixmap("../frogpilot/assets/other_images/standard_kaofui.png"), "Standard"}, + {QPixmap("../frogpilot/assets/other_images/relaxed_kaofui.png"), "Relaxed"} + }; + + transitionTimer.start(); + + connect(this, &QPushButton::pressed, this, &DistanceButton::buttonPressed); + connect(this, &QPushButton::released, this, &DistanceButton::buttonReleased); +} + +void DistanceButton::buttonPressed() { + paramsMemory.putBool("OnroadDistanceButtonPressed", true); +} + +void DistanceButton::buttonReleased() { + paramsMemory.putBool("OnroadDistanceButtonPressed", false); +} + +void DistanceButton::updateState() { + if (personality != static_cast(scene.personality)) { + transitionTimer.restart(); + } + + personality = static_cast(scene.personality); +} + +void DistanceButton::paintEvent(QPaintEvent *event) { + QPainter p(this); + p.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + + constexpr qreal fadeDuration = 1000.0; + constexpr qreal textDuration = 3000.0; + int elapsed = transitionTimer.elapsed(); + + qreal textOpacity = qBound(0.0, 1.0 - ((elapsed - textDuration) / fadeDuration), 1.0); + qreal imageOpacity = qBound(0.0, (elapsed - textDuration) / fadeDuration, 1.0); + + auto &[profileImage, profileText] = scene.use_kaofui_icons ? profile_data_kaofui[personality] : profile_data[personality]; + + if (textOpacity != 0.0) { + p.setOpacity(textOpacity); + p.setFont(InterFont(40, QFont::Bold)); + p.setPen(Qt::white); + QRect textRect(-25, 0, width(), height() + 95); + p.drawText(textRect, Qt::AlignCenter, profileText); + } + + if (imageOpacity != 0.0) { + drawIcon(p, QPoint((btn_size / 2) * 1.25, btn_size / 2 + 95), profileImage, Qt::transparent, imageOpacity); + } +} + void AnnotatedCameraWidget::drawLeadInfo(QPainter &p) { static QElapsedTimer timer; static bool isFiveSecondsPassed = false; diff --git a/selfdrive/ui/qt/onroad.h b/selfdrive/ui/qt/onroad.h index 0e7d44f58..eac4279a2 100644 --- a/selfdrive/ui/qt/onroad.h +++ b/selfdrive/ui/qt/onroad.h @@ -59,6 +59,30 @@ private: QPixmap staticElements; }; +class DistanceButton : public QPushButton { +public: + explicit DistanceButton(QWidget *parent = nullptr); + + void buttonPressed(); + void buttonReleased(); + void updateState(); + +protected: + void paintEvent(QPaintEvent *event) override; + +private: + Params paramsMemory{"/dev/shm/params"}; + + UIScene &scene; + + int personality; + + QElapsedTimer transitionTimer; + + QVector> profile_data; + QVector> profile_data_kaofui; +}; + class ExperimentalButton : public QPushButton { Q_OBJECT @@ -146,6 +170,7 @@ private: UIScene &scene; Compass *compass_img; + DistanceButton *distance_btn; QHBoxLayout *bottom_layout; @@ -157,6 +182,7 @@ private: bool experimentalMode; bool leadInfo; bool mapOpen; + bool onroadDistanceButton; bool showAlwaysOnLateralStatusBar; bool showConditionalExperimentalStatusBar; bool turnSignalLeft; diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index 4c8a43f22..093e3626e 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -348,6 +348,8 @@ void ui_update_frogpilot_params(UIState *s) { scene.unlimited_road_ui_length = scene.model_ui && params.getBool("UnlimitedLength"); bool quality_of_life_controls = params.getBool("QOLControls"); + scene.onroad_distance_button = scene.longitudinal_control && quality_of_life_controls && params.getBool("OnroadDistanceButton"); + scene.use_kaofui_icons = scene.onroad_distance_button && params.getBool("KaofuiIcons"); scene.reverse_cruise = quality_of_life_controls && params.getBool("ReverseCruise"); scene.reverse_cruise_ui = scene.reverse_cruise && params.getBool("ReverseCruiseUI"); diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index a39db7630..2f9a78e14 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -201,6 +201,7 @@ typedef struct UIScene { bool model_ui; bool numerical_temp; bool online; + bool onroad_distance_button; bool reverse; bool reverse_cruise; bool reverse_cruise_ui; @@ -212,6 +213,7 @@ typedef struct UIScene { bool turn_signal_left; bool turn_signal_right; bool unlimited_road_ui_length; + bool use_kaofui_icons; bool use_si; float acceleration;