@@ -326,6 +326,7 @@ typedef struct _webui_window_t {
326326 bool connected ; // Fast check
327327 size_t server_port ;
328328 char * url ;
329+ char * public_url ;
329330 const char * html ;
330331 char * server_root_path ;
331332 #ifdef _WIN32
@@ -632,6 +633,7 @@ static int _webui_serve_file(_webui_window_t* win, struct mg_connection* client,
632633static int _webui_external_file_handler (_webui_window_t * win , struct mg_connection * client , size_t client_id );
633634static int _webui_interpret_file (_webui_window_t * win , struct mg_connection * client , char * index , size_t client_id );
634635static void _webui_webview_update (_webui_window_t * win );
636+ static const char * _webui_get_local_ip (void );
635637// WebView
636638#ifdef _WIN32
637639// Microsoft Windows
@@ -3157,13 +3159,24 @@ const char* webui_get_url(size_t window) {
31573159 return NULL ;
31583160 _webui_window_t * win = _webui .wins [window ];
31593161
3160- // Check if server is started
3162+ // Check if local server is started
31613163 if (_webui_is_empty (win -> url )) {
3162- // Start server
3164+ // Start local server
3165+ bool backup = _webui .config .show_wait_connection ;
3166+ _webui .config .show_wait_connection = true;
31633167 webui_show_browser (window , "<html><head><script src=\"webui.js\"></script></head></html>" , NoBrowser );
3168+ _webui .config .show_wait_connection = backup ;
31643169 }
31653170
3166- return (const char * ) win -> url ;
3171+ // Get local IP of first NIC
3172+ const char * ip = _webui_get_local_ip ();
3173+ if (win -> public_url != NULL )
3174+ _webui_free_mem ((void * )win -> public_url );
3175+ win -> public_url = (char * )_webui_malloc (64 ); // [http][ip][port]
3176+ WEBUI_SN_PRINTF_DYN (win -> public_url , 64 , WEBUI_HTTP_PROTOCOL "%s:%zu" , ip , win -> server_port );
3177+ _webui_free_mem ((void * )ip );
3178+
3179+ return (const char * ) win -> public_url ;
31673180}
31683181
31693182void webui_set_public (size_t window , bool status ) {
@@ -7902,6 +7915,121 @@ static bool _webui_tls_generate_self_signed_cert(char* root_cert, char* root_key
79027915}
79037916#endif
79047917
7918+ static const char * _webui_get_local_ip (void ) {
7919+ char * buf = malloc (64 );
7920+ if (!buf ) return NULL ;
7921+ #ifdef _WIN32
7922+ WSADATA wsa ;
7923+ if (WSAStartup (MAKEWORD (2 ,2 ), & wsa ) != 0 ) {
7924+ free (buf );
7925+ return NULL ;
7926+ }
7927+ buf [0 ] = '\0' ;
7928+ // Approach 1: gethostname + getaddrinfo
7929+ char hostname [256 ] = {0 };
7930+ if (gethostname (hostname , sizeof (hostname )) == 0 ) {
7931+ struct addrinfo hints = {0 }, * res = NULL ;
7932+ hints .ai_family = AF_INET ;
7933+ if (getaddrinfo (hostname , NULL , & hints , & res ) == 0 ) {
7934+ char first_ip [INET_ADDRSTRLEN ] = {0 };
7935+ for (struct addrinfo * p = res ; p ; p = p -> ai_next ) {
7936+ if (p -> ai_family == AF_INET ) {
7937+ struct sockaddr_in * sa = (struct sockaddr_in * )p -> ai_addr ;
7938+ char ip [INET_ADDRSTRLEN ];
7939+ if (inet_ntop (AF_INET , & sa -> sin_addr , ip , sizeof (ip ))) {
7940+ if (strcmp (ip , "127.0.0.1" )) {
7941+ strncpy (buf , ip , 63 );
7942+ buf [63 ] = '\0' ;
7943+ break ;
7944+ }
7945+ if (!first_ip [0 ]) {
7946+ strncpy (first_ip , ip , sizeof (first_ip )- 1 );
7947+ }
7948+ }
7949+ }
7950+ }
7951+ if (!buf [0 ] && first_ip [0 ]) {
7952+ strncpy (buf , first_ip , 63 );
7953+ buf [63 ] = '\0' ;
7954+ }
7955+ freeaddrinfo (res );
7956+ }
7957+ }
7958+ if (buf [0 ]) {
7959+ WSACleanup ();
7960+ return buf ;
7961+ }
7962+ // Approach 2: WSAIoctl SIO_GET_INTERFACE_LIST
7963+ SOCKET s = socket (AF_INET , SOCK_DGRAM , 0 );
7964+ if (s != INVALID_SOCKET ) {
7965+ #define MAX_IFACES 32
7966+ INTERFACE_INFO ifaces [MAX_IFACES ];
7967+ DWORD retlen ;
7968+ if (!WSAIoctl (s , SIO_GET_INTERFACE_LIST , NULL , 0 ,
7969+ ifaces , sizeof (ifaces ), & retlen , NULL , NULL )) {
7970+ int n = retlen / sizeof (INTERFACE_INFO );
7971+ char first_ip [INET_ADDRSTRLEN ] = {0 };
7972+ for (int i = 0 ; i < n ; i ++ ) {
7973+ struct sockaddr_in * sa = (struct sockaddr_in * )& ifaces [i ].iiAddress ;
7974+ char ip [INET_ADDRSTRLEN ];
7975+ if (inet_ntop (AF_INET , & sa -> sin_addr , ip , sizeof (ip ))) {
7976+ if (!strcmp (ip , "0.0.0.0" )) continue ;
7977+ if (strcmp (ip , "127.0.0.1" )) {
7978+ strncpy (buf , ip , 63 );
7979+ buf [63 ] = '\0' ;
7980+ break ;
7981+ }
7982+ if (!first_ip [0 ]) {
7983+ strncpy (first_ip , ip , sizeof (first_ip )- 1 );
7984+ }
7985+ }
7986+ }
7987+ if (!buf [0 ] && first_ip [0 ]) {
7988+ strncpy (buf , first_ip , 63 );
7989+ buf [63 ] = '\0' ;
7990+ }
7991+ }
7992+ closesocket (s );
7993+ }
7994+ WSACleanup ();
7995+ if (!buf [0 ]) {
7996+ free (buf );
7997+ return NULL ;
7998+ }
7999+ return buf ;
8000+ #else
8001+ struct ifaddrs * ifaddr = NULL , * ifa ;
8002+ if (getifaddrs (& ifaddr ) == -1 ) {
8003+ free (buf );
8004+ return NULL ;
8005+ }
8006+ // first non-loopback IPv4
8007+ for (ifa = ifaddr ; ifa ; ifa = ifa -> ifa_next ) {
8008+ if (ifa -> ifa_addr && ifa -> ifa_addr -> sa_family == AF_INET &&
8009+ (ifa -> ifa_flags & IFF_UP ) && !(ifa -> ifa_flags & IFF_LOOPBACK )) {
8010+ struct sockaddr_in * sa = (struct sockaddr_in * )ifa -> ifa_addr ;
8011+ if (inet_ntop (AF_INET , & sa -> sin_addr , buf , 64 )) {
8012+ freeifaddrs (ifaddr );
8013+ return buf ;
8014+ }
8015+ }
8016+ }
8017+ // fallback first IPv4
8018+ for (ifa = ifaddr ; ifa ; ifa = ifa -> ifa_next ) {
8019+ if (ifa -> ifa_addr && ifa -> ifa_addr -> sa_family == AF_INET ) {
8020+ struct sockaddr_in * sa = (struct sockaddr_in * )ifa -> ifa_addr ;
8021+ if (inet_ntop (AF_INET , & sa -> sin_addr , buf , 64 )) {
8022+ freeifaddrs (ifaddr );
8023+ return buf ;
8024+ }
8025+ }
8026+ }
8027+ freeifaddrs (ifaddr );
8028+ free (buf );
8029+ return NULL ;
8030+ #endif
8031+ }
8032+
79058033static bool _webui_show_window (_webui_window_t * win , struct mg_connection * client , const char * content , int type , size_t browser ) {
79068034
79078035 #ifdef WEBUI_LOG
0 commit comments