Skip to content

Commit ec92aa7

Browse files
committed
Add function to show QR Code of an entry's wiki link
1 parent c4ad90e commit ec92aa7

File tree

4 files changed

+201
-7
lines changed

4 files changed

+201
-7
lines changed

include/overlays/overlay.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ namespace Overlays {
3636
void ShowCredits();
3737
std::string SelectDir(const std::string &oldDir, const std::string &msg);
3838
void SelectTheme();
39+
void ShowQrCodeUrl(const std::string &title, const std::string &url);
3940
};
4041

4142
#endif

romfs/lang/en/app.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,9 @@
110110
"OP_INSTALLING": "Installing",
111111
"OP_MOVING": "Moving",
112112
"OP_WAITING": "Waiting",
113+
"OPEN_URL_WEB_BROWSER": "Open URL in Web Browser (SELECT)",
114+
"OPEN_URL_WEB_BROWSER_DISABLED": "The web browser cannot be used\nwhile the Queue is running.",
115+
"QR_CODE_GEN_FAILED": "Failed to generate QR Code.",
113116
"QUEUE": "Queue",
114117
"QUEUE_POSITION": "Queue position",
115118
"QUEUE_PROGRESS": "Step: %d / %d",

source/menu/entryInfo.cpp

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#include "common.hpp"
2828
#include "files.hpp"
29+
#include "overlay.hpp"
2930
#include "storeUtils.hpp"
3031
#include "structs.hpp"
3132

@@ -106,13 +107,7 @@ void StoreUtils::EntryHandle(bool &showMark, bool &fetch, bool &sFetch, int &mod
106107
}
107108

108109
if (touching(touch, wiki) && entry->GetWiki() != "") {
109-
char *buf = new char[0x1000]; // Needs to be this size size it gets memcpy'd to
110-
int length = entry->GetWiki().size();
111-
memcpy(buf, entry->GetWiki().c_str(), length);
112-
buf[length] = 0;
113-
aptLaunchSystemApplet(APPID_WEB, buf, length + 1, 0);
114-
115-
delete[] buf;
110+
Overlays::ShowQrCodeUrl(entry->GetTitle() + " Wiki", entry->GetWiki());
116111
}
117112
}
118113

