diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index e63359da05..7199399d41 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -1,6 +1,5 @@ -import os import json -Import('qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') +Import('env', 'qt_env', 'arch', 'common', 'messaging', 'visionipc', 'transformations') base_libs = [common, messaging, visionipc, transformations, 'm', 'OpenCL', 'ssl', 'crypto', 'pthread'] + qt_env["LIBS"] @@ -76,8 +75,15 @@ if GetOption('extras'): if arch != "Darwin": # build installers - senv = qt_env.Clone() - senv['LINKFLAGS'].append('-Wl,-strip-debug') + raylib_env = env.Clone() + raylib_env['LIBPATH'] += [f'#third_party/raylib/{arch}/'] + raylib_env['LINKFLAGS'].append('-Wl,-strip-debug') + + raylib_libs = common + ["raylib"] + if arch == "larch64": + raylib_libs += ["GLESv2", "wayland-client", "wayland-egl", "EGL"] + else: + raylib_libs += ["GL"] release = "release3" installers = [ @@ -87,17 +93,19 @@ if GetOption('extras'): ("openpilot_internal", "nightly-dev"), ] - cont = senv.Command(f"installer/continue_openpilot.o", f"installer/continue_openpilot.sh", - "ld -r -b binary -o $TARGET $SOURCE") + cont = raylib_env.Command("installer/continue_openpilot.o", "installer/continue_openpilot.sh", + "ld -r -b binary -o $TARGET $SOURCE") + inter = raylib_env.Command("installer/inter_ttf.o", "installer/inter-ascii.ttf", + "ld -r -b binary -o $TARGET $SOURCE") for name, branch in installers: d = {'BRANCH': f"'\"{branch}\"'"} if "internal" in name: d['INTERNAL'] = "1" - obj = senv.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d) - f = senv.Program(f"installer/installers/installer_{name}", [obj, cont], LIBS=qt_libs) + obj = raylib_env.Object(f"installer/installers/installer_{name}.o", ["installer/installer.cc"], CPPDEFINES=d) + f = raylib_env.Program(f"installer/installers/installer_{name}", [obj, cont, inter], LIBS=raylib_libs) # keep installers small - assert f[0].get_size() < 370*1e3 + assert f[0].get_size() < 1300*1e3, f[0].get_size() # build watch3 if arch in ['x86_64', 'aarch64', 'Darwin'] or GetOption('extras'): diff --git a/selfdrive/ui/installer/installer.cc b/selfdrive/ui/installer/installer.cc index 84486e61be..7326e089ab 100644 --- a/selfdrive/ui/installer/installer.cc +++ b/selfdrive/ui/installer/installer.cc @@ -1,19 +1,15 @@ -#include - -#include +#include +#include #include #include -#include - -#include -#include -#include -#include +#include "common/swaglog.h" #include "common/util.h" -#include "selfdrive/ui/installer/installer.h" -#include "selfdrive/ui/qt/util.h" -#include "selfdrive/ui/qt/qt_window.h" +#include "third_party/raylib/include/raylib.h" + +int freshClone(); +int cachedFetch(const std::string &cache); +int executeGitCommand(const std::string &cmd); std::string get_str(std::string const s) { std::string::size_type pos = s.find('?'); @@ -28,136 +24,108 @@ const std::string BRANCH_STR = get_str(BRANCH "? #define GIT_SSH_URL "git@github.com:commaai/openpilot.git" #define CONTINUE_PATH "/data/continue.sh" -const QString CACHE_PATH = "/data/openpilot.cache"; +const std::string CACHE_PATH = "/data/openpilot.cache"; #define INSTALL_PATH "/data/openpilot" #define TMP_INSTALL_PATH "/data/tmppilot" extern const uint8_t str_continue[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_start"); extern const uint8_t str_continue_end[] asm("_binary_selfdrive_ui_installer_continue_openpilot_sh_end"); +extern const uint8_t inter_ttf[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_start"); +extern const uint8_t inter_ttf_end[] asm("_binary_selfdrive_ui_installer_inter_ascii_ttf_end"); + +Font font; void run(const char* cmd) { int err = std::system(cmd); assert(err == 0); } -Installer::Installer(QWidget *parent) : QWidget(parent) { - QVBoxLayout *layout = new QVBoxLayout(this); - layout->setContentsMargins(150, 290, 150, 150); - layout->setSpacing(0); - - QLabel *title = new QLabel(tr("Installing...")); - title->setStyleSheet("font-size: 90px; font-weight: 600;"); - layout->addWidget(title, 0, Qt::AlignTop); - - layout->addSpacing(170); - - bar = new QProgressBar(); - bar->setRange(0, 100); - bar->setTextVisible(false); - bar->setFixedHeight(72); - layout->addWidget(bar, 0, Qt::AlignTop); - - layout->addSpacing(30); - - val = new QLabel("0%"); - val->setStyleSheet("font-size: 70px; font-weight: 300;"); - layout->addWidget(val, 0, Qt::AlignTop); - - layout->addStretch(); - - QObject::connect(&proc, QOverload::of(&QProcess::finished), this, &Installer::cloneFinished); - QObject::connect(&proc, &QProcess::readyReadStandardError, this, &Installer::readProgress); - - QTimer::singleShot(100, this, &Installer::doInstall); - - setStyleSheet(R"( - * { - font-family: Inter; - color: white; - background-color: black; - } - QProgressBar { - border: none; - background-color: #292929; - } - QProgressBar::chunk { - background-color: #364DEF; - } - )"); +void renderProgress(int progress) { + BeginDrawing(); + ClearBackground(BLACK); + DrawTextEx(font, "Installing...", (Vector2){150, 290}, 110, 0, WHITE); + Rectangle bar = {150, 570, (float)GetScreenWidth() - 300, 72}; + DrawRectangleRec(bar, (Color){41, 41, 41, 255}); + progress = std::clamp(progress, 0, 100); + bar.width *= progress / 100.0f; + DrawRectangleRec(bar, (Color){70, 91, 234, 255}); + DrawTextEx(font, (std::to_string(progress) + "%").c_str(), (Vector2){150, 670}, 85, 0, WHITE); + EndDrawing(); } -void Installer::updateProgress(int percent) { - bar->setValue(percent); - val->setText(QString("%1%").arg(percent)); - update(); -} - -void Installer::doInstall() { +int doInstall() { // wait for valid time while (!util::system_time_valid()) { - usleep(500 * 1000); - qDebug() << "Waiting for valid time"; + util::sleep_for(500); + LOGD("Waiting for valid time"); } // cleanup previous install attempts run("rm -rf " TMP_INSTALL_PATH " " INSTALL_PATH); // do the install - if (QDir(CACHE_PATH).exists()) { - cachedFetch(CACHE_PATH); + if (util::file_exists(CACHE_PATH)) { + return cachedFetch(CACHE_PATH); } else { - freshClone(); + return freshClone(); } } -void Installer::freshClone() { - qDebug() << "Doing fresh clone"; - proc.start("git", {"clone", "--progress", GIT_URL.c_str(), "-b", BRANCH_STR.c_str(), - "--depth=1", "--recurse-submodules", TMP_INSTALL_PATH}); +int freshClone() { + LOGD("Doing fresh clone"); + std::string cmd = util::string_format("git clone --progress %s -b %s --depth=1 --recurse-submodules %s 2>&1", + GIT_URL.c_str(), BRANCH_STR.c_str(), TMP_INSTALL_PATH); + return executeGitCommand(cmd); } -void Installer::cachedFetch(const QString &cache) { - qDebug() << "Fetching with cache: " << cache; +int cachedFetch(const std::string &cache) { + LOGD("Fetching with cache: %s", cache.c_str()); - run(QString("cp -rp %1 %2").arg(cache, TMP_INSTALL_PATH).toStdString().c_str()); - int err = chdir(TMP_INSTALL_PATH); - assert(err == 0); - run(("git remote set-branches --add origin " + BRANCH_STR).c_str()); + run(util::string_format("cp -rp %s %s", cache.c_str(), TMP_INSTALL_PATH).c_str()); + run(util::string_format("cd %s && git remote set-branches --add origin %s", TMP_INSTALL_PATH, BRANCH_STR.c_str()).c_str()); - updateProgress(10); + renderProgress(10); - proc.setWorkingDirectory(TMP_INSTALL_PATH); - proc.start("git", {"fetch", "--progress", "origin", BRANCH_STR.c_str()}); + return executeGitCommand(util::string_format("cd %s && git fetch --progress origin %s 2>&1", TMP_INSTALL_PATH, BRANCH_STR.c_str())); } -void Installer::readProgress() { - const QVector> stages = { +int executeGitCommand(const std::string &cmd) { + static const std::array stages = { // prefix, weight in percentage - {"Receiving objects: ", 91}, - {"Resolving deltas: ", 2}, - {"Updating files: ", 7}, + std::pair{"Receiving objects: ", 91}, + std::pair{"Resolving deltas: ", 2}, + std::pair{"Updating files: ", 7}, }; - auto line = QString(proc.readAllStandardError()); - - int base = 0; - for (const QPair kv : stages) { - if (line.startsWith(kv.first)) { - auto perc = line.split(kv.first)[1].split("%")[0]; - int p = base + int(perc.toFloat() / 100. * kv.second); - updateProgress(p); - break; + FILE *pipe = popen(cmd.c_str(), "r"); + if (!pipe) return -1; + + char buffer[512]; + while (fgets(buffer, sizeof(buffer), pipe) != nullptr) { + std::string line(buffer); + int base = 0; + for (const auto &[text, weight] : stages) { + if (line.find(text) != std::string::npos) { + size_t percentPos = line.find("%"); + if (percentPos != std::string::npos && percentPos >= 3) { + int percent = std::stoi(line.substr(percentPos - 3, 3)); + int progress = base + int(percent / 100. * weight); + renderProgress(progress); + } + break; + } + base += weight; } - base += kv.second; } + return pclose(pipe); } -void Installer::cloneFinished(int exitCode, QProcess::ExitStatus exitStatus) { - qDebug() << "git finished with " << exitCode; +void cloneFinished(int exitCode) { + LOGD("git finished with %d", exitCode); assert(exitCode == 0); - updateProgress(100); + renderProgress(100); // ensure correct branch is checked out int err = chdir(TMP_INSTALL_PATH); @@ -203,13 +171,17 @@ void Installer::cloneFinished(int exitCode, QProcess::ExitStatus exitStatus) { run("mv /data/continue.sh.new " CONTINUE_PATH); // wait for the installed software's UI to take over - QTimer::singleShot(60 * 1000, &QCoreApplication::quit); + util::sleep_for(60 * 1000); } int main(int argc, char *argv[]) { - initApp(argc, argv); - QApplication a(argc, argv); - Installer installer; - setMainWindow(&installer); - return a.exec(); + InitWindow(2160, 1080, "Installer"); + font = LoadFontFromMemory(".ttf", inter_ttf, inter_ttf_end - inter_ttf, 120, NULL, 0); + SetTextureFilter(font.texture, TEXTURE_FILTER_BILINEAR); + renderProgress(0); + int result = doInstall(); + cloneFinished(result); + CloseWindow(); + UnloadFont(font); + return 0; } diff --git a/selfdrive/ui/installer/installer.h b/selfdrive/ui/installer/installer.h deleted file mode 100644 index de3af0ff39..0000000000 --- a/selfdrive/ui/installer/installer.h +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -class Installer : public QWidget { - Q_OBJECT - -public: - explicit Installer(QWidget *parent = 0); - -private slots: - void updateProgress(int percent); - - void readProgress(); - void cloneFinished(int exitCode, QProcess::ExitStatus exitStatus); - -private: - QLabel *val; - QProgressBar *bar; - QProcess proc; - - void doInstall(); - void freshClone(); - void cachedFetch(const QString &cache); -}; diff --git a/selfdrive/ui/installer/inter-ascii.ttf b/selfdrive/ui/installer/inter-ascii.ttf new file mode 100644 index 0000000000..5d480c515a --- /dev/null +++ b/selfdrive/ui/installer/inter-ascii.ttf @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1ef26a4099ef867f3493389379d882381a2491cdbfa41a086be8899a2154dcb3 +size 26160