Skip to content

Commit 3f8f47d

Browse files
committed
test
1 parent b0ccb00 commit 3f8f47d

File tree

1 file changed

+176
-7
lines changed

1 file changed

+176
-7
lines changed

src/detail/browser.cpp

Lines changed: 176 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,49 +3,86 @@
33
#include "nlohmann/json.hpp"
44
#include "plotly/logger.hpp"
55
#include "websockets_client.hpp"
6-
#include <arpa/inet.h>
76
#include <array>
87
#include <chrono>
9-
#include <csignal>
108
#include <cstdio>
119
#include <cstdlib>
12-
#include <fcntl.h>
1310
#include <filesystem>
1411
#include <functional>
1512
#include <future>
16-
#include <ifaddrs.h>
17-
#include <netinet/in.h>
1813
#include <string>
1914
#include <string_view>
15+
#include <utility>
16+
#include <vector>
17+
18+
#ifdef _WIN32
19+
#include <iphlpapi.h>
20+
#include <shlobj.h>
21+
#include <windows.h>
22+
#include <winsock2.h>
23+
#include <ws2tcpip.h>
24+
#pragma comment(lib, "iphlpapi.lib")
25+
#pragma comment(lib, "ws2_32.lib")
26+
#else
27+
#include <arpa/inet.h>
28+
#include <csignal>
29+
#include <fcntl.h>
30+
#include <ifaddrs.h>
31+
#include <netinet/in.h>
2032
#include <sys/socket.h>
2133
#include <sys/types.h>
2234
#include <sys/wait.h>
2335
#include <unistd.h>
24-
#include <utility>
25-
#include <vector>
36+
#endif
2637