source/overlays/showQrCode.cpp

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/*
2+
* This file is part of Universal-Updater
3+
* Copyright (C) 2019-2021 Universal-Team
4+
* Copyright (C) 2025 Alvin Wong
5+
*
6+
* This program is free software: you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* This program is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
18+
*
19+
* Additional Terms 7.b and 7.c of GPLv3 apply to this file:
20+
* * Requiring preservation of specified reasonable legal notices or
21+
* author attributions in that material or in the Appropriate Legal
22+
* Notices displayed by works containing it.
23+
* * Prohibiting misrepresentation of the origin of that material,
24+
* or requiring that modified versions of such material be marked in
25+
* reasonable ways as different from the original version.
26+
*/
27+
28+
#include "animation.hpp"
29+
#include "common.hpp"
30+
#include "overlay.hpp"
31+
#include "qrcodegen.h"
32+
33+
extern bool touching(touchPosition touch, Structs::ButtonPos button);
34+
extern bool QueueRuns;
35+
36+
static const Structs::ButtonPos backButton{ 4, 0, 24, 24 };
37+
static const Structs::ButtonPos browserButton = { 4, 212, 312, 22 };
38+
39+
static std::vector<std::string> wrapUrlLines(std::string_view url, uint32_t maxLines = std::numeric_limits<uint32_t>::max()) {
40+
// Crudely line wrap the URL:
41+
std::vector<std::string> wrappedUrlLines;
42+
std::string line;
43+
size_t lastWrap = 0;
44+
for (size_t i = 0; i < url.size(); i++) {
45+
char c = url[i];
46+
line.push_back(c);
47+
48+
if (i - lastWrap < 30) continue;
49+
50+
bool wrap = false;
51+
switch (c) {
52+
case '-':
53+
case '/':
54+
case '_':
55+
case '?':
56+
case '=':
57+
case '&':
58+
case ' ':
59+
wrap = true;
60+
break;
61+
default:
62+
if (i - lastWrap >= 40) wrap = true;
63+
break;
64+
}
65+
66+
if (wrap) {
67+
if (wrappedUrlLines.size() + 1 >= maxLines) {
68+
line.append("...");
69+
break;
70+
}
71+
if (line[line.size() - 1] == ' ') {
72+
// add keyboard return symbol to visualize whitespace
73+
line.append("\uE056");
74+
}
75+
wrappedUrlLines.emplace_back(std::move(line));
76+
lastWrap = i;
77+
}
78+
}
79+
if (!line.empty()) wrappedUrlLines.emplace_back(std::move(line));
80+
return wrappedUrlLines;
81+
}
82+
83+
/* Show a QR Code URL. */
84+
void Overlays::ShowQrCodeUrl(const std::string &title, const std::string &url) {
85+
86+
std::unique_ptr<uint8_t[]> qrcode = std::make_unique<uint8_t[]>(qrcodegen_BUFFER_LEN_MAX);
87+
std::unique_ptr<uint8_t[]> tempBuffer = std::make_unique<uint8_t[]>(qrcodegen_BUFFER_LEN_MAX);
88+
bool ok = qrcodegen_encodeText(url.c_str(), tempBuffer.get(), qrcode.get(), qrcodegen_Ecc_LOW, qrcodegen_VERSION_MIN, qrcodegen_VERSION_MAX, qrcodegen_Mask_AUTO, true);
89+
if (!ok) {
90+
Msg::DisplayMsg(Lang::get("QR_CODE_GEN_FAILED"));
91+
return;
92+
}
93+
94+
int size = qrcodegen_getSize(qrcode.get());
95+
if (size <= 0 || size > 256) {
96+
Msg::DisplayMsg(Lang::get("QR_CODE_GEN_FAILED"));
97+
return;
98+
}
99+
100+
C3D_Tex tex;
101+
102+
C3D_TexInit(&tex, 256, 256, GPU_TEXCOLOR::GPU_A8);
103+
C3D_TexSetFilter(&tex, GPU_NEAREST, GPU_NEAREST);
104+
C3D_TexSetWrap(&tex, GPU_CLAMP_TO_EDGE, GPU_CLAMP_TO_EDGE);
105+
106+
for (int x = 0; x < size; x++) {
107+
for (int y = 0; y < size; y++) {
108+
const u32 dstPos = ((((y >> 3) * (256 >> 3) + (x >> 3)) << 6) +
109+
((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) |
110+
((x & 4) << 2) | ((y & 4) << 3)));
111+
112+
((uint8_t *)tex.data)[dstPos] = qrcodegen_getModule(qrcode.get(), x, y) ? 255 : 0;
113+
}
114+
}
115+
116+
constexpr int border = 4;
117+
int scale = 240 / (size + border * 2);
118+
int drawSize = size * scale;
119+
int drawX = (400 - drawSize) / 2;
120+
int drawY = (240 - drawSize) / 2;
121+
int drawBorder = border * scale;
122+
123+
C2D_Image qrImage;
124+
Tex3DS_SubTexture subtex{(u16)drawSize, (u16)drawSize, 0.0f, 1.0f, size / 256.f, 1.0f - (size / 256.f)};
125+
qrImage.tex = &tex;
126+
qrImage.subtex = &subtex;
127+
C2D_Tint tint{
128+
.color = BLACK,
129+
.blend = 1.f,
130+
};
131+
C2D_ImageTint imageTint{
132+
.corners = {tint, tint, tint, tint},
133+
};
134+
135+
std::vector<std::string> wrappedUrlLines = wrapUrlLines(url, 8);
136+
137+
bool doOut = false;
138+
139+
while(!doOut) {
140+
Gui::clearTextBufs();
141+
C3D_FrameBegin(C3D_FRAME_SYNCDRAW);
142+
C2D_TargetClear(Top, TRANSPARENT);
143+
C2D_TargetClear(Bottom, TRANSPARENT);
144+
145+
GFX::DrawTop();
146+
147+
C2D_DrawRectSolid(drawX - drawBorder, drawY - drawBorder, 0.75f, drawSize + drawBorder * 2, drawSize + drawBorder * 2, WHITE);
148+
C2D_DrawImageAt(qrImage, drawX, drawY, 1.f, &imageTint);
149+
150+
Animation::QueueEntryDone();
151+
GFX::DrawBottom();
152+
Gui::Draw_Rect(0, 0, 320, 25, UIThemes->BarColor());
153+
Gui::Draw_Rect(0, 25, 320, 1, UIThemes->BarOutline());
154+
GFX::DrawIcon(sprites_arrow_idx, backButton.x, backButton.y, UIThemes->TextColor());
155+
Gui::DrawStringCentered(0, 2, 0.6, UIThemes->TextColor(), title, 310, 0, font);
156+
157+
int y = 34;
158+
for (const std::string &urlLine : wrappedUrlLines) {
159+
Gui::DrawStringCentered(0, y, 0.6f, UIThemes->TextColor(), urlLine, 312, 0, font);
160+
y += 20;
161+
if (y >= browserButton.y) break;
162+
}
163+
164+
const bool browserAllowed = !QueueRuns && aptIsHomeAllowed();
165+
Gui::Draw_Rect(browserButton.x, browserButton.y, browserButton.w, browserButton.h, browserAllowed ? UIThemes->MarkSelected() : UIThemes->MarkUnselected());
166+
Gui::DrawStringCentered(0, browserButton.y + 4, 0.45f, UIThemes->TextColor(), Lang::get("OPEN_URL_WEB_BROWSER"), 255, 0, font);
167+
168+
C3D_FrameEnd(0);
169+
170+
hidScanInput();
171+
touchPosition touch;
172+
hidTouchRead(&touch);
173+
Animation::HandleQueueEntryDone();
174+
175+
if ((hidKeysDown() & KEY_START) || (hidKeysDown() & KEY_B) || (hidKeysDown() & KEY_A) || (hidKeysDown() & KEY_TOUCH && touching(touch, backButton))) doOut = true;
176+
177+
if ((hidKeysDown() & KEY_SELECT) || (hidKeysDown() & KEY_TOUCH && touching(touch, browserButton))) {
178+
if (browserAllowed) {
179+
char *buf = new char[0x1000]; // Needs to be this size size it gets memcpy'd to
180+
int length = url.size();
181+
memcpy(buf, url.c_str(), length);
182+
buf[length] = 0;
183+
aptLaunchSystemApplet(APPID_WEB, buf, length + 1, 0);
184+
185+
delete[] buf;
186+
187+
doOut = true;
188+
} else {
189+
Msg::waitMsg(Lang::get("OPEN_URL_WEB_BROWSER_DISABLED"));
190+
}
191+
}
192+
}
193+
194+
C3D_TexDelete(&tex);
195+
}

0 commit comments

Comments
 (0)