feat: Settings submenu (Settings.../About...), git column, projects tab
- main.cpp: registrar info About via fn_ui::about_window_set_info - views.cpp: nueva columna "Git" en la tabla Apps (remote/local/-) - data.h/cpp + data_http.cpp: AppRow gana repo_url + dir_path - views.cpp: actions bar (Reindex / + Add / Reload / inbox) y modal Add - views.cpp: tab Projects con tree + detalle anidado - data_http.cpp: load_projects_http, load_project_detail_http, http_post_* - http_client.cpp: SO_RCVTIMEO en Windows como DWORD ms (timeout 5 ms bug) - CMakeLists: limpieza de srcs duplicados con fn_framework - app.md: notas operativas y estado actual Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+24
-6
@@ -8,15 +8,19 @@
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#include <mutex>
|
||||
#pragma comment(lib, "ws2_32.lib")
|
||||
|
||||
// std::call_once para evitar race condition si hay peticiones simultaneas
|
||||
// desde multiples threads (main + runners).
|
||||
static std::once_flag g_wsa_once;
|
||||
static bool g_wsa_ok = false;
|
||||
static bool wsa_init() {
|
||||
static bool done = false;
|
||||
if (!done) {
|
||||
std::call_once(g_wsa_once, []() {
|
||||
WSADATA wsa;
|
||||
done = (WSAStartup(MAKEWORD(2, 2), &wsa) == 0);
|
||||
}
|
||||
return done;
|
||||
g_wsa_ok = (WSAStartup(MAKEWORD(2, 2), &wsa) == 0);
|
||||
});
|
||||
return g_wsa_ok;
|
||||
}
|
||||
typedef SOCKET sock_t;
|
||||
#define SOCK_INVALID INVALID_SOCKET
|
||||
@@ -64,12 +68,19 @@ HttpResponse HttpClient::request(const std::string& method, const std::string& p
|
||||
return resp;
|
||||
}
|
||||
|
||||
// Set timeout
|
||||
// Timeout — Windows y POSIX usan formatos distintos para SO_{RCV,SND}TIMEO.
|
||||
// Windows: DWORD milisegundos. POSIX: struct timeval.
|
||||
#ifdef _WIN32
|
||||
DWORD timeout_ms = static_cast<DWORD>(timeout_sec_ * 1000);
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout_ms, sizeof(timeout_ms));
|
||||
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&timeout_ms, sizeof(timeout_ms));
|
||||
#else
|
||||
struct timeval tv;
|
||||
tv.tv_sec = timeout_sec_;
|
||||
tv.tv_usec = 0;
|
||||
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
|
||||
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
|
||||
#endif
|
||||
|
||||
// Connect
|
||||
struct sockaddr_in addr;
|
||||
@@ -79,7 +90,14 @@ HttpResponse HttpClient::request(const std::string& method, const std::string& p
|
||||
addr.sin_addr.s_addr = inet_addr(host_.c_str());
|
||||
|
||||
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) != 0) {
|
||||
int err = SOCK_ERR;
|
||||
SOCK_CLOSE(sock);
|
||||
// Reportamos el errno/WSAError en el body para que el toast sea util.
|
||||
char buf[128];
|
||||
std::snprintf(buf, sizeof(buf),
|
||||
"connect() failed to %s:%d (err=%d)",
|
||||
host_.c_str(), port_, err);
|
||||
resp.body = buf;
|
||||
return resp;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user