diff --git a/selfdrive/controls/controlsd.py b/selfdrive/controls/controlsd.py index 6fa2d6ff0..b17be8ccf 100644 --- a/selfdrive/controls/controlsd.py +++ b/selfdrive/controls/controlsd.py @@ -190,6 +190,7 @@ class Controls: self.always_on_lateral_active = False self.drive_added = False + self.onroad_distance_pressed = False self.openpilot_crashed_triggered = False self.update_toggles = False @@ -671,12 +672,13 @@ 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 any(not be.pressed and be.type == ButtonType.gapAdjustCruise for be in CS.buttonEvents) or self.onroad_distance_pressed: menu_open = self.display_timer > 0 or not self.sm['frogpilotCarState'].hasMenu - if menu_open: + if not self.params_memory.get_bool("OnroadDistanceButtonPressed") and menu_open: self.personality = (self.personality - 1) % 3 self.params.put_nonblocking('LongitudinalPersonality', str(self.personality)) self.display_timer = 350 + self.onroad_distance_pressed = self.params_memory.get_bool("OnroadDistanceButtonPressed") self.display_timer -= 1 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/ui/qt/onroad/annotated_camera.cc b/selfdrive/ui/qt/onroad/annotated_camera.cc index 3a8f55d83..2694a736f 100644 --- a/selfdrive/ui/qt/onroad/annotated_camera.cc +++ b/selfdrive/ui/qt/onroad/annotated_camera.cc @@ -286,6 +286,8 @@ void AnnotatedCameraWidget::drawDriverState(QPainter &painter, const UIState *s) int x = rightHandDM ? width() - offset : offset; if (rightHandDM && map_settings_btn->isEnabled()) { x -= 250; + } else if (onroadDistanceButton) { + x += 250; } offset += showAlwaysOnLateralStatusBar || showConditionalExperimentalStatusBar ? 25 : 0; int y = height() - offset; @@ -465,6 +467,9 @@ void AnnotatedCameraWidget::showEvent(QShowEvent *event) { void AnnotatedCameraWidget::initializeFrogPilotWidgets() { bottom_layout = new QHBoxLayout(); + distance_btn = new DistanceButton(this); + bottom_layout->addWidget(distance_btn, 0, Qt::AlignBottom | Qt::AlignLeft); + QSpacerItem *spacer = new QSpacerItem(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum); bottom_layout->addItem(spacer); @@ -514,6 +519,14 @@ void AnnotatedCameraWidget::paintFrogPilotWidgets(QPainter &painter, const UISce map_settings_btn_bottom->setVisible(!hideBottomIcons); bottom_layout->setAlignment(map_settings_btn_bottom, (rightHandDM ? Qt::AlignLeft : Qt::AlignRight) | Qt::AlignBottom); } + + onroadDistanceButton = scene.onroad_distance_button; + bool enableDistanceButton = onroadDistanceButton && !hideBottomIcons; + distance_btn->setVisible(enableDistanceButton); + if (enableDistanceButton) { + distance_btn->updateState(scene); + bottom_layout->setAlignment(distance_btn, (rightHandDM ? Qt::AlignRight : Qt::AlignLeft) | Qt::AlignBottom); + } } void AnnotatedCameraWidget::drawStatusBar(QPainter &p) { diff --git a/selfdrive/ui/qt/onroad/annotated_camera.h b/selfdrive/ui/qt/onroad/annotated_camera.h index 31d8bdb39..cf31b91d3 100644 --- a/selfdrive/ui/qt/onroad/annotated_camera.h +++ b/selfdrive/ui/qt/onroad/annotated_camera.h @@ -50,11 +50,14 @@ private: // FrogPilot variables Params paramsMemory{"/dev/shm/params"}; + DistanceButton *distance_btn; + QHBoxLayout *bottom_layout; bool alwaysOnLateralActive; bool experimentalMode; bool mapOpen; + bool onroadDistanceButton; bool showAlwaysOnLateralStatusBar; bool showConditionalExperimentalStatusBar; diff --git a/selfdrive/ui/qt/onroad/buttons.cc b/selfdrive/ui/qt/onroad/buttons.cc index 177b9b92d..764b49638 100644 --- a/selfdrive/ui/qt/onroad/buttons.cc +++ b/selfdrive/ui/qt/onroad/buttons.cc @@ -65,3 +65,58 @@ void MapSettingsButton::paintEvent(QPaintEvent *event) { QPainter p(this); drawIcon(p, QPoint(btn_size / 2, btn_size / 2), settings_img, QColor(0, 0, 0, 166), isDown() ? 0.6 : 1.0); } + +// FrogPilot buttons + +// DistanceButton +DistanceButton::DistanceButton(QWidget *parent) : QPushButton(parent) { + setFixedSize(btn_size * 1.5, btn_size * 1.5); + + 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(const UIScene &scene) { + bool stateChanged = (trafficModeActive != scene.traffic_mode_active) || + (personality != static_cast(scene.personality) + 1 && !trafficModeActive); + + if (stateChanged) { + personality = static_cast(scene.personality) + 1; + trafficModeActive = scene.traffic_mode_active; + + int profile = trafficModeActive ? 0 : personality; + std::tie(profileImage, profileText) = (scene.use_kaofui_icons ? profileDataKaofui : profileData)[profile]; + + transitionTimer.restart(); + update(); + } else if (transitionTimer.isValid()) { + update(); + } else { + return; + } +} + +void DistanceButton::paintEvent(QPaintEvent *event) { + QPainter p(this); + p.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); + + int elapsed = transitionTimer.elapsed(); + qreal textOpacity = qBound(0.0, 1.0 - ((elapsed - 3000.0) / 1000.0), 1.0); + qreal imageOpacity = 1.0 - textOpacity; + + p.setOpacity(textOpacity); + p.setFont(InterFont(40, QFont::Bold)); + p.setPen(Qt::white); + QRect textRect(-25, 0, width(), height() + btn_size / 2); + p.drawText(textRect, Qt::AlignCenter, profileText); + + drawIcon(p, QPoint((btn_size / 2) * 1.25, btn_size), profileImage, Qt::transparent, imageOpacity); +} diff --git a/selfdrive/ui/qt/onroad/buttons.h b/selfdrive/ui/qt/onroad/buttons.h index 8a3c8dec6..ecd73efbb 100644 --- a/selfdrive/ui/qt/onroad/buttons.h +++ b/selfdrive/ui/qt/onroad/buttons.h @@ -42,3 +42,42 @@ private: }; void drawIcon(QPainter &p, const QPoint ¢er, const QPixmap &img, const QBrush &bg, float opacity); + +// FrogPilot buttons +class DistanceButton : public QPushButton { + Q_OBJECT + +public: + explicit DistanceButton(QWidget *parent = 0); + void updateState(const UIScene &scene); + +private: + void buttonPressed(); + void buttonReleased(); + void paintEvent(QPaintEvent *event) override; + + bool trafficModeActive; + + int personality; + + QElapsedTimer transitionTimer; + + QPixmap profileImage; + QString profileText; + + Params paramsMemory{"/dev/shm/params"}; + + const QVector> profileData = { + {QPixmap("../frogpilot/assets/other_images/traffic.png"), "Traffic"}, + {QPixmap("../frogpilot/assets/other_images/aggressive.png"), "Aggressive"}, + {QPixmap("../frogpilot/assets/other_images/standard.png"), "Standard"}, + {QPixmap("../frogpilot/assets/other_images/relaxed.png"), "Relaxed"} + }; + + const QVector> profileDataKaofui = { + {QPixmap("../frogpilot/assets/other_images/traffic_kaofui.png"), "Traffic"}, + {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"} + }; +}; diff --git a/selfdrive/ui/ui.cc b/selfdrive/ui/ui.cc index eecb05d24..28876cf14 100644 --- a/selfdrive/ui/ui.cc +++ b/selfdrive/ui/ui.cc @@ -278,6 +278,10 @@ void ui_update_frogpilot_params(UIState *s, Params ¶ms) { scene.conditional_speed_lead = scene.conditional_experimental ? params.getInt("CESpeedLead") : 0; scene.show_cem_status_bar = scene.conditional_experimental && !params.getBool("HideCEMStatusBar"); + bool driving_personalities = scene.longitudinal_control && params.getBool("DrivingPersonalities"); + scene.onroad_distance_button = driving_personalities && params.getBool("OnroadDistanceButton"); + scene.use_kaofui_icons = scene.onroad_distance_button && params.getBool("KaofuiIcons"); + scene.tethering_config = params.getInt("TetheringEnabled"); if (scene.tethering_config == 2) { WifiManager(s).setTetheringEnabled(true); diff --git a/selfdrive/ui/ui.h b/selfdrive/ui/ui.h index aa64852d9..afebca34e 100644 --- a/selfdrive/ui/ui.h +++ b/selfdrive/ui/ui.h @@ -124,11 +124,13 @@ typedef struct UIScene { bool experimental_mode; bool map_open; bool online; + bool onroad_distance_button; bool parked; bool right_hand_drive; bool show_aol_status_bar; bool show_cem_status_bar; bool tethering_enabled; + bool use_kaofui_icons; int alert_size; int conditional_speed;