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
2738namespace plotly ::detail {
2839
2940auto 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
3854auto 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
4364auto 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
4874auto 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
91129auto 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
164265auto 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
239378auto 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