diff --git a/selfdrive/assets/fonts/Pretendard-LICENSE.txt b/selfdrive/assets/fonts/Pretendard-LICENSE.txt new file mode 100644 index 0000000000..497b88f9fa --- /dev/null +++ b/selfdrive/assets/fonts/Pretendard-LICENSE.txt @@ -0,0 +1,94 @@ +Copyright (c) 2021, Kil Hyung-jin (https://github.com/orioncactus/pretendard), +with Reserved Font Name Pretendard. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/selfdrive/assets/fonts/Pretendard-Medium.ttf b/selfdrive/assets/fonts/Pretendard-Medium.ttf new file mode 100644 index 0000000000..1db67c68fb Binary files /dev/null and b/selfdrive/assets/fonts/Pretendard-Medium.ttf differ diff --git a/selfdrive/assets/fonts/Pretendard-SemiBold.ttf b/selfdrive/assets/fonts/Pretendard-SemiBold.ttf new file mode 100644 index 0000000000..9f2690f097 Binary files /dev/null and b/selfdrive/assets/fonts/Pretendard-SemiBold.ttf differ diff --git a/selfdrive/ui/qt/spinner.cc b/selfdrive/ui/qt/spinner.cc index 6aafbd647a..edb20d89cb 100644 --- a/selfdrive/ui/qt/spinner.cc +++ b/selfdrive/ui/qt/spinner.cc @@ -13,11 +13,16 @@ #include #include +#include +#include +#include #include #include #include +#include #include #include +#include #include "system/hardware/hw.h" #include "selfdrive/ui/qt/qt_window.h" @@ -62,6 +67,15 @@ static QString recoveryLabel() { return ip + QStringLiteral(":6999"); } +static QString spinnerFontFamily() { + QString font_path = QCoreApplication::applicationDirPath() + "/../assets/fonts/Pretendard-SemiBold.ttf"; + int font_id = QFontDatabase::addApplicationFont(font_path); + if (font_id < 0) return QStringLiteral("Inter"); + + QStringList families = QFontDatabase::applicationFontFamilies(font_id); + return families.isEmpty() ? QStringLiteral("Inter") : families.first(); +} + TrackWidget::TrackWidget(QWidget *parent) : QWidget(parent) { setAttribute(Qt::WA_OpaquePaintEvent); setFixedSize(spinner_size); @@ -101,27 +115,40 @@ void TrackWidget::paintEvent(QPaintEvent *event) { Spinner::Spinner(QWidget *parent) : QWidget(parent) { QGridLayout *main_layout = new QGridLayout(this); main_layout->setSpacing(0); - main_layout->setMargin(200); + main_layout->setContentsMargins(200, 170, 200, 70); + + QString font_family = spinnerFontFamily(); ipLabel = new QLabel(recoveryLabel()); ipLabel->setObjectName("ipLabel"); + ipLabel->setFont(QFont(font_family, 84)); ipLabel->setAlignment(Qt::AlignCenter); main_layout->addWidget(ipLabel, 0, 0, Qt::AlignHCenter | Qt::AlignTop); main_layout->addWidget(new TrackWidget(this), 1, 0, Qt::AlignHCenter | Qt::AlignVCenter); text = new QLabel(); - text->setWordWrap(true); + text->setObjectName("statusLabel"); + text->setFixedWidth(1000); + text->setFixedHeight(68); + text->setFont(QFont(font_family, 52)); + text->setWordWrap(false); text->setVisible(false); text->setAlignment(Qt::AlignCenter); - main_layout->addWidget(text, 2, 0, Qt::AlignHCenter); progress_bar = new QProgressBar(); progress_bar->setRange(5, 100); progress_bar->setTextVisible(false); progress_bar->setVisible(false); + progress_bar->setFixedWidth(1000); progress_bar->setFixedHeight(20); - main_layout->addWidget(progress_bar, 2, 0, Qt::AlignHCenter); + + QVBoxLayout *progress_layout = new QVBoxLayout(); + progress_layout->setContentsMargins(0, 0, 0, 0); + progress_layout->setSpacing(6); + progress_layout->addWidget(text, 0, Qt::AlignHCenter); + progress_layout->addWidget(progress_bar, 0, Qt::AlignHCenter); + main_layout->addLayout(progress_layout, 2, 0, Qt::AlignHCenter); main_layout->setRowStretch(0, 0); main_layout->setRowStretch(1, 1); @@ -131,7 +158,7 @@ Spinner::Spinner(QWidget *parent) : QWidget(parent) { QObject::connect(ipTimer, &QTimer::timeout, this, &Spinner::refreshIPLabel); ipTimer->start(5000); - setStyleSheet(R"( + setStyleSheet(QString(R"( Spinner { background-color: black; } @@ -142,8 +169,13 @@ Spinner::Spinner(QWidget *parent) : QWidget(parent) { } QLabel#ipLabel { color: white; - font-size: 50px; - font-family: monospace; + font-size: 84px; + font-family: "%1"; + } + QLabel#statusLabel { + color: #cfcfcf; + font-size: 52px; + font-family: "%1"; } QProgressBar { background-color: #373737; @@ -155,7 +187,7 @@ Spinner::Spinner(QWidget *parent) : QWidget(parent) { border-radius: 10px; background-color: white; } - )"); + )").arg(font_family)); notifier = new QSocketNotifier(fileno(stdin), QSocketNotifier::Read); QObject::connect(notifier, &QSocketNotifier::activated, this, &Spinner::update); @@ -171,11 +203,13 @@ void Spinner::update(int n) { if (line.length()) { bool number = std::all_of(line.begin(), line.end(), ::isdigit); - text->setVisible(!number); - progress_bar->setVisible(number); - text->setText(QString::fromStdString(line)); if (number) { + progress_bar->setVisible(true); progress_bar->setValue(std::stoi(line)); + } else { + QString status = QString::fromStdString(line); + text->setText(text->fontMetrics().elidedText(status, Qt::ElideRight, text->width())); + text->setVisible(true); } } } diff --git a/selfdrive/ui/qt/text.cc b/selfdrive/ui/qt/text.cc index c5963f2ab9..7b5ce5e244 100644 --- a/selfdrive/ui/qt/text.cc +++ b/selfdrive/ui/qt/text.cc @@ -1,8 +1,12 @@ #include +#include +#include +#include #include #include #include #include +#include #include #include #include @@ -60,18 +64,30 @@ static QString recoveryLabel() { return ip + QStringLiteral(":6999"); } +static QString textWindowFontFamily() { + QString font_path = QCoreApplication::applicationDirPath() + "/../assets/fonts/Pretendard-SemiBold.ttf"; + int font_id = QFontDatabase::addApplicationFont(font_path); + if (font_id < 0) return QStringLiteral("Inter"); + + QStringList families = QFontDatabase::applicationFontFamilies(font_id); + return families.isEmpty() ? QStringLiteral("Inter") : families.first(); +} + int main(int argc, char *argv[]) { initApp(argc, argv); QApplication a(argc, argv); QWidget window; setMainWindow(&window); + QString font_family = textWindowFontFamily(); + QGridLayout *main_layout = new QGridLayout(&window); main_layout->setMargin(50); main_layout->setSpacing(20); QLabel *ipLabel = new QLabel(recoveryLabel()); ipLabel->setObjectName("ipLabel"); + ipLabel->setFont(QFont(font_family, 84)); ipLabel->setAlignment(Qt::AlignCenter); main_layout->addWidget(ipLabel, 0, 0, Qt::AlignHCenter | Qt::AlignTop); @@ -110,7 +126,7 @@ int main(int argc, char *argv[]) { main_layout->setRowStretch(0, 0); main_layout->setRowStretch(1, 1); - window.setStyleSheet(R"( + window.setStyleSheet(QString(R"( * { outline: none; color: white; @@ -119,8 +135,8 @@ int main(int argc, char *argv[]) { } QLabel#ipLabel { color: white; - font-size: 50px; - font-family: monospace; + font-size: 84px; + font-family: "%1"; } QPushButton { padding: 50px; @@ -130,7 +146,7 @@ int main(int argc, char *argv[]) { border-radius: 20px; margin-right: 40px; } - )"); + )").arg(font_family)); return a.exec(); } diff --git a/system/manager/build.py b/system/manager/build.py index 93c0546c85..9b196c937b 100755 --- a/system/manager/build.py +++ b/system/manager/build.py @@ -20,6 +20,7 @@ def build(spinner: Spinner, dirty: bool = False, minimal: bool = False) -> None: env = os.environ.copy() env['SCONS_PROGRESS'] = "1" + env['PYTHONUNBUFFERED'] = "1" nproc = os.cpu_count() if nproc is None: nproc = 2 @@ -35,13 +36,14 @@ def build(spinner: Spinner, dirty: bool = False, minimal: bool = False) -> None: compile_output: list[bytes] = [] for n in (nproc, nproc/2, 1): compile_output.clear() - scons: subprocess.Popen = subprocess.Popen(["scons", f"-j{int(n)}", "--cache-populate", *extra_args], cwd=BASEDIR, env=env, stderr=subprocess.PIPE) - assert scons.stderr is not None + scons: subprocess.Popen = subprocess.Popen(["scons", f"-j{int(n)}", "--cache-populate", *extra_args], cwd=BASEDIR, env=env, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) + assert scons.stdout is not None - # Read progress from stderr and update spinner + # Read progress and build output from scons and update spinner. while scons.poll() is None: try: - line = scons.stderr.readline() + line = scons.stdout.readline() if line is None: continue line = line.rstrip() @@ -52,7 +54,9 @@ def build(spinner: Spinner, dirty: bool = False, minimal: bool = False) -> None: spinner.update_progress(MAX_BUILD_PROGRESS * min(1., i / TOTAL_SCONS_NODES), 100.) elif len(line): compile_output.append(line) - print(line.decode('utf8', 'replace')) + line_text = line.decode('utf8', 'replace') + spinner.update(line_text) + print(line_text) except Exception: pass @@ -61,8 +65,8 @@ def build(spinner: Spinner, dirty: bool = False, minimal: bool = False) -> None: if scons.returncode != 0: # Read remaining output - if scons.stderr is not None: - compile_output += scons.stderr.read().split(b'\n') + if scons.stdout is not None: + compile_output += scons.stdout.read().split(b'\n') # Build failed log errors error_s = b"\n".join(compile_output).decode('utf8', 'replace')