fix submodules again
This commit is contained in:
4
.gitmodules
vendored
4
.gitmodules
vendored
@@ -6,7 +6,7 @@
|
||||
path = Source/Horizon-OC-Monitor/lib/libultrahand
|
||||
url = https://github.com/ppkantorski/libultrahand
|
||||
branch = main
|
||||
[submodule "hoc-clk/libultrahand"]
|
||||
path = hoc-clk/libultrahand
|
||||
[submodule "Source/hoc-clk/overlay/lib/libultrahand"]
|
||||
path = Source/hoc-clk/overlay/lib/libultrahand
|
||||
url = https://github.com/ppkantorski/libultrahand
|
||||
branch = main
|
||||
|
||||
@@ -159,6 +159,9 @@ inline u32 offsetWidthVar = 112;
|
||||
inline std::string lastOverlayFilename;
|
||||
inline std::string lastOverlayMode;
|
||||
|
||||
inline std::string returnOverlayPath{ult::OVERLAY_PATH + "ovlmenu.ovl"};
|
||||
inline bool skipRumbleDoubleClick{false};
|
||||
|
||||
inline std::mutex jumpItemMutex;
|
||||
inline std::string jumpItemName;
|
||||
inline std::string jumpItemValue;
|
||||
@@ -246,6 +249,11 @@ namespace tsl {
|
||||
inline const std::vector<std::string> s_dividerSpecialChars = {ult::DIVIDER_SYMBOL};
|
||||
inline const std::vector<std::string> s_footerSpecialChars = {"\uE0E1","\uE0E0","\uE0ED","\uE0EE","\uE0E5"};
|
||||
|
||||
// Windowed-mode notification Y offset in touch space.
|
||||
// Set to (g_win_pos_y * 2/3) by windowed overlay; 0 in normal mode.
|
||||
// X is handled by ult::layerEdge which already exists for this purpose.
|
||||
inline s32 layerEdgeY = 0;
|
||||
|
||||
// Booleans
|
||||
inline std::atomic<bool> clearGlyphCacheNow(false);
|
||||
|
||||
@@ -395,10 +403,10 @@ namespace tsl {
|
||||
extern Color onTextColor;
|
||||
extern Color offTextColor;
|
||||
|
||||
#if IS_LAUNCHER_DIRECTIVE
|
||||
//#if IS_LAUNCHER_DIRECTIVE
|
||||
extern Color dynamicLogoRGB1;
|
||||
extern Color dynamicLogoRGB2;
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
extern bool invertBGClickColor;
|
||||
|
||||
@@ -500,7 +508,7 @@ namespace tsl {
|
||||
namespace elm { class Element; }
|
||||
|
||||
void shiftItemFocus(elm::Element* element); // forward declare
|
||||
|
||||
|
||||
namespace impl {
|
||||
|
||||
/**
|
||||
@@ -1879,38 +1887,33 @@ namespace tsl {
|
||||
}
|
||||
}
|
||||
|
||||
static void processRoundedRectChunk(Renderer* self, const s32 x, const s32 y, const s32 w, const s32 h,
|
||||
const s32 radius, const Color& color,
|
||||
const s32 startRow, const s32 endRow) {
|
||||
// --- Optimized rounded rectangle chunk processor ---
|
||||
static void processRoundedRectChunk(Renderer* self,
|
||||
const s32 x, const s32 y,
|
||||
const s32 w, const s32 h,
|
||||
const s32 radius,
|
||||
const Color& color,
|
||||
const s32 startRow, const s32 endRow)
|
||||
{
|
||||
if (radius <= 0) return;
|
||||
|
||||
|
||||
const s32 x_end = x + w;
|
||||
const s32 y_end = y + h;
|
||||
|
||||
|
||||
const s32 clip_x = std::max(0, x);
|
||||
const s32 clip_x_end = std::min<s32>(cfg::FramebufferWidth, x_end);
|
||||
|
||||
|
||||
const s32 left_arc_end = x + radius - 1;
|
||||
const s32 right_arc_start = x_end - radius;
|
||||
const s32 top_arc_end = y + radius - 1;
|
||||
const s32 bottom_arc_start = y_end - radius;
|
||||
|
||||
const int cx2_left = 2 * (x + radius);
|
||||
const int cx2_right = 2 * (x_end - radius);
|
||||
const int cy2_top = 2 * (y + radius);
|
||||
const int cy2_bottom = 2 * (y_end - radius);
|
||||
|
||||
|
||||
const long long r2_scaled = 4LL * radius * radius;
|
||||
const long long reject_threshold = (2LL*radius + 2)*(2LL*radius + 2);
|
||||
|
||||
|
||||
const u8 base_a = color.a;
|
||||
|
||||
// Pre-compute sample offsets (constant per corner)
|
||||
const int sx_left = ((x + radius) & 1) ? -1 : 1;
|
||||
const int sx_right = ((x_end - radius) & 1) ? -1 : 1;
|
||||
const int sy_top = ((y + radius) & 1) ? -1 : 1;
|
||||
const int sy_bottom = ((y_end - radius) & 1) ? -1 : 1;
|
||||
|
||||
|
||||
// Precompute batch arrays
|
||||
alignas(64) u8 redArray[512], greenArray[512], blueArray[512], alphaArray[512];
|
||||
const uint8x16_t rv = vdupq_n_u8(color.r);
|
||||
const uint8x16_t gv = vdupq_n_u8(color.g);
|
||||
@@ -1922,14 +1925,15 @@ namespace tsl {
|
||||
vst1q_u8(blueArray + i, bv);
|
||||
vst1q_u8(alphaArray + i, av);
|
||||
}
|
||||
|
||||
|
||||
for (s32 yc = startRow; yc < endRow; ++yc) {
|
||||
if (yc < y || yc >= y_end) continue;
|
||||
|
||||
|
||||
const bool is_top = (yc <= top_arc_end);
|
||||
const bool in_arc_rows = is_top || (yc >= bottom_arc_start);
|
||||
|
||||
|
||||
if (!in_arc_rows) {
|
||||
// Full-width batch for middle flat area
|
||||
s32 xs = std::max(clip_x, x);
|
||||
s32 xe = std::min(clip_x_end, x_end);
|
||||
for (s32 xp = xs; xp < xe; xp += 512)
|
||||
@@ -1937,38 +1941,37 @@ namespace tsl {
|
||||
std::min(512, xe - xp));
|
||||
continue;
|
||||
}
|
||||
|
||||
const int cy2 = is_top ? cy2_top : cy2_bottom;
|
||||
const int py2 = 2 * yc + 1;
|
||||
const int sy = is_top ? sy_top : sy_bottom;
|
||||
|
||||
// Quick row reject
|
||||
|
||||
const int cy2 = is_top ? 2*(y + radius) : 2*(y_end - radius);
|
||||
const int py2 = 2*yc + 1;
|
||||
const long long dy = py2 - cy2;
|
||||
if (dy * dy > reject_threshold) continue;
|
||||
|
||||
const s32 xe = std::min(clip_x_end, x_end);
|
||||
if (dy*dy > reject_threshold) continue;
|
||||
|
||||
s32 xp = std::max(clip_x, x);
|
||||
|
||||
// Left arc
|
||||
const s32 xe = std::min(clip_x_end, x_end);
|
||||
|
||||
// Left corner arc
|
||||
for (; xp <= left_arc_end && xp < xe; ++xp) {
|
||||
sampleAndBlendArcPixel(self, xp, yc, 2*xp + 1, cx2_left, sx_left,
|
||||
py2, cy2, sy, r2_scaled, color, base_a);
|
||||
sampleAndBlendArcPixel(self, xp, yc,
|
||||
2*xp + 1, 2*(x + radius), 1,
|
||||
py2, cy2, 1,
|
||||
r2_scaled, color, base_a);
|
||||
}
|
||||
|
||||
// Middle flat
|
||||
|
||||
// Middle flat area
|
||||
s32 mid_start = std::max(xp, left_arc_end + 1);
|
||||
s32 mid_end = std::min(xe, right_arc_start);
|
||||
if (mid_start < mid_end) {
|
||||
for (s32 bx = mid_start; bx < mid_end; bx += 512)
|
||||
self->setPixelBlendDstBatch(bx, yc, redArray, greenArray, blueArray, alphaArray,
|
||||
std::min(512, mid_end - bx));
|
||||
}
|
||||
|
||||
// Right arc
|
||||
for (s32 bx = mid_start; bx < mid_end; bx += 512)
|
||||
self->setPixelBlendDstBatch(bx, yc, redArray, greenArray, blueArray, alphaArray,
|
||||
std::min(512, mid_end - bx));
|
||||
|
||||
// Right corner arc
|
||||
xp = std::max(xp, right_arc_start);
|
||||
for (; xp < xe; ++xp) {
|
||||
sampleAndBlendArcPixel(self, xp, yc, 2*xp + 1, cx2_right, sx_right,
|
||||
py2, cy2, sy, r2_scaled, color, base_a);
|
||||
sampleAndBlendArcPixel(self, xp, yc,
|
||||
2*xp + 1, 2*(x_end - radius), 1,
|
||||
py2, cy2, 1,
|
||||
r2_scaled, color, base_a);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2186,120 +2189,136 @@ namespace tsl {
|
||||
// RGBA4444 processing - no expansion needed
|
||||
const uint8x16_t mask_low = vdupq_n_u8(0x0F);
|
||||
|
||||
inline void processBMPChunk(const u32 x, const u32 y, const s32 imageW, const u8 *preprocessedData,
|
||||
const s32 startRow, const s32 endRow, const u8 globalAlphaLimit,
|
||||
const bool useBarrier = true, const bool preserveAlpha = false) {
|
||||
inline void processBMPChunk(const u32 x, const u32 y, const s32 imageW, const u8* preprocessedData,
|
||||
const s32 startRow, const s32 endRow, const u8 globalAlphaLimit,
|
||||
const bool useBarrier = true, const bool preserveAlpha = false)
|
||||
{
|
||||
const s32 bytesPerRow = imageW * 2;
|
||||
const s32 endX16 = imageW & ~15;
|
||||
const s32 endX16 = imageW & ~15; // multiple of 16
|
||||
const uint8x16_t mask_low = vdupq_n_u8(0x0F);
|
||||
const uint8x16_t alpha_limit_vec = vdupq_n_u8(globalAlphaLimit);
|
||||
|
||||
Color* const framebuffer = static_cast<Color*>(this->getCurrentFramebuffer());
|
||||
Color* const framebuffer = static_cast<Color*>(Renderer::getCurrentFramebuffer());
|
||||
|
||||
const bool hasScissor = !this->m_scissoringStack.empty();
|
||||
const auto scissor = hasScissor ? this->m_scissoringStack.top() : ScissoringConfig{};
|
||||
const bool hasScissor = !Renderer::m_scissoringStack.empty();
|
||||
const auto scissor = hasScissor ? Renderer::m_scissoringStack.top() : ScissoringConfig{};
|
||||
|
||||
// Precompute Y offsets
|
||||
std::vector<u32> yParts(endRow - startRow);
|
||||
for (s32 y1 = startRow; y1 < endRow; ++y1) {
|
||||
const u32 baseY = y + y1;
|
||||
yParts[y1 - startRow] = ((((baseY & 127) >> 4) + ((baseY >> 7) * offsetWidthVar)) << 9)
|
||||
+ ((baseY & 8) << 5) + ((baseY & 6) << 4) + ((baseY & 1) << 3);
|
||||
}
|
||||
|
||||
for (s32 y1 = startRow; y1 < endRow; ++y1) {
|
||||
const u32 baseY = y + y1;
|
||||
if (hasScissor && (baseY < scissor.y || baseY >= scissor.y_max)) [[unlikely]] continue;
|
||||
|
||||
if (hasScissor && (baseY < scissor.y || baseY >= scissor.y_max)) [[unlikely]]
|
||||
continue;
|
||||
|
||||
const u32 yPart = ((((baseY & 127) >> 4) + ((baseY >> 7) * offsetWidthVar)) << 9)
|
||||
+ ((baseY & 8) << 5) + ((baseY & 6) << 4) + ((baseY & 1) << 3);
|
||||
|
||||
const u8 *rowPtr = preprocessedData + (y1 * bytesPerRow);
|
||||
const u32 yPart = yParts[y1 - startRow];
|
||||
const u8* rowPtr = preprocessedData + (y1 * bytesPerRow);
|
||||
s32 x1 = 0;
|
||||
|
||||
// --- Vectorized 16-pixel loop ---
|
||||
for (; x1 < endX16; x1 += 16) {
|
||||
const u8* ptr = rowPtr + (x1 << 1);
|
||||
|
||||
uint8x16x2_t packed = vld2q_u8(ptr);
|
||||
uint8x16_t high1 = vshrq_n_u8(packed.val[0], 4);
|
||||
uint8x16_t low1 = vandq_u8(packed.val[0], mask_low);
|
||||
uint8x16_t high2 = vshrq_n_u8(packed.val[1], 4);
|
||||
uint8x16_t low2 = vminq_u8(vandq_u8(packed.val[1], mask_low), alpha_limit_vec);
|
||||
|
||||
alignas(16) u8 red_vals[16], green_vals[16], blue_vals[16], alpha_vals[16];
|
||||
vst1q_u8(red_vals, high1);
|
||||
vst1q_u8(green_vals, low1);
|
||||
vst1q_u8(blue_vals, high2);
|
||||
vst1q_u8(alpha_vals, low2);
|
||||
uint8x16_t red = vshrq_n_u8(packed.val[0], 4);
|
||||
uint8x16_t green = vandq_u8(packed.val[0], mask_low);
|
||||
uint8x16_t blue = vshrq_n_u8(packed.val[1], 4);
|
||||
uint8x16_t alpha = vminq_u8(vandq_u8(packed.val[1], mask_low), alpha_limit_vec);
|
||||
|
||||
alignas(16) u8 r[16], g[16], b[16], a[16];
|
||||
vst1q_u8(r, red);
|
||||
vst1q_u8(g, green);
|
||||
vst1q_u8(b, blue);
|
||||
vst1q_u8(a, alpha);
|
||||
|
||||
const u32 baseX = x + x1;
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
const u8 a = alpha_vals[i];
|
||||
if (a == 0) [[unlikely]] continue;
|
||||
if (a[i] == 0) continue;
|
||||
|
||||
const u32 px = baseX + i;
|
||||
if (hasScissor && (px < scissor.x || px >= scissor.x_max)) [[unlikely]] continue;
|
||||
const u32 offset = yPart + ((px >> 5) << 12)
|
||||
+ ((px & 16) << 3) + ((px & 8) << 1) + (px & 7);
|
||||
const Color src = framebuffer[offset];
|
||||
framebuffer[offset] = {
|
||||
blendColor(src.r, red_vals[i], a),
|
||||
blendColor(src.g, green_vals[i], a),
|
||||
blendColor(src.b, blue_vals[i], a),
|
||||
static_cast<u8>(preserveAlpha ? src.a : (a + ((src.a * (0xF - a)) >> 4)))
|
||||
};
|
||||
if (hasScissor && (px < scissor.x || px >= scissor.x_max)) continue;
|
||||
|
||||
const u32 offset = yPart + ((px >> 5) << 12) + ((px & 16) << 3) + ((px & 8) << 1) + (px & 7);
|
||||
Color& dst = framebuffer[offset];
|
||||
|
||||
dst.r = blendColor(dst.r, r[i], a[i]);
|
||||
dst.g = blendColor(dst.g, g[i], a[i]);
|
||||
dst.b = blendColor(dst.b, b[i], a[i]);
|
||||
if (!preserveAlpha)
|
||||
dst.a = static_cast<u8>(a[i] + ((dst.a * (0xF - a[i])) >> 4));
|
||||
}
|
||||
}
|
||||
|
||||
// --- Scalar leftover pixels ---
|
||||
for (; x1 < imageW; ++x1) {
|
||||
const u8 p1 = rowPtr[x1 << 1];
|
||||
const u8 p2 = rowPtr[(x1 << 1) + 1];
|
||||
const u8 alpha = std::min(static_cast<u8>(p2 & 0x0F), globalAlphaLimit);
|
||||
if (alpha == 0) [[unlikely]] continue;
|
||||
const u8 alphaVal = std::min<u8>(p2 & 0x0F, globalAlphaLimit);
|
||||
if (alphaVal == 0) continue;
|
||||
|
||||
const u32 px = x + x1;
|
||||
if (hasScissor && (px < scissor.x || px >= scissor.x_max)) [[unlikely]] continue;
|
||||
const u32 offset = yPart + ((px >> 5) << 12)
|
||||
+ ((px & 16) << 3) + ((px & 8) << 1) + (px & 7);
|
||||
const Color bg = framebuffer[offset];
|
||||
framebuffer[offset] = {
|
||||
blendColor(bg.r, static_cast<u8>(p1 >> 4), alpha),
|
||||
blendColor(bg.g, static_cast<u8>(p1 & 0x0F), alpha),
|
||||
blendColor(bg.b, static_cast<u8>(p2 >> 4), alpha),
|
||||
static_cast<u8>(preserveAlpha ? bg.a : (alpha + ((bg.a * (0xF - alpha)) >> 4)))
|
||||
};
|
||||
if (hasScissor && (px < scissor.x || px >= scissor.x_max)) continue;
|
||||
|
||||
const u32 offset = yPart + ((px >> 5) << 12) + ((px & 16) << 3) + ((px & 8) << 1) + (px & 7);
|
||||
Color& dst = framebuffer[offset];
|
||||
|
||||
dst.r = dst.r + (((p1 >> 4) - dst.r) * alphaVal >> 4);
|
||||
dst.g = dst.g + (((p1 & 0x0F) - dst.g) * alphaVal >> 4);
|
||||
dst.b = dst.b + (((p2 >> 4) - dst.b) * alphaVal >> 4);
|
||||
if (!preserveAlpha)
|
||||
dst.a = static_cast<u8>(alphaVal + ((dst.a * (0xF - alphaVal)) >> 4));
|
||||
}
|
||||
}
|
||||
|
||||
if (useBarrier)
|
||||
ult::inPlotBarrier.arrive_and_wait();
|
||||
if (useBarrier) ult::inPlotBarrier.arrive_and_wait();
|
||||
}
|
||||
|
||||
inline void drawBitmapRGBA4444(const u32 x, const u32 y, const u32 imageW, const u32 imageH,
|
||||
const u8 *preprocessedData, float opacity = 1.0f, bool preserveAlpha = false) {
|
||||
// --- Draw bitmap RGBA4444 ---
|
||||
inline void drawBitmapRGBA4444(const u32 x, const u32 y, const u32 imageW, const u32 imageH,
|
||||
const u8* preprocessedData, float opacity = 1.0f, bool preserveAlpha = false)
|
||||
{
|
||||
const u8 globalAlphaLimit = static_cast<u8>(0xF * opacity);
|
||||
|
||||
|
||||
// Small width -> single-threaded
|
||||
if (imageW < 448) {
|
||||
processBMPChunk(x, y, imageW, preprocessedData, 0, imageH, globalAlphaLimit, false, preserveAlpha);
|
||||
return;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < ult::numThreads; ++i) {
|
||||
const u32 startRow = i * ult::bmpChunkSize;
|
||||
const u32 endRow = std::min(startRow + ult::bmpChunkSize, imageH);
|
||||
ult::renderThreads[i] = std::thread([this, x, y, imageW, preprocessedData, startRow, endRow, globalAlphaLimit, preserveAlpha](){
|
||||
|
||||
// Multi-threaded rendering
|
||||
const u32 numThreads = ult::numThreads;
|
||||
const u32 chunkSize = (imageH + numThreads - 1) / numThreads;
|
||||
|
||||
for (u32 t = 0; t < numThreads; ++t) {
|
||||
const u32 startRow = t * chunkSize;
|
||||
const u32 endRow = std::min(startRow + chunkSize, imageH);
|
||||
|
||||
ult::renderThreads[t] = std::thread([=, this]() {
|
||||
processBMPChunk(x, y, imageW, preprocessedData, startRow, endRow, globalAlphaLimit, true, preserveAlpha);
|
||||
});
|
||||
}
|
||||
for (auto& t : ult::renderThreads) t.join();
|
||||
|
||||
for (auto& th : ult::renderThreads) th.join();
|
||||
}
|
||||
|
||||
// --- Draw wallpaper ---
|
||||
inline void drawWallpaper() {
|
||||
if (!ult::expandedMemory || ult::refreshWallpaper.load(std::memory_order_acquire)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ult::expandedMemory || ult::refreshWallpaper.load(std::memory_order_acquire)) return;
|
||||
|
||||
ult::inPlot.store(true, std::memory_order_release);
|
||||
|
||||
if (!ult::wallpaperData.empty() &&
|
||||
!ult::refreshWallpaper.load(std::memory_order_acquire) &&
|
||||
ult::correctFrameSize) {
|
||||
drawBitmapRGBA4444(0, 0, cfg::FramebufferWidth, cfg::FramebufferHeight,
|
||||
ult::wallpaperData.data(), Renderer::s_opacity, true);
|
||||
|
||||
if (!ult::wallpaperData.empty() &&
|
||||
!ult::refreshWallpaper.load(std::memory_order_acquire) &&
|
||||
ult::correctFrameSize)
|
||||
{
|
||||
drawBitmapRGBA4444(0, 0, cfg::FramebufferWidth, cfg::FramebufferHeight,
|
||||
ult::wallpaperData.data(), Renderer::s_opacity, true);
|
||||
}
|
||||
|
||||
|
||||
ult::inPlot.store(false, std::memory_order_release);
|
||||
}
|
||||
|
||||
@@ -2827,6 +2846,13 @@ namespace tsl {
|
||||
cfg::LayerHeight += cfg::ScreenHeight/720. * verticalUnderscanPixels;
|
||||
} else if (ult::correctFrameSize) {
|
||||
cfg::LayerWidth += horizontalUnderscanPixels;
|
||||
} else if (horizontalUnderscanPixels > 0) {
|
||||
// General case: any non-standard FB size (e.g. windowed GB).
|
||||
// Scale the correction proportionally to the fraction of the
|
||||
// full 1280-logical-space width this layer occupies.
|
||||
cfg::LayerWidth += static_cast<int>(
|
||||
horizontalUnderscanPixels *
|
||||
(float(cfg::FramebufferWidth) / float(cfg::LayerMaxWidth)) + 0.5f);
|
||||
}
|
||||
|
||||
// Update position if using right alignment
|
||||
@@ -3064,30 +3090,35 @@ namespace tsl {
|
||||
// Optimized glyph rendering
|
||||
inline void renderGlyph(std::shared_ptr<FontManager::Glyph> glyph, float x, float y, const Color& color, bool skipAlphaLimit = false) {
|
||||
if (!glyph->glyphBmp || color.a == 0) [[unlikely]] return;
|
||||
|
||||
|
||||
const s32 xPos = static_cast<s32>(x + glyph->bounds[0]);
|
||||
const s32 yPos = static_cast<s32>(y + glyph->bounds[1]);
|
||||
|
||||
|
||||
const s32 glyphWidth = glyph->width;
|
||||
const s32 glyphHeight = glyph->height;
|
||||
|
||||
// Clipping
|
||||
if (xPos >= cfg::FramebufferWidth || yPos >= cfg::FramebufferHeight ||
|
||||
xPos + glyph->width <= 0 || yPos + glyph->height <= 0) [[unlikely]] return;
|
||||
|
||||
xPos + glyphWidth <= 0 || yPos + glyphHeight <= 0) [[unlikely]] return;
|
||||
|
||||
const s32 startX = std::max(0, -xPos);
|
||||
const s32 startY = std::max(0, -yPos);
|
||||
const s32 endX = std::min(glyph->width, static_cast<s32>(cfg::FramebufferWidth) - xPos);
|
||||
const s32 endY = std::min(glyph->height, static_cast<s32>(cfg::FramebufferHeight) - yPos);
|
||||
const s32 endX = std::min(glyphWidth, static_cast<s32>(cfg::FramebufferWidth) - xPos);
|
||||
const s32 endY = std::min(glyphHeight, static_cast<s32>(cfg::FramebufferHeight) - yPos);
|
||||
|
||||
const u8 alphaLimit = skipAlphaLimit ? color.a : static_cast<u8>(0xF * Renderer::s_opacity);
|
||||
const uint8_t* bmpPtr = glyph->glyphBmp + startY * glyph->width;
|
||||
|
||||
for (s32 bmpY = startY; bmpY < endY; ++bmpY, bmpPtr += glyph->width) {
|
||||
const uint8_t* bmpPtr = glyph->glyphBmp + startY * glyphWidth;
|
||||
|
||||
for (s32 bmpY = startY; bmpY < endY; ++bmpY, bmpPtr += glyphWidth) {
|
||||
const s32 pixelY = yPos + bmpY;
|
||||
|
||||
|
||||
for (s32 bmpX = startX; bmpX < endX; ++bmpX) {
|
||||
u8 alpha = bmpPtr[bmpX] >> 4;
|
||||
if (alpha == 0) [[unlikely]] continue;
|
||||
|
||||
|
||||
alpha = (alpha < alphaLimit) ? alpha : alphaLimit;
|
||||
const s32 pixelX = xPos + bmpX;
|
||||
|
||||
|
||||
if (alpha == 0xF) [[likely]] {
|
||||
this->setPixel(pixelX, pixelY, color);
|
||||
} else {
|
||||
@@ -3120,6 +3151,16 @@ namespace tsl {
|
||||
screenshotsAreForceDisabled.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the current framebuffer address
|
||||
*
|
||||
* @return Framebuffer address
|
||||
*/
|
||||
inline void* getCurrentFramebuffer() {
|
||||
return this->m_currentFramebuffer;
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
Renderer() {}
|
||||
|
||||
@@ -3147,15 +3188,6 @@ namespace tsl {
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Get the current framebuffer address
|
||||
*
|
||||
* @return Framebuffer address
|
||||
*/
|
||||
inline void* getCurrentFramebuffer() {
|
||||
return this->m_currentFramebuffer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get the next framebuffer address
|
||||
*
|
||||
@@ -3265,6 +3297,10 @@ namespace tsl {
|
||||
cfg::LayerHeight += cfg::ScreenHeight/720. *verticalUnderscanPixels;
|
||||
else if (ult::correctFrameSize)
|
||||
cfg::LayerWidth += horizontalUnderscanPixels;
|
||||
else if (horizontalUnderscanPixels > 0)
|
||||
cfg::LayerWidth += static_cast<int>(
|
||||
horizontalUnderscanPixels *
|
||||
(float(cfg::FramebufferWidth) / float(cfg::LayerMaxWidth)) + 0.5f);
|
||||
|
||||
if (this->m_initialized)
|
||||
return;
|
||||
@@ -4072,7 +4108,7 @@ namespace tsl {
|
||||
};
|
||||
|
||||
|
||||
#if IS_LAUNCHER_DIRECTIVE
|
||||
//#if IS_LAUNCHER_DIRECTIVE
|
||||
// Simple utility function to draw the dynamic "Ultra" part of the logo
|
||||
static s32 drawDynamicUltraText(gfx::Renderer* renderer, s32 startX, s32 y, u32 fontSize,
|
||||
const tsl::Color& staticColor, bool useNotificationMethod = false) {
|
||||
@@ -4148,7 +4184,7 @@ namespace tsl {
|
||||
return totalWidth;
|
||||
}
|
||||
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
/**
|
||||
* @brief The base frame which can contain another view
|
||||
@@ -6714,6 +6750,7 @@ namespace tsl {
|
||||
m_clickAnimationProgress = 0;
|
||||
// Only trigger normal click if we weren't in a hold
|
||||
if (!wasHolding) {
|
||||
tsl::shiftItemFocus(this);
|
||||
return onClick(determineKeyOnTouchRelease());
|
||||
}
|
||||
}
|
||||
@@ -8077,10 +8114,10 @@ namespace tsl {
|
||||
(this->m_focused && ult::useSelectionValue) ? selectedValueTextColor : onTextColor);
|
||||
|
||||
if (m_icon[0] != '\0')
|
||||
renderer->drawString(this->m_icon, false, this->getX()+42, this->getY() + 50+2+2, 30, tsl::style::color::ColorText);
|
||||
renderer->drawString(this->m_icon, false, this->getX()+42, this->getY() + 50+2+2, 30, ((!this->m_focused || !ult::useSelectionText) ? defaultTextColor : selectedTextColor));
|
||||
} else {
|
||||
if (m_icon[0] != '\0')
|
||||
renderer->drawString(this->m_icon, false, this->getX()+42, this->getY() + 50+2+2, 30, tsl::style::color::ColorText);
|
||||
renderer->drawString(this->m_icon, false, this->getX()+42, this->getY() + 50+2+2, 30, ((!this->m_focused || !ult::useSelectionText) ? defaultTextColor : selectedTextColor));
|
||||
}
|
||||
|
||||
if (m_lastBottomBound != this->getTopBound())
|
||||
@@ -11674,7 +11711,7 @@ namespace tsl {
|
||||
ult::FALSE_STR
|
||||
);
|
||||
tsl::setNextOverlay(
|
||||
ult::OVERLAY_PATH + "ovlmenu.ovl"
|
||||
returnOverlayPath
|
||||
);
|
||||
tsl::Overlay::get()->close();
|
||||
break;
|
||||
@@ -11865,7 +11902,7 @@ namespace tsl {
|
||||
ult::TRUE_STR
|
||||
);
|
||||
|
||||
tsl::setNextOverlay(ult::OVERLAY_PATH + "ovlmenu.ovl", "--direct --comboReturn");
|
||||
tsl::setNextOverlay(returnOverlayPath, "--direct --comboReturn");
|
||||
fireLaunch();
|
||||
return;
|
||||
}
|
||||
@@ -11923,7 +11960,7 @@ namespace tsl {
|
||||
}
|
||||
#else
|
||||
if (idx == WaiterObject_HomeButton || idx == WaiterObject_PowerButton) { // Changed condition to exclude capture button
|
||||
if (shData->overlayOpen) {
|
||||
if (shData->overlayOpen && !disableHiding) {
|
||||
tsl::Overlay::get()->hide();
|
||||
shData->overlayOpen = false;
|
||||
}
|
||||
@@ -12349,6 +12386,19 @@ namespace tsl {
|
||||
}
|
||||
}
|
||||
|
||||
// Detect -returning: overlay was launched as a return from a sub-mode
|
||||
// (e.g. windowed → normal). Uses exit feedback instead of enter feedback.
|
||||
// Only needed in the non-STATUS_MONITOR, non-LAUNCHER path below.
|
||||
#if !IS_STATUS_MONITOR_DIRECTIVE && !IS_LAUNCHER_DIRECTIVE
|
||||
bool isReturningLaunch = false;
|
||||
for (u8 arg = 1; arg < argc; arg++) {
|
||||
if (strcmp(argv[arg], "-returning") == 0) {
|
||||
isReturningLaunch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool skipCombo = false;
|
||||
#if IS_LAUNCHER_DIRECTIVE
|
||||
bool comboReturn = false;
|
||||
@@ -12431,7 +12481,7 @@ namespace tsl {
|
||||
Thread backgroundFeedbackThread;
|
||||
threadCreate(&backgroundFeedbackThread, impl::backgroundFeedbackPoller, nullptr, nullptr, 0x1000, 0x2c, -2);
|
||||
threadStart(&backgroundFeedbackThread);
|
||||
|
||||
|
||||
Thread backgroundEventThread;
|
||||
threadCreate(&backgroundEventThread, impl::backgroundEventPoller, &shData, nullptr, 0x2000, 0x2c, -2);
|
||||
threadStart(&backgroundEventThread);
|
||||
@@ -12598,7 +12648,7 @@ namespace tsl {
|
||||
launchComboHasTriggered.store(true, std::memory_order_release); // for isolating sound effect
|
||||
|
||||
if (usingPackageLauncher || directMode) {
|
||||
tsl::setNextOverlay(ult::OVERLAY_PATH + "ovlmenu.ovl");
|
||||
tsl::setNextOverlay(returnOverlayPath);
|
||||
}
|
||||
|
||||
hlp::requestForeground(false);
|
||||
@@ -12670,7 +12720,11 @@ namespace tsl {
|
||||
triggerEnterFeedback();
|
||||
}
|
||||
#else
|
||||
triggerEnterFeedback();
|
||||
if (isReturningLaunch) {
|
||||
triggerExitFeedback();
|
||||
} else {
|
||||
triggerEnterFeedback();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
@@ -12745,7 +12799,7 @@ namespace tsl {
|
||||
if (directMode && !launchComboHasTriggered.load(std::memory_order_acquire)) {
|
||||
if (!disableSound.load(std::memory_order_acquire) && ult::useSoundEffects)
|
||||
ult::Audio::playExitSound();
|
||||
if (ult::useHapticFeedback) {
|
||||
if (ult::useHapticFeedback && !skipRumbleDoubleClick) {
|
||||
ult::rumbleDoubleClickStandalone();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -141,10 +141,10 @@ Color ultPackageVersionTextColor;
|
||||
Color onTextColor;
|
||||
Color offTextColor;
|
||||
|
||||
#if IS_LAUNCHER_DIRECTIVE
|
||||
//#if IS_LAUNCHER_DIRECTIVE
|
||||
Color dynamicLogoRGB1;
|
||||
Color dynamicLogoRGB2;
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
bool invertBGClickColor = false;
|
||||
|
||||
@@ -329,10 +329,10 @@ void initializeThemeVars() {
|
||||
logoColor1 = getColor("logo_color_1");
|
||||
logoColor2 = getColor("logo_color_2");
|
||||
|
||||
#if IS_LAUNCHER_DIRECTIVE
|
||||
//#if IS_LAUNCHER_DIRECTIVE
|
||||
dynamicLogoRGB1 = getColor("dynamic_logo_color_1");
|
||||
dynamicLogoRGB2 = getColor("dynamic_logo_color_2");
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
defaultBackgroundAlpha = getAlpha("bg_alpha");
|
||||
defaultBackgroundColor = getColor("bg_color", defaultBackgroundAlpha);
|
||||
@@ -985,6 +985,9 @@ bool NotificationPrompt::hasActiveFile(std::string_view fname) const {
|
||||
}
|
||||
|
||||
int NotificationPrompt::findHitSlot_NoLock(s32 tx, s32 ty) const {
|
||||
// In windowed mode the layer is offset in touch space; layerEdge already
|
||||
// accounts for X. Subtract layerEdgeY to bring ty into framebuffer-local space.
|
||||
ty -= layerEdgeY;
|
||||
const s32 sx = (ult::useRightAlignment
|
||||
? static_cast<s32>(tsl::cfg::FramebufferWidth) - NOTIF_WIDTH
|
||||
: 0) + static_cast<s32>(ult::layerEdge);
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include <ctime>
|
||||
|
||||
namespace ult {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
//#if USING_LOGGING_DIRECTIVE
|
||||
|
||||
// Specify the log file path
|
||||
extern const std::string defaultLogFilePath;
|
||||
@@ -41,5 +41,5 @@ namespace ult {
|
||||
*/
|
||||
void logMessage(const char* message);
|
||||
void logMessage(const std::string& message);
|
||||
#endif
|
||||
//#endif
|
||||
}
|
||||
@@ -36,5 +36,6 @@ namespace ult {
|
||||
void rumbleDoubleClick();
|
||||
void processRumbleStop(u64 nowNs);
|
||||
void processRumbleDoubleClick(u64 nowNs);
|
||||
void rumbleClickStandalone();
|
||||
void rumbleDoubleClickStandalone();
|
||||
}
|
||||
@@ -18,7 +18,7 @@
|
||||
#include "debug_funcs.hpp"
|
||||
|
||||
namespace ult {
|
||||
#if USING_LOGGING_DIRECTIVE
|
||||
//#if USING_LOGGING_DIRECTIVE
|
||||
// Define static variables
|
||||
const std::string defaultLogFilePath = "sdmc:/switch/.packages/log.txt";
|
||||
std::string logFilePath = defaultLogFilePath;
|
||||
@@ -48,5 +48,5 @@ namespace ult {
|
||||
void logMessage(const std::string& message) {
|
||||
logMessage(message.c_str());
|
||||
}
|
||||
#endif
|
||||
//#endif
|
||||
}
|
||||
@@ -181,6 +181,14 @@ namespace ult {
|
||||
}
|
||||
}
|
||||
|
||||
void rumbleClickStandalone() {
|
||||
// Match regular rumbleClick() behavior, but blocking
|
||||
sendVibration(&vibrationStop);
|
||||
sendVibration2x(&hapticsPreset);
|
||||
svcSleepThread(RUMBLE_DURATION_NS);
|
||||
sendVibration(&vibrationStop);
|
||||
}
|
||||
|
||||
|
||||
void rumbleDoubleClickStandalone() {
|
||||
// Standalone uses sleeps, but still use cached style for decision
|
||||
|
||||
Submodule hoc-clk/libultrahand deleted from 8a1dbe9910
Reference in New Issue
Block a user