2738
namespace plotly::detail {
2839

2940
auto isDisplayAvailable() -> bool {
41+
#ifdef _WIN32
42+
// On Windows, GUI is always available unless running as a service
43+
return true;
44+
#else
3045
// Check if DISPLAY environment variable is set
3146
const char *displayEnv = std::getenv("DISPLAY");
3247
if (displayEnv == nullptr) {
3348
return false;
3449
}
3550
return true;
51+
#endif
3652
}
3753

3854
auto isChromiumAvailable() -> bool {
55+
#ifdef _WIN32
56+
// Check if chromium is installed on Windows
57+
return system("where chrome.exe > nul 2>&1") == 0;
58+
#else
3959
// Check if chromium is installed
4060
return system("which chromium > /dev/null 2>&1") == 0;
61+
#endif
4162
}
4263

4364
auto isGoogleChromeAvailable() -> bool {
65+
#ifdef _WIN32
66+
// Check if google-chrome is installed on Windows
67+
return system("where chrome.exe > nul 2>&1") == 0;
68+
#else
4469
// Check if google-chrome is installed
4570
return system("which google-chrome > /dev/null 2>&1") == 0;
71+
#endif
4672
}
4773

4874
auto openBrowser(const std::string_view url) -> bool {
75+
#ifdef _WIN32
76+
// Use ShellExecute on Windows
77+
std::string urlStr(url);
78+
HINSTANCE result = ShellExecuteA(nullptr, "open", urlStr.c_str(), nullptr,
79+
nullptr, SW_SHOWNORMAL);
80+
if (reinterpret_cast<INT_PTR>(result) <= 32) {
81+
plotly::logError("Failed to open browser on Windows");
82+
return false;
83+
}
84+
return true;
85+
#else
4986
pid_t pid = fork();
5087

5188
if (pid < 0) {
@@ -86,11 +123,74 @@ auto openBrowser(const std::string_view url) -> bool {
86123

87124
return true;
88125
}
126+
#endif
89127
}
90128

91129
auto openChromiumWithHeadlessMode(const std::string_view url,
92130
int remoteDebuggingPort)
93131
-> std::pair<bool, std::function<void()>> {
132+
#ifdef _WIN32
133+
// Windows implementation using CreateProcess
134+
std::string portArg =
135+
"--remote-debugging-port=" + std::to_string(remoteDebuggingPort);
136+
std::string urlStr(url);
137+
138+
// Try common Chrome installation paths on Windows
139+
std::vector<std::string> chromePaths = {
140+
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
141+
"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
142+
std::string(std::getenv("LOCALAPPDATA") ? std::getenv("LOCALAPPDATA")
143+
: "") +
144+
"\\Google\\Chrome\\Application\\chrome.exe"};
145+
146+
PROCESS_INFORMATION pi = {nullptr, nullptr, 0, 0};
147+
STARTUPINFOA si;
148+
ZeroMemory(&si, sizeof(si));
149+
si.cb = sizeof(si);
150+
si.dwFlags = STARTF_USESHOWWINDOW;
151+
si.wShowWindow = SW_HIDE;
152+
153+
bool success = false;
154+
for (const auto &chromePath : chromePaths) {
155+
std::string cmdLine = "\"" + chromePath + "\" --headless --disable-gpu " +
156+
"--no-sandbox --disable-dev-shm-usage " +
157+
"--disable-extensions " +
158+
"--enable-features=NetworkService,"
159+
"NetworkServiceInProcess " +
160+
portArg + " \"" + urlStr + "\"";
161+
162+
plotly::logTrace("Trying to open with: %s", chromePath.c_str());
163+
164+
if (CreateProcessA(nullptr, const_cast<char *>(cmdLine.c_str()), nullptr,
165+
nullptr, FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &si,
166+
&pi)) {
167+
plotly::logDebug(
168+
"Chromium in headless mode opened successfully (PID: %lu)",
169+
pi.dwProcessId);
170+
success = true;
171+
break;
172+
}
173+
}
174+
175+
if (!success) {
176+
plotly::logError(
177+
"Failed to open chromium in headless mode - no browsers available");
178+
return {false, []() -> void {}};
179+
}
180+
181+
// Return success with a function that terminates the process
182+
HANDLE hProcess = pi.hProcess;
183+
DWORD processId = pi.dwProcessId;
184+
CloseHandle(pi.hThread);
185+
186+
return {true, [hProcess, processId]() -> void {
187+
plotly::logDebug("Terminating chromium in headless mode (PID: %lu)",
188+
processId);
189+
TerminateProcess(hProcess, 0);
190+
WaitForSingleObject(hProcess, INFINITE);
191+
CloseHandle(hProcess);
192+
}};
193+
#else
94194
pid_t pid = fork();
95195

96196
if (pid < 0) {
@@ -159,10 +259,48 @@ auto openChromiumWithHeadlessMode(const std::string_view url,
159259
waitpid(pid, nullptr, 0);
160260
}};
161261
}
262+
#endif
162263
}
163264

164265
auto getIpv4Addresses() -> std::vector<std::string> {
165266
std::vector<std::string> result;
267+
#ifdef _WIN32
268+
// Windows implementation using GetAdaptersAddresses
269+
ULONG bufferSize = 15000;
270+
std::vector<BYTE> buffer(bufferSize);
271+
auto *addresses = reinterpret_cast<IP_ADAPTER_ADDRESSES *>(buffer.data());
272+
273+
ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST |
274+
GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME;
275+
276+
ULONG ret =
277+
GetAdaptersAddresses(AF_INET, flags, nullptr, addresses, &bufferSize);
278+
279+
if (ret == ERROR_BUFFER_OVERFLOW) {
280+
buffer.resize(bufferSize);
281+
addresses = reinterpret_cast<IP_ADAPTER_ADDRESSES *>(buffer.data());
282+
ret = GetAdaptersAddresses(AF_INET, flags, nullptr, addresses, &bufferSize);
283+
}
284+
285+
if (ret != NO_ERROR) {
286+
return result;
287+
}
288+
289+
for (auto *adapter = addresses; adapter != nullptr; adapter = adapter->Next) {
290+
for (auto *unicast = adapter->FirstUnicastAddress; unicast != nullptr;
291+
unicast = unicast->Next) {
292+
auto *sockaddr =
293+
reinterpret_cast<struct sockaddr_in *>(unicast->Address.lpSockaddr);
294+
if (sockaddr->sin_family == AF_INET) {
295+
std::array<char, INET_ADDRSTRLEN> ip{};
296+
if (inet_ntop(AF_INET, &(sockaddr->sin_addr), ip.data(),
297+
INET_ADDRSTRLEN) != nullptr) {
298+
result.emplace_back(ip.data());
299+
}
300+
}
301+
}
302+
}
303+
#else
166304
struct ifaddrs *ifaddr = nullptr;
167305

168306
if (getifaddrs(&ifaddr) == -1) {
@@ -185,6 +323,7 @@ auto getIpv4Addresses() -> std::vector<std::string> {
185323
}
186324

187325
freeifaddrs(ifaddr);
326+
#endif
188327
return result;
189328
}
190329

@@ -237,6 +376,35 @@ auto setDownloadDirectory(const std::filesystem::path &directory,
237376
}
238377

239378
auto getDefaultDownloadDirectory() -> std::filesystem::path {
379+
#ifdef _WIN32
380+
// Windows implementation using SHGetKnownFolderPath
381+
PWSTR path = nullptr;
382+
HRESULT hr = SHGetKnownFolderPath(FOLDERID_Downloads, 0, nullptr, &path);
383+
384+
if (SUCCEEDED(hr) && path != nullptr) {
385+
// Convert wide string to narrow string
386+
int size =
387+
WideCharToMultiByte(CP_UTF8, 0, path, -1, nullptr, 0, nullptr, nullptr);
388+
std::string result(size - 1, 0);
389+
WideCharToMultiByte(CP_UTF8, 0, path, -1, &result[0], size, nullptr,
390+
nullptr);
391+
CoTaskMemFree(path);
392+
return {result};
393+
}
394+
395+
if (path != nullptr) {
396+
CoTaskMemFree(path);
397+
}
398+
399+
// Fallback to USERPROFILE\Downloads
400+
const char *userProfile = std::getenv("USERPROFILE");
401+
if (userProfile != nullptr) {
402+
return {std::filesystem::path(userProfile) / "Downloads"};
403+
}
404+
405+
// Last resort fallback
406+
return {std::filesystem::temp_directory_path()};
407+
#else
240408
std::array<char, 128> buffer{};
241409
std::string result;
242410
FILE *pipe = popen("xdg-user-dir DOWNLOAD", "r");
@@ -269,6 +437,7 @@ auto getDefaultDownloadDirectory() -> std::filesystem::path {
269437
}
270438

271439
return {result};
440+
#endif
272441
}
273442

274443
} // namespace plotly::detail

0 commit comments

Comments
 (0)