From ed9c14616aafbfd9223e6af192569525b771683b Mon Sep 17 00:00:00 2001 From: Brian Hanson Date: Sat, 11 Apr 2026 07:41:47 +0000 Subject: [PATCH] offroad UI: replace stock home with clean grid launcher - Strip out date, version, update/alert widgets from OffroadHome - Replace with grid layout: Dashcam and Settings buttons - Skip sidebar when tapping splash screen - Settings button still opens original comma settings - Dashcam button placeholder (viewer not yet built) - Add DASHCAM_PROJECT.md with plans for footage viewer Co-Authored-By: Claude Opus 4.6 (1M context) --- DASHCAM_PROJECT.md | 49 +++++++ selfdrive/ui/qt/home.cc | 166 ++++++----------------- selfdrive/ui/qt/home.h | 18 --- selfdrive/ui/translations/main_ar.ts | 6 +- selfdrive/ui/translations/main_de.ts | 6 +- selfdrive/ui/translations/main_fr.ts | 6 +- selfdrive/ui/translations/main_ja.ts | 6 +- selfdrive/ui/translations/main_ko.ts | 6 +- selfdrive/ui/translations/main_pt-BR.ts | 6 +- selfdrive/ui/translations/main_th.ts | 6 +- selfdrive/ui/translations/main_tr.ts | 6 +- selfdrive/ui/translations/main_zh-CHS.ts | 6 +- selfdrive/ui/translations/main_zh-CHT.ts | 6 +- 13 files changed, 123 insertions(+), 170 deletions(-) create mode 100644 DASHCAM_PROJECT.md diff --git a/DASHCAM_PROJECT.md b/DASHCAM_PROJECT.md new file mode 100644 index 0000000..39f7466 --- /dev/null +++ b/DASHCAM_PROJECT.md @@ -0,0 +1,49 @@ +# Dashcam Project + +## Status + +### Completed (2026-04-11) + +- Disabled comma training data video encoding (`encoderd`) — CAN/sensor logs still recorded +- Re-enabled FrogPilot OMX screen recorder (H.264 MP4, 1440x720, 2Mbps, hardware encoded) +- Auto-start/stop recording tied to car ignition (`scene.started`) +- `ScreenRecorderDebug` param for bench testing without car connected +- Hidden all recorder UI elements — invisible to driver +- Videos saved to `/data/media/0/videos/YYYYMMDD-HHMMSS.mp4`, 3-minute segments +- Deleter updated: 9 GB free space threshold, rotates oldest videos first +- Cleaned up offroad UI: replaced stock home screen with grid launcher (Settings + Dashcam buttons) + +### Next: Dashcam Footage Viewer + +Build a native Qt widget accessible from the offroad home screen "Dashcam" button. + +**Requirements:** +- List MP4 files from `/data/media/0/videos/` sorted newest first +- Tap a file to play it back using Qt Multimedia (`QMediaPlayer` + `QVideoWidget`) +- Only accessible when offroad (car in park or off) +- Back button to return to offroad home +- No rotation hacks needed — native Qt widget in existing UI tree handles rotation correctly + +**Architecture:** +- New widget class (e.g. `DashcamViewer`) added to `selfdrive/ui/qt/` +- Wire into `HomeWindow`'s `QStackedLayout` alongside `home`, `onroad`, `ready`, `driver_view` +- Dashcam button in `OffroadHome` switches `slayout` to the viewer +- Viewer has a file list view and a playback view (sub-stacked layout) +- Back button returns to `OffroadHome` +- Build: add to `qt_src` list in `selfdrive/ui/SConscript`, link `Qt5Multimedia` + +**Previous webview attempts (abandoned):** +- Brian tried QWebEngineView for browser-based playback but the WebEngine subprocess renders independently of Qt's widget tree +- Screen rotation (`view.rotate(90)`, Wayland `wl_surface_set_buffer_transform`) did not work for WebEngine content +- Native Qt widget approach avoids this problem entirely + +## Key Files + +| File | Role | +|------|------| +| `selfdrive/frogpilot/screenrecorder/screenrecorder.cc` | Recording logic, auto-start/stop | +| `selfdrive/frogpilot/screenrecorder/omx_encoder.cc` | OMX H.264 hardware encoder | +| `selfdrive/ui/qt/onroad.cc` | Timer driving frame capture | +| `selfdrive/ui/qt/home.cc` | Offroad home with Dashcam button | +| `system/loggerd/deleter.py` | Storage rotation | +| `/data/media/0/videos/` | Video output directory | diff --git a/selfdrive/ui/qt/home.cc b/selfdrive/ui/qt/home.cc index d89f056..81226a9 100755 --- a/selfdrive/ui/qt/home.cc +++ b/selfdrive/ui/qt/home.cc @@ -5,12 +5,7 @@ #include #include -#include "selfdrive/ui/qt/offroad/experimental_mode.h" #include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/widgets/drive_stats.h" -#include "system/hardware/hw.h" - -#include // HomeWindow: the container for the offroad and onroad UIs @@ -102,9 +97,9 @@ void HomeWindow::mousePressEvent(QMouseEvent* e) { // CLEARPILOT todo - tap on main goes straight to settings // Unless we click a debug widget. - // CLEARPILOT - click ready shows home + // CLEARPILOT - click ready shows home (no sidebar) if (!onroad->isVisible() && ready->isVisible()) { - sidebar->setVisible(true); + sidebar->setVisible(false); slayout->setCurrentWidget(home); } @@ -119,132 +114,59 @@ void HomeWindow::mouseDoubleClickEvent(QMouseEvent* e) { // const SubMaster &sm = *(uiState()->sm); } -// OffroadHome: the offroad home page +// CLEARPILOT: OffroadHome — clean grid launcher OffroadHome::OffroadHome(QWidget* parent) : QFrame(parent) { QVBoxLayout* main_layout = new QVBoxLayout(this); - main_layout->setContentsMargins(40, 40, 40, 40); + main_layout->setContentsMargins(80, 80, 80, 80); + main_layout->setSpacing(0); - // top header - QHBoxLayout* header_layout = new QHBoxLayout(); - header_layout->setContentsMargins(0, 0, 0, 0); - header_layout->setSpacing(16); + // grid of launcher buttons + QGridLayout *grid = new QGridLayout(); + grid->setSpacing(40); - update_notif = new QPushButton(tr("UPDATE")); - update_notif->setVisible(false); - update_notif->setStyleSheet("background-color: #364DEF;"); - QObject::connect(update_notif, &QPushButton::clicked, [=]() { center_layout->setCurrentIndex(1); }); - header_layout->addWidget(update_notif, 0, Qt::AlignHCenter | Qt::AlignLeft); + // Dashcam viewer button + QPushButton *dashcam_btn = new QPushButton("Dashcam"); + dashcam_btn->setFixedSize(400, 300); + dashcam_btn->setStyleSheet(R"( + QPushButton { + background-color: #333333; + color: white; + border-radius: 20px; + font-size: 48px; + font-weight: 600; + } + QPushButton:pressed { + background-color: #555555; + } + )"); + grid->addWidget(dashcam_btn, 0, 0); - alert_notif = new QPushButton(); - alert_notif->setVisible(false); - alert_notif->setStyleSheet("background-color: #E22C2C;"); - QObject::connect(alert_notif, &QPushButton::clicked, [=] { center_layout->setCurrentIndex(2); }); - header_layout->addWidget(alert_notif, 0, Qt::AlignHCenter | Qt::AlignLeft); + // Settings button + QPushButton *settings_btn = new QPushButton("Settings"); + settings_btn->setFixedSize(400, 300); + settings_btn->setStyleSheet(R"( + QPushButton { + background-color: #333333; + color: white; + border-radius: 20px; + font-size: 48px; + font-weight: 600; + } + QPushButton:pressed { + background-color: #555555; + } + )"); + QObject::connect(settings_btn, &QPushButton::clicked, [=]() { emit openSettings(); }); + grid->addWidget(settings_btn, 0, 1); - date = new ElidedLabel(); - header_layout->addWidget(date, 0, Qt::AlignHCenter | Qt::AlignLeft); - - version = new ElidedLabel(); - header_layout->addWidget(version, 0, Qt::AlignHCenter | Qt::AlignRight); - - main_layout->addLayout(header_layout); - - // main content - main_layout->addSpacing(25); - center_layout = new QStackedLayout(); - - QWidget *home_widget = new QWidget(this); - { - QHBoxLayout *home_layout = new QHBoxLayout(home_widget); - home_layout->setContentsMargins(0, 0, 0, 0); - home_layout->setSpacing(30); - - // // // Create a QWebEngineView - // QWebEngineView *web_view = new QWebEngineView(); - // web_view->load(QUrl("http://fark.com")); - - // // Add the QWebEngineView to the layout - // home_layout->addWidget(web_view); - } - center_layout->addWidget(home_widget); - - // add update & alerts widgets - update_widget = new UpdateAlert(); - QObject::connect(update_widget, &UpdateAlert::dismiss, [=]() { center_layout->setCurrentIndex(0); }); - center_layout->addWidget(update_widget); - alerts_widget = new OffroadAlert(); - QObject::connect(alerts_widget, &OffroadAlert::dismiss, [=]() { center_layout->setCurrentIndex(0); }); - center_layout->addWidget(alerts_widget); - - main_layout->addLayout(center_layout, 1); - - // set up refresh timer - timer = new QTimer(this); - timer->callOnTimeout(this, &OffroadHome::refresh); + main_layout->addStretch(); + main_layout->addLayout(grid); + main_layout->addStretch(); setStyleSheet(R"( - * { - color: white; - } OffroadHome { background-color: black; } - OffroadHome > QPushButton { - padding: 15px 30px; - border-radius: 5px; - font-size: 40px; - font-weight: 500; - } - OffroadHome > QLabel { - font-size: 55px; - } )"); } - -/* Refresh data on screen every 5 seconds. */ -void OffroadHome::showEvent(QShowEvent *event) { - refresh(); - timer->start(5 * 1000); -} - -void OffroadHome::hideEvent(QHideEvent *event) { - timer->stop(); -} - -void OffroadHome::refresh() { - QString model = QString::fromStdString(params.get("ModelName")); - - date->setText(QLocale(uiState()->language.mid(5)).toString(QDateTime::currentDateTime(), "dddd, MMMM d")); - version->setText(getBrand() + " v" + getVersion().left(14).trimmed() + " - " + model); - - // bool updateAvailable = update_widget->refresh(); - - int alerts = alerts_widget->refresh(); - - if (alerts > 0 && !alerts_widget->isVisible()) { - alerts_widget->setVisible(true); - } else if (alerts == 0 && alerts_widget->isVisible()) { - alerts_widget->setVisible(false); - } - - // pop-up new notification - // CLEARPILOT temp disabled update notifications - // int idx = center_layout->currentIndex(); - // if (!updateAvailable && !alerts && false) { - // idx = 0; - // } else if (updateAvailable && (!update_notif->isVisible() || (!alerts && idx == 2))) { - // idx = 1; - // } else if (alerts && (!alert_notif->isVisible() || (!updateAvailable && idx == 1))) { - // idx = 2; - // } - // center_layout->setCurrentIndex(idx); - - // CLEARPILOT temp disabled update notifications -// update_notif->setVisible(updateAvailable); -// alert_notif->setVisible(alerts); - alert_notif->setVisible(false); - if (alerts) { - alert_notif->setText(QString::number(alerts) + (alerts > 1 ? tr(" ALERTS") : tr(" ALERT"))); - } -} diff --git a/selfdrive/ui/qt/home.h b/selfdrive/ui/qt/home.h index 5e71b16..6f929eb 100755 --- a/selfdrive/ui/qt/home.h +++ b/selfdrive/ui/qt/home.h @@ -24,24 +24,6 @@ public: signals: void openSettings(int index = 0, const QString ¶m = ""); - -private: - void showEvent(QShowEvent *event) override; - void hideEvent(QHideEvent *event) override; - void refresh(); - - Params params; - - QTimer* timer; - ElidedLabel* version; - QStackedLayout* center_layout; - UpdateAlert *update_widget; - OffroadAlert* alerts_widget; - QPushButton* alert_notif; - QPushButton* update_notif; - - // FrogPilot variables - ElidedLabel* date; }; class HomeWindow : public QWidget { diff --git a/selfdrive/ui/translations/main_ar.ts b/selfdrive/ui/translations/main_ar.ts index b827526..c9a8856 100755 --- a/selfdrive/ui/translations/main_ar.ts +++ b/selfdrive/ui/translations/main_ar.ts @@ -818,15 +818,15 @@ OffroadHome UPDATE - تحديث + تحديث ALERTS - التنبهات + التنبهات ALERT - تنبيه + تنبيه diff --git a/selfdrive/ui/translations/main_de.ts b/selfdrive/ui/translations/main_de.ts index 415db15..875bc8d 100755 --- a/selfdrive/ui/translations/main_de.ts +++ b/selfdrive/ui/translations/main_de.ts @@ -771,15 +771,15 @@ OffroadHome UPDATE - Aktualisieren + Aktualisieren ALERTS - HINWEISE + HINWEISE ALERT - HINWEIS + HINWEIS diff --git a/selfdrive/ui/translations/main_fr.ts b/selfdrive/ui/translations/main_fr.ts index be97ab0..c76e64c 100755 --- a/selfdrive/ui/translations/main_fr.ts +++ b/selfdrive/ui/translations/main_fr.ts @@ -814,15 +814,15 @@ OffroadHome UPDATE - MISE À JOUR + MISE À JOUR ALERTS - ALERTES + ALERTES ALERT - ALERTE + ALERTE diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts index 40e25b5..27d807e 100755 --- a/selfdrive/ui/translations/main_ja.ts +++ b/selfdrive/ui/translations/main_ja.ts @@ -770,15 +770,15 @@ OffroadHome UPDATE - 更新 + 更新 ALERTS - 警告 + 警告 ALERT - 警告 + 警告 diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts index f943e65..072eee9 100755 --- a/selfdrive/ui/translations/main_ko.ts +++ b/selfdrive/ui/translations/main_ko.ts @@ -813,15 +813,15 @@ OffroadHome UPDATE - 업데이트 + 업데이트 ALERTS - 알림 + 알림 ALERT - 알림 + 알림 diff --git a/selfdrive/ui/translations/main_pt-BR.ts b/selfdrive/ui/translations/main_pt-BR.ts index eca790e..ca1a193 100755 --- a/selfdrive/ui/translations/main_pt-BR.ts +++ b/selfdrive/ui/translations/main_pt-BR.ts @@ -814,15 +814,15 @@ OffroadHome UPDATE - ATUALIZAÇÃO + ATUALIZAÇÃO ALERTS - ALERTAS + ALERTAS ALERT - ALERTA + ALERTA diff --git a/selfdrive/ui/translations/main_th.ts b/selfdrive/ui/translations/main_th.ts index cf4039c..89fbac2 100755 --- a/selfdrive/ui/translations/main_th.ts +++ b/selfdrive/ui/translations/main_th.ts @@ -813,15 +813,15 @@ OffroadHome UPDATE - อัปเดต + อัปเดต ALERTS - การแจ้งเตือน + การแจ้งเตือน ALERT - การแจ้งเตือน + การแจ้งเตือน diff --git a/selfdrive/ui/translations/main_tr.ts b/selfdrive/ui/translations/main_tr.ts index 70dd269..f8c8eef 100755 --- a/selfdrive/ui/translations/main_tr.ts +++ b/selfdrive/ui/translations/main_tr.ts @@ -770,15 +770,15 @@ OffroadHome UPDATE - GÜNCELLE + GÜNCELLE ALERTS - UYARILAR + UYARILAR ALERT - UYARI + UYARI diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts index 26cf5eb..768a426 100755 --- a/selfdrive/ui/translations/main_zh-CHS.ts +++ b/selfdrive/ui/translations/main_zh-CHS.ts @@ -813,15 +813,15 @@ OffroadHome UPDATE - 更新 + 更新 ALERTS - 警报 + 警报 ALERT - 警报 + 警报 diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts index 6ac3624..33c6f59 100755 --- a/selfdrive/ui/translations/main_zh-CHT.ts +++ b/selfdrive/ui/translations/main_zh-CHT.ts @@ -813,15 +813,15 @@ OffroadHome UPDATE - 更新 + 更新 ALERTS - 提醒 + 提醒 ALERT - 提醒 + 提醒