redesign, broadcast server crash fix

This commit is contained in:
2026-04-26 12:41:58 +03:00
parent 4ffa6ed970
commit 64b30e9835
20 changed files with 771 additions and 167 deletions
+11
View File
@@ -1,6 +1,8 @@
#include <MainApplication.hpp>
#include "util.hpp"
#include "main.hpp"
#include <server.hpp>
#include <unistd.h>
static int nxlink_sock = -1;
@@ -19,6 +21,10 @@ extern "C" void userAppInit() {
}
extern "C" void userAppExit() {
cancelServerTransfer();
for (int i = 0; i < 150 && !isServerWorkersIdle(); i++) {
usleep(10000);
}
if (nxlink_sock != -1) {
close(nxlink_sock);
}
@@ -50,6 +56,11 @@ int main() {
renderer_opts.UseImage(pu::ui::render::IMGAllFlags);
renderer_opts.UseAudio(pu::ui::render::MixerAllFlags);
renderer_opts.UseTTF();
renderer_opts.SetExtraDefaultFontSize(theme::type::Caption);
renderer_opts.SetExtraDefaultFontSize(theme::type::Label);
renderer_opts.SetExtraDefaultFontSize(theme::type::Body);
renderer_opts.SetExtraDefaultFontSize(theme::type::Title);
renderer_opts.SetExtraDefaultFontSize(theme::type::Display);
auto renderer = pu::ui::render::Renderer::New(renderer_opts);
+273 -97
View File
@@ -6,43 +6,256 @@
#include <server.hpp>
#include <TransferOverlay.hpp>
static std::vector<uint64_t> accSids, devSids, bcatSids, cacheSids;
namespace ui {
extern MainApplication *mainApp;
namespace {
constexpr int ListX = theme::space::lg;
constexpr int ListW = 760;
constexpr int PanelX = ListX + ListW + theme::space::xl;
constexpr int PanelW = theme::layout::ScreenW - PanelX - theme::space::lg;
constexpr int ContentY = theme::layout::ContentTop + theme::space::md;
constexpr int ContentH = theme::layout::ContentH - 2 * theme::space::md;
constexpr int BtnH = 56;
constexpr int BtnW = PanelW - 2 * theme::space::lg;
}
TitlesLayout::TitlesLayout() : Layout::Layout() {
using namespace theme;
this->titlesMenu = pu::ui::elm::Menu::New(
ListX, ContentY, ListW,
color::BgBase, color::BgSurface2,
88, 6);
this->titlesMenu->SetScrollbarColor(color::Primary);
this->titlesMenu->SetItemsFocusColor(color::BgSurface2);
this->titlesMenu->SetOnSelectionChanged([this]() { this->refreshPanel(); });
this->SetBackgroundColor(color::BgBase);
this->Add(this->titlesMenu);
this->panelBg = pu::ui::elm::Rectangle::New(
PanelX, ContentY, PanelW, ContentH, color::BgSurface, radius::lg);
this->Add(this->panelBg);
this->panelTitle = pu::ui::elm::TextBlock::New(
PanelX + space::lg, ContentY + space::lg, "");
this->panelTitle->SetFont(type::font(type::Title));
this->panelTitle->SetColor(color::TextPrimary);
this->Add(this->panelTitle);
this->panelHint = pu::ui::elm::TextBlock::New(
PanelX + space::lg, ContentY + space::lg + 48, "Pick an action:");
this->panelHint->SetFont(type::font(type::Body));
this->panelHint->SetColor(color::TextSecondary);
this->Add(this->panelHint);
int btnY = ContentY + 200;
this->btnTransferBg = pu::ui::elm::Rectangle::New(
PanelX + space::lg, btnY, BtnW, BtnH, color::BgSurface2, radius::md);
this->Add(this->btnTransferBg);
this->btnTransferText = pu::ui::elm::TextBlock::New(
PanelX + space::lg + space::md, btnY + 14, "Transfer to PC");
this->btnTransferText->SetFont(type::font(type::Body));
this->btnTransferText->SetColor(color::TextSecondary);
this->Add(this->btnTransferText);
int btnY2 = btnY + BtnH + space::md;
this->btnReceiveBg = pu::ui::elm::Rectangle::New(
PanelX + space::lg, btnY2, BtnW, BtnH, color::BgSurface2, radius::md);
this->Add(this->btnReceiveBg);
this->btnReceiveText = pu::ui::elm::TextBlock::New(
PanelX + space::lg + space::md, btnY2 + 14, "Receive from PC");
this->btnReceiveText->SetFont(type::font(type::Body));
this->btnReceiveText->SetColor(color::TextSecondary);
this->Add(this->btnReceiveText);
this->panelFooter = pu::ui::elm::TextBlock::New(
PanelX + space::lg,
ContentY + ContentH - space::lg - 18,
"Save data only");
this->panelFooter->SetFont(type::font(type::Caption));
this->panelFooter->SetColor(color::TextMuted);
this->Add(this->panelFooter);
this->emptyText = pu::ui::elm::TextBlock::New(
ListX + ListW / 2 - 280, ContentY + ContentH / 2 - 40,
"No save data on this profile");
this->emptyText->SetFont(type::font(type::Display));
this->emptyText->SetColor(color::TextPrimary);
this->emptyText->SetVisible(false);
this->Add(this->emptyText);
this->emptySub = pu::ui::elm::TextBlock::New(
ListX + ListW / 2 - 220, ContentY + ContentH / 2 + 16,
"Play something first, then come back.");
this->emptySub->SetFont(type::font(type::Body));
this->emptySub->SetColor(color::TextMuted);
this->emptySub->SetVisible(false);
this->Add(this->emptySub);
this->header = std::make_unique<HeaderBar>(this, "Save Transfer");
this->hints = std::make_unique<HintBar>(this);
this->updateHints();
}
void TitlesLayout::InitTitles() {
using namespace theme;
Logger::getInstance().log(Logger::INFO, "InitTitles");
auto it = this->menuCache.find(g_currentUId);
std::vector<pu::ui::elm::MenuItem::Ref>* items;
if (it != this->menuCache.end()) {
this->titlesMenu = it->second;
items = &it->second;
} else {
auto menu = pu::ui::elm::Menu::New(0, 0, 1280, COLOR("#67000000"), COLOR("#170909FF"), 94, 7);
std::vector<pu::ui::elm::MenuItem::Ref> built;
for (size_t i = 0; i < getTitleCount(g_currentUId); i++) {
Title title;
getTitle(title, g_currentUId, i);
auto titleItem = pu::ui::elm::MenuItem::New(title.name().c_str());
titleItem->SetColor(COLOR("#FFFFFFFF"));
menu->AddItem(titleItem);
titleItem->SetColor(color::TextPrimary);
built.push_back(titleItem);
}
this->menuCache.emplace(g_currentUId, menu);
this->titlesMenu = menu;
auto inserted = this->menuCache.emplace(g_currentUId, std::move(built));
items = &inserted.first->second;
}
this->Clear();
this->Add(this->titlesMenu);
this->titlesMenu->ClearItems();
for (auto& item : *items) {
this->titlesMenu->AddItem(item);
}
this->titlesMenu->SetSelectedIndex(0);
this->SetBackgroundColor(BACKGROUND_COLOR);
const bool empty = items->empty();
this->titlesMenu->SetVisible(!empty);
this->panelBg->SetVisible(!empty);
this->panelTitle->SetVisible(!empty);
this->panelHint->SetVisible(!empty);
this->btnTransferBg->SetVisible(!empty);
this->btnTransferText->SetVisible(!empty);
this->btnReceiveBg->SetVisible(!empty);
this->btnReceiveText->SetVisible(!empty);
this->panelFooter->SetVisible(!empty);
this->emptyText->SetVisible(empty);
this->emptySub->SetVisible(empty);
this->focus = TitlesFocus::List;
this->action = TitlesAction::Transfer;
this->refreshPanel();
this->refreshButtons();
this->updateHints();
this->header->SetUser(g_currentUId, Account::username(g_currentUId));
}
void TitlesLayout::refreshPanel() {
if (this->titlesMenu->GetItems().empty()) return;
int idx = this->titlesMenu->GetSelectedIndex();
Title title;
getTitle(title, g_currentUId, idx);
this->panelTitle->SetText(StringUtils::elide(title.name(), 24));
}
void TitlesLayout::refreshButtons() {
using namespace theme;
const bool active = (focus == TitlesFocus::Actions);
if (active && action == TitlesAction::Transfer) {
this->btnTransferBg->SetColor(color::Primary);
this->btnTransferText->SetColor(color::TextPrimary);
this->btnReceiveBg->SetColor(color::BgSurface2);
this->btnReceiveText->SetColor(color::TextSecondary);
} else if (active && action == TitlesAction::Receive) {
this->btnTransferBg->SetColor(color::BgSurface2);
this->btnTransferText->SetColor(color::TextSecondary);
this->btnReceiveBg->SetColor(color::Accent);
this->btnReceiveText->SetColor(color::BgBase);
} else {
this->btnTransferBg->SetColor(color::BgSurface2);
this->btnTransferText->SetColor(color::TextSecondary);
this->btnReceiveBg->SetColor(color::BgSurface2);
this->btnReceiveText->SetColor(color::TextSecondary);
}
}
void TitlesLayout::updateHints() {
if (focus == TitlesFocus::List) {
this->hints->SetHints({{"A", "Choose action"}, {"B", "Back"}, {"+", "Quit"}});
} else {
this->hints->SetHints({{"A", "Confirm"}, {"B", "Back"}});
}
}
void TitlesLayout::runTransfer(int index, Title& title) {
auto ovl = TransferOverlay::New("Transferring save data...");
this->titlesMenu->SetVisible(false);
mainApp->StartOverlay(ovl);
this->LockInput();
if (transfer_files(index, g_currentUId) != 0) {
mainApp->EndOverlay();
this->titlesMenu->SetVisible(true);
this->UnlockInput();
mainApp->CreateShowDialog("Transfer", "Failed to start transfer.", {"OK"}, true);
return;
}
while (!isClientTransferDone()) {
ovl->SetStatus(getClientStatusText());
ovl->SetProgressVisible(isClientProgressKnown());
ovl->SetProgress(getClientProgress());
mainApp->CallForRender();
if (mainApp->GetButtonsDown() & HidNpadButton_B) {
cancelClientTransfer();
}
svcSleepThread(16666666LL);
}
mainApp->EndOverlay();
this->titlesMenu->SetVisible(true);
this->UnlockInput();
if (isClientConnectionFailed()) {
mainApp->CreateShowDialog("Transfer", getClientFailReason(), {"OK"}, true);
} else if (isClientTransferCancelled()) {
mainApp->CreateShowDialog("Transfer", "Transfer cancelled.", {"OK"}, true);
} else {
mainApp->CreateShowDialog("Transfer", "Save data sent successfully!", {"OK"}, true);
}
}
void TitlesLayout::runReceive(int index, Title& title) {
if (startSendingThread() != 0) {
mainApp->CreateShowDialog("Receive", "Failed to start receiver.\nCheck network connection.", {"OK"}, true);
return;
}
auto ovl = TransferOverlay::New("Receiving save data...");
this->titlesMenu->SetVisible(false);
mainApp->StartOverlay(ovl);
this->LockInput();
while (!isServerTransferDone()) {
ovl->SetStatus(getServerStatusText());
ovl->SetProgress(getServerProgress());
mainApp->CallForRender();
if (mainApp->GetButtonsDown() & HidNpadButton_B) {
cancelServerTransfer();
}
svcSleepThread(16666666LL);
}
mainApp->EndOverlay();
this->titlesMenu->SetVisible(true);
this->UnlockInput();
if (isServerTransferCancelled()) {
mainApp->CreateShowDialog("Receive", "Transfer cancelled.", {"OK"}, true);
return;
}
auto restoreResult = io::restore(index, g_currentUId, 0, title.name());
if (std::get<0>(restoreResult)) {
mainApp->CreateShowDialog("Receive", "Save data received and restored successfully!", {"OK"}, true);
} else {
mainApp->CreateShowDialog("Receive", "Restore failed:\n" + std::get<2>(restoreResult), {"OK"}, true);
}
}
void TitlesLayout::onInput(u64 Down, u64 Up, u64 Held, pu::ui::TouchPoint Pos) {
if (m_inputLocked) return;
if (Down & HidNpadButton_B) {
mainApp->LoadLayout(mainApp->usersLayout);
return;
}
if (Down & HidNpadButton_Plus) {
cancelClientTransfer();
cancelServerTransfer();
@@ -50,87 +263,50 @@ namespace ui {
return;
}
if (Down & HidNpadButton_A) {
auto index = this->titlesMenu->GetSelectedIndex();
Title title;
getTitle(title, g_currentUId, index);
printf("userid is 0x%lX%lX\n", title.userId().uid[1], title.userId().uid[0]);
// printf("current game index is %i\n", index);
int opt = mainApp->CreateShowDialog(title.name().c_str(), "What do you want?", { "Transfer", "Receive" }, false);
printf("opt is %i\n", opt);
switch (opt) {
case 0: {
// Transfer selected
{
auto ovl = TransferOverlay::New("Transferring save data...");
this->titlesMenu->SetVisible(false);
mainApp->StartOverlay(ovl);
this->LockInput();
if (transfer_files(index, g_currentUId) != 0) {
mainApp->EndOverlay();
this->titlesMenu->SetVisible(true);
this->UnlockInput();
mainApp->CreateShowDialog("Transfer", "Failed to start transfer.", {"OK"}, true);
break;
}
while (!isClientTransferDone()) {
ovl->SetStatus(getClientStatusText());
ovl->SetProgressVisible(isClientProgressKnown());
ovl->SetProgress(getClientProgress());
mainApp->CallForRender();
if (mainApp->GetButtonsDown() & HidNpadButton_B) {
cancelClientTransfer();
}
svcSleepThread(16666666LL);
}
mainApp->EndOverlay();
this->titlesMenu->SetVisible(true);
this->UnlockInput();
}
if (isClientConnectionFailed()) {
mainApp->CreateShowDialog("Transfer", getClientFailReason(), {"OK"}, true);
} else if (isClientTransferCancelled()) {
mainApp->CreateShowDialog("Transfer", "Transfer cancelled.", {"OK"}, true);
} else {
mainApp->CreateShowDialog("Transfer", "Save data sent successfully!", {"OK"}, true);
}
break;
}
case 1: {
// Receive selected
if (startSendingThread() != 0) {
mainApp->CreateShowDialog("Receive", "Failed to start receiver.\nCheck network connection.", {"OK"}, true);
break;
}
{
auto ovl = TransferOverlay::New("Receiving save data...");
this->titlesMenu->SetVisible(false);
mainApp->StartOverlay(ovl);
this->LockInput();
while (!isServerTransferDone()) {
ovl->SetStatus(getServerStatusText());
ovl->SetProgress(getServerProgress());
mainApp->CallForRender();
if (mainApp->GetButtonsDown() & HidNpadButton_B) {
cancelServerTransfer();
}
svcSleepThread(16666666LL);
}
mainApp->EndOverlay();
this->titlesMenu->SetVisible(true);
this->UnlockInput();
}
if (isServerTransferCancelled()) {
mainApp->CreateShowDialog("Receive", "Transfer cancelled.", {"OK"}, true);
break;
}
auto restoreResult = io::restore(index, g_currentUId, 0, title.name());
if (std::get<0>(restoreResult)) {
mainApp->CreateShowDialog("Receive", "Save data received and restored successfully!", {"OK"}, true);
} else {
mainApp->CreateShowDialog("Receive", "Restore failed:\n" + std::get<2>(restoreResult), {"OK"}, true);
}
break;
if (focus == TitlesFocus::List) {
if (Down & HidNpadButton_B) {
this->header->SetUser(std::nullopt, "");
mainApp->LoadLayout(mainApp->usersLayout);
return;
}
if (Down & HidNpadButton_A) {
if (this->titlesMenu->GetItems().empty()) return;
this->lockedListIndex = this->titlesMenu->GetSelectedIndex();
this->focus = TitlesFocus::Actions;
this->action = TitlesAction::Transfer;
this->refreshButtons();
this->updateHints();
return;
}
} else {
if (this->titlesMenu->GetSelectedIndex() != this->lockedListIndex) {
this->titlesMenu->SetSelectedIndex(this->lockedListIndex);
}
if (Down & HidNpadButton_B) {
this->focus = TitlesFocus::List;
this->refreshButtons();
this->updateHints();
return;
}
if (Down & (HidNpadButton_Up | HidNpadButton_Down |
HidNpadButton_StickLUp | HidNpadButton_StickLDown)) {
this->action = (action == TitlesAction::Transfer)
? TitlesAction::Receive : TitlesAction::Transfer;
this->refreshButtons();
return;
}
if (Down & HidNpadButton_A) {
int idx = this->titlesMenu->GetSelectedIndex();
Title title;
getTitle(title, g_currentUId, idx);
TitlesAction chosen = action;
this->focus = TitlesFocus::List;
this->refreshButtons();
this->updateHints();
if (chosen == TitlesAction::Transfer) {
this->runTransfer(idx, title);
} else {
this->runReceive(idx, title);
}
}
}
+24 -9
View File
@@ -6,26 +6,42 @@ namespace ui {
extern MainApplication *mainApp;
UsersLayout::UsersLayout() : Layout::Layout() {
this->usersMenu = pu::ui::elm::Menu::New(0, 0, 1280, COLOR("#67000000"), COLOR("#170909FF"), 94, 7);
this->usersMenu->SetScrollbarColor(COLOR("#170909FF"));
using namespace theme;
this->usersMenu = pu::ui::elm::Menu::New(
0, layout::ContentTop + space::md,
layout::ScreenW,
color::BgBase, color::BgSurface2,
88, 6);
this->usersMenu->SetScrollbarColor(color::Primary);
this->usersMenu->SetItemsFocusColor(color::BgSurface2);
for (AccountUid const& uid : Account::ids()) {
auto item = pu::ui::elm::MenuItem::New(Account::username(uid));
item->SetColor(COLOR("#FFFFFFFF"));
item->SetColor(color::TextPrimary);
this->usersMenu->AddItem(item);
}
this->loadingBg = pu::ui::elm::Rectangle::New(0, 0, 1280, 720, pu::ui::Color(30, 30, 30, 220));
this->loadingBg = pu::ui::elm::Rectangle::New(
0, 0, layout::ScreenW, layout::ScreenH, color::Scrim);
this->loadingBg->SetVisible(false);
this->loadingText = pu::ui::elm::TextBlock::New(480, 340, "Reading game list...");
this->loadingText->SetColor(pu::ui::Color(255, 255, 255, 255));
this->loadingText = pu::ui::elm::TextBlock::New(
layout::ScreenW / 2 - 120,
layout::ScreenH / 2 - 12,
"Loading saves...");
this->loadingText->SetFont(type::font(type::Body));
this->loadingText->SetColor(color::TextSecondary);
this->loadingText->SetVisible(false);
this->SetBackgroundColor(BACKGROUND_COLOR);
this->SetBackgroundColor(color::BgBase);
this->Add(this->usersMenu);
this->Add(this->loadingBg);
this->Add(this->loadingText);
this->header = std::make_unique<HeaderBar>(this, "Select a user");
this->hints = std::make_unique<HintBar>(this);
this->hints->SetHints({{"A", "Select"}, {"+", "Quit"}});
}
int32_t UsersLayout::GetCurrentIndex() {
@@ -34,8 +50,7 @@ namespace ui {
void UsersLayout::onInput(u64 Down, u64 Up, u64 Held, pu::ui::TouchPoint Pos) {
if (Down & HidNpadButton_Plus) {
mainApp->Close();
return;
svcExitProcess();
}
if (Down & HidNpadButton_A) {
+37
View File
@@ -26,6 +26,8 @@
#include "account.hpp"
#include <main.hpp>
#include <sys/stat.h>
#include <cstdio>
static std::map<AccountUid, User> mUsers;
@@ -85,6 +87,41 @@ std::string Account::username(AccountUid id)
return got->second.name;
}
std::string Account::iconPath(AccountUid id)
{
char path[128];
snprintf(path, sizeof(path), "sdmc:/switch/NXST/cache/%016lX%016lX.jpg",
id.uid[0], id.uid[1]);
struct stat st;
if (stat(path, &st) == 0 && st.st_size > 0) return std::string(path);
mkdir("sdmc:/switch", 0755);
mkdir("sdmc:/switch/NXST", 0755);
mkdir("sdmc:/switch/NXST/cache", 0755);
AccountProfile profile;
if (R_FAILED(accountGetProfile(&profile, id))) return "";
u32 imgSize = 0;
if (R_FAILED(accountProfileGetImageSize(&profile, &imgSize)) || imgSize == 0) {
accountProfileClose(&profile);
return "";
}
std::vector<u8> buf(imgSize);
u32 outSize = 0;
Result r = accountProfileLoadImage(&profile, buf.data(), imgSize, &outSize);
accountProfileClose(&profile);
if (R_FAILED(r) || outSize == 0) return "";
FILE* f = fopen(path, "wb");
if (!f) return "";
fwrite(buf.data(), 1, outSize, f);
fclose(f);
return std::string(path);
}
AccountUid Account::selectAccount(void)
{
LibAppletArgs args;
+57 -20
View File
@@ -26,20 +26,36 @@ static TransferState g_server_state;
static std::atomic<int> g_server_client_sock{-1};
static std::atomic<int> g_server_listen_sock{-1};
static std::atomic<int> g_broadcast_sock{-1};
static std::atomic<bool> g_accept_thread_active{false};
static std::atomic<bool> g_broadcast_thread_active{false};
static pthread_t g_broadcast_thread{};
bool isServerTransferDone() { return g_server_state.done.load(); }
bool isServerTransferCancelled() { return g_server_state.cancelled.load(); }
bool isServerWorkersIdle() { return !g_accept_thread_active.load() && !g_broadcast_thread_active.load(); }
double getServerProgress() { return g_server_state.progress(); }
std::string getServerStatusText() { return g_server_state.getStatus(); }
void cancelServerTransfer() {
g_server_state.cancelled.store(true);
int sock = g_server_client_sock.load();
if (sock >= 0) shutdown(sock, SHUT_RDWR);
int lsock = g_server_listen_sock.load();
if (lsock >= 0) shutdown(lsock, SHUT_RDWR);
int bsock = g_broadcast_sock.load();
if (bsock >= 0) shutdown(bsock, SHUT_RDWR);
int sock = g_server_client_sock.exchange(-1);
if (sock >= 0) {
shutdown(sock, SHUT_RDWR);
close(sock);
}
int lsock = g_server_listen_sock.exchange(-1);
if (lsock >= 0) {
shutdown(lsock, SHUT_RDWR);
close(lsock);
}
int bsock = g_broadcast_sock.exchange(-1);
if (bsock >= 0) {
shutdown(bsock, SHUT_RDWR);
close(bsock);
}
if (g_broadcast_thread_active.load()) {
pthread_cancel(g_broadcast_thread);
}
}
#ifdef __SWITCH__
@@ -166,13 +182,17 @@ static void* handle_client(void* socket_desc) {
receive_file(client_socket, filename_str, file_size);
}
close(client_socket);
int owned_client = g_server_client_sock.exchange(-1);
if (owned_client == client_socket) {
close(client_socket);
}
return nullptr;
}
struct AcceptArgs { int server_fd; };
static void* accept_and_handle(void* arg) {
g_accept_thread_active.store(true);
int server_fd = static_cast<AcceptArgs*>(arg)->server_fd;
delete static_cast<AcceptArgs*>(arg);
@@ -180,8 +200,10 @@ static void* accept_and_handle(void* arg) {
sockaddr_in client_addr{};
socklen_t client_len = sizeof(client_addr);
int client_socket = accept(server_fd, (sockaddr*)&client_addr, &client_len);
g_server_listen_sock.store(-1);
close(server_fd);
int owned_listen = g_server_listen_sock.exchange(-1);
if (owned_listen == server_fd) {
close(server_fd);
}
if (client_socket >= 0) {
g_server_client_sock.store(client_socket);
@@ -191,23 +213,27 @@ static void* accept_and_handle(void* arg) {
} else {
close(client_socket);
}
g_server_client_sock.store(-1);
}
g_server_state.done.store(true);
g_accept_thread_active.store(false);
return nullptr;
}
static void* broadcast_listener(void* arg) {
Socket udp(socket(AF_INET, SOCK_DGRAM, 0));
if (!udp.valid()) {
g_broadcast_thread_active.store(true);
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, nullptr);
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, nullptr);
int udp = socket(AF_INET, SOCK_DGRAM, 0);
if (udp < 0) {
perror("broadcast_listener: socket");
g_broadcast_thread_active.store(false);
return nullptr;
}
g_broadcast_sock.store(udp.fd);
g_broadcast_sock.store(udp);
struct timeval tv{0, 100000}; // 100ms poll so cancel is detected quickly
struct timeval tv{0, 20000}; // 20ms poll so cancel/exit wins race with socketExit
setsockopt(udp, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
sockaddr_in addr{};
@@ -217,7 +243,9 @@ static void* broadcast_listener(void* arg) {
if (bind(udp, (sockaddr*)&addr, sizeof(addr)) < 0) {
perror("broadcast_listener: bind");
g_broadcast_sock.store(-1);
int owned = g_broadcast_sock.exchange(-1);
if (owned == udp) close(udp);
g_broadcast_thread_active.store(false);
return nullptr;
}
@@ -226,7 +254,9 @@ static void* broadcast_listener(void* arg) {
group.imr_interface.s_addr = htonl(INADDR_ANY);
if (setsockopt(udp, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group, sizeof(group)) < 0) {
perror("broadcast_listener: setsockopt");
g_broadcast_sock.store(-1);
int owned = g_broadcast_sock.exchange(-1);
if (owned == udp) close(udp);
g_broadcast_thread_active.store(false);
return nullptr;
}
@@ -250,21 +280,28 @@ static void* broadcast_listener(void* arg) {
}
}
g_broadcast_sock.store(-1);
int owned = g_broadcast_sock.exchange(-1);
if (owned == udp) close(udp);
g_broadcast_thread_active.store(false);
return nullptr;
}
int startSendingThread() {
g_server_state.reset();
g_server_state.setStatus("Waiting for connection...");
pthread_t broadcast_thread;
if (pthread_create(&broadcast_thread, nullptr, broadcast_listener, nullptr) != 0) {
perror("startSendingThread: broadcast thread");
return 1;
}
g_broadcast_thread = broadcast_thread;
pthread_detach(broadcast_thread);
Socket server(socket(AF_INET, SOCK_STREAM, 0));
if (!server.valid()) {
perror("startSendingThread: socket");
cancelServerTransfer();
return 1;
}
@@ -278,20 +315,20 @@ int startSendingThread() {
if (bind(server, (sockaddr*)&addr, sizeof(addr)) < 0) {
perror("startSendingThread: bind");
cancelServerTransfer();
return 1;
}
if (listen(server, 3) < 0) {
perror("startSendingThread: listen");
cancelServerTransfer();
return 1;
}
g_server_state.reset();
g_server_state.setStatus("Waiting for connection...");
AcceptArgs* acc_args = new AcceptArgs{server.fd};
pthread_t accept_thread;
if (pthread_create(&accept_thread, nullptr, accept_and_handle, acc_args) != 0) {
delete acc_args;
cancelServerTransfer();
return 1;
}
pthread_detach(accept_thread);
+10
View File
@@ -126,6 +126,16 @@ std::string StringUtils::removeNotAscii(std::string str)
return str;
}
std::string StringUtils::elide(const std::string& s, size_t maxChars)
{
if (s.size() <= maxChars || maxChars < 6) return s;
constexpr const char* dots = "...";
size_t budget = maxChars - 3;
size_t head = (budget + 1) / 2;
size_t tail = budget - head;
return s.substr(0, head) + dots + s.substr(s.size() - tail);
}
HidsysNotificationLedPattern blinkLedPattern(u8 times)
{
HidsysNotificationLedPattern pattern;