diff --git a/Firmware/main/src/apps/gameboy_app.cpp b/Firmware/main/src/apps/gameboy_app.cpp index 9e7c50d..63e7ad2 100644 --- a/Firmware/main/src/apps/gameboy_app.cpp +++ b/Firmware/main/src/apps/gameboy_app.cpp @@ -1053,9 +1053,11 @@ private: fpsLastSampleMs = nowMs; } - char fpsBuf[16]; - std::snprintf(fpsBuf, sizeof(fpsBuf), "%u FPS", static_cast(fpsCurrent)); - const std::string fpsText(fpsBuf); + char fpsValueBuf[16]; + std::snprintf(fpsValueBuf, sizeof(fpsValueBuf), "%u", static_cast(fpsCurrent)); + const std::string fpsValue(fpsValueBuf); + const std::string fpsLabel = "FPS"; + const std::string fpsText = fpsValue + " FPS"; const std::string scaleHint = (scaleMode == ScaleMode::FullHeight) ? "START+B NORMAL" : "START+B SCALE"; if (scaleMode == ScaleMode::FullHeight) { @@ -1069,46 +1071,69 @@ private: const int maxLeftX = std::max(0, screenWidth - rotatedWidth); const int maxRightXBase = std::max(0, screenWidth - rotatedWidth); + + const int horizontalPadding = 8; + const int fpsLineGap = 4; + const int fpsLabelWidth = font16x8::measureText(fpsLabel, 1, 1); + const int fpsValueWidth = font16x8::measureText(fpsValue, 1, 1); + const int fpsBlockWidth = std::max(fpsLabelWidth, fpsValueWidth); + int fpsX = std::max(0, screenWidth - fpsBlockWidth - horizontalPadding); + const int fpsY = horizontalPadding; + font16x8::drawText(framebuffer, fpsX, fpsY, fpsLabel, 1, true, 1); + font16x8::drawText(framebuffer, fpsX, fpsY + font16x8::kGlyphHeight + fpsLineGap, fpsValue, 1, true, 1); + const int reservedTop = fpsY + (font16x8::kGlyphHeight * 2) + fpsLineGap + horizontalPadding; + if (!activeRomName.empty()) { - const int textHeight = measureVerticalText(activeRomName, textScale); - const int maxOrigin = std::max(0, screenHeight - textHeight); - int leftX = std::clamp((leftMargin - rotatedWidth) / 2, 0, maxLeftX); - int leftY = std::clamp((screenHeight - textHeight) / 2, 0, maxOrigin); - drawTextRotated(framebuffer, leftX, leftY, activeRomName, false, textScale, true, 1); + const std::string rotatedRomName(activeRomName.rbegin(), activeRomName.rend()); + const int textHeight = measureVerticalText(rotatedRomName, textScale); + const int maxOrigin = std::max(0, screenHeight - textHeight); + int leftX = 8; + int leftY = std::clamp((screenHeight - textHeight) / 2, 0, maxOrigin); + drawTextRotated(framebuffer, leftX, leftY, rotatedRomName, true, textScale, true, 1); + if (!statusMessage.empty()) { + const std::string rotatedStatusMessage(statusMessage.rbegin(), statusMessage.rend()); + const int textHeight = measureVerticalText(rotatedStatusMessage, textScale); + const int maxOrigin = std::max(0, screenHeight - textHeight); + leftX = leftX + 20; + leftY = std::clamp((screenHeight - textHeight) / 2, 0, maxOrigin); + drawTextRotated(framebuffer, leftX, leftY, rotatedStatusMessage, true, textScale, true, 1); + } } - const int gap = 8; - int totalRight = 0; - const auto accumulateHeight = [&](std::string_view text) { - if (text.empty()) - return; - if (totalRight > 0) - totalRight += gap; - totalRight += measureVerticalText(text, textScale); - }; - accumulateHeight(fpsText); - accumulateHeight("START+SELECT BACK"); - accumulateHeight(scaleHint); - if (!statusMessage.empty()) - accumulateHeight(statusMessage); - const int maxRightOrigin = std::max(0, screenHeight - totalRight); - int rightY = std::clamp((screenHeight - totalRight) / 2, 0, maxRightOrigin); - int rightX = screenWidth - rightMargin + std::max(0, (rightMargin - rotatedWidth) / 2); - rightX = std::clamp(rightX, 0, maxRightXBase); + std::vector rightTexts; + rightTexts.reserve(2U); + rightTexts.emplace_back("START+SELECT BACK"); + rightTexts.emplace_back(scaleHint); - const auto drawRight = [&](std::string_view text) { + const int gap = 8; + int totalRightHeight = 0; + for (std::string_view text: rightTexts) { if (text.empty()) - return; - drawTextRotated(framebuffer, rightX, rightY, text, true, textScale, true, 1); - rightY += measureVerticalText(text, textScale); - rightY += gap; - }; - drawRight(fpsText); - drawRight("START+SELECT BACK"); - drawRight(scaleHint); - if (!statusMessage.empty()) - drawRight(statusMessage); + continue; + std::string rotated(text.rbegin(), text.rend()); + if (totalRightHeight > 0) + totalRightHeight += gap; + totalRightHeight += measureVerticalText(rotated, textScale); + } + + const int maxRightOrigin = std::max(0, screenHeight - totalRightHeight); + int rightY = std::clamp((screenHeight - totalRightHeight) / 2, 0, maxRightOrigin); + if (rightY < reservedTop) + rightY = std::min(std::max(reservedTop, 0), maxRightOrigin); + + int rightX = screenWidth - 20; + + for (size_t i = 0; i < rightTexts.size(); ++i) { + std::string_view text = rightTexts[i]; + if (text.empty()) + continue; + std::string rotated(text.rbegin(), text.rend()); + rightY = screenHeight - measureVerticalText(rotated, textScale) - 8; + drawTextRotated(framebuffer, rightX, rightY, rotated, true, textScale, true, 1); + rightX -= 20; + } + } else { if (!activeRomName.empty()) font16x8::drawText(framebuffer, 16, 16, activeRomName, 1, true, 1);