diff --git a/selfdrive/ui/SConscript b/selfdrive/ui/SConscript index 19dcc625e2fa61..62f41523337521 100644 --- a/selfdrive/ui/SConscript +++ b/selfdrive/ui/SConscript @@ -110,3 +110,5 @@ if GetOption('extras') and arch != "Darwin": # build watch3 if arch in ['x86_64', 'aarch64', 'Darwin'] or GetOption('extras'): qt_env.Program("watch3", ["watch3.cc"], LIBS=qt_libs + ['common', 'msgq', 'visionipc']) + +SConscript(['raylib/SConscript']) diff --git a/selfdrive/ui/raylib/.gitignore b/selfdrive/ui/raylib/.gitignore new file mode 100644 index 00000000000000..c66ae096aa77c3 --- /dev/null +++ b/selfdrive/ui/raylib/.gitignore @@ -0,0 +1 @@ +_spinner diff --git a/selfdrive/ui/raylib/SConscript b/selfdrive/ui/raylib/SConscript new file mode 100644 index 00000000000000..e544564ac6e4ce --- /dev/null +++ b/selfdrive/ui/raylib/SConscript @@ -0,0 +1,16 @@ +Import('env', 'arch', 'common') + +raylib_env = env.Clone() +raylib_util_lib = env.Library("raylib_util_lib", ['util.cc'], LIBS='raylib') +linked_libs = ['raylib', raylib_util_lib, common] +raylib_env['LIBPATH'] += [f'#third_party/raylib/{arch}/'] + +mac_frameworks = [] +if arch == "Darwin": + mac_frameworks += ['OpenCL', 'CoreVideo', 'Cocoa', 'GLUT', 'CoreFoundation', 'OpenGL', 'IOKit'] +elif arch == 'larch64': + linked_libs += [] +else: + linked_libs.append('OpenCL') + +raylib_env.Program("_spinner", ["spinner.cc"], LIBS=linked_libs, FRAMEWORKS=mac_frameworks) diff --git a/selfdrive/ui/raylib/spinner.cc b/selfdrive/ui/raylib/spinner.cc new file mode 100644 index 00000000000000..99aa5f3269c70b --- /dev/null +++ b/selfdrive/ui/raylib/spinner.cc @@ -0,0 +1,69 @@ +#include +#include +#include + +#include "selfdrive/ui/raylib/util.h" +#include "third_party/raylib/include/raylib.h" + +constexpr int kProgressBarWidth = 1000; +constexpr int kProgressBarHeight = 20; +constexpr float kRotationRate = 12.0f; +constexpr int kMargin = 200; +constexpr int kTextureSize = 360; +constexpr int kFontSize = 80; + +int main(int argc, char *argv[]) { + initApp("spinner", 30); + + // Turn off input buffering for std::cin + std::cin.sync_with_stdio(false); + std::cin.tie(nullptr); + + Texture2D commaTexture = LoadTextureResized("../../assets/img_spinner_comma.png", kTextureSize); + Texture2D spinnerTexture = LoadTextureResized("../../assets/img_spinner_track.png", kTextureSize); + + float rotation = 0.0f; + std::string userInput; + + while (!WindowShouldClose()) { + BeginDrawing(); + ClearBackground(BLACK); + + rotation = fmod(rotation + kRotationRate, 360.0f); + Vector2 center = {GetScreenWidth() / 2.0f, GetScreenHeight() / 2.0f}; + const Vector2 spinnerOrigin{kTextureSize / 2.0f, kTextureSize / 2.0f}; + const Vector2 commaPosition{center.x - kTextureSize / 2.0f, center.y - kTextureSize / 2.0f}; + + // Draw rotating spinner and static comma logo + DrawTexturePro(spinnerTexture, {0, 0, (float)kTextureSize, (float)kTextureSize}, + {center.x, center.y, (float)kTextureSize, (float)kTextureSize}, + spinnerOrigin, rotation, WHITE); + DrawTextureV(commaTexture, commaPosition, WHITE); + + // Check for user input + if (std::cin.rdbuf()->in_avail() > 0) { + std::getline(std::cin, userInput); + } + + // Display either a progress bar or user input text based on input + if (!userInput.empty()) { + float yPos = GetScreenHeight() - kMargin - kProgressBarHeight; + if (std::all_of(userInput.begin(), userInput.end(), ::isdigit)) { + Rectangle bar = {center.x - kProgressBarWidth / 2.0f, yPos, kProgressBarWidth, kProgressBarHeight}; + DrawRectangleRounded(bar, 0.5f, 10, GRAY); + + int progress = std::clamp(std::stoi(userInput), 0, 100); + bar.width *= progress / 100.0f; + DrawRectangleRounded(bar, 0.5f, 10, RAYWHITE); + } else { + Vector2 textSize = MeasureTextEx(getFont(), userInput.c_str(), kFontSize, 1.0); + DrawTextEx(getFont(), userInput.c_str(), {center.x - textSize.x / 2, yPos}, kFontSize, 1.0, WHITE); + } + } + + EndDrawing(); + } + + CloseWindow(); + return 0; +} diff --git a/selfdrive/ui/raylib/util.cc b/selfdrive/ui/raylib/util.cc new file mode 100644 index 00000000000000..73c0e4e0b73054 --- /dev/null +++ b/selfdrive/ui/raylib/util.cc @@ -0,0 +1,56 @@ +#include "selfdrive/ui/raylib/util.h" + +#include + +#undef GREEN +#undef RED +#undef YELLOW +#include "common/swaglog.h" +#include "system/hardware/hw.h" + +constexpr std::array(FontWeight::Count)> FONT_FILE_PATHS = { + "../../assets/fonts/Inter-Black.ttf", + "../../assets/fonts/Inter-Bold.ttf", + "../../assets/fonts/Inter-ExtraBold.ttf", + "../../assets/fonts/Inter-ExtraLight.ttf", + "../../assets/fonts/Inter-Medium.ttf", + "../../assets/fonts/Inter-Regular.ttf", + "../../assets/fonts/Inter-SemiBold.ttf", + "../../assets/fonts/Inter-Thin.ttf", +}; + +struct FontManager { + FontManager() { + for (int i = 0; i < fonts.size(); ++i) { + fonts[i] = LoadFontEx(FONT_FILE_PATHS[i], 120, nullptr, 250); + SetTextureFilter(fonts[i].texture, TEXTURE_FILTER_TRILINEAR); + } + } + + ~FontManager() { + for (auto &f : fonts) UnloadFont(f); + } + + std::array(FontWeight::Count)> fonts; +}; + +const Font& getFont(FontWeight weight) { + static FontManager font_manager; + return font_manager.fonts[(int)weight]; +} + +Texture2D LoadTextureResized(const char *fileName, int size) { + Image img = LoadImage(fileName); + ImageResize(&img, size, size); + Texture2D texture = LoadTextureFromImage(img); + SetTextureFilter(texture, TEXTURE_FILTER_TRILINEAR); + return texture; +} + +void initApp(const char *title, int fps) { + Hardware::set_display_power(true); + Hardware::set_brightness(65); + // SetTraceLogLevel(LOG_NONE); + InitWindow(0, 0, title); + SetTargetFPS(fps); +} diff --git a/selfdrive/ui/raylib/util.h b/selfdrive/ui/raylib/util.h new file mode 100644 index 00000000000000..da2ec7118be5ef --- /dev/null +++ b/selfdrive/ui/raylib/util.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "third_party/raylib/include/raylib.h" + +enum class FontWeight { + Normal, + Bold, + ExtraBold, + ExtraLight, + Medium, + Regular, + SemiBold, + Thin, + Count // To represent the total number of fonts +}; + +void initApp(const char *title, int fps); +const Font& getFont(FontWeight weight = FontWeight::Normal); +Texture2D LoadTextureResized(const char *fileName, int size);