Compare commits

...

11 Commits

Author SHA1 Message Date
armv9
adef6cd4af 4.0.1 2024-12-12 17:03:36 +09:00
armv9
399b171adf misc change 2024-12-12 17:03:32 +09:00
armv9
6e4c180428 fix: dns outbound 2024-12-12 17:02:37 +09:00
armv9
75c6496151 change: domain rule use domain_suffix 2024-12-12 17:02:14 +09:00
Integral
62c59f6fd3
refactor: replace non-empty QString constructors with QStringLiteral() 2024-11-03 12:50:41 +08:00
armv9
12d6fc24e7 4.0-beta4 2024-10-09 13:32:41 +09:00
armv9
6de7c588b6 update core 2024-10-09 13:32:41 +09:00
armv9
99c8d50943 add url test button on main window 2024-10-09 13:32:41 +09:00
极速蜗牛
2a177256ce
Add /usr/share/sing-box to core asset search path (#1350) 2024-07-20 13:23:24 +08:00
Chi_Tang
b1d1674912
fixed pkgbuild for 4.0 (#1348) 2024-07-13 17:36:39 +08:00
armv9
2dd9cf45eb update readme 2024-07-13 15:57:07 +09:00
30 changed files with 212 additions and 133 deletions

18
.github/workflows/update-pkgbuild.yml vendored Normal file
View File

@ -0,0 +1,18 @@
name: AUR CI
on:
push:
branches:
- main
paths-ignore:
- '**.md'
- 'LICENSE'
- '!.github/workflows/**'
jobs:
update:
runs-on: ubuntu-latest
steps:
- uses: chitang233/aur-pkgbuild-builder@main
with:
deploy_key: ${{ secrets.DEPLOY_KEY }}
package_name: 'nekoray-git'

View File

@ -353,7 +353,7 @@ namespace Qv2ray::components::proxy {
// execute and get the code // execute and get the code
const auto returnCode = QProcess::execute(action.first, action.second); const auto returnCode = QProcess::execute(action.first, action.second);
// print out the commands and result codes // print out the commands and result codes
DEBUG(QString("[%1] Program: %2, Args: %3").arg(returnCode).arg(action.first).arg(action.second.join(";"))); DEBUG(QStringLiteral("[%1] Program: %2, Args: %3").arg(returnCode).arg(action.first).arg(action.second.join(";")));
// give the code back // give the code back
results << (returnCode == QProcess::NormalExit); results << (returnCode == QProcess::NormalExit);
} }
@ -423,7 +423,7 @@ namespace Qv2ray::components::proxy {
// execute and get the code // execute and get the code
const auto returnCode = QProcess::execute(action.first, action.second); const auto returnCode = QProcess::execute(action.first, action.second);
// print out the commands and result codes // print out the commands and result codes
DEBUG(QString("[%1] Program: %2, Args: %3").arg(returnCode).arg(action.first).arg(action.second.join(";"))); DEBUG(QStringLiteral("[%1] Program: %2, Args: %3").arg(returnCode).arg(action.first).arg(action.second.join(";")));
} }
#else #else

View File

@ -191,13 +191,13 @@ QVariant QJsonModel::data(const QModelIndex &index, int role) const {
if (role == Qt::DisplayRole) { if (role == Qt::DisplayRole) {
if (index.column() == 0) if (index.column() == 0)
return QString("%1").arg(item->key()); return QStringLiteral("%1").arg(item->key());
if (index.column() == 1) if (index.column() == 1)
return QString("%1").arg(item->value()); return QStringLiteral("%1").arg(item->value());
} else if (Qt::EditRole == role) { } else if (Qt::EditRole == role) {
if (index.column() == 1) { if (index.column() == 1) {
return QString("%1").arg(item->value()); return QStringLiteral("%1").arg(item->value());
} }
} }

View File

@ -8,16 +8,6 @@ Support Windows / Linux out of the box now.
目前支持 Windows / Linux 开箱即用 目前支持 Windows / Linux 开箱即用
## 4.x 开发计划
软件定位:电脑端节点调试软件。更新频率随机,可用性无保证。机场订阅用户建议使用 Clash Verge Rev 等。
1. 移除 Xray 核心,更新 sing-box。
2. 移除一些没用的功能。
3. 更新文档。
4. 更新部分依赖
5. 移除 macos 遗留
## 下载 / Download ## 下载 / Download
### GitHub Releases (Portable ZIP) ### GitHub Releases (Portable ZIP)

View File

@ -29,23 +29,41 @@ namespace NekoGui {
return tun_name; return tun_name;
} }
void MergeJson(const QJsonObject &custom, QJsonObject &outbound) { void MergeJson(QJsonObject &dst, const QJsonObject &src) {
// 合并 // 合并
if (custom.isEmpty()) return; if (src.isEmpty()) return;
for (const auto &key: custom.keys()) { for (const auto &key: src.keys()) {
if (outbound.contains(key)) { auto v_src = src[key];
auto v = custom[key]; if (dst.contains(key)) {
auto v_orig = outbound[key]; auto v_dst = dst[key];
if (v.isObject() && v_orig.isObject()) { // isObject 则合并? if (v_src.isObject() && v_dst.isObject()) { // isObject 则合并?
auto vo = v.toObject(); auto v_src_obj = v_src.toObject();
QJsonObject vo_orig = v_orig.toObject(); auto v_dst_obj = v_dst.toObject();
MergeJson(vo, vo_orig); MergeJson(v_dst_obj, v_src_obj);
outbound[key] = vo_orig; dst[key] = v_dst_obj;
} else { } else {
outbound[key] = v; dst[key] = v_src;
}
} else if (v_src.isArray()) {
if (key.startsWith("+")) {
auto key2 = SubStrAfter(key, "+");
auto v_dst = dst[key2];
auto v_src_arr = v_src.toArray();
auto v_dst_arr = v_dst.toArray();
QJSONARRAY_ADD(v_src_arr, v_dst_arr)
dst[key2] = v_src_arr;
} else if (key.endsWith("+")) {
auto key2 = SubStrBefore(key, "+");
auto v_dst = dst[key2];
auto v_src_arr = v_src.toArray();
auto v_dst_arr = v_dst.toArray();
QJSONARRAY_ADD(v_dst_arr, v_src_arr)
dst[key2] = v_dst_arr;
} else {
dst[key] = v_src;
} }
} else { } else {
outbound[key] = custom[key]; dst[key] = v_src;
} }
} }
} }
@ -68,7 +86,7 @@ namespace NekoGui {
} }
// apply custom config // apply custom config
MergeJson(QString2QJsonObject(ent->bean->custom_config), result->coreConfig); MergeJson(result->coreConfig, QString2QJsonObject(ent->bean->custom_config));
return result; return result;
} }
@ -76,7 +94,7 @@ namespace NekoGui {
QString BuildChain(int chainId, const std::shared_ptr<BuildConfigStatus> &status) { QString BuildChain(int chainId, const std::shared_ptr<BuildConfigStatus> &status) {
auto group = profileManager->GetGroup(status->ent->gid); auto group = profileManager->GetGroup(status->ent->gid);
if (group == nullptr) { if (group == nullptr) {
status->result->error = QString("This profile is not in any group, your data may be corrupted."); status->result->error = QStringLiteral("This profile is not in any group, your data may be corrupted.");
return {}; return {};
} }
@ -88,11 +106,11 @@ namespace NekoGui {
for (auto id: list) { for (auto id: list) {
resolved += profileManager->GetProfile(id); resolved += profileManager->GetProfile(id);
if (resolved.last() == nullptr) { if (resolved.last() == nullptr) {
status->result->error = QString("chain missing ent: %1").arg(id); status->result->error = QStringLiteral("chain missing ent: %1").arg(id);
break; break;
} }
if (resolved.last()->type == "chain") { if (resolved.last()->type == "chain") {
status->result->error = QString("chain in chain is not allowed: %1").arg(id); status->result->error = QStringLiteral("chain in chain is not allowed: %1").arg(id);
break; break;
} }
} }
@ -109,7 +127,7 @@ namespace NekoGui {
if (group->front_proxy_id >= 0) { if (group->front_proxy_id >= 0) {
auto fEnt = profileManager->GetProfile(group->front_proxy_id); auto fEnt = profileManager->GetProfile(group->front_proxy_id);
if (fEnt == nullptr) { if (fEnt == nullptr) {
status->result->error = QString("front proxy ent not found."); status->result->error = QStringLiteral("front proxy ent not found.");
return {}; return {};
} }
ents += resolveChain(fEnt); ents += resolveChain(fEnt);
@ -342,7 +360,7 @@ namespace NekoGui {
} }
// apply custom outbound settings // apply custom outbound settings
MergeJson(QString2QJsonObject(ent->bean->custom_outbound), outbound); MergeJson(outbound, QString2QJsonObject(ent->bean->custom_outbound));
// Bypass Lookup for the first profile // Bypass Lookup for the first profile
auto serverAddress = ent->bean->serverAddress; auto serverAddress = ent->bean->serverAddress;
@ -486,7 +504,7 @@ namespace NekoGui {
} else if (item.startsWith("keyword:")) { } else if (item.startsWith("keyword:")) {
domain_keyword += item.replace("keyword:", "").toLower(); domain_keyword += item.replace("keyword:", "").toLower();
} else { } else {
domain_full += item.toLower(); domain_subdomain += item.toLower();
} }
} }
} }
@ -537,7 +555,7 @@ namespace NekoGui {
} }
dnsRules.append(QJsonObject{ dnsRules.append(QJsonObject{
{"outbound", "any"}, {"outbound", "any"},
{"server", "direct"}, {"server", "dns-direct"},
}); });
// block // block
@ -797,4 +815,4 @@ namespace NekoGui {
return QFileInfo(file2).absoluteFilePath(); return QFileInfo(file2).absoluteFilePath();
} }
} // namespace NekoGui } // namespace NekoGui

View File

@ -42,7 +42,7 @@ namespace NekoGui {
// Load Proxys // Load Proxys
QList<int> delProfile; QList<int> delProfile;
for (auto id: profilesIdOrder) { for (auto id: profilesIdOrder) {
auto ent = LoadProxyEntity(QString("profiles/%1.json").arg(id)); auto ent = LoadProxyEntity(QStringLiteral("profiles/%1.json").arg(id));
// Corrupted profile? // Corrupted profile?
if (ent == nullptr || ent->bean == nullptr || ent->bean->version == -114514) { if (ent == nullptr || ent->bean == nullptr || ent->bean->version == -114514) {
delProfile << id; delProfile << id;
@ -58,7 +58,7 @@ namespace NekoGui {
auto loadedOrder = groupsTabOrder; auto loadedOrder = groupsTabOrder;
groupsTabOrder = {}; groupsTabOrder = {};
for (auto id: groupsIdOrder) { for (auto id: groupsIdOrder) {
auto ent = LoadGroup(QString("groups/%1.json").arg(id)); auto ent = LoadGroup(QStringLiteral("groups/%1.json").arg(id));
// Corrupted group? // Corrupted group?
if (ent->id != id) { if (ent->id != id) {
continue; continue;
@ -103,7 +103,7 @@ namespace NekoGui {
auto newId = i++; auto newId = i++;
profile->id = newId; profile->id = newId;
profile->gid = gidOld2New[gid]; profile->gid = gidOld2New[gid];
profile->fn = QString("profiles/%1.json").arg(newId); profile->fn = QStringLiteral("profiles/%1.json").arg(newId);
profile->Save(); profile->Save();
newProfiles[newId] = profile; newProfiles[newId] = profile;
newProfilesIdOrder << newId; newProfilesIdOrder << newId;
@ -122,7 +122,7 @@ namespace NekoGui {
auto group = groups[oldGid]; auto group = groups[oldGid];
QFile::remove(group->fn); QFile::remove(group->fn);
group->id = newId; group->id = newId;
group->fn = QString("groups/%1.json").arg(newId); group->fn = QStringLiteral("groups/%1.json").arg(newId);
group->Save(); group->Save();
newGroups[newId] = group; newGroups[newId] = group;
newGroupsIdOrder << newId; newGroupsIdOrder << newId;
@ -227,7 +227,7 @@ namespace NekoGui {
if (latency < 0) { if (latency < 0) {
return QObject::tr("Unavailable"); return QObject::tr("Unavailable");
} else if (latency > 0) { } else if (latency > 0) {
return UNICODE_LRO + QString("%1 ms").arg(latency); return UNICODE_LRO + QStringLiteral("%1 ms").arg(latency);
} else { } else {
return ""; return "";
} }
@ -268,7 +268,7 @@ namespace NekoGui {
profiles[ent->id] = ent; profiles[ent->id] = ent;
profilesIdOrder.push_back(ent->id); profilesIdOrder.push_back(ent->id);
ent->fn = QString("profiles/%1.json").arg(ent->id); ent->fn = QStringLiteral("profiles/%1.json").arg(ent->id);
ent->Save(); ent->Save();
return true; return true;
} }
@ -278,7 +278,7 @@ namespace NekoGui {
if (dataStore->started_id == id) return; if (dataStore->started_id == id) return;
profiles.erase(id); profiles.erase(id);
profilesIdOrder.removeAll(id); profilesIdOrder.removeAll(id);
QFile(QString("profiles/%1.json").arg(id)).remove(); QFile(QStringLiteral("profiles/%1.json").arg(id)).remove();
} }
void ProfileManager::MoveProfile(const std::shared_ptr<ProxyEntity> &ent, int gid) { void ProfileManager::MoveProfile(const std::shared_ptr<ProxyEntity> &ent, int gid) {
@ -342,7 +342,7 @@ namespace NekoGui {
groupsIdOrder.push_back(ent->id); groupsIdOrder.push_back(ent->id);
groupsTabOrder.push_back(ent->id); groupsTabOrder.push_back(ent->id);
ent->fn = QString("groups/%1.json").arg(ent->id); ent->fn = QStringLiteral("groups/%1.json").arg(ent->id);
ent->Save(); ent->Save();
return true; return true;
} }
@ -359,7 +359,7 @@ namespace NekoGui {
groups.erase(gid); groups.erase(gid);
groupsIdOrder.removeAll(gid); groupsIdOrder.removeAll(gid);
groupsTabOrder.removeAll(gid); groupsTabOrder.removeAll(gid);
QFile(QString("groups/%1.json").arg(gid)).remove(); QFile(QStringLiteral("groups/%1.json").arg(gid)).remove();
} }
std::shared_ptr<Group> ProfileManager::GetGroup(int id) { std::shared_ptr<Group> ProfileManager::GetGroup(int id) {
@ -391,4 +391,4 @@ namespace NekoGui {
} }
} }
} // namespace NekoGui } // namespace NekoGui

View File

@ -29,12 +29,12 @@ namespace NekoGui_traffic {
} }
[[nodiscard]] QString DisplaySpeed() const { [[nodiscard]] QString DisplaySpeed() const {
return UNICODE_LRO + QString("%1↑ %2↓").arg(ReadableSize(uplink_rate), ReadableSize(downlink_rate)); return UNICODE_LRO + QStringLiteral("%1↑ %2↓").arg(ReadableSize(uplink_rate), ReadableSize(downlink_rate));
} }
[[nodiscard]] QString DisplayTraffic() const { [[nodiscard]] QString DisplayTraffic() const {
if (downlink + uplink == 0) return ""; if (downlink + uplink == 0) return "";
return UNICODE_LRO + QString("%1↑ %2↓").arg(ReadableSize(uplink), ReadableSize(downlink)); return UNICODE_LRO + QStringLiteral("%1↑ %2↓").arg(ReadableSize(uplink), ReadableSize(downlink));
} }
}; };
} // namespace NekoGui_traffic } // namespace NekoGui_traffic

View File

@ -38,7 +38,7 @@ namespace NekoGui_fmt {
} }
QString AbstractBean::DisplayTypeAndName() { QString AbstractBean::DisplayTypeAndName() {
return QString("[%1] %2").arg(DisplayType(), DisplayName()); return QStringLiteral("[%1] %2").arg(DisplayType(), DisplayName());
} }
void AbstractBean::ResolveDomainToIP(const std::function<void()> &onFinished) { void AbstractBean::ResolveDomainToIP(const std::function<void()> &onFinished) {

View File

@ -9,7 +9,7 @@
#define WriteTempFile(fn, data) \ #define WriteTempFile(fn, data) \
QDir dir; \ QDir dir; \
if (!dir.exists("temp")) dir.mkdir("temp"); \ if (!dir.exists("temp")) dir.mkdir("temp"); \
QFile f(QString("temp/") + fn); \ QFile f(QStringLiteral("temp/") + fn); \
bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); \ bool ok = f.open(QIODevice::WriteOnly | QIODevice::Truncate); \
if (ok) { \ if (ok) { \
f.write(data); \ f.write(data); \
@ -50,7 +50,7 @@ namespace NekoGui_fmt {
return 1; return 1;
}; };
if (!forceExternal && (proxy_type == proxy_TUIC || hopPort.trimmed().isEmpty())) { if (!forceExternal) {
// sing-box support // sing-box support
return 0; return 0;
} else { } else {

View File

@ -13,7 +13,7 @@ namespace NekoGui_fmt {
url.setScheme("http"); url.setScheme("http");
} }
} else { } else {
url.setScheme(QString("socks%1").arg(socks_http_type)); url.setScheme(QStringLiteral("socks%1").arg(socks_http_type));
} }
if (!name.isEmpty()) url.setFragment(name); if (!name.isEmpty()) url.setFragment(name);
if (!username.isEmpty()) url.setUserName(username); if (!username.isEmpty()) url.setUserName(username);
@ -222,4 +222,4 @@ namespace NekoGui_fmt {
return url.toString(QUrl::FullyEncoded); return url.toString(QUrl::FullyEncoded);
} }
} // namespace NekoGui_fmt } // namespace NekoGui_fmt

View File

@ -40,6 +40,7 @@ require (
github.com/libdns/cloudflare v0.1.1 // indirect github.com/libdns/cloudflare v0.1.1 // indirect
github.com/libdns/libdns v0.2.2 // indirect github.com/libdns/libdns v0.2.2 // indirect
github.com/logrusorgru/aurora v2.0.3+incompatible // indirect github.com/logrusorgru/aurora v2.0.3+incompatible // indirect
github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d // indirect
github.com/mholt/acmez v1.2.0 // indirect github.com/mholt/acmez v1.2.0 // indirect
github.com/miekg/dns v1.1.59 // indirect github.com/miekg/dns v1.1.59 // indirect
github.com/onsi/ginkgo/v2 v2.9.7 // indirect github.com/onsi/ginkgo/v2 v2.9.7 // indirect
@ -52,19 +53,18 @@ require (
github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 // indirect github.com/sagernet/cloudflare-tls v0.0.0-20231208171750-a4483c1b7cd1 // indirect
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f // indirect github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f // indirect
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba // indirect github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba // indirect
github.com/sagernet/quic-go v0.45.1-beta.2 // indirect github.com/sagernet/quic-go v0.47.0-beta.2 // indirect
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 // indirect
github.com/sagernet/sing v0.4.1 // indirect github.com/sagernet/sing v0.4.3 // indirect
github.com/sagernet/sing-dns v0.2.1-0.20240624030536-ca4a5f7afb65 // indirect github.com/sagernet/sing-dns v0.2.3 // indirect
github.com/sagernet/sing-mux v0.2.0 // indirect github.com/sagernet/sing-mux v0.2.0 // indirect
github.com/sagernet/sing-quic v0.2.0-beta.12 // indirect github.com/sagernet/sing-quic v0.2.2 // indirect
github.com/sagernet/sing-shadowsocks v0.2.6 // indirect github.com/sagernet/sing-shadowsocks v0.2.7 // indirect
github.com/sagernet/sing-shadowsocks2 v0.2.0 // indirect github.com/sagernet/sing-shadowsocks2 v0.2.0 // indirect
github.com/sagernet/sing-shadowtls v0.1.4 // indirect github.com/sagernet/sing-shadowtls v0.1.4 // indirect
github.com/sagernet/sing-tun v0.3.2 // indirect github.com/sagernet/sing-tun v0.3.3 // indirect
github.com/sagernet/sing-vmess v0.1.8 // indirect github.com/sagernet/sing-vmess v0.1.12 // indirect
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 // indirect
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 // indirect
github.com/sagernet/utls v1.5.4 // indirect github.com/sagernet/utls v1.5.4 // indirect
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 // indirect github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 // indirect
github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect github.com/sagernet/ws v0.0.0-20231204124109-acfe8907c854 // indirect
@ -80,16 +80,16 @@ require (
golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
golang.org/x/mod v0.18.0 // indirect golang.org/x/mod v0.18.0 // indirect
golang.org/x/net v0.25.0 // indirect golang.org/x/net v0.25.0 // indirect
golang.org/x/sync v0.7.0 // indirect golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.21.0 // indirect golang.org/x/sys v0.25.0 // indirect
golang.org/x/text v0.16.0 // indirect golang.org/x/text v0.18.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 // indirect golang.zx2c4.com/wireguard/wgctrl v0.0.0-20230429144221-925a1e7659e6 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240227224415-6ceb2ff114de // indirect
google.golang.org/grpc v1.63.2 // indirect google.golang.org/grpc v1.63.2 // indirect
google.golang.org/protobuf v1.33.0 // indirect google.golang.org/protobuf v1.33.0 // indirect
lukechampine.com/blake3 v1.2.1 // indirect lukechampine.com/blake3 v1.3.0 // indirect
) )
replace grpc_server => ../../grpc_server replace grpc_server => ../../grpc_server
@ -98,7 +98,7 @@ replace github.com/matsuridayo/libneko => ../../../../libneko
replace github.com/sagernet/sing-box => ../../../../sing-box replace github.com/sagernet/sing-box => ../../../../sing-box
// replace github.com/sagernet/sing-quic => ../../../../sing-quic replace github.com/sagernet/sing-quic => ../../../../sing-quic
// replace github.com/sagernet/sing => ../../../../sing // replace github.com/sagernet/sing => ../../../../sing

View File

@ -94,6 +94,8 @@ github.com/libdns/libdns v0.2.2 h1:O6ws7bAfRPaBsgAYt8MDe2HcNBGC29hkZ9MX2eUSX3s=
github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ= github.com/libdns/libdns v0.2.2/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8= github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczGlG91VSDkswnjF5A8=
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d h1:j9LtzkYstLFoNvXW824QQeN7Y26uPL5249kzWKbzO9U=
github.com/metacubex/tfo-go v0.0.0-20240821025650-e9be0afd5e7d/go.mod h1:c7bVFM9f5+VzeZ/6Kg77T/jrg1Xp8QpqlSHvG/aXVts=
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30= github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE= github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs= github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
@ -125,33 +127,29 @@ github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f h1:NkhuupzH5ch7b/Y
github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f/go.mod h1:KXmw+ouSJNOsuRpg4wgwwCQuunrGz4yoAqQjsLjc6N0= github.com/sagernet/gvisor v0.0.0-20240428053021-e691de28565f/go.mod h1:KXmw+ouSJNOsuRpg4wgwwCQuunrGz4yoAqQjsLjc6N0=
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba h1:EY5AS7CCtfmARNv2zXUOrsEMPFDGYxaw65JzA2p51Vk= github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba h1:EY5AS7CCtfmARNv2zXUOrsEMPFDGYxaw65JzA2p51Vk=
github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM= github.com/sagernet/netlink v0.0.0-20240523065131-45e60152f9ba/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=
github.com/sagernet/quic-go v0.45.1-beta.2 h1:zkEeCbhdFFkrxKcuIRBtXNKci/1t2J/39QSG/sPvlmc= github.com/sagernet/quic-go v0.47.0-beta.2 h1:1tCGWFOSaXIeuQaHrwOMJIYvlupjTcaVInGQw5ArULU=
github.com/sagernet/quic-go v0.45.1-beta.2/go.mod h1:+N3FqM9DAzOWfe64uxXuBejVJwX7DeW7BslzLO6N/xI= github.com/sagernet/quic-go v0.47.0-beta.2/go.mod h1:bLVKvElSEMNv7pu7SZHscW02TYigzQ5lQu3Nh4wNh8Q=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691 h1:5Th31OC6yj8byLGkEnIYp6grlXfo1QYUfiYFGjewIdc=
github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU= github.com/sagernet/reality v0.0.0-20230406110435-ee17307e7691/go.mod h1:B8lp4WkQ1PwNnrVMM6KyuFR20pU8jYBD+A4EhJovEXU=
github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo= github.com/sagernet/sing v0.2.18/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=
github.com/sagernet/sing v0.4.1 h1:zVlpE+7k7AFoC2pv6ReqLf0PIHjihL/jsBl5k05PQFk= github.com/sagernet/sing v0.4.3 h1:Ty/NAiNnVd6844k7ujlL5lkzydhcTH5Psc432jXA4Y8=
github.com/sagernet/sing v0.4.1/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls= github.com/sagernet/sing v0.4.3/go.mod h1:ieZHA/+Y9YZfXs2I3WtuwgyCZ6GPsIR7HdKb1SdEnls=
github.com/sagernet/sing-dns v0.2.1-0.20240624030536-ca4a5f7afb65 h1:lcCe7E1csuyUA3RCvpFcIYOy6FIifDthKaCrUjLG4xA= github.com/sagernet/sing-dns v0.2.3 h1:YzeBUn2tR38F7HtvGEQ0kLRLmZWMEgi/+7wqa4Twb1k=
github.com/sagernet/sing-dns v0.2.1-0.20240624030536-ca4a5f7afb65/go.mod h1:dArgyPZmK8+zDBVRMjV3r12zHgnTara0ahrWwSe/eQE= github.com/sagernet/sing-dns v0.2.3/go.mod h1:BJpJv6XLnrUbSyIntOT6DG9FW0f4fETmPAHvNjOprLg=
github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo= github.com/sagernet/sing-mux v0.2.0 h1:4C+vd8HztJCWNYfufvgL49xaOoOHXty2+EAjnzN3IYo=
github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ= github.com/sagernet/sing-mux v0.2.0/go.mod h1:khzr9AOPocLa+g53dBplwNDz4gdsyx/YM3swtAhlkHQ=
github.com/sagernet/sing-quic v0.2.0-beta.12 h1:BhvA5mmrDFEyDUQB5eeu+9UhF+ieyuNJ5Rsb0dAG3QY= github.com/sagernet/sing-shadowsocks v0.2.7 h1:zaopR1tbHEw5Nk6FAkM05wCslV6ahVegEZaKMv9ipx8=
github.com/sagernet/sing-quic v0.2.0-beta.12/go.mod h1:YVpLfVi8BvYM7NMrjmnvcRm3E8iMETf1gFQmTQDN9jI= github.com/sagernet/sing-shadowsocks v0.2.7/go.mod h1:0rIKJZBR65Qi0zwdKezt4s57y/Tl1ofkaq6NlkzVuyE=
github.com/sagernet/sing-shadowsocks v0.2.6 h1:xr7ylAS/q1cQYS8oxKKajhuQcchd5VJJ4K4UZrrpp0s=
github.com/sagernet/sing-shadowsocks v0.2.6/go.mod h1:j2YZBIpWIuElPFL/5sJAj470bcn/3QQ5lxZUNKLDNAM=
github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg= github.com/sagernet/sing-shadowsocks2 v0.2.0 h1:wpZNs6wKnR7mh1wV9OHwOyUr21VkS3wKFHi+8XwgADg=
github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ= github.com/sagernet/sing-shadowsocks2 v0.2.0/go.mod h1:RnXS0lExcDAovvDeniJ4IKa2IuChrdipolPYWBv9hWQ=
github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k= github.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=
github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4= github.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=
github.com/sagernet/sing-tun v0.3.2 h1:z0bLUT/YXH9RrJS9DsIpB0Bb9afl2hVJOmHd0zA3HJY= github.com/sagernet/sing-tun v0.3.3 h1:LZnQNmfGcNG2KPTPkLgc+Lo7k606QJVkPp2DnjriwUk=
github.com/sagernet/sing-tun v0.3.2/go.mod h1:DxLIyhjWU/HwGYoX0vNGg2c5QgTQIakphU1MuERR5tQ= github.com/sagernet/sing-tun v0.3.3/go.mod h1:DxLIyhjWU/HwGYoX0vNGg2c5QgTQIakphU1MuERR5tQ=
github.com/sagernet/sing-vmess v0.1.8 h1:XVWad1RpTy9b5tPxdm5MCU8cGfrTGdR8qCq6HV2aCNc= github.com/sagernet/sing-vmess v0.1.12 h1:2gFD8JJb+eTFMoa8FIVMnknEi+vCSfaiTXTfEYAYAPg=
github.com/sagernet/sing-vmess v0.1.8/go.mod h1:vhx32UNzTDUkNwOyIjcZQohre1CaytquC5mPplId8uA= github.com/sagernet/sing-vmess v0.1.12/go.mod h1:luTSsfyBGAc9VhtCqwjR+dt1QgqBhuYBCONB/POhF8I=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7 h1:DImB4lELfQhplLTxeq2z31Fpv8CQqqrUwTbrIRumZqQ=
github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo= github.com/sagernet/smux v0.0.0-20231208180855-7041f6ea79e7/go.mod h1:FP9X2xjT/Az1EsG/orYYoC+5MojWnuI7hrffz8fGwwo=
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6 h1:z3SJQhVyU63FT26Wn/UByW6b7q8QKB0ZkPqsyqcz2PI=
github.com/sagernet/tfo-go v0.0.0-20231209031829-7b5343ac1dc6/go.mod h1:73xRZuxwkFk4aiLw28hG8W6o9cr2UPrGL9pdY2UTbvY=
github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co= github.com/sagernet/utls v1.5.4 h1:KmsEGbB2dKUtCNC+44NwAdNAqnqQ6GA4pTO0Yik56co=
github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s= github.com/sagernet/utls v1.5.4/go.mod h1:CTGxPWExIloRipK3XFpYv0OVyhO8kk3XCGW/ieyTh1s=
github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 h1:R0OMYAScomNAVpTfbHFpxqJpvwuhxSRi+g6z7gZhABs= github.com/sagernet/wireguard-go v0.0.0-20231215174105-89dec3b2f3e8 h1:R0OMYAScomNAVpTfbHFpxqJpvwuhxSRi+g6z7gZhABs=
@ -229,8 +227,8 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -245,15 +243,15 @@ golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@ -296,5 +294,5 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
lukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI= lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
lukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=

View File

@ -18,6 +18,16 @@ popd
#### ####
if [ ! -d "sing-quic" ]; then
git clone --no-checkout https://github.com/MatsuriDayo/sing-quic.git
fi
pushd sing-quic
git checkout "$COMMIT_SING_QUIC"
popd
####
if [ ! -d "libneko" ]; then if [ ! -d "libneko" ]; then
git clone --no-checkout https://github.com/MatsuriDayo/libneko.git git clone --no-checkout https://github.com/MatsuriDayo/libneko.git
fi fi

View File

@ -1,2 +1,3 @@
export COMMIT_SING_BOX="cf36758f11b7c144e1211801753cc91f06ff2906" export COMMIT_SING_BOX="06557f6cef23160668122a17a818b378b5a216b5"
export COMMIT_SING_QUIC="b49ce60d9b3622d5238fee96bfd3c5f6e3915b42"
export COMMIT_LIBNEKO="1c47a3af71990a7b2192e03292b4d246c308ef0b" export COMMIT_LIBNEKO="1c47a3af71990a7b2192e03292b4d246c308ef0b"

View File

@ -51,7 +51,7 @@ namespace NekoGui_network {
for (const auto &err: errors) { for (const auto &err: errors) {
error_str << err.errorString(); error_str << err.errorString();
} }
MW_show_log(QString("SSL Errors: %1 %2").arg(error_str.join(","), NekoGui::dataStore->sub_insecure ? "(Ignored)" : "")); MW_show_log(QStringLiteral("SSL Errors: %1 %2").arg(error_str.join(","), NekoGui::dataStore->sub_insecure ? "(Ignored)" : ""));
}); });
// Wait for response // Wait for response
auto abortTimer = new QTimer; auto abortTimer = new QTimer;

View File

@ -349,7 +349,7 @@ namespace NekoGui {
} }
QString Routing::DisplayRouting() const { QString Routing::DisplayRouting() const {
return QString("[Proxy] %1\n[Proxy] %2\n[Direct] %3\n[Direct] %4\n[Block] %5\n[Block] %6\n[Default Outbound] %7\n[DNS] %8") return QStringLiteral("[Proxy] %1\n[Proxy] %2\n[Direct] %3\n[Direct] %4\n[Block] %5\n[Block] %6\n[Default Outbound] %7\n[DNS] %8")
.arg(SplitLinesSkipSharp(proxy_domain).join(","), 10) .arg(SplitLinesSkipSharp(proxy_domain).join(","), 10)
.arg(SplitLinesSkipSharp(proxy_ip).join(","), 10) .arg(SplitLinesSkipSharp(proxy_ip).join(","), 10)
.arg(SplitLinesSkipSharp(direct_domain).join(","), 10) .arg(SplitLinesSkipSharp(direct_domain).join(","), 10)
@ -419,6 +419,7 @@ namespace NekoGui {
search << QApplication::applicationDirPath(); search << QApplication::applicationDirPath();
search << "/usr/share/sing-geoip"; search << "/usr/share/sing-geoip";
search << "/usr/share/sing-geosite"; search << "/usr/share/sing-geosite";
search << "/usr/share/sing-box";
search << "/usr/lib/nekobox"; search << "/usr/lib/nekobox";
search << "/usr/share/nekobox"; search << "/usr/share/nekobox";
for (const auto &dir: search) { for (const auto &dir: search) {

View File

@ -17,5 +17,5 @@ namespace NekoGui {
bool IsAdmin(); bool IsAdmin();
} // namespace NekoGui } // namespace NekoGui
#define ROUTES_PREFIX_NAME QString("routes_box") #define ROUTES_PREFIX_NAME QStringLiteral("routes_box")
#define ROUTES_PREFIX QString(ROUTES_PREFIX_NAME + "/") #define ROUTES_PREFIX QString(ROUTES_PREFIX_NAME + "/")

View File

@ -1 +1 @@
4.0-beta3-2024-07-13 4.0.1-2024-12-12

View File

@ -205,7 +205,7 @@ namespace NekoGui_rpc {
#define NOT_OK \ #define NOT_OK \
*rpcOK = false; \ *rpcOK = false; \
onError(QString("QNetworkReply::NetworkError code: %1\n").arg(status)); onError(QStringLiteral("QNetworkReply::NetworkError code: %1\n").arg(status));
void Client::Exit() { void Client::Exit() {
libcore::EmptyReq request; libcore::EmptyReq request;

View File

@ -56,7 +56,7 @@ LONG __stdcall CreateCrashHandler(EXCEPTION_POINTERS *pException) {
} }
// 创建消息提示 // 创建消息提示
QMessageBox::warning(NULL, "Application crashed", QMessageBox::warning(NULL, "Application crashed",
QString("ErrorCode: %1 ErrorAddr:%2 ErrorFlag: %3 ErrorPara: %4\nVersion: %5\nDump file at %6") QStringLiteral("ErrorCode: %1 ErrorAddr:%2 ErrorFlag: %3 ErrorPara: %4\nVersion: %5\nDump file at %6")
.arg(errCode) .arg(errCode)
.arg(errAddr) .arg(errAddr)
.arg(errFlag) .arg(errFlag)

View File

@ -1492,6 +1492,10 @@ End: %2</source>
<source>Stop Testing</source> <source>Stop Testing</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>URL Test</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ProxyItem</name> <name>ProxyItem</name>
@ -1632,6 +1636,10 @@ Direct: %2</source>
<source>Default</source> <source>Default</source>
<translation type="unfinished">پیش فرض</translation> <translation type="unfinished">پیش فرض</translation>
</message> </message>
<message>
<source>The last speed test did not exit completely, please wait. If it persists, please restart the program.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>Qv2ray::ui::widgets::AutoCompleteTextEdit</name> <name>Qv2ray::ui::widgets::AutoCompleteTextEdit</name>

View File

@ -1498,6 +1498,10 @@ End: %2</source>
<source>Stop Testing</source> <source>Stop Testing</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<source>URL Test</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>ProxyItem</name> <name>ProxyItem</name>
@ -1645,6 +1649,10 @@ Release note:
<source>Default</source> <source>Default</source>
<translation>По умолчанию</translation> <translation>По умолчанию</translation>
</message> </message>
<message>
<source>The last speed test did not exit completely, please wait. If it persists, please restart the program.</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>Qv2ray::ui::widgets::AutoCompleteTextEdit</name> <name>Qv2ray::ui::widgets::AutoCompleteTextEdit</name>

View File

@ -1488,7 +1488,7 @@ Split by line.</source>
</message> </message>
<message> <message>
<source>In and Out IP</source> <source>In and Out IP</source>
<translation>IP</translation> <translation> IP</translation>
</message> </message>
<message> <message>
<source>Test Options</source> <source>Test Options</source>
@ -1498,6 +1498,10 @@ Split by line.</source>
<source>Stop Testing</source> <source>Stop Testing</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>URL Test</source>
<translation>URL </translation>
</message>
</context> </context>
<context> <context>
<name>ProxyItem</name> <name>ProxyItem</name>
@ -1645,6 +1649,10 @@ Release note:
<source>Default</source> <source>Default</source>
<translation></translation> <translation></translation>
</message> </message>
<message>
<source>The last speed test did not exit completely, please wait. If it persists, please restart the program.</source>
<translation>退</translation>
</message>
</context> </context>
<context> <context>
<name>Qv2ray::ui::widgets::AutoCompleteTextEdit</name> <name>Qv2ray::ui::widgets::AutoCompleteTextEdit</name>

View File

@ -59,7 +59,7 @@ DialogBasicSettings::DialogBasicSettings(QWidget *parent)
// Common // Common
ui->log_level->addItems(QString("trace debug info warn error fatal panic").split(" ")); ui->log_level->addItems(QStringLiteral("trace debug info warn error fatal panic").split(" "));
ui->mux_protocol->addItems({"h2mux", "smux", "yamux"}); ui->mux_protocol->addItems({"h2mux", "smux", "yamux"});
refresh_auth(); refresh_auth();

View File

@ -25,7 +25,7 @@ DialogManageRoutes::DialogManageRoutes(QWidget *parent) : QDialog(parent), ui(ne
// //
ui->outbound_domain_strategy->addItems(Preset::SingBox::DomainStrategy); ui->outbound_domain_strategy->addItems(Preset::SingBox::DomainStrategy);
ui->domainStrategyCombo->addItems(Preset::SingBox::DomainStrategy); ui->domainStrategyCombo->addItems(Preset::SingBox::DomainStrategy);
qsValue += QString("prefer_ipv4 prefer_ipv6 ipv4_only ipv6_only").split(" "); qsValue += QStringLiteral("prefer_ipv4 prefer_ipv6 ipv4_only ipv6_only").split(" ");
ui->dns_object->setPlaceholderText(DecodeB64IfValid("ewogICJzZXJ2ZXJzIjogW10sCiAgInJ1bGVzIjogW10sCiAgImZpbmFsIjogIiIsCiAgInN0cmF0ZWd5IjogIiIsCiAgImRpc2FibGVfY2FjaGUiOiBmYWxzZSwKICAiZGlzYWJsZV9leHBpcmUiOiBmYWxzZSwKICAiaW5kZXBlbmRlbnRfY2FjaGUiOiBmYWxzZSwKICAicmV2ZXJzZV9tYXBwaW5nIjogZmFsc2UsCiAgImZha2VpcCI6IHt9Cn0=")); ui->dns_object->setPlaceholderText(DecodeB64IfValid("ewogICJzZXJ2ZXJzIjogW10sCiAgInJ1bGVzIjogW10sCiAgImZpbmFsIjogIiIsCiAgInN0cmF0ZWd5IjogIiIsCiAgImRpc2FibGVfY2FjaGUiOiBmYWxzZSwKICAiZGlzYWJsZV9leHBpcmUiOiBmYWxzZSwKICAiaW5kZXBlbmRlbnRfY2FjaGUiOiBmYWxzZSwKICAicmV2ZXJzZV9tYXBwaW5nIjogZmFsc2UsCiAgImZha2VpcCI6IHt9Cn0="));
dnsHelpDocumentUrl = "https://sing-box.sagernet.org/configuration/dns/"; dnsHelpDocumentUrl = "https://sing-box.sagernet.org/configuration/dns/";
// //

View File

@ -118,7 +118,7 @@ void EditCustom::onStart(std::shared_ptr<NekoGui::ProxyEntity> _ent) {
auto command = QStringList{extR->program}; auto command = QStringList{extR->program};
command += extR->arguments; command += extR->arguments;
auto btn = QMessageBox::information(this, tr("Preview config"), auto btn = QMessageBox::information(this, tr("Preview config"),
QString("Command: %1\n\n%2").arg(QStringList2Command(command), extR->config_export), QStringLiteral("Command: %1\n\n%2").arg(QStringList2Command(command), extR->config_export),
"OK", "Copy", "", 0, 0); "OK", "Copy", "", 0, 0);
if (btn == 1) { if (btn == 1) {
QApplication::clipboard()->setText(extR->config_export); QApplication::clipboard()->setText(extR->config_export);

View File

@ -101,8 +101,9 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
ui->toolButton_server->setMenu(ui->menu_server); ui->toolButton_server->setMenu(ui->menu_server);
ui->menubar->setVisible(false); ui->menubar->setVisible(false);
connect(ui->toolButton_document, &QToolButton::clicked, this, [=] { QDesktopServices::openUrl(QUrl("https://matsuridayo.github.io/")); }); connect(ui->toolButton_document, &QToolButton::clicked, this, [=] { QDesktopServices::openUrl(QUrl("https://matsuridayo.github.io/")); });
connect(ui->toolButton_ads, &QToolButton::clicked, this, [=] { QDesktopServices::openUrl(QUrl("https://matsuricom.pages.dev/")); }); connect(ui->toolButton_ads, &QToolButton::clicked, this, [=] { QDesktopServices::openUrl(QUrl("https://neko-box.pages.dev/喵")); });
connect(ui->toolButton_update, &QToolButton::clicked, this, [=] { runOnNewThread([=] { CheckUpdate(); }); }); connect(ui->toolButton_update, &QToolButton::clicked, this, [=] { runOnNewThread([=] { CheckUpdate(); }); });
connect(ui->toolButton_url_test, &QToolButton::clicked, this, [=] { speedtest_current_group(1, true); });
// Setup log UI // Setup log UI
ui->splitter->restoreState(DecodeB64IfValid(NekoGui::dataStore->splitter_state)); ui->splitter->restoreState(DecodeB64IfValid(NekoGui::dataStore->splitter_state));
@ -333,10 +334,10 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWi
neko_set_spmode_vpn(false); neko_set_spmode_vpn(false);
}); });
connect(ui->menu_qr, &QAction::triggered, this, [=]() { display_qr_link(false); }); connect(ui->menu_qr, &QAction::triggered, this, [=]() { display_qr_link(false); });
connect(ui->menu_tcp_ping, &QAction::triggered, this, [=]() { speedtest_current_group(0); }); connect(ui->menu_tcp_ping, &QAction::triggered, this, [=]() { speedtest_current_group(0, false); });
connect(ui->menu_url_test, &QAction::triggered, this, [=]() { speedtest_current_group(1); }); connect(ui->menu_url_test, &QAction::triggered, this, [=]() { speedtest_current_group(1, false); });
connect(ui->menu_full_test, &QAction::triggered, this, [=]() { speedtest_current_group(2); }); connect(ui->menu_full_test, &QAction::triggered, this, [=]() { speedtest_current_group(2, false); });
connect(ui->menu_stop_testing, &QAction::triggered, this, [=]() { speedtest_current_group(114514); }); connect(ui->menu_stop_testing, &QAction::triggered, this, [=]() { speedtest_current_group(114514, false); });
// //
auto set_selected_or_group = [=](int mode) { auto set_selected_or_group = [=](int mode) {
// 0=group 1=select 2=unknown(menu is hide) // 0=group 1=select 2=unknown(menu is hide)
@ -477,7 +478,7 @@ void MainWindow::show_group(int gid) {
auto group = NekoGui::profileManager->GetGroup(gid); auto group = NekoGui::profileManager->GetGroup(gid);
if (group == nullptr) { if (group == nullptr) {
MessageBoxWarning(tr("Error"), QString("No such group: %1").arg(gid)); MessageBoxWarning(tr("Error"), QStringLiteral("No such group: %1").arg(gid));
NekoGui::dataStore->refreshing_group = false; NekoGui::dataStore->refreshing_group = false;
return; return;
} }
@ -628,7 +629,7 @@ void MainWindow::on_commitDataRequest() {
// //
if (!isMaximized()) { if (!isMaximized()) {
auto olds = NekoGui::dataStore->mw_size; auto olds = NekoGui::dataStore->mw_size;
auto news = QString("%1x%2").arg(size().width()).arg(size().height()); auto news = QStringLiteral("%1x%2").arg(size().width()).arg(size().height());
if (olds != news) { if (olds != news) {
NekoGui::dataStore->mw_size = news; NekoGui::dataStore->mw_size = news;
} }
@ -831,12 +832,12 @@ void MainWindow::refresh_status(const QString &traffic_update) {
if (last_test_time.addSecs(2) < QTime::currentTime()) { if (last_test_time.addSecs(2) < QTime::currentTime()) {
auto txt = running == nullptr ? tr("Not Running") auto txt = running == nullptr ? tr("Not Running")
: QString("[%1] %2").arg(group_name, running->bean->DisplayName()).left(30); : QStringLiteral("[%1] %2").arg(group_name, running->bean->DisplayName()).left(30);
ui->label_running->setText(txt); ui->label_running->setText(txt);
} }
// //
auto display_socks = DisplayAddress(NekoGui::dataStore->inbound_address, NekoGui::dataStore->inbound_socks_port); auto display_socks = DisplayAddress(NekoGui::dataStore->inbound_address, NekoGui::dataStore->inbound_socks_port);
auto inbound_txt = QString("Mixed: %1").arg(display_socks); auto inbound_txt = QStringLiteral("Mixed: %1").arg(display_socks);
ui->label_inbound->setText(inbound_txt); ui->label_inbound->setText(inbound_txt);
// //
ui->checkBox_VPN->setChecked(NekoGui::dataStore->spmode_vpn); ui->checkBox_VPN->setChecked(NekoGui::dataStore->spmode_vpn);
@ -1166,7 +1167,7 @@ void MainWindow::on_menu_profile_debug_info_triggered() {
if (ents.count() != 1) return; if (ents.count() != 1) return;
auto btn = QMessageBox::information(this, software_name, ents.first()->ToJsonBytes(), "OK", "Edit", "Reload", 0, 0); auto btn = QMessageBox::information(this, software_name, ents.first()->ToJsonBytes(), "OK", "Edit", "Reload", 0, 0);
if (btn == 1) { if (btn == 1) {
QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(QString("profiles/%1.json").arg(ents.first()->id)).absoluteFilePath())); QDesktopServices::openUrl(QUrl::fromLocalFile(QFileInfo(QStringLiteral("profiles/%1.json").arg(ents.first()->id)).absoluteFilePath()));
} else if (btn == 2) { } else if (btn == 2) {
NekoGui::dataStore->Load(); NekoGui::dataStore->Load();
NekoGui::profileManager->LoadManager(); NekoGui::profileManager->LoadManager();
@ -1207,10 +1208,10 @@ void MainWindow::on_menu_export_config_triggered() {
if (ent->bean->DisplayCoreType() != software_core_name) return; if (ent->bean->DisplayCoreType() != software_core_name) return;
auto result = BuildConfig(ent, false, true); auto result = BuildConfig(ent, false, true);
QString config_core = QJsonObject2QString(result->coreConfig, true); QString config_core = QJsonObject2QString(result->coreConfig, false);
QApplication::clipboard()->setText(config_core); QApplication::clipboard()->setText(config_core);
QMessageBox msg(QMessageBox::Information, tr("Config copied"), config_core); QMessageBox msg(QMessageBox::Information, tr("Config copied"), tr("Config copied"));
msg.addButton("Copy core config", QMessageBox::YesRole); msg.addButton("Copy core config", QMessageBox::YesRole);
msg.addButton("Copy test config", QMessageBox::NoRole); msg.addButton("Copy test config", QMessageBox::NoRole);
msg.addButton(QMessageBox::Ok); msg.addButton(QMessageBox::Ok);
@ -1799,7 +1800,7 @@ bool MainWindow::StartVPNProcess() {
// //
vpn_process->setProcessChannelMode(QProcess::ForwardedChannels); vpn_process->setProcessChannelMode(QProcess::ForwardedChannels);
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
vpn_process->start("osascript", {"-e", QString("do shell script \"%1\" with administrator privileges") vpn_process->start("osascript", {"-e", QStringLiteral("do shell script \"%1\" with administrator privileges")
.arg("bash " + scriptPath)}); .arg("bash " + scriptPath)});
#else #else
vpn_process->start("pkexec", {"bash", scriptPath}); vpn_process->start("pkexec", {"bash", scriptPath});
@ -1822,7 +1823,7 @@ bool MainWindow::StopVPNProcess(bool unconditional) {
#else #else
QProcess p; QProcess p;
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
p.start("osascript", {"-e", QString("do shell script \"%1\" with administrator privileges") p.start("osascript", {"-e", QStringLiteral("do shell script \"%1\" with administrator privileges")
.arg("pkill -2 -U 0 nekobox_core")}); .arg("pkill -2 -U 0 nekobox_core")});
#else #else
if (unconditional) { if (unconditional) {

View File

@ -185,7 +185,7 @@ private:
static void setup_grpc(); static void setup_grpc();
void speedtest_current_group(int mode); void speedtest_current_group(int mode, bool test_group);
void speedtest_current(); void speedtest_current();

View File

@ -205,6 +205,19 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QToolButton" name="toolButton_url_test">
<property name="text">
<string>URL Test</string>
</property>
<property name="popupMode">
<enum>QToolButton::InstantPopup</enum>
</property>
<property name="toolButtonStyle">
<enum>Qt::ToolButtonTextUnderIcon</enum>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
<item> <item>
@ -665,7 +678,7 @@
<string notr="true">Tcp Ping</string> <string notr="true">Tcp Ping</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string notr="true">Ctrl+Shift+T</string> <string notr="true">Ctrl+Alt+T</string>
</property> </property>
</action> </action>
<action name="menu_url_test"> <action name="menu_url_test">
@ -673,7 +686,7 @@
<string notr="true">Url Test</string> <string notr="true">Url Test</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string notr="true">Ctrl+Shift+U</string> <string notr="true">Ctrl+Alt+U</string>
</property> </property>
</action> </action>
<action name="menu_clear_test_result"> <action name="menu_clear_test_result">
@ -681,7 +694,7 @@
<string>Clear Test Result</string> <string>Clear Test Result</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string notr="true">Ctrl+Shift+C</string> <string notr="true">Ctrl+Alt+C</string>
</property> </property>
</action> </action>
<action name="menu_export_config"> <action name="menu_export_config">
@ -726,7 +739,7 @@
<string>Remove Duplicates</string> <string>Remove Duplicates</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string notr="true">Ctrl+Shift+D</string> <string notr="true">Ctrl+Alt+D</string>
</property> </property>
</action> </action>
<action name="actionfake"> <action name="actionfake">
@ -774,7 +787,7 @@
<string>Remove Unavailable</string> <string>Remove Unavailable</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string notr="true">Ctrl+Shift+R</string> <string notr="true">Ctrl+Alt+R</string>
</property> </property>
</action> </action>
<action name="menu_full_test"> <action name="menu_full_test">
@ -782,7 +795,7 @@
<string>Full Test</string> <string>Full Test</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string notr="true">Ctrl+Shift+F</string> <string notr="true">Ctrl+Alt+F</string>
</property> </property>
</action> </action>
<action name="menu_hotkey_settings"> <action name="menu_hotkey_settings">
@ -803,7 +816,7 @@
<string>Copy links of selected (Neko Links)</string> <string>Copy links of selected (Neko Links)</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string notr="true">Ctrl+Alt+C</string> <string notr="true">Ctrl+N</string>
</property> </property>
</action> </action>
<action name="actionfake_2"> <action name="actionfake_2">
@ -859,7 +872,7 @@
<string>Resolve domain</string> <string>Resolve domain</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string notr="true">Ctrl+Shift+I</string> <string notr="true">Ctrl+Alt+I</string>
</property> </property>
</action> </action>
<action name="menu_vpn_settings"> <action name="menu_vpn_settings">
@ -902,6 +915,9 @@
<property name="text"> <property name="text">
<string>Stop Testing</string> <string>Stop Testing</string>
</property> </property>
<property name="shortcut">
<string notr="true">Ctrl+Alt+S</string>
</property>
</action> </action>
</widget> </widget>
<customwidgets> <customwidgets>

View File

@ -58,8 +58,14 @@ void MainWindow::setup_grpc() {
inline bool speedtesting = false; inline bool speedtesting = false;
inline QList<QThread *> speedtesting_threads = {}; inline QList<QThread *> speedtesting_threads = {};
void MainWindow::speedtest_current_group(int mode) { void MainWindow::speedtest_current_group(int mode, bool test_group) {
if (speedtesting) {
MessageBoxWarning(software_name, QObject::tr("The last speed test did not exit completely, please wait. If it persists, please restart the program."));
return;
}
auto profiles = get_selected_or_group(); auto profiles = get_selected_or_group();
if (test_group) profiles = NekoGui::profileManager->CurrentGroup()->ProfilesWithOrder();
if (profiles.isEmpty()) return; if (profiles.isEmpty()) return;
auto group = NekoGui::profileManager->CurrentGroup(); auto group = NekoGui::profileManager->CurrentGroup();
if (group->archive) return; if (group->archive) return;
@ -75,11 +81,6 @@ void MainWindow::speedtest_current_group(int mode) {
} }
#ifndef NKR_NO_GRPC #ifndef NKR_NO_GRPC
if (speedtesting) {
MessageBoxWarning(software_name, "The last speed test did not exit completely, please wait. If it persists, please restart the program.");
return;
}
QStringList full_test_flags; QStringList full_test_flags;
if (mode == libcore::FullTest) { if (mode == libcore::FullTest) {
auto w = new QDialog(this); auto w = new QDialog(this);
@ -238,6 +239,7 @@ void MainWindow::speedtest_current_group(int mode) {
lock_return.lock(); lock_return.lock();
lock_return.unlock(); lock_return.unlock();
speedtesting = false; speedtesting = false;
MW_show_log(QObject::tr("Speedtest finished."));
}); });
#endif #endif
} }
@ -262,12 +264,12 @@ void MainWindow::speedtest_current() {
runOnUiThread([=] { runOnUiThread([=] {
if (!result.error().empty()) { if (!result.error().empty()) {
MW_show_log(QString("UrlTest error: %1").arg(result.error().c_str())); MW_show_log(QStringLiteral("UrlTest error: %1").arg(result.error().c_str()));
} }
if (latency <= 0) { if (latency <= 0) {
ui->label_running->setText(tr("Test Result") + ": " + tr("Unavailable")); ui->label_running->setText(tr("Test Result") + ": " + tr("Unavailable"));
} else if (latency > 0) { } else if (latency > 0) {
ui->label_running->setText(tr("Test Result") + ": " + QString("%1 ms").arg(latency)); ui->label_running->setText(tr("Test Result") + ": " + QStringLiteral("%1 ms").arg(latency));
} }
}); });
}); });