diff --git a/.gitignore b/.gitignore index a7f5b7d8..2ebe771a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,5 @@ # Build *.exe -*.dll -*.so -*.dylib -*.a *.ilk *.pdb *.lib @@ -13,7 +9,6 @@ *.o *.def *.obj -*.dyn # IDE .idea/ diff --git a/build/Linux/Clang/Makefile b/build/Linux/Clang/Makefile index c133fd4c..96d8f494 100644 --- a/build/Linux/Clang/Makefile +++ b/build/Linux/Clang/Makefile @@ -8,13 +8,13 @@ all: release debug: # Static with Debug info - @echo "Build WebUI Library (Debug Static)..." + @echo "Build WebUI Library (Clang Debug Static)..." @clang -g -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" @clang -g -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @llvm-ar rc libwebui-2-static-x64.a webui.o mongoose.o @llvm-ranlib libwebui-2-static-x64.a # Dynamic with Debug info - @echo "Build WebUI Library (Debug Dynamic)..." + @echo "Build WebUI Library (Clang Debug Dynamic)..." @clang -g -fPIC -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" @clang -g -fPIC -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @clang -g -shared -o webui-2-x64.so webui.o mongoose.o @@ -24,13 +24,13 @@ debug: release: # Static Release - @echo "Build WebUI Library (Release Static)..." + @echo "Build WebUI Library (Clang Release Static)..." @clang -Os -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" @clang -Os -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @llvm-ar rc libwebui-2-static-x64.a webui.o mongoose.o @llvm-ranlib libwebui-2-static-x64.a # Dynamic Release - @echo "Build WebUI Library (Release Dynamic)..." + @echo "Build WebUI Library (Clang Release Dynamic)..." @clang -O3 -fPIC -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" @clang -O3 -fPIC -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @clang -shared -o webui-2-x64.so webui.o mongoose.o diff --git a/build/Linux/GCC/Makefile b/build/Linux/GCC/Makefile index cea91dc3..fa793d3d 100644 --- a/build/Linux/GCC/Makefile +++ b/build/Linux/GCC/Makefile @@ -8,13 +8,13 @@ all: release debug: # Static with Debug info - @echo "Build WebUI Library (Debug Static)..." + @echo "Build WebUI Library (GCC Debug Static)..." @gcc -g -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" -Wno-stringop-overread @gcc -g -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @ar rc libwebui-2-static-x64.a webui.o mongoose.o @ranlib libwebui-2-static-x64.a # Dynamic with Debug info - @echo "Build WebUI Library (Debug Dynamic)..." + @echo "Build WebUI Library (GCC Debug Dynamic)..." @gcc -g -fPIC -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" -Wno-stringop-overread @gcc -g -fPIC -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @gcc -g -shared -o webui-2-x64.so webui.o mongoose.o @@ -24,13 +24,13 @@ debug: release: # Static Release - @echo "Build WebUI Library (Release Static)..." + @echo "Build WebUI Library (GCC Release Static)..." @gcc -Os -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" -Wno-stringop-overread @gcc -Os -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @ar rc libwebui-2-static-x64.a webui.o mongoose.o @ranlib libwebui-2-static-x64.a # Dynamic Release - @echo "Build WebUI Library (Release Dynamic)..." + @echo "Build WebUI Library (GCC Release Dynamic)..." @gcc -O3 -fPIC -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" -Wno-stringop-overread @gcc -O3 -fPIC -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @gcc -shared -o webui-2-x64.so webui.o mongoose.o diff --git a/build/Windows/GCC/Makefile b/build/Windows/GCC/Makefile index ee662b99..1845ebdd 100644 --- a/build/Windows/GCC/Makefile +++ b/build/Windows/GCC/Makefile @@ -9,13 +9,13 @@ all: release debug: # Static with Debug info - @echo Build WebUI Library (Debug Static)... + @echo Build WebUI Library (GCC Debug Static)... @gcc -g -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" -Wno-stringop-overread @gcc -g -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" -DWEBUI_LOG @ar rc libwebui-2-static-x64.a webui.o mongoose.o @ranlib libwebui-2-static-x64.a # Dynamic with Debug info - @echo Build WebUI Library (Debug Dynamic)... + @echo Build WebUI Library (GCC Debug Dynamic)... @gcc -g -fPIC -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" -Wno-stringop-overread @gcc -g -fPIC -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" -DWEBUI_LOG @gcc -g -shared -o webui-2-x64.dll webui.o mongoose.o -lws2_32 @@ -25,13 +25,13 @@ debug: release: # Static Release - @echo Build WebUI Library (Release Static)... + @echo Build WebUI Library (GCC Release Static)... @gcc -Os -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" -Wno-stringop-overread @gcc -Os -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @ar rc libwebui-2-static-x64.a webui.o mongoose.o @ranlib libwebui-2-static-x64.a # Dynamic Release - @echo Build WebUI Library (Release Dynamic)... + @echo Build WebUI Library (GCC Release Dynamic)... @gcc -O3 -fPIC -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" -Wno-stringop-overread @gcc -O3 -fPIC -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @gcc -shared -o webui-2-x64.dll webui.o mongoose.o -lws2_32 diff --git a/build/Windows/GCC/webui-2-x64.dll b/build/Windows/GCC/webui-2-x64.dll new file mode 100644 index 00000000..37b13d18 Binary files /dev/null and b/build/Windows/GCC/webui-2-x64.dll differ diff --git a/build/Windows/MSVC/Makefile b/build/Windows/MSVC/Makefile index a743b196..99fc54a7 100644 --- a/build/Windows/MSVC/Makefile +++ b/build/Windows/MSVC/Makefile @@ -5,30 +5,30 @@ all: release debug: # Static with Debug info - @echo Build WebUI Library (Debug Static)... - @cl /Zi /Fomongoose.obj /c /EHsc "../../../src/mongoose.c" /I "../../../include" >nul 2>&1 - @cl /Zi /Fowebui.obj /c /EHsc /DWEBUI_LOG "../../../src/webui.c" /I "../../../include" >nul 2>&1 - @lib /OUT:webui-2-static-x64.lib webui.obj mongoose.obj >nul 2>&1 + @echo Build WebUI Library (MSVC Debug Static)... + @cl /Zi /Fomongoose.obj /c /EHsc "../../../src/mongoose.c" /I "../../../include" 1>NUL 2>&1 + @cl /Zi /Fowebui.obj /c /EHsc /DWEBUI_LOG "../../../src/webui.c" /I "../../../include" 1>NUL 2>&1 + @lib /OUT:webui-2-static-x64.lib webui.obj mongoose.obj 1>NUL 2>&1 # Dynamic with Debug info - @echo Build WebUI Library (Debug Dynamic)... - @cl /Zi /Fomongoose.obj /c /EHsc "../../../src/mongoose.c" /I "../../../include" >nul 2>&1 - @cl /Zi /Fowebui.obj /c /EHsc /DWEBUI_LOG "../../../src/webui.c" /I "../../../include" >nul 2>&1 - @link /DLL /OUT:webui-2-x64.dll webui.obj mongoose.obj user32.lib Advapi32.lib >nul 2>&1 + @echo Build WebUI Library (MSVC Debug Dynamic)... + @cl /Zi /Fomongoose.obj /c /EHsc "../../../src/mongoose.c" /I "../../../include" 1>NUL 2>&1 + @cl /Zi /Fowebui.obj /c /EHsc /DWEBUI_LOG "../../../src/webui.c" /I "../../../include" 1>NUL 2>&1 + @link /DLL /OUT:webui-2-x64.dll webui.obj mongoose.obj user32.lib Advapi32.lib 1>NUL 2>&1 # Clean @- del *.obj >nul 2>&1 @echo Done. release: # Static Release - @echo Build WebUI Library (Release Static)... - @cl /Fomongoose.obj /c /EHsc "../../../src/mongoose.c" /I "../../../include" >nul 2>&1 - @cl /Fowebui.obj /c /EHsc "../../../src/webui.c" /I "../../../include" >nul 2>&1 - @lib /OUT:webui-2-static-x64.lib webui.obj mongoose.obj >nul 2>&1 + @echo Build WebUI Library (MSVC Release Static)... + @cl /Fomongoose.obj /c /EHsc "../../../src/mongoose.c" /I "../../../include" 1>NUL 2>&1 + @cl /Fowebui.obj /c /EHsc "../../../src/webui.c" /I "../../../include" 1>NUL 2>&1 + @lib /OUT:webui-2-static-x64.lib webui.obj mongoose.obj 1>NUL 2>&1 # Dynamic Release - @echo Build WebUI Library (Release Dynamic)... - @cl /Fomongoose.obj /c /EHsc "../../../src/mongoose.c" /I "../../../include" >nul 2>&1 - @cl /Fowebui.obj /c /EHsc "../../../src/webui.c" /I "../../../include" >nul 2>&1 - @link /DLL /OUT:webui-2-x64.dll webui.obj mongoose.obj user32.lib Advapi32.lib >nul 2>&1 + @echo Build WebUI Library (MSVC Release Dynamic)... + @cl /Fomongoose.obj /c /EHsc "../../../src/mongoose.c" /I "../../../include" 1>NUL 2>&1 + @cl /Fowebui.obj /c /EHsc "../../../src/webui.c" /I "../../../include" 1>NUL 2>&1 + @link /DLL /OUT:webui-2-x64.dll webui.obj mongoose.obj user32.lib Advapi32.lib 1>NUL 2>&1 # Clean @- del *.obj >nul 2>&1 @- del *.ilk >nul 2>&1 diff --git a/build/Windows/MSVC/webui-2-x64.dll b/build/Windows/MSVC/webui-2-x64.dll new file mode 100644 index 00000000..4dcaca02 Binary files /dev/null and b/build/Windows/MSVC/webui-2-x64.dll differ diff --git a/build/Windows/TCC/Makefile b/build/Windows/TCC/Makefile index 713b8865..389abb64 100644 --- a/build/Windows/TCC/Makefile +++ b/build/Windows/TCC/Makefile @@ -9,12 +9,12 @@ all: release debug: # Static with Debug info - @echo Build WebUI Library (Debug Static)... + @echo Build WebUI Library (TCC Debug Static)... @tcc -g -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" @tcc -g -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" -DWEBUI_LOG @tcc -m64 -ar rcs libwebui-2-static-x64.a webui.o mongoose.o # Dynamic with Debug info - @echo Build WebUI Library (Debug Dynamic)... + @echo Build WebUI Library (TCC Debug Dynamic)... @tcc -g -fPIC -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" @tcc -g -fPIC -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" -DWEBUI_LOG @tcc -g -shared -o webui-2-x64.dll webui.o mongoose.o -lws2_32 -lAdvapi32 @@ -24,12 +24,12 @@ debug: release: # Static Release - @echo Build WebUI Library (Release Static)... + @echo Build WebUI Library (TCC Release Static)... @tcc -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" @tcc -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @tcc -m64 -ar rcs libwebui-2-static-x64.a webui.o mongoose.o # Dynamic Release - @echo Build WebUI Library (Release Dynamic)... + @echo Build WebUI Library (TCC Release Dynamic)... @tcc -fPIC -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" @tcc -fPIC -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @tcc -shared -o webui-2-x64.dll webui.o mongoose.o -lws2_32 -lAdvapi32 diff --git a/build/macOS/Clang/Makefile b/build/macOS/Clang/Makefile index e6076417..421be80e 100644 --- a/build/macOS/Clang/Makefile +++ b/build/macOS/Clang/Makefile @@ -8,13 +8,13 @@ all: release debug: # Static with Debug info - @echo "Build WebUI Library (Debug Static)..." + @echo "Build WebUI Library (Clang Debug Static)..." @clang -g -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" @clang -DWEBUI_LOG -g -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @ar rc libwebui-2-static-x64.a webui.o mongoose.o @ranlib libwebui-2-static-x64.a # Dynamic with Debug info - @echo "Build WebUI Library (Debug Dynamic)..." + @echo "Build WebUI Library (Clang Debug Dynamic)..." @clang -g -fPIC -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" @clang -DWEBUI_LOG -g -fPIC -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @clang -g -shared -o webui-2-x64.dyn webui.o mongoose.o @@ -24,13 +24,13 @@ debug: release: # Static Release - @echo "Build WebUI Library (Release Static)..." + @echo "Build WebUI Library (Clang Release Static)..." @clang -Os -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" @clang -Os -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @ar rc libwebui-2-static-x64.a webui.o mongoose.o @ranlib libwebui-2-static-x64.a # Dynamic Release - @echo "Build WebUI Library (Release Dynamic)..." + @echo "Build WebUI Library (Clang Release Dynamic)..." @clang -O3 -fPIC -m64 -o mongoose.o -I "$(INCLUDE)" -c "$(SOURCE)/mongoose.c" @clang -O3 -fPIC -m64 -o webui.o -I "$(INCLUDE)" -c "$(SOURCE)/webui.c" @clang -shared -o webui-2-x64.dyn webui.o mongoose.o diff --git a/examples/C++/hello_world/Linux/Clang/Makefile b/examples/C++/hello_world/Linux/Clang/Makefile index ffeceede..14865a3a 100644 --- a/examples/C++/hello_world/Linux/Clang/Makefile +++ b/examples/C++/hello_world/Linux/Clang/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C++ Example # Linux - Clang diff --git a/examples/C++/hello_world/Linux/GCC/Makefile b/examples/C++/hello_world/Linux/GCC/Makefile index 00097c0c..6fa1e5f5 100644 --- a/examples/C++/hello_world/Linux/GCC/Makefile +++ b/examples/C++/hello_world/Linux/GCC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C++ Example # Linux - GCC diff --git a/examples/C++/hello_world/Windows/GCC/Makefile b/examples/C++/hello_world/Windows/GCC/Makefile index 3257c92a..1e2da473 100644 --- a/examples/C++/hello_world/Windows/GCC/Makefile +++ b/examples/C++/hello_world/Windows/GCC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C++ Example # Windows - GCC diff --git a/examples/C++/hello_world/Windows/MSVC/Makefile b/examples/C++/hello_world/Windows/MSVC/Makefile index f9b12a77..989c4ea5 100644 --- a/examples/C++/hello_world/Windows/MSVC/Makefile +++ b/examples/C++/hello_world/Windows/MSVC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C++ Example # Windows - Microsoft Visual C diff --git a/examples/C++/hello_world/Windows/VS2019/hello_world/hello_world.cpp b/examples/C++/hello_world/Windows/VS2019/hello_world/hello_world.cpp index 52929400..7da9eda2 100644 --- a/examples/C++/hello_world/Windows/VS2019/hello_world/hello_world.cpp +++ b/examples/C++/hello_world/Windows/VS2019/hello_world/hello_world.cpp @@ -1,91 +1,20 @@ +// The C++ wrapper is coming soon. +// Please see the C example for now. - -extern "C" { - #include +extern "C"{ + #include "webui.h" } -// Window struct -webui_window_t* my_window; - -// UI HTML -const char* my_html = "" -"WebUI 2 - C++ Visual Studio Example" -"" -"

WebUI 2 - C++ Visual Studio Example


" -"

" -" - " -""; - -// Check the password function -void check_the_password(webui_event_t* e) { - - // This function get called every time the user click on "MyButton1" - - webui_script_t js; - js.script = " return document.getElementById(\"MyInput\").value; "; - js.timeout = 3; - - // Run the JavaScript on the UI (Web Browser) - webui_script(my_window, &js); - - // Check if there is any JavaScript error - if (js.result.error) { - - printf("JavaScript Error: %s\n", js.result.data); - return; - } - - // Get the password - const char* password = js.result.data; - printf("Password: %s\n", password); - - // Check the password - if (strcmp(password, "123456") == 0) { - - // Correct password - js.script = "alert('Good. Password is correct.')"; - webui_script(my_window, &js); - } - else { - - // Wrong password - js.script = "alert('Sorry. Wrong password.')"; - webui_script(my_window, &js); - } - - // Free data resources - webui_script_cleanup(&js); -} - -void close_the_application(webui_event_t* e) { - - // Close all opened windows - webui_exit(); -} +#include int main() { - // Create a window - my_window = webui_new_window(); - - // Bind HTML elements with functions - webui_bind(my_window, "MyButton1", check_the_password); - webui_bind(my_window, "MyButton2", close_the_application); - - // Show the window - if (!webui_show(my_window, my_html, webui.browser.chrome)) // Run the window on Chrome - webui_show(my_window, my_html, webui.browser.any); // If not, run on any other installed web browser - - // Wait until all windows get closed - webui_wait(); - - return 0; + std::cout << "The C++ wrapper is coming soon. Please see the C examples for now."; + return 0; } -int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, PSTR cmdline, int cmdshow) { - - main(); -} +#if defined(_MSC_VER) + int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, PSTR cmdline, int cmdshow) { + main(); + } +#endif diff --git a/examples/C++/hello_world/Windows/VS2022/hello_world/hello_world.cpp b/examples/C++/hello_world/Windows/VS2022/hello_world/hello_world.cpp index 52929400..7da9eda2 100644 --- a/examples/C++/hello_world/Windows/VS2022/hello_world/hello_world.cpp +++ b/examples/C++/hello_world/Windows/VS2022/hello_world/hello_world.cpp @@ -1,91 +1,20 @@ +// The C++ wrapper is coming soon. +// Please see the C example for now. - -extern "C" { - #include +extern "C"{ + #include "webui.h" } -// Window struct -webui_window_t* my_window; - -// UI HTML -const char* my_html = "" -"WebUI 2 - C++ Visual Studio Example" -"" -"

WebUI 2 - C++ Visual Studio Example


" -"

" -" - " -""; - -// Check the password function -void check_the_password(webui_event_t* e) { - - // This function get called every time the user click on "MyButton1" - - webui_script_t js; - js.script = " return document.getElementById(\"MyInput\").value; "; - js.timeout = 3; - - // Run the JavaScript on the UI (Web Browser) - webui_script(my_window, &js); - - // Check if there is any JavaScript error - if (js.result.error) { - - printf("JavaScript Error: %s\n", js.result.data); - return; - } - - // Get the password - const char* password = js.result.data; - printf("Password: %s\n", password); - - // Check the password - if (strcmp(password, "123456") == 0) { - - // Correct password - js.script = "alert('Good. Password is correct.')"; - webui_script(my_window, &js); - } - else { - - // Wrong password - js.script = "alert('Sorry. Wrong password.')"; - webui_script(my_window, &js); - } - - // Free data resources - webui_script_cleanup(&js); -} - -void close_the_application(webui_event_t* e) { - - // Close all opened windows - webui_exit(); -} +#include int main() { - // Create a window - my_window = webui_new_window(); - - // Bind HTML elements with functions - webui_bind(my_window, "MyButton1", check_the_password); - webui_bind(my_window, "MyButton2", close_the_application); - - // Show the window - if (!webui_show(my_window, my_html, webui.browser.chrome)) // Run the window on Chrome - webui_show(my_window, my_html, webui.browser.any); // If not, run on any other installed web browser - - // Wait until all windows get closed - webui_wait(); - - return 0; + std::cout << "The C++ wrapper is coming soon. Please see the C examples for now."; + return 0; } -int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, PSTR cmdline, int cmdshow) { - - main(); -} +#if defined(_MSC_VER) + int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, PSTR cmdline, int cmdshow) { + main(); + } +#endif diff --git a/examples/C++/hello_world/main.cpp b/examples/C++/hello_world/main.cpp index 43d4bf78..7da9eda2 100644 --- a/examples/C++/hello_world/main.cpp +++ b/examples/C++/hello_world/main.cpp @@ -9,7 +9,7 @@ extern "C"{ int main() { - std::cout << "The C++ wrapper is coming soon. Please see the C example for now."; + std::cout << "The C++ wrapper is coming soon. Please see the C examples for now."; return 0; } diff --git a/examples/C/call_c_from_js/Linux/Clang/Makefile b/examples/C/call_c_from_js/Linux/Clang/Makefile index 0b924d49..62e313f9 100644 --- a/examples/C/call_c_from_js/Linux/Clang/Makefile +++ b/examples/C/call_c_from_js/Linux/Clang/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Linux - Clang diff --git a/examples/C/call_c_from_js/Linux/GCC/Makefile b/examples/C/call_c_from_js/Linux/GCC/Makefile index fc3a0382..098bc492 100644 --- a/examples/C/call_c_from_js/Linux/GCC/Makefile +++ b/examples/C/call_c_from_js/Linux/GCC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Linux - GCC diff --git a/examples/C/call_c_from_js/Windows/GCC/Makefile b/examples/C/call_c_from_js/Windows/GCC/Makefile index 2b2e5124..27f68aa3 100644 --- a/examples/C/call_c_from_js/Windows/GCC/Makefile +++ b/examples/C/call_c_from_js/Windows/GCC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Windows - GCC diff --git a/examples/C/call_c_from_js/Windows/MSVC/Makefile b/examples/C/call_c_from_js/Windows/MSVC/Makefile index 54d29053..671d7a2d 100644 --- a/examples/C/call_c_from_js/Windows/MSVC/Makefile +++ b/examples/C/call_c_from_js/Windows/MSVC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Windows - Microsoft Visual C diff --git a/examples/C/call_c_from_js/Windows/TCC/Makefile b/examples/C/call_c_from_js/Windows/TCC/Makefile index 88fb5a21..5cdb9418 100644 --- a/examples/C/call_c_from_js/Windows/TCC/Makefile +++ b/examples/C/call_c_from_js/Windows/TCC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Windows - Tiny C Compiler diff --git a/examples/C/call_c_from_js/macOS/Clang/Makefile b/examples/C/call_c_from_js/macOS/Clang/Makefile index 36fff9b5..74116ba3 100644 --- a/examples/C/call_c_from_js/macOS/Clang/Makefile +++ b/examples/C/call_c_from_js/macOS/Clang/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # macOS - Clang diff --git a/examples/C/call_c_from_js/main.c b/examples/C/call_c_from_js/main.c index eec41bf8..88f0440c 100644 --- a/examples/C/call_c_from_js/main.c +++ b/examples/C/call_c_from_js/main.c @@ -103,7 +103,7 @@ int main() { ""; // Create a window - webui_window_t* my_window = webui_new_window(); + void* my_window = webui_new_window(); // Bind HTML elements with C functions webui_bind(my_window, "MyID_One", my_function_string); diff --git a/examples/C/call_js_from_c/Linux/Clang/Makefile b/examples/C/call_js_from_c/Linux/Clang/Makefile index 0b924d49..62e313f9 100644 --- a/examples/C/call_js_from_c/Linux/Clang/Makefile +++ b/examples/C/call_js_from_c/Linux/Clang/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Linux - Clang diff --git a/examples/C/call_js_from_c/Linux/GCC/Makefile b/examples/C/call_js_from_c/Linux/GCC/Makefile index fc3a0382..098bc492 100644 --- a/examples/C/call_js_from_c/Linux/GCC/Makefile +++ b/examples/C/call_js_from_c/Linux/GCC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Linux - GCC diff --git a/examples/C/call_js_from_c/Windows/GCC/Makefile b/examples/C/call_js_from_c/Windows/GCC/Makefile index 2b2e5124..27f68aa3 100644 --- a/examples/C/call_js_from_c/Windows/GCC/Makefile +++ b/examples/C/call_js_from_c/Windows/GCC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Windows - GCC diff --git a/examples/C/call_js_from_c/Windows/MSVC/Makefile b/examples/C/call_js_from_c/Windows/MSVC/Makefile index 54d29053..671d7a2d 100644 --- a/examples/C/call_js_from_c/Windows/MSVC/Makefile +++ b/examples/C/call_js_from_c/Windows/MSVC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Windows - Microsoft Visual C diff --git a/examples/C/call_js_from_c/Windows/TCC/Makefile b/examples/C/call_js_from_c/Windows/TCC/Makefile index 88fb5a21..5cdb9418 100644 --- a/examples/C/call_js_from_c/Windows/TCC/Makefile +++ b/examples/C/call_js_from_c/Windows/TCC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Windows - Tiny C Compiler diff --git a/examples/C/call_js_from_c/macOS/Clang/Makefile b/examples/C/call_js_from_c/macOS/Clang/Makefile index 36fff9b5..74116ba3 100644 --- a/examples/C/call_js_from_c/macOS/Clang/Makefile +++ b/examples/C/call_js_from_c/macOS/Clang/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # macOS - Clang diff --git a/examples/C/call_js_from_c/main.c b/examples/C/call_js_from_c/main.c index d96f849e..27b1884f 100644 --- a/examples/C/call_js_from_c/main.c +++ b/examples/C/call_js_from_c/main.c @@ -12,38 +12,28 @@ void my_function_count(webui_event_t* e) { // This function gets called every time the user clicks on "MyButton1" - // Create a WebUI JavaScript struct - webui_script_t MyJavaScript = { - .script = "return GetCount();", - .timeout = 3 - }; + // Create a buffer to hold the response + char response[64]; // Run JavaScript - webui_script(e->window, &MyJavaScript); + if(!webui_script(e->window, "return GetCount();", 0, response, 64)) { - // Check if there is any JavaScript error - if(MyJavaScript.result.error) { - - printf("JavaScript Error: %s\n", MyJavaScript.result.data); + printf("JavaScript Error: %s\n", response); return; } // Get the count - int count = atoi(MyJavaScript.result.data); + int count = atoi(response); // Increment count++; // Generate a JavaScript - char buf[64]; - sprintf(buf, "SetCount(%d);", count); + char js[64]; + sprintf(js, "SetCount(%d);", count); - // Run JavaScript - MyJavaScript.script = buf; - webui_script(e->window, &MyJavaScript); - - // Free data resources - webui_script_cleanup(&MyJavaScript); + // Run JavaScript (Quick Way) + webui_run(e->window, js); } int main() { @@ -88,7 +78,7 @@ int main() { ""; // Create a window - webui_window_t* my_window = webui_new_window(); + void* my_window = webui_new_window(); // Bind HTML elements with C functions webui_bind(my_window, "MyButton1", my_function_count); diff --git a/examples/C/minimal/Linux/Clang/Makefile b/examples/C/minimal/Linux/Clang/Makefile index 0b924d49..62e313f9 100644 --- a/examples/C/minimal/Linux/Clang/Makefile +++ b/examples/C/minimal/Linux/Clang/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Linux - Clang diff --git a/examples/C/minimal/Linux/GCC/Makefile b/examples/C/minimal/Linux/GCC/Makefile index fc3a0382..098bc492 100644 --- a/examples/C/minimal/Linux/GCC/Makefile +++ b/examples/C/minimal/Linux/GCC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Linux - GCC diff --git a/examples/C/minimal/Windows/GCC/Makefile b/examples/C/minimal/Windows/GCC/Makefile index 2b2e5124..27f68aa3 100644 --- a/examples/C/minimal/Windows/GCC/Makefile +++ b/examples/C/minimal/Windows/GCC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Windows - GCC diff --git a/examples/C/minimal/Windows/MSVC/Makefile b/examples/C/minimal/Windows/MSVC/Makefile index 54d29053..671d7a2d 100644 --- a/examples/C/minimal/Windows/MSVC/Makefile +++ b/examples/C/minimal/Windows/MSVC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Windows - Microsoft Visual C diff --git a/examples/C/minimal/Windows/TCC/Makefile b/examples/C/minimal/Windows/TCC/Makefile index 88fb5a21..5cdb9418 100644 --- a/examples/C/minimal/Windows/TCC/Makefile +++ b/examples/C/minimal/Windows/TCC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Windows - Tiny C Compiler diff --git a/examples/C/minimal/macOS/Clang/Makefile b/examples/C/minimal/macOS/Clang/Makefile index ddeba65e..0d77fc79 100644 --- a/examples/C/minimal/macOS/Clang/Makefile +++ b/examples/C/minimal/macOS/Clang/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # macOS - Clang diff --git a/examples/C/minimal/main.c b/examples/C/minimal/main.c index 622ce9f2..a122d7e0 100644 --- a/examples/C/minimal/main.c +++ b/examples/C/minimal/main.c @@ -4,7 +4,7 @@ int main() { - webui_window_t* my_window = webui_new_window(); + void* my_window = webui_new_window(); webui_show(my_window, "Hello"); webui_wait(); return 0; diff --git a/examples/C/serve_a_folder/Linux/Clang/Makefile b/examples/C/serve_a_folder/Linux/Clang/Makefile index 0b924d49..62e313f9 100644 --- a/examples/C/serve_a_folder/Linux/Clang/Makefile +++ b/examples/C/serve_a_folder/Linux/Clang/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Linux - Clang diff --git a/examples/C/serve_a_folder/Linux/GCC/Makefile b/examples/C/serve_a_folder/Linux/GCC/Makefile index fc3a0382..098bc492 100644 --- a/examples/C/serve_a_folder/Linux/GCC/Makefile +++ b/examples/C/serve_a_folder/Linux/GCC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Linux - GCC diff --git a/examples/C/serve_a_folder/Windows/GCC/Makefile b/examples/C/serve_a_folder/Windows/GCC/Makefile index 2b2e5124..27f68aa3 100644 --- a/examples/C/serve_a_folder/Windows/GCC/Makefile +++ b/examples/C/serve_a_folder/Windows/GCC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Windows - GCC diff --git a/examples/C/serve_a_folder/Windows/MSVC/Makefile b/examples/C/serve_a_folder/Windows/MSVC/Makefile index 54d29053..671d7a2d 100644 --- a/examples/C/serve_a_folder/Windows/MSVC/Makefile +++ b/examples/C/serve_a_folder/Windows/MSVC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Windows - Microsoft Visual C diff --git a/examples/C/serve_a_folder/Windows/TCC/Makefile b/examples/C/serve_a_folder/Windows/TCC/Makefile index 88fb5a21..5cdb9418 100644 --- a/examples/C/serve_a_folder/Windows/TCC/Makefile +++ b/examples/C/serve_a_folder/Windows/TCC/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # Windows - Tiny C Compiler diff --git a/examples/C/serve_a_folder/macOS/Clang/Makefile b/examples/C/serve_a_folder/macOS/Clang/Makefile index ddeba65e..0d77fc79 100644 --- a/examples/C/serve_a_folder/macOS/Clang/Makefile +++ b/examples/C/serve_a_folder/macOS/Clang/Makefile @@ -1,4 +1,4 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # C99 Example # macOS - Clang diff --git a/examples/C/serve_a_folder/main.c b/examples/C/serve_a_folder/main.c index 0564f5dd..3d04d1dc 100644 --- a/examples/C/serve_a_folder/main.c +++ b/examples/C/serve_a_folder/main.c @@ -2,8 +2,8 @@ #include "webui.h" -webui_window_t* my_window; -webui_window_t* my_second_window; +void* my_window; +void* my_second_window; void exit_app(webui_event_t* e) { diff --git a/examples/Go/hello_world/README.md b/examples/Go/hello_world/README.md index d2be3bd5..70576145 100644 --- a/examples/Go/hello_world/README.md +++ b/examples/Go/hello_world/README.md @@ -3,5 +3,6 @@ ```sh git clone https://github.com/alifcommunity/webui.git cd webui\examples\Go\hello_world +go clean go build ``` diff --git a/examples/Go/hello_world/go.mod b/examples/Go/hello_world/go.mod index 0e8f4f46..5101f27c 100644 --- a/examples/Go/hello_world/go.mod +++ b/examples/Go/hello_world/go.mod @@ -1,3 +1,3 @@ module github.com/alifcommunity/webui -go 1.19 +go 1.20 diff --git a/examples/Go/hello_world/main.go b/examples/Go/hello_world/main.go index 5be07fef..a931e727 100644 --- a/examples/Go/hello_world/main.go +++ b/examples/Go/hello_world/main.go @@ -52,46 +52,50 @@ const dashboard_html string = `

Welcome !


+ Call Secret() function and get the response +
+
+ +

` -func Exit(e webui.Event) { +func Exit(e webui.Event) string { webui.Exit() + return "" } -func Check(e webui.Event) { +func Secret(e webui.Event) string { - // Script to get the text value - MyScript := webui.JavaScript{ - Timeout: 10, - Script: " return document.getElementById('MyInput').value; ", - } + return "I Love Go!" +} + +func Check(e webui.Event) string { + + // Create new JavaScript object + js := webui.NewJavaScript() // Run the script - webui.RunJavaScript(e.Window, &MyScript) - - // Check if any error - if !MyScript.Error { - - fmt.Printf("Password: %s\n", MyScript.Data) - - // Check the password - if MyScript.Data == "123456" { - - webui.Show(e.Window, dashboard_html) - } else { - - MyScript.Script = " document.getElementById('err').innerHTML = 'Sorry. Wrong password'; " - webui.RunJavaScript(e.Window, &MyScript) - } - } else { + if !webui.Script(e.Window, &js, "return document.getElementById('MyInput').value;") { // There is an error in our script - fmt.Printf("JavaScript Error: %s\n", MyScript.Data) + fmt.Printf("JavaScript Error: %s\n", js.Response) + return "" } + + fmt.Printf("Password: [%s]\n", js.Response) + + // Check the password + if js.Response == "123456" { + webui.Show(e.Window, dashboard_html) + } else { + webui.Script(e.Window, &js, "document.getElementById('err').innerHTML = 'Sorry. Wrong password';") + } + + return "" } func main() { @@ -101,6 +105,7 @@ func main() { // Bind webui.Bind(my_window, "CheckPassword", Check) + webui.Bind(my_window, "Sec", Secret) webui.Bind(my_window, "Exit", Exit) // Show window diff --git a/examples/Go/hello_world/webui/webui.go b/examples/Go/hello_world/webui/webui.go index eec5e0f1..5c60e39f 100644 --- a/examples/Go/hello_world/webui/webui.go +++ b/examples/Go/hello_world/webui/webui.go @@ -1,131 +1,200 @@ /* - WebUI Library 2.1.1 - - http://webui.me - https://github.com/alifcommunity/webui - - Copyright (c) 2020-2023 Hassan Draga. - Licensed under GNU General Public License v2.0. - All rights reserved. - Canada. + WebUI Library 2.2.0 + http://_webui_core.me + https://github.com/alifcommunity/webui + Copyright (c) 2020-2023 Hassan Draga. + Licensed under GNU General Public License v2.0. + All rights reserved. + Canada. */ package webui /* -// [?] Change the library path as you need -#cgo CFLAGS: -I ./ -I ../../../../include -#cgo windows LDFLAGS: -L ./ -L ../../../../build/Windows/GCC/ -L ../../../../build/Windows/MSVC/ -lwebui-2-static-x64 -lws2_32 -#cgo darwin LDFLAGS: -L ./ -L ../../../../build/macOS/GCC/ -L ../../../../build/macOS/Clang/ -lwebui-2-static-x64 -lpthread -lm -#cgo linux LDFLAGS: -L ./ -L ../../../../build/Linux/GCC/ -L ../../../../build/Linux/Clang/ -lwebui-2-static-x64 -lpthread -lm +#cgo CFLAGS: -I ./ +#cgo windows LDFLAGS: -L ./ -lwebui-2-static-x64 -lws2_32 +#cgo darwin LDFLAGS: -L ./ -lwebui-2-static-x64 -lpthread -lm +#cgo linux LDFLAGS: -L ./ -lwebui-2-static-x64 -lpthread -lm #include -extern void webui_go_handler(webui_window_t* _window, unsigned int _element_id, unsigned int _window_id, char* _element_name); -static void webui_bind_go_handler(webui_event_t* e) { - webui_go_handler(e->window, e->element_id, e->window_id, e->element_name); +extern void go_webui_event(void* _window, unsigned int _event_type, char* _element, char* _data, char* _response); +static void go_webui_events_handler(webui_event_t* e) { + go_webui_event(e->window, e->type, e->element, e->data, (char*)&e->response); } -static unsigned int webui_bind_go(webui_window_t* win, const char* element) { - return webui_bind(win, element, webui_bind_go_handler); +static void go_webui_bind(void* win, const char* element) { + webui_bind(win, element, go_webui_events_handler); } */ +import "C" + import ( - "C" + "bytes" + "strconv" + "unsafe" ) -const AnyBrowser uint = 0 -const Chrome uint = 1 -const Firefox uint = 2 -const Edge uint = 3 -const Safari uint = 4 -const Chromium uint = 5 -const Custom uint = 99 - -// Event Struct -type Event struct { - Window *C.webui_window_t - ElementID uint - WindowID uint - ElementName string -} - -// JavaScript Struct -type JavaScript struct { - Timeout uint - Script string - Error bool - Length uint - Data string -} +// Heap +var isIni bool = false // User Go Callback Functions list -var fun_list [64][256]func(Event) +var fun_list map[string]func(Event) string -//export webui_go_handler -func webui_go_handler(_window *C.webui_window_t, _element_id C.uint, _window_id C.uint, _element_name *C.char) { +// Web browsers enum +const AnyBrowser uint = 0 // 0. Default recommended web browser +const Chrome uint = 1 // 1. Google Chrome +const Firefox uint = 2 // 2. Mozilla Firefox +const Edge uint = 3 // 3. Microsoft Edge +const Safari uint = 4 // 4. Apple Safari +const Chromium uint = 5 // 5. The Chromium Project +const Opera uint = 6 // 6. Opera Browser +const Brave uint = 7 // 7. The Brave Browser +const Vivaldi uint = 8 // 8. The Vivaldi Browser +const Epic uint = 9 // 9. The Epic Browser +const Yandex uint = 10 // 10. The Yandex Browser - var window *C.webui_window_t = (*C.webui_window_t)(_window) - var element_id uint = uint(_element_id) - var window_id uint = uint(_window_id) - var element_name string = C.GoString(_element_name) +// Events enum +const WEBUI_EVENT_DISCONNECTED uint = 0 // 0. Window disconnection event +const WEBUI_EVENT_CONNECTED uint = 1 // 1. Window connection event +const WEBUI_EVENT_MULTI_CONNECTION uint = 2 // 2. New window connection event +const WEBUI_EVENT_UNWANTED_CONNECTION uint = 3 // 3. New unwanted window connection event +const WEBUI_EVENT_MOUSE_CLICK uint = 4 // 4. Mouse click event +const WEBUI_EVENT_NAVIGATION uint = 5 // 5. Window navigation event +const WEBUI_EVENT_CALLBACK uint = 6 // 6. Function call event +// Events struct +type Event struct { + Window unsafe.Pointer + EventType uint + Element string + Data string +} + +// JavaScript struct +type JavaScript struct { + Timeout uint + BufferSize uint + Response string +} + +// Initilializing +func Ini() { + if isIni { + return + } + isIni = true + fun_list = make(map[string]func(Event) string) +} + +// JavaScript object constructor +func NewJavaScript() JavaScript { + Ini() + js := JavaScript{ + Timeout: 0, + BufferSize: (1024 * 8), + Response: "", + } + return js +} + +// This function receives all events +// +//export go_webui_event +func go_webui_event(window unsafe.Pointer, _event_type C.uint, _element *C.char, _data *C.char, _response *C.char) { + + Ini() + + // Create a new event struct + var event_type uint = uint(_event_type) + var element string = C.GoString(_element) + var data string = C.GoString(_data) e := Event{ - Window: window, - ElementID: element_id, - WindowID: window_id, - ElementName: element_name, + Window: window, + EventType: event_type, + Element: element, + Data: data, } - fun_list[window_id][element_id](e) -} + // Call user callback function + var window_id uint = uint(C.webui_interface_get_window_id(unsafe.Pointer(window))) + var func_id string = strconv.Itoa(int(window_id)) + element + response := string(fun_list[func_id](e)) -func RunJavaScript(window *C.webui_window_t, js *JavaScript) { - - // Interface - c_js := C.webui_script_interface_t{ - script: C.CString(js.Script), - timeout: 30, // uint(js.Timeout), - error: C.bool(false), - // length: uint(0), - // data: C.CString(nil), + // Set the response back + if len(response) > 0 { + c_response := C.CString(response) + C.webui_interface_set_response(_response, c_response) } - - C.webui_script_interface_struct(window, &c_js) - - js.Error = bool(c_js.error) - js.Data = C.GoString(c_js.data) } -func NewWindow() *C.webui_window_t { +// Run a JavaScript, and get the response back (Make sure your local buffer can hold the response). +func Script(window unsafe.Pointer, js *JavaScript, script string) bool { - return C.webui_new_window() + Ini() + + // Convert the JavaScript from Go-String to C-String + c_script := C.CString(script) + + // Create a local buffer to hold the response + ResponseBuffer := make([]byte, uint64(js.BufferSize)) + + // Create a pointer to the local buffer + ptr := (*C.char)(unsafe.Pointer(&ResponseBuffer[0])) + + // Run the JavaScript and wait for response + status := C.webui_script(window, c_script, C.uint(js.Timeout), ptr, C.size_t(uint64(js.BufferSize))) + + // Copy the response to the users struct + ResponseLen := bytes.IndexByte(ResponseBuffer[:], 0) + js.Response = string(ResponseBuffer[:ResponseLen]) + + // return the status of the JavaScript execution + // True: No JavaScript error. + // False: JavaScript error. + return bool(status) } +// Create a new window object +func NewWindow() unsafe.Pointer { + Ini() + // Create a new window object + // this return a (void pointer) and we should + // never change it. It's only managed by WebUI + return unsafe.Pointer(C.webui_new_window()) +} + +// Close all opened windows func Exit() { - + Ini() C.webui_exit() } -func Show(window *C.webui_window_t, content string) { - +// Show a window using a embedded HTML, or a file. If the window is already opened then it will be refreshed. +func Show(window unsafe.Pointer, content string) { + Ini() c_content := C.CString(content) C.webui_show(window, c_content) } -func Open(window *C.webui_window_t, url string, browser uint) { - - c_url := C.CString(url) - C.webui_open(window, c_url, C.uint(browser)) -} - +// Wait until all opened windows get closed. func Wait() { - + Ini() C.webui_wait() } -func Bind(window *C.webui_window_t, element string, callback func(Event)) { +// Bind a specific html element click event with a function. Empty element means all events. +func Bind(window unsafe.Pointer, element string, callback func(Event) string) { + Ini() + + // Convert element from Go-String to C-String c_element := C.CString(element) - var window_id uint = uint(C._webui_window_get_number(window)) - var cb_index uint = uint(C.webui_bind_go(window, c_element)) + C.go_webui_bind(window, c_element) - fun_list[window_id][cb_index] = callback + // Get the window ID + var window_id uint = uint(C.webui_interface_get_window_id(window)) + + // Generate a unique ID for this element + var func_id string = strconv.Itoa(int(window_id)) + element + + // Add the user callback function to the list + fun_list[func_id] = callback } diff --git a/examples/Go/hello_world/webui/webui.h b/examples/Go/hello_world/webui/webui.h new file mode 100644 index 00000000..07b08828 --- /dev/null +++ b/examples/Go/hello_world/webui/webui.h @@ -0,0 +1,189 @@ +/* + WebUI Library 2.2.0 + http://webui.me + https://github.com/alifcommunity/webui + Copyright (c) 2020-2023 Hassan Draga. + Licensed under GNU General Public License v2.0. + All rights reserved. + Canada. +*/ + +#ifndef _WEBUI_H +#define _WEBUI_H + +#define WEBUI_VERSION "2.2.0" + +// Dynamic Library Exports +#if defined(_MSC_VER) || defined(__TINYC__) + #ifndef WEBUI_EXPORT + #define WEBUI_EXPORT __declspec(dllexport) + #endif +#else + #ifndef WEBUI_EXPORT + #define WEBUI_EXPORT extern + #endif +#endif + +// -- C STD --------------------------- +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__GNUC__) || defined(__TINYC__) + #include +#endif + +// -- Windows ------------------------- +#ifdef _WIN32 + #ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN + #endif + #include + #include + #include + #include + #include + #include + #define WEBUI_GET_CURRENT_DIR _getcwd + #define WEBUI_FILE_EXIST _access + #define WEBUI_POPEN _popen + #define WEBUI_PCLOSE _pclose + #define WEBUI_MAX_PATH MAX_PATH +#endif + +// -- Linux --------------------------- +#ifdef __linux__ + #include + #include + #include + #include + #include + #include + #include + #include + #define WEBUI_GET_CURRENT_DIR getcwd + #define WEBUI_FILE_EXIST access + #define WEBUI_POPEN popen + #define WEBUI_PCLOSE pclose + #define WEBUI_MAX_PATH PATH_MAX +#endif + +// -- Apple --------------------------- +#ifdef __APPLE__ + #include + #include + #include + #include + #include + #include + #include + #include // PATH_MAX + #include + #define WEBUI_GET_CURRENT_DIR getcwd + #define WEBUI_FILE_EXIST access + #define WEBUI_POPEN popen + #define WEBUI_PCLOSE pclose + #define WEBUI_MAX_PATH PATH_MAX +#endif + +// -- Enums --------------------------- +enum webui_browsers { + AnyBrowser = 0, // 0. Default recommended web browser + Chrome, // 1. Google Chrome + Firefox, // 2. Mozilla Firefox + Edge, // 3. Microsoft Edge + Safari, // 4. Apple Safari + Chromium, // 5. The Chromium Project + Opera, // 6. Opera Browser + Brave, // 7. The Brave Browser + Vivaldi, // 8. The Vivaldi Browser + Epic, // 9. The Epic Browser + Yandex, // 10. The Yandex Browser +}; + +enum webui_runtimes { + None = 0, // 0. Prevent WebUI from using any runtime for .js and .ts files + Deno, // 1. Use Deno runtime for .js and .ts files + NodeJS, // 2. Use Nodejs runtime for .js files +}; + +enum webui_events { + WEBUI_EVENT_DISCONNECTED = 0, // 0. Window disconnection event + WEBUI_EVENT_CONNECTED, // 1. Window connection event + WEBUI_EVENT_MULTI_CONNECTION, // 2. New window connection event + WEBUI_EVENT_UNWANTED_CONNECTION, // 3. New unwanted window connection event + WEBUI_EVENT_MOUSE_CLICK, // 4. Mouse click event + WEBUI_EVENT_NAVIGATION, // 5. Window navigation event + WEBUI_EVENT_CALLBACK, // 6. Function call event +}; + +// -- Structs ------------------------- +typedef struct webui_event_t { + void* window; // Pointer to the window object + unsigned int type; // Event type + char* element; // HTML element ID + char* data; // JavaScript data + char* response; // Callback response +} webui_event_t; + +// -- Definitions --------------------- +// Create a new webui window object. +WEBUI_EXPORT void* webui_new_window(void); +// Bind a specific html element click event with a function. Empty element means all events. +WEBUI_EXPORT unsigned int webui_bind(void* window, const char* element, void (*func)(webui_event_t* e)); +// Show a window using a embedded HTML, or a file. If the window is already opened then it will be refreshed. +WEBUI_EXPORT bool webui_show(void* window, const char* content); +// Same as webui_show(). But with a specific web browser. +WEBUI_EXPORT bool webui_show_browser(void* window, const char* content, unsigned int browser); +// Wait until all opened windows get closed. +WEBUI_EXPORT void webui_wait(void); +// Close a specific window. +WEBUI_EXPORT void webui_close(void* window); +// Close all opened windows. webui_wait() will break. +WEBUI_EXPORT void webui_exit(void); + +// -- Other --------------------------- +WEBUI_EXPORT bool webui_is_shown(void* window); +WEBUI_EXPORT void webui_set_timeout(unsigned int second); +WEBUI_EXPORT void webui_set_icon(void* window, const char* icon, const char* type); +WEBUI_EXPORT void webui_set_multi_access(void* window, bool status); + +// -- JavaScript ---------------------- +// Quickly run a JavaScript. +WEBUI_EXPORT bool webui_run(void* window, const char* script); +// Run a JavaScript, and get the response back (Make sure your local buffer can hold the response). +WEBUI_EXPORT bool webui_script(void* window, const char* script, unsigned int timeout, char* buffer, size_t buffer_length); +// Chose between Deno and Nodejs runtime for .js and .ts files. +WEBUI_EXPORT void webui_set_runtime(void* window, unsigned int runtime); +// Parse argument as integer. +WEBUI_EXPORT long long int webui_get_int(webui_event_t* e); +// Parse argument as string. +WEBUI_EXPORT const char* webui_get_string(webui_event_t* e); +// Parse argument as boolean. +WEBUI_EXPORT bool webui_get_bool(webui_event_t* e); +// Return the response to JavaScript as integer. +WEBUI_EXPORT void webui_return_int(webui_event_t* e, long long int n); +// Return the response to JavaScript as string. +WEBUI_EXPORT void webui_return_string(webui_event_t* e, char* s); +// Return the response to JavaScript as boolean. +WEBUI_EXPORT void webui_return_bool(webui_event_t* e, bool b); + +// -- Interface ----------------------- +// Bind a specific html element click event with a function. Empty element means all events. This replace webui_bind(). +WEBUI_EXPORT unsigned int webui_interface_bind(void* window, const char* element, void (*func)(void*, unsigned int, char*, char*, char*)); +// When using `webui_interface_bind()` you need this function to easily set your response. +WEBUI_EXPORT void webui_interface_set_response(char* ptr, const char* response); +// Check if the app still running or not. This replace webui_wait(). +WEBUI_EXPORT bool webui_interface_is_app_running(void); +// Get window unique ID +WEBUI_EXPORT unsigned int webui_interface_get_window_id(void* window); + +#endif /* _WEBUI_H */ diff --git a/packages/PyPI/LICENSE b/examples/Python/PyPI/Package/LICENSE similarity index 100% rename from packages/PyPI/LICENSE rename to examples/Python/PyPI/Package/LICENSE diff --git a/packages/PyPI/README.md b/examples/Python/PyPI/Package/README.md similarity index 100% rename from packages/PyPI/README.md rename to examples/Python/PyPI/Package/README.md diff --git a/packages/PyPI/pyproject.toml b/examples/Python/PyPI/Package/pyproject.toml similarity index 97% rename from packages/PyPI/pyproject.toml rename to examples/Python/PyPI/Package/pyproject.toml index 8f5e1e43..805d597e 100644 --- a/packages/PyPI/pyproject.toml +++ b/examples/Python/PyPI/Package/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "webui2" -version = "2.1.1" +version = "2.2.0" authors = [ { name="Hassan Draga" }, ] diff --git a/packages/PyPI/src/webui/__init__.py b/examples/Python/PyPI/Package/src/webui/__init__.py similarity index 100% rename from packages/PyPI/src/webui/__init__.py rename to examples/Python/PyPI/Package/src/webui/__init__.py diff --git a/examples/Python/PyPI/Package/src/webui/webui-2-x64.dll b/examples/Python/PyPI/Package/src/webui/webui-2-x64.dll new file mode 100644 index 00000000..4dcaca02 Binary files /dev/null and b/examples/Python/PyPI/Package/src/webui/webui-2-x64.dll differ diff --git a/packages/PyPI/src/webui/webui.py b/examples/Python/PyPI/Package/src/webui/webui.py similarity index 99% rename from packages/PyPI/src/webui/webui.py rename to examples/Python/PyPI/Package/src/webui/webui.py index 9f92bf09..fc3db472 100644 --- a/packages/PyPI/src/webui/webui.py +++ b/examples/Python/PyPI/Package/src/webui/webui.py @@ -1,5 +1,5 @@ -# WebUI Library 2.1.1 +# WebUI Library 2.2.0 # # http://webui.me # https://github.com/alifcommunity/webui @@ -26,7 +26,6 @@ PTR_PTR_CHAR = ctypes.POINTER(PTR_CHAR) # Event class event: - element_id = 0 window_id = 0 element_name = "" data = "" @@ -137,7 +136,7 @@ class window: err_library_not_found('bind') return cb_index = int( - WebUI.webui_bind_interface( + WebUI.webui_interface_bind( self.window, element.encode('utf-8'), self.c_events)) diff --git a/packages/README.md b/examples/Python/PyPI/README.md similarity index 100% rename from packages/README.md rename to examples/Python/PyPI/README.md diff --git a/examples/Python/dev/main.py b/examples/Python/PyPI/test_package.py similarity index 95% rename from examples/Python/dev/main.py rename to examples/Python/PyPI/test_package.py index 323e9b4b..03a1f24a 100644 --- a/examples/Python/dev/main.py +++ b/examples/Python/PyPI/test_package.py @@ -1,12 +1,12 @@ # This script is for debugging & development of the WebUI Python wrapper -# The source code is located at 'webui/packages/PyPI/src/webui/webui.py' +# The source code is located at 'webui/examples/Python/PyPI/Package/src/webui/webui.py' # [!] Make sure to remove the WebUI package # pip uninstall webui2 # Import the WebUI local module import sys -sys.path.append('../../../packages/PyPI/src/webui') +sys.path.append('Package/src/webui') import webui # Use the local WebUI Dynamic lib diff --git a/examples/Python/hello_world/main.py b/examples/Python/hello_world.py similarity index 100% rename from examples/Python/hello_world/main.py rename to examples/Python/hello_world.py diff --git a/examples/Python/minimal/main.py b/examples/Python/minimal.py similarity index 100% rename from examples/Python/minimal/main.py rename to examples/Python/minimal.py diff --git a/examples/Python/serve_folder/second.html b/examples/Python/second.html similarity index 100% rename from examples/Python/serve_folder/second.html rename to examples/Python/second.html diff --git a/examples/Python/serve_folder/index.html b/examples/Python/serve_folder.html similarity index 100% rename from examples/Python/serve_folder/index.html rename to examples/Python/serve_folder.html diff --git a/examples/Python/serve_folder/main.py b/examples/Python/serve_folder.py similarity index 100% rename from examples/Python/serve_folder/main.py rename to examples/Python/serve_folder.py diff --git a/examples/Rust/README.md b/examples/Rust/README.md new file mode 100644 index 00000000..0d607412 --- /dev/null +++ b/examples/Rust/README.md @@ -0,0 +1,13 @@ +# WebUI Examples - Rust + +# NOTICE: + +## The Rust wrapper still needs to be completed. You can finish it and submit the patch as PR. Or, if you prefer to maintain it in your repo, you can create a new issue and submit the link to your Rust wrapper repo. Thank you. + +```sh +git clone https://github.com/alifcommunity/webui.git +cd webui\examples\Rust\hello_world +cargo clean +cargo build +cargo run +``` diff --git a/examples/Rust/hello_world/Cargo.toml b/examples/Rust/hello_world/Cargo.toml index 3789e707..63d82297 100644 --- a/examples/Rust/hello_world/Cargo.toml +++ b/examples/Rust/hello_world/Cargo.toml @@ -2,5 +2,7 @@ name = "my-webui-application" version = "0.1.0" build = "build.rs" +edition = "2021" [dependencies] +lazy_static = "1.4.0" diff --git a/examples/Rust/hello_world/src/Webui.rs b/examples/Rust/hello_world/src/Webui.rs index 8f20d8b9..551239d9 100644 --- a/examples/Rust/hello_world/src/Webui.rs +++ b/examples/Rust/hello_world/src/Webui.rs @@ -1,13 +1,11 @@ /* - WebUI Library 2.1.1 - - http://webui.me - https://github.com/alifcommunity/webui - - Copyright (c) 2020-2023 Hassan Draga. - Licensed under GNU General Public License v2.0. - All rights reserved. - Canada. + WebUI Library 2.2.0 + http://_webui_core.me + https://github.com/alifcommunity/webui + Copyright (c) 2020-2023 Hassan Draga. + Licensed under GNU General Public License v2.0. + All rights reserved. + Canada. */ // Flags @@ -22,484 +20,238 @@ use std::os::raw::c_char; use std::ffi::CString; use std::ffi::CStr; +use std::collections::HashMap; +use lazy_static::lazy_static; +use std::sync::Mutex; -// --[WebUI Library References]-------- -pub type size_t = ::std::os::raw::c_ulonglong; -pub const WEBUI_MAX_ARRAY: u32 = 32; -pub const __bool_true_false_are_defined: u32 = 1; +// Consts pub const true_: u32 = 1; pub const false_: u32 = 0; +pub const __bool_true_false_are_defined: u32 = 1; +pub type size_t = ::std::os::raw::c_ulong; +pub type wchar_t = ::std::os::raw::c_int; + +// Event struct #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct webui_event_t { - pub window_id: ::std::os::raw::c_uint, - pub element_id: ::std::os::raw::c_uint, - pub element_name: *mut ::std::os::raw::c_char, - pub window: *mut webui_window_t, + pub window: *mut ::std::os::raw::c_void, + pub type_: ::std::os::raw::c_uint, + pub element: *mut ::std::os::raw::c_char, pub data: *mut ::std::os::raw::c_char, - pub data_len: ::std::os::raw::c_uint, + pub response: *mut ::std::os::raw::c_char, } -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct webui_window_core_t { - pub window_number: ::std::os::raw::c_uint, - pub server_running: bool, - pub connected: bool, - pub server_handled: bool, - pub multi_access: bool, - pub server_root: bool, - pub server_port: ::std::os::raw::c_uint, - pub url: *mut ::std::os::raw::c_char, - pub html: *const ::std::os::raw::c_char, - pub html_cpy: *const ::std::os::raw::c_char, - pub icon: *const ::std::os::raw::c_char, - pub icon_type: *const ::std::os::raw::c_char, - pub CurrentBrowser: ::std::os::raw::c_uint, - pub browser_path: *mut ::std::os::raw::c_char, - pub profile_path: *mut ::std::os::raw::c_char, - pub connections: ::std::os::raw::c_uint, - pub runtime: ::std::os::raw::c_uint, - pub detect_process_close: bool, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct webui_window_t { - pub core: webui_window_core_t, - pub path: *mut ::std::os::raw::c_char, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct webui_javascript_result_t { - pub error: bool, - pub length: ::std::os::raw::c_uint, - pub data: *const ::std::os::raw::c_char, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct webui_script_t { - pub script: *const ::std::os::raw::c_char, - pub timeout: ::std::os::raw::c_uint, - pub result: webui_javascript_result_t, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct webui_cb_t { - pub win: *mut webui_window_t, - pub webui_internal_id: *mut ::std::os::raw::c_char, - pub element_name: *mut ::std::os::raw::c_char, - pub data: *mut ::std::os::raw::c_char, - pub data_len: ::std::os::raw::c_uint, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct webui_cmd_async_t { - pub win: *mut webui_window_t, - pub cmd: *mut ::std::os::raw::c_char, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct webui_custom_browser_t { - pub app: *mut ::std::os::raw::c_char, - pub arg: *mut ::std::os::raw::c_char, - pub auto_link: bool, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct webui_browser_t { - pub any: ::std::os::raw::c_uint, - pub chrome: ::std::os::raw::c_uint, - pub firefox: ::std::os::raw::c_uint, - pub edge: ::std::os::raw::c_uint, - pub safari: ::std::os::raw::c_uint, - pub chromium: ::std::os::raw::c_uint, - pub opera: ::std::os::raw::c_uint, - pub brave: ::std::os::raw::c_uint, - pub vivaldi: ::std::os::raw::c_uint, - pub epic: ::std::os::raw::c_uint, - pub yandex: ::std::os::raw::c_uint, - pub current: ::std::os::raw::c_uint, - pub custom: ::std::os::raw::c_uint, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct webui_runtime_t { - pub none: ::std::os::raw::c_uint, - pub deno: ::std::os::raw::c_uint, - pub nodejs: ::std::os::raw::c_uint, -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct webui_t { - pub servers: ::std::os::raw::c_uint, - pub connections: ::std::os::raw::c_uint, - pub custom_browser: *mut webui_custom_browser_t, - pub wait_for_socket_window: bool, - pub html_elements: [*mut ::std::os::raw::c_char; 32usize], - pub used_ports: [::std::os::raw::c_uint; 32usize], - pub last_window: ::std::os::raw::c_uint, - pub startup_timeout: ::std::os::raw::c_uint, - pub use_timeout: bool, - pub timeout_extra: bool, - pub exit_now: bool, - pub run_responses: [*const ::std::os::raw::c_char; 32usize], - pub run_done: [bool; 32usize], - pub run_error: [bool; 32usize], - pub run_last_id: ::std::os::raw::c_uint, - pub browser: webui_browser_t, - pub runtime: webui_runtime_t, - pub initialized: bool, - pub cb: [::std::option::Option; - 32usize], - pub cb_interface: [::std::option::Option< - unsafe extern "C" fn( - element_id: ::std::os::raw::c_uint, - window_id: ::std::os::raw::c_uint, - element_name: *mut ::std::os::raw::c_char, - window: *mut webui_window_t, - ), - >; 32usize], - pub executable_path: *mut ::std::os::raw::c_char, - pub ptr_list: [*mut ::std::os::raw::c_void; 32usize], - pub ptr_position: ::std::os::raw::c_uint, - pub ptr_size: [usize; 32usize], + +// References + +extern "C" { + pub fn webui_new_window() -> *mut ::std::os::raw::c_void; } extern "C" { - pub static mut webui: webui_t; + pub fn webui_bind( + window: *mut ::std::os::raw::c_void, + element: *const ::std::os::raw::c_char, + func: ::std::option::Option + ) -> ::std::os::raw::c_uint; +} +extern "C" { + pub fn webui_show( + window: *mut ::std::os::raw::c_void, + content: *const ::std::os::raw::c_char + ) -> bool; +} +extern "C" { + pub fn webui_show_browser( + window: *mut ::std::os::raw::c_void, + content: *const ::std::os::raw::c_char, + browser: ::std::os::raw::c_uint + ) -> bool; } extern "C" { pub fn webui_wait(); } +extern "C" { + pub fn webui_close(window: *mut ::std::os::raw::c_void); +} extern "C" { pub fn webui_exit(); } extern "C" { - pub fn webui_is_any_window_running() -> bool; + pub fn webui_is_shown(window: *mut ::std::os::raw::c_void) -> bool; } extern "C" { pub fn webui_set_timeout(second: ::std::os::raw::c_uint); } -extern "C" { - pub fn webui_new_window() -> *mut webui_window_t; -} -extern "C" { - pub fn webui_show( - win: *mut webui_window_t, - content: *const ::std::os::raw::c_char - ) -> bool; -} extern "C" { pub fn webui_set_icon( - win: *mut webui_window_t, - icon_s: *const ::std::os::raw::c_char, - type_s: *const ::std::os::raw::c_char, + window: *mut ::std::os::raw::c_void, + icon: *const ::std::os::raw::c_char, + type_: *const ::std::os::raw::c_char ); } extern "C" { - pub fn webui_multi_access(win: *mut webui_window_t, status: bool); + pub fn webui_set_multi_access(window: *mut ::std::os::raw::c_void, status: bool); } extern "C" { - pub fn _webui_set_root_folder( - win: *mut webui_window_t, - path: *const ::std::os::raw::c_char, + pub fn webui_run( + window: *mut ::std::os::raw::c_void, + script: *const ::std::os::raw::c_char ) -> bool; } -extern "C" { - pub fn webui_new_server( - win: *mut webui_window_t, - path: *const ::std::os::raw::c_char, - ) -> *const ::std::os::raw::c_char; -} -extern "C" { - pub fn webui_close(win: *mut webui_window_t); -} -extern "C" { - pub fn webui_is_shown(win: *mut webui_window_t) -> bool; -} extern "C" { pub fn webui_script( - win: *mut webui_window_t, - script: *mut webui_script_t, - ); -} -extern "C" { - pub fn webui_bind( - win: *mut webui_window_t, - element: *const ::std::os::raw::c_char, - func: ::std::option::Option< - // unsafe extern "C" fn(e: *mut webui_event_t), - unsafe fn(e: *mut webui_event_t), - >, - ) -> ::std::os::raw::c_uint; -} -extern "C" { - pub fn webui_open( - win: *mut webui_window_t, - url: *const ::std::os::raw::c_char, - browser: ::std::os::raw::c_uint, - ) -> bool; -} -extern "C" { - pub fn webui_script_cleanup(script: *mut webui_script_t); -} -extern "C" { - pub fn webui_script_runtime( - win: *mut webui_window_t, - runtime: ::std::os::raw::c_uint, - ); -} -extern "C" { - pub fn _webui_wait_process(win: *mut webui_window_t, status: bool); -} -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct webui_script_interface_t { - pub script: *mut ::std::os::raw::c_char, - pub timeout: ::std::os::raw::c_uint, - pub error: bool, - pub length: ::std::os::raw::c_uint, - pub data: *const ::std::os::raw::c_char, -} -extern "C" { - pub fn webui_bind_interface( - win: *mut webui_window_t, - element: *const ::std::os::raw::c_char, - func: ::std::option::Option< - // unsafe extern "C" fn( - // element_id: ::std::os::raw::c_uint, - // window_id: ::std::os::raw::c_uint, - // element_name: *mut ::std::os::raw::c_char, - // window: *mut webui_window_t, - // ), - unsafe fn( - element_id: ::std::os::raw::c_uint, - window_id: ::std::os::raw::c_uint, - element_name: *mut ::std::os::raw::c_char, - window: *mut webui_window_t, - // TODO: Add char*, char** - ), - >, - ) -> ::std::os::raw::c_uint; -} -extern "C" { - pub fn webui_script_interface( - win: *mut webui_window_t, + window: *mut ::std::os::raw::c_void, script: *const ::std::os::raw::c_char, timeout: ::std::os::raw::c_uint, - error: *mut bool, - length: *mut ::std::os::raw::c_uint, - data: *mut ::std::os::raw::c_char, // TODO: Change this from char* to char** - ); + buffer: *mut ::std::os::raw::c_char, + buffer_length: size_t + ) -> bool; } extern "C" { - pub fn webui_script_interface_struct( - win: *mut webui_window_t, - //js_int: *mut webui_script_interface_t, - js_int: &webui_script_interface_t, - ); + pub fn webui_set_runtime(window: *mut ::std::os::raw::c_void, runtime: ::std::os::raw::c_uint); } extern "C" { - pub fn _webui_init(); + pub fn webui_get_int(e: *mut webui_event_t) -> ::std::os::raw::c_longlong; } extern "C" { - pub fn _webui_get_cb_index( - webui_internal_id: *mut ::std::os::raw::c_char, + pub fn webui_get_string(e: *mut webui_event_t) -> *const ::std::os::raw::c_char; +} +extern "C" { + pub fn webui_get_bool(e: *mut webui_event_t) -> bool; +} +extern "C" { + pub fn webui_return_int(e: *mut webui_event_t, n: ::std::os::raw::c_longlong); +} +extern "C" { + pub fn webui_return_string(e: *mut webui_event_t, s: *mut ::std::os::raw::c_char); +} +extern "C" { + pub fn webui_return_bool(e: *mut webui_event_t, b: bool); +} +extern "C" { + pub fn webui_interface_bind( + window: *mut ::std::os::raw::c_void, + element: *const ::std::os::raw::c_char, + func: ::std::option::Option< + unsafe fn( + arg1: *mut ::std::os::raw::c_void, + arg2: ::std::os::raw::c_uint, + arg3: *mut ::std::os::raw::c_char, + arg4: *mut ::std::os::raw::c_char, + arg5: *mut ::std::os::raw::c_char + ) + > ) -> ::std::os::raw::c_uint; } extern "C" { - pub fn _webui_set_cb_index( - webui_internal_id: *mut ::std::os::raw::c_char, + pub fn webui_interface_set_response( + ptr: *mut ::std::os::raw::c_char, + response: *const ::std::os::raw::c_char + ); +} +extern "C" { + pub fn webui_interface_is_app_running() -> bool; +} +extern "C" { + pub fn webui_interface_get_window_id( + window: *mut ::std::os::raw::c_void ) -> ::std::os::raw::c_uint; } -extern "C" { - pub fn _webui_get_free_port() -> ::std::os::raw::c_uint; -} -extern "C" { - pub fn _webui_get_new_window_number() -> ::std::os::raw::c_uint; -} -extern "C" { - pub fn _webui_wait_for_startup(); -} -extern "C" { - pub fn _webui_free_port(port: ::std::os::raw::c_uint); -} -extern "C" { - pub fn _webui_set_custom_browser(p: *mut webui_custom_browser_t); -} -extern "C" { - pub fn _webui_get_current_path() -> *mut ::std::os::raw::c_char; -} -extern "C" { - pub fn _webui_window_receive( - win: *mut webui_window_t, - packet: *const ::std::os::raw::c_char, - len: usize, - ); -} -extern "C" { - pub fn _webui_window_send( - win: *mut webui_window_t, - packet: *mut ::std::os::raw::c_char, - packets_size: usize, - ); -} -extern "C" { - pub fn _webui_window_event( - win: *mut webui_window_t, - element_id: *mut ::std::os::raw::c_char, - element: *mut ::std::os::raw::c_char, - data: *mut ::std::os::raw::c_char, - data_len: ::std::os::raw::c_uint, - ); -} -extern "C" { - pub fn _webui_window_get_number( - win: *mut webui_window_t, - ) -> ::std::os::raw::c_uint; -} -extern "C" { - pub fn _webui_window_open( - win: *mut webui_window_t, - link: *mut ::std::os::raw::c_char, - browser: ::std::os::raw::c_uint, - ); -} -extern "C" { - pub fn _webui_cmd_sync( - cmd: *mut ::std::os::raw::c_char, - show: bool, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn _webui_cmd_async( - cmd: *mut ::std::os::raw::c_char, - show: bool, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn _webui_run_browser( - win: *mut webui_window_t, - cmd: *mut ::std::os::raw::c_char, - ) -> ::std::os::raw::c_int; -} -extern "C" { - pub fn _webui_clean(); -} -extern "C" { - pub fn _webui_browser_exist( - win: *mut webui_window_t, - browser: ::std::os::raw::c_uint, - ) -> bool; -} -extern "C" { - pub fn _webui_browser_get_temp_path( - browser: ::std::os::raw::c_uint, - ) -> *const ::std::os::raw::c_char; -} -extern "C" { - pub fn _webui_folder_exist(folder: *mut ::std::os::raw::c_char) -> bool; -} -extern "C" { - pub fn _webui_browser_create_profile_folder( - win: *mut webui_window_t, - browser: ::std::os::raw::c_uint, - ) -> bool; -} -extern "C" { - pub fn _webui_browser_start_edge( - win: *mut webui_window_t, - address: *const ::std::os::raw::c_char, - ) -> bool; -} -extern "C" { - pub fn _webui_browser_start_firefox( - win: *mut webui_window_t, - address: *const ::std::os::raw::c_char, - ) -> bool; -} -extern "C" { - pub fn _webui_browser_start_custom( - win: *mut webui_window_t, - address: *const ::std::os::raw::c_char, - ) -> bool; -} -extern "C" { - pub fn _webui_browser_start_chrome( - win: *mut webui_window_t, - address: *const ::std::os::raw::c_char, - ) -> bool; -} -extern "C" { - pub fn _webui_browser_start( - win: *mut webui_window_t, - address: *const ::std::os::raw::c_char, - browser: ::std::os::raw::c_uint, - ) -> bool; -} -extern "C" { - pub fn _webui_system_win32( - cmd: *mut ::std::os::raw::c_char, - show: bool, - ) -> ::std::os::raw::c_int; -} - -// --[Tools]--------------------------- - -// fn char_to_string(c : *mut ::std::os::raw::c_char) -> String { -// let cstr = unsafe {CStr::from_ptr(c)}; -// let s : String = String::from_utf8_lossy(cstr.to_bytes()).to_string(); -// return s; -// } - -fn char_to_string(c : *const i8) -> String { - - let cstr = unsafe {CStr::from_ptr(c)}; - let s : String = String::from_utf8_lossy(cstr.to_bytes()).to_string(); - return s; -} - -fn cstr_to_string(c : CString) -> String { - - let s : String = String::from_utf8_lossy(c.to_bytes()).to_string(); - return s; -} // --[Wrapper]------------------------- +// Browsers pub const AnyBrowser: u32 = 0; pub const Chrome: u32 = 1; pub const Firefox: u32 = 2; pub const Edge: u32 = 3; pub const Safari: u32 = 4; pub const Chromium: u32 = 5; -pub const opera: u32 = 6; -pub const brave: u32 = 7; -pub const vivaldi: u32 = 8; -pub const epic: u32 = 9; -pub const yandex: u32 = 10; -pub const current: u32 = 0; -pub const Custom: u32 = 99; +pub const Opera: u32 = 6; +pub const Brave: u32 = 7; +pub const Vivaldi: u32 = 8; +pub const Epic: u32 = 9; +pub const Yandex: u32 = 10; + +// Runtimes +pub const None: u32 = 0; +pub const Deno: u32 = 1; +pub const NodeJS: u32 = 2; + +// Events +pub const WEBUI_EVENT_DISCONNECTED: u32 = 0; +pub const WEBUI_EVENT_CONNECTED: u32 = 1; +pub const WEBUI_EVENT_MULTI_CONNECTION: u32 = 2; +pub const WEBUI_EVENT_UNWANTED_CONNECTION: u32 = 3; +pub const WEBUI_EVENT_MOUSE_CLICK: u32 = 4; +pub const WEBUI_EVENT_NAVIGATION: u32 = 5; +pub const WEBUI_EVENT_CALLBACK: u32 = 6; pub struct JavaScript { pub timeout: u32, - pub script: String, - pub error: bool, - pub data: String, + pub script: String, + pub error: bool, + pub data: String, } pub struct Event { - pub ElementId: u32, - pub WindowId: u32, - pub ElementName: String, - pub Window: *mut webui_window_t, + pub Window: *mut ::std::os::raw::c_void, + pub EventType: u32, + pub Element: String, + pub Data: String, } // List of Rust user functions (2-dimensional array) -static mut func_list: [[Option:: ()>; 64]; 64] = [[None; 64]; 64]; +// static mut func_list: [[Option:: ()>; 64]; 64] = [[64; 64]; 64]; +// static mut func_array: Vec> = vec![vec![]; 1024]; +// static mut elements_map = HashMap::::new(); +// static mut elements_map: HashMap::new(); -pub fn RunJavaScript(win: *mut webui_window_t, js: &mut JavaScript) { +type FunctionType = fn(Event); +const ROWS: usize = 64; +const COLS: usize = 64; +static mut GLOBAL_ARRAY: Option<[[Option; COLS]; ROWS]> = None; +lazy_static! { + static ref elements_map: HashMap = HashMap::new(); + // static mut func_array: Vec> = vec![vec![]; 1024]; +} + +// Save a string in the map and return its index +fn save_string(map: &mut HashMap, s: &str) -> usize { + // Check if the string already exists in the map + if let Some(&index) = map.get(s) { + return index; + } + + // If the string does not exist, add it to the map and return the new index + let index = map.len(); + map.insert(s.to_owned(), index); + index +} + +// Search for a string in the map and return its index if found, or -1 if not found +fn find_string(map: &HashMap, s: &str) -> isize { + if let Some(&index) = map.get(s) { + index as isize + } else { + -1 + } +} + +fn char_to_string(c: *const i8) -> String { + let cstr = unsafe { CStr::from_ptr(c) }; + let s: String = String::from_utf8_lossy(cstr.to_bytes()).to_string(); + return s; +} + +fn cstr_to_string(c: CString) -> String { + let s: String = String::from_utf8_lossy(c.to_bytes()).to_string(); + return s; +} + +pub fn RunJavaScript(win: *mut ::std::os::raw::c_void, js: &mut JavaScript) { unsafe { - // Script String to i8/u8 let script_cpy = String::from(js.script.clone()); let script_c_str = CString::new(script_cpy).unwrap(); @@ -508,46 +260,41 @@ pub fn RunJavaScript(win: *mut webui_window_t, js: &mut JavaScript) { let script: webui_script_interface_t = webui_script_interface_t { timeout: js.timeout, script: script_c_char as *mut i8, - data: script_c_char, // 'data' TODO: Should be null + data: script_c_char, error: false, length: 0, }; + // deprecated webui_script_interface_struct(win, &script); + // TODO: `webui_script_interface_struct` is deprecated. use `webui_script` instead. js.error = script.error; js.data = char_to_string(script.data); } } -pub fn NewWindow() -> *mut webui_window_t { - +pub fn NewWindow() -> *mut ::std::os::raw::c_void { unsafe { - + GLOBAL_ARRAY = Some([[None; COLS]; ROWS]); return webui_new_window(); } } pub fn Wait() { - unsafe { - webui_wait(); } } pub fn Exit() { - unsafe { - webui_exit(); } } -pub fn Show(win: *mut webui_window_t, content: &str) -> bool { - +pub fn Show(win: *mut ::std::os::raw::c_void, content: &str) -> bool { unsafe { - // Content String to i8/u8 let content_c_str = CString::new(content).unwrap(); let content_c_char: *const c_char = content_c_str.as_ptr() as *const c_char; @@ -556,47 +303,75 @@ pub fn Show(win: *mut webui_window_t, content: &str) -> bool { } } -fn events_handler (element_id: ::std::os::raw::c_uint, window_id: ::std::os::raw::c_uint, element_name: *mut ::std::os::raw::c_char, window: *mut webui_window_t) { +fn events_handler( + _window: *mut ::std::os::raw::c_void, + _event_type: ::std::os::raw::c_uint, + _element: *mut ::std::os::raw::c_char, + _data: *mut ::std::os::raw::c_char, + _response: *mut ::std::os::raw::c_char +) { + let Window: *mut ::std::os::raw::c_void = _window; + let EventType: u32 = _event_type; + let Element: String = char_to_string(_element); + let Data: String = char_to_string(_data); - let ElementId: u32 = element_id; - let WindowId: u32 = window_id; - let ElementName: String = char_to_string(element_name); - let Window: *mut webui_window_t = window; + let element_index = find_string(&elements_map, &Element); + if element_index < 0 { + return; + } let E = Event { - ElementId: ElementId, - WindowId: WindowId, - ElementName: ElementName, Window: Window, + EventType: EventType, + Element: Element, + Data: Data, }; // Call the Rust user function - let WindowId_64 = WindowId as usize; - let ElementId_64 = ElementId as usize; + let window_id = webui_interface_get_window_id(_window); + let window_id_64 = window_id as usize; + let element_index_64 = element_index as usize; unsafe { - (func_list[WindowId_64][ElementId_64]).expect("non-null function pointer")(E); + // func_list[window_id_64][element_index_64].expect("non-null function pointer")(E); + // func_array[window_id_64][element_index_64](E); + // if let Some(func) = GLOBAL_ARRAY[window_id_64][element_index_64] { + // func(E.clone()); + // } + if let Some(func) = GLOBAL_ARRAY.as_ref().unwrap()[window_id_64][element_index_64] { + func(E); + } } } -pub fn Bind(win: *mut webui_window_t, element: &str, func: fn(e: Event)) { - +pub fn Bind(win: *mut ::std::os::raw::c_void, element: &str, func: fn(Event)) { // Element String to i8/u8 let element_c_str = CString::new(element).unwrap(); let element_c_char: *const c_char = element_c_str.as_ptr() as *const c_char; // Bind unsafe { - - let f: Option = Some(events_handler); - - let window_id: ::std::os::raw::c_uint = _webui_window_get_number(win); - let cb_index: ::std::os::raw::c_uint = webui_bind_interface(win, element_c_char, f); + let f: Option< + unsafe fn( + _window: *mut ::std::os::raw::c_void, + _event_type: ::std::os::raw::c_uint, + _element: *mut ::std::os::raw::c_char, + _data: *mut ::std::os::raw::c_char, + _response: *mut ::std::os::raw::c_char + ) + > = Some(events_handler); + let element_index = save_string(&mut elements_map, element); + let window_id = webui_interface_get_window_id(win); let window_id_64 = window_id as usize; - let cb_index_64 = cb_index as usize; + let element_index_64 = element_index as usize; + + webui_interface_bind(win, element_c_char, f); // Add the Rust user function to the list - let user_cb: Option = Some(func); - func_list[window_id_64][cb_index_64] = user_cb; + // let user_cb: Option = Some(func); + // func_list[window_id_64][element_index_64] = user_cb; + // func_array[window_id_64][element_index_64] = func; + // GLOBAL_ARRAY[window_id_64][element_index_64] = Some(func as FunctionType); + GLOBAL_ARRAY.as_mut().unwrap()[window_id_64][element_index_64] = Some(func as FunctionType); } } diff --git a/examples/Rust/hello_world/src/main.rs b/examples/Rust/hello_world/src/main.rs index 56ce0839..2610e714 100644 --- a/examples/Rust/hello_world/src/main.rs +++ b/examples/Rust/hello_world/src/main.rs @@ -1,5 +1,3 @@ - - mod Webui; fn close_the_application (_e: Webui::Event) { diff --git a/examples/TypeScript/Deno/README.md b/examples/TypeScript/Deno/README.md index 50fb57ca..31d85b13 100644 --- a/examples/TypeScript/Deno/README.md +++ b/examples/TypeScript/Deno/README.md @@ -1,7 +1,24 @@ # WebUI Examples - Deno -WebUI can create a web server and use [Deno](https://deno.land/) runtimes to parse `TypeScript` and `JavaScript` files. Another way is simply create a web server using Deno and run a simple system command to run a basic WebUI application to open a window. +This is an example of how to use the WebUI dynamic library in Deno to create an HTML5 based user interface. Tested using Deno v1.32.3 (v8 11.2.214.9, typescript 5.0.3). + +### Deno Hello World + +This example shows how to use the WebUI Dynamic Library in Deno. + + 1. Download and [Install Deno](https://github.com/denoland/deno/releases) (*Or just copy deno binary file into this folder*) + 2. Download WebUI pre-built Library (*[webui.me](https://webui.me/)*) + 3. Run `deno run --allow-all --unstable hello_world.ts` + +Folder structure example (*Windows*) + + [My Folder] + * deno + * hello_world.ts + * module/webui.ts + * module/`webui-2-x64` library + diff --git a/examples/TypeScript/Deno/deno_server/README.md b/examples/TypeScript/Deno/deno_server/README.md deleted file mode 100644 index 25e888dd..00000000 --- a/examples/TypeScript/Deno/deno_server/README.md +++ /dev/null @@ -1,16 +0,0 @@ -### Deno Server - -This example shows how to show a simple WebUI window using a Deno-Web-Server. - - 1. Download and [Install Deno](https://github.com/denoland/deno/releases) (*Or just copy deno binary file into this folder*) - 2. Download WebUI pre-built Library (*[webui.me](https://webui.me/)*) - 3. Run `deno run --allow-all --unstable deno_server.ts` - -Folder structure example (*Windows*) - - [My Folder] - * deno.exe (if not installed) - * deno_server.ts --> Import webui.ts -> Load `webui-2-x64` library - * file1.js - * file2.ts - * ... diff --git a/examples/TypeScript/Deno/deno_server/deno_server.ts b/examples/TypeScript/Deno/deno_server/deno_server.ts deleted file mode 100644 index dd5d0fff..00000000 --- a/examples/TypeScript/Deno/deno_server/deno_server.ts +++ /dev/null @@ -1,40 +0,0 @@ - - -// Run this script by: -// deno run --allow-all --unstable deno_server.ts - -// Import WebUI module -import * as webui from "../module/webui.ts"; - -// Import Deno Server Module -import { serve } from "https://deno.land/std@0.158.0/http/server.ts"; - -// Optional - Set a custom library path: -// const lib_full_path = '../../../../build/Windows/MSVC/webui-2-x64.dll'; -// console.log("Looking for the WebUI dynamic library at: " + lib_full_path); -// webui.set_lib_path(lib_full_path); - -// Deno Server Listener -const port = 8080; -const url = "http://localhost:" + 8080; -const handler = (request: Request): Response => { - const body = `This is a Deno-Web-Server example. Your user-agent is:\n\n${ - request.headers.get("user-agent") ?? "Unknown" - }`; - return new Response(body, { status: 200 }); -}; -serve(handler, { port }); - -// Create new window -const my_window = webui.new_window(); - -// Show the window -if(!webui.open(my_window, url, webui.browser.chrome)) - webui.open(my_window, url, webui.browser.any); - -// Wait until all windows get closed -await webui.wait(); - -// The window is closed. -console.log('Thank you.'); -Deno.exit(0); diff --git a/examples/TypeScript/Deno/hello_world.html b/examples/TypeScript/Deno/hello_world.html new file mode 100644 index 00000000..eca8e29b --- /dev/null +++ b/examples/TypeScript/Deno/hello_world.html @@ -0,0 +1,40 @@ + + + + WebUI 2 - Deno Hello World Example + + + +

WebUI 2 - Deno Hello World (File)


+ A:

+ B:

+
A + B = ?


+ - + + + + + + + diff --git a/examples/TypeScript/Deno/hello_world/hello_world.ts b/examples/TypeScript/Deno/hello_world.ts similarity index 59% rename from examples/TypeScript/Deno/hello_world/hello_world.ts rename to examples/TypeScript/Deno/hello_world.ts index debb5d0b..35b381f3 100644 --- a/examples/TypeScript/Deno/hello_world/hello_world.ts +++ b/examples/TypeScript/Deno/hello_world.ts @@ -2,7 +2,7 @@ // Run this script by: // deno run --allow-all --unstable hello_world.ts -// Import WebUI module +// Import WebUI module (Local file) import * as webui from "../module/webui.ts"; // Optional - Set a custom library path: @@ -34,15 +34,15 @@ const my_html = ` - @@ -51,32 +51,38 @@ const my_html = ` function calculate(e : webui.event) { - // Create a JS struct - const MyJS: webui.js = { - Script: "", - Timeout: 0, - }; - - // Call JS function `get_A()` - MyJS.Script = "return get_A();"; - webui.run(e.win, MyJS); - const A = MyJS.Data; + // Create a JavaScript object + const my_js = webui.js; - // Check for error - if(MyJS.Error) { - console.log("Error in the JavaScript: " + MyJS.Data); - } + // Settings if needed + // my_js.timeout = 30; // Set javascript execution timeout + // my_js.response_size = 64; // Set the response size in bytes - // Call JS function `get_B()` - MyJS.Script = "return get_B();"; - webui.run(e.win, MyJS); - const B = MyJS.Data; + // Call a js function + if(!webui.script(e.win, my_js, "return get_A()")) { + // Error + console.log("Error in the JavaScript: " + my_js.response); + return; + } - const C : Number = parseInt(A) + parseInt(B); + // Get A + const A = my_js.response; - // Set the result - MyJS.Script = "set_result(" + C + ");"; - webui.run(e.win, MyJS); + // Call a js function + if(!webui.script(e.win, my_js, "return get_B();")) { + // Error + console.log("Error in the JavaScript: " + my_js.response); + return; + } + + // Get B + const B = my_js.response; + + // Calculate + const C : number = parseInt(A) + parseInt(B); + + // Run js (Quick Way) + webui.run(e.win, "set_result(" + C + ");"); } // Create new window @@ -85,12 +91,12 @@ const my_window = webui.new_window(); // Bind webui.bind(my_window, "Calculate", calculate); webui.bind(my_window, "Exit", function(e : webui.event) { - // Close all windows and exit - webui.exit(); + // Close all windows and exit + webui.exit(); }); // Show the window -webui.show(my_window, my_html); +webui.show(my_window, my_html); // Or webui.show(my_window, 'hello_world.html'); // Wait until all windows get closed await webui.wait(); diff --git a/examples/TypeScript/Deno/hello_world/README.md b/examples/TypeScript/Deno/hello_world/README.md deleted file mode 100644 index 71be411a..00000000 --- a/examples/TypeScript/Deno/hello_world/README.md +++ /dev/null @@ -1,13 +0,0 @@ -### Deno Server - -This example shows how to use the WebUI Dynamic Library in Deno. - - 1. Download and [Install Deno](https://github.com/denoland/deno/releases) (*Or just copy deno binary file into this folder*) - 2. Download WebUI pre-built Library (*[webui.me](https://webui.me/)*) - 3. Run `deno run --allow-all --unstable hello_world.ts` - -Folder structure example (*Windows*) - - [My Folder] - * deno.exe (if not installed) - * hello_world.ts --> Import webui.ts -> Load `webui-2-x64` library diff --git a/examples/TypeScript/Deno/module/webui.ts b/examples/TypeScript/Deno/module/webui.ts deleted file mode 100644 index f11207aa..00000000 --- a/examples/TypeScript/Deno/module/webui.ts +++ /dev/null @@ -1,309 +0,0 @@ -/* - WebUI Library 2.1.1 - - http://webui.me - https://github.com/alifcommunity/webui - - Copyright (c) 2020-2023 Hassan Draga. - Licensed under GNU General Public License v2.0. - All rights reserved. - Canada. -*/ - -import { existsSync } from "https://deno.land/std/fs/mod.ts"; -// import { readCString } from "https://deno.land/std/c/strings/mod.ts"; - -export const version = '2.1.1'; -const encoder = new TextEncoder(); -const decoder = new TextDecoder(); -let lib_loaded = false; -let webui_lib; - -export const browser = {}; -browser.any = 0; -browser.chrome = 1; -browser.firefox = 2; -browser.edge = 3; -browser.safari = 4; -browser.chromium = 5; - -export interface event { - element_id: number; - window_id: number; - element_name_ptr: string; - win: Deno.Pointer; - data: string; -} - -export interface js { - Timeout: number; - Script: string; - Error: boolean; - Length: number; - Data: string; -} - -// Determine the library name based -// on the current operating system -let lib_name : string; -let os_sep : string; -if (Deno.build.os === 'windows') { - lib_name = 'webui-2-x64.dll'; - os_sep = '\\'; -} -else if (Deno.build.os === 'linux') { - lib_name = 'webui-2-x64.so'; - os_sep = '/'; -} -else { - lib_name = 'webui-2-x64.dyn'; - os_sep = '/'; -} - -// Full path to the library name -let lib_path = './' + lib_name; - -// Check if a file exist -function is_file_exist(path : string): boolean { - // TODO: existsSync() is deprecated - return existsSync(path); -} - -// Convert String to C-String -function string_to_uint8array(value: string) : Uint8Array { - return encoder.encode(value + '\0'); -} - -// Get current folder path -function get_current_module_path() : string { - const __dirname = new URL('.', import.meta.url).pathname; - let directory = String(__dirname); - if (Deno.build.os === 'windows') { - // Remove '/' - let buf = directory.substring(1); - directory = buf; - // Replace '/' by '\' - buf = directory.replaceAll('/', os_sep); - directory = buf; - } - return directory; -} - -// Convert C-String to String -function uint8array_to_string(value: ArrayBuffer) : string { - return decoder.decode(value); -} - -// Load the library -function load_lib() { - if(lib_loaded) - return; - - // Check if the library file exist - if(!is_file_exist(lib_path)) { - let lib_path_cwd = get_current_module_path() + lib_name; - if(!is_file_exist(lib_path_cwd)) { - console.log('WebUI Error: File not found (' + lib_path + ') or (' + lib_path_cwd + ')'); - Deno.exit(1); - } - lib_path = lib_path_cwd; - } - - // Load the library - // FFI - webui_lib = Deno.dlopen( - lib_path, - { - webui_wait: { - parameters: [], - result: 'void', - nonblocking: true, - }, - webui_new_window: { - parameters: [], - result: 'pointer', - nonblocking: false, - }, - webui_new_server: { - parameters: ['pointer', 'buffer'], - result: 'pointer', - nonblocking: false, - }, - webui_show: { - parameters: ['pointer', 'buffer'], - result: 'i32', - nonblocking: false, - }, - webui_open: { - parameters: ['pointer', 'buffer', 'u32'], - result: 'i32', - nonblocking: false, - }, - webui_bind_interface: { - parameters: ['pointer', 'buffer', 'function'], - result: 'u32', - nonblocking: false, - }, - webui_is_app_running: { - parameters: [], - result: 'i32', - nonblocking: false, - }, - webui_exit: { - parameters: [], - result: 'void', - nonblocking: false, - }, - webui_script_interface: { - parameters: ['pointer', 'buffer', 'u32', 'buffer', 'buffer', 'buffer'], - result: 'void', - nonblocking: false, - }, - webui_clean_mem: { - parameters: ['pointer'], - result: 'void', - nonblocking: false, - }, - } as const, - ); - - // Make sure we don't load twice - lib_loaded = true; -} - -export function set_lib_path(path : string) { - lib_path = path; -} - -export function new_window() : Deno.Pointer { - load_lib(); - return webui_lib.symbols.webui_new_window(); -} - -export function new_server(win : Deno.Pointer = null, path : string) : string { - load_lib(); - const ptr = webui_lib.symbols.webui_new_server(win, string_to_uint8array(path)); - const dataView = new Deno.UnsafePointerView(ptr); - return String(dataView.getCString()); -} - -export function show(win : Deno.Pointer, content : string) : number { - load_lib(); - return webui_lib.symbols.webui_show(win, string_to_uint8array(content)); -} - -export function open(win, url : string, browser : number) : number { - load_lib(); - return webui_lib.symbols.webui_open(win, string_to_uint8array(url), browser); -} - -export function exit() { - load_lib(); - webui_lib.symbols.webui_exit(); -} - -// - - - - - - - - - - - - - - - - -// TODO: We should use the Non-blocking FFI to call -// `webui_lib.symbols.webui_wait()`. but it breaks -// the main thread. Lets do it in another way for now. - const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); - export async function wait() { - load_lib(); - while(true) { - await sleep(50); - if(!webui_lib.symbols.webui_is_app_running()) - break; - } - } -// - - - - - - - - - - - - - - - - - -export function run(win : number, javascript : js) { - load_lib(); - - // Buffers - const bool_buffer = new Uint8Array(1); // 1 byte bool - const integer_buffer = new Uint8Array(4); // 4 bytes integer - const pointer_buffer = new Uint8Array(8); // 8 bytes pointer (x64) - - // Execute the script - webui_lib.symbols.webui_script_interface(win, string_to_uint8array(javascript.Script), javascript.Timeout, bool_buffer, integer_buffer, pointer_buffer); - - let error : Boolean = false; - let length : Number = 0; - let data : String = ""; - - // Resolve boolean - if(bool_buffer[0] === 0) - error = false; - else - error = true; - - // Resolve number - length = Number(new Uint32Array(integer_buffer.buffer)[0]); - - // Resolve string - const charPointer = Deno.UnsafePointer.create(BigInt(new BigUint64Array(pointer_buffer.buffer)[0])); - data = String(Deno.UnsafePointerView.getCString(charPointer, 0)); - - // Update - javascript.Error = Boolean(error); - javascript.Length = Number(length); - javascript.Data = String(data); - - // Clean memory - webui_lib.symbols.webui_clean_mem(charPointer); -} - -export function bind(win : number, element : string, func : Function) : number { - load_lib(); - const callbackResource = new Deno.UnsafeCallback( - { - parameters: ['u32', 'u32', 'pointer', 'pointer', 'pointer', 'pointer'], - result: 'void', - } as const, - ( - param_element_id: Deno.u32, - param_window_id: Deno.u32, - param_element_name_ptr: Deno.Pointer, - param_win: Deno.Pointer, - param_data: Deno.Pointer, - param_response: Deno.PointerValue, - ) => { - - // Create elements - const element_id = parseInt(param_element_id); - const window_id = parseInt(param_window_id); - const element_name = new Deno.UnsafePointerView(param_element_name_ptr).getCString(); - const win = param_win; - const data = new Deno.UnsafePointerView(param_data).getCString(); - - // Create struct - const e: event = { - element_id: element_id, - window_id: window_id, - element_name: element_name, - win: win, - data: data, - }; - - // Call the user callback - const result = String(func(e)); - - // -- This code is by ChatGPT (AI) -------- - const resultBytes = encoder.encode(result); // Convert the string to bytes - const buf = new Uint8Array(resultBytes.length + 1); // Allocate a buffer to hold the string - buf.set(resultBytes); // Copy the bytes of the string into the buffer - buf[resultBytes.length] = 0; // Set the null terminator - // ---------------------------------------- - - // 8 bytes ie. pointer size on a 64 bit machine, use BigUin64Array to get a writable and suitable view into it. - const response_writable = new BigUint64Array(Deno.UnsafePointerView.getArrayBuffer(param_response, 8)); - - // Write our string response buffer address into the `param_response` memory slot. - response_writable[0] = BigInt(Deno.UnsafePointer.value(Deno.UnsafePointer.of(buf))); - }, - ); - - webui_lib.symbols.webui_bind_interface(win, string_to_uint8array(element), callbackResource.pointer); - return 0; -} diff --git a/examples/TypeScript/Deno/webui-2-x64.dll b/examples/TypeScript/Deno/webui-2-x64.dll new file mode 100644 index 00000000..4dcaca02 Binary files /dev/null and b/examples/TypeScript/Deno/webui-2-x64.dll differ diff --git a/examples/TypeScript/Deno/webui.ts b/examples/TypeScript/Deno/webui.ts new file mode 100644 index 00000000..581e0490 --- /dev/null +++ b/examples/TypeScript/Deno/webui.ts @@ -0,0 +1,287 @@ +/* + WebUI Library 2.2.0 + + http://webui.me + https://github.com/alifcommunity/webui + + Copyright (c) 2020-2023 Hassan Draga. + Licensed under GNU General Public License v2.0. + All rights reserved. + Canada. +*/ + +import { existsSync } from "https://deno.land/std/fs/mod.ts"; + +export const version = '2.2.0'; + +const encoder = new TextEncoder(); +const decoder = new TextDecoder(); +let lib_loaded = false; +let webui_lib; + +export const browser = { + AnyBrowser: 0, // 0. Default recommended web browser + Chrome: 1, // 1. Google Chrome + Firefox: 2, // 2. Mozilla Firefox + Edge: 3, // 3. Microsoft Edge + Safari: 4, // 4. Apple Safari + Chromium: 5, // 5. The Chromium Project + Opera: 6, // 6. Opera Browser + Brave: 7, // 7. The Brave Browser + Vivaldi: 8, // 8. The Vivaldi Browser + Epic: 9, // 9. The Epic Browser + Yandex: 10, // 10. The Yandex Browser +}; + +export interface event { + win: Deno.Pointer, + type: number, + element: string, + data: string, +} + +export const js = { + timeout: 0, + BufferSize: (1024 * 8), + response: "", +} + +// Determine the library name based +// on the current operating system +let lib_name: string; +let os_sep: string; +if (Deno.build.os === 'windows') { + lib_name = 'webui-2-x64.dll'; + os_sep = '\\'; +} +else if (Deno.build.os === 'linux') { + lib_name = 'webui-2-x64.so'; + os_sep = '/'; +} +else { + lib_name = 'webui-2-x64.dyn'; + os_sep = '/'; +} + +// Full path to the library name +let lib_path = './' + lib_name; + +// Check if a file exist +function is_file_exist(path: string): boolean { + // TODO: existsSync() is deprecated + return existsSync(path); +} + +// Convert String to C-String +function string_to_uint8array(value: string): Uint8Array { + return encoder.encode(value + '\0'); +} + +// Get current folder path +function get_current_module_path(): string { + const __dirname = new URL('.', import.meta.url).pathname; + let directory = String(__dirname); + if (Deno.build.os === 'windows') { + // Remove '/' + let buf = directory.substring(1); + directory = buf; + // Replace '/' by '\' + buf = directory.replaceAll('/', os_sep); + directory = buf; + } + return directory; +} + +// Convert C-String to String +function uint8array_to_string(value: ArrayBuffer): string { + return decoder.decode(value); +} + +// Load the library +function load_lib() { + if(lib_loaded) + return; + + // Check if the library file exist + if(!is_file_exist(lib_path)) { + let lib_path_cwd = get_current_module_path() + lib_name; + if(!is_file_exist(lib_path_cwd)) { + console.log('WebUI Error: File not found (' + lib_path + ') or (' + lib_path_cwd + ')'); + Deno.exit(1); + } + lib_path = lib_path_cwd; + } + + // Load the library + // FFI + webui_lib = Deno.dlopen( + lib_path, + { + webui_wait: { + // void webui_wait(void) + parameters: [], + result: 'void', + nonblocking: true, + }, + webui_interface_is_app_running: { + // bool webui_interface_is_app_running(void) + parameters: [], + result: 'i32', + nonblocking: false, + }, + webui_new_window: { + // void* webui_new_window(void) + parameters: [], + result: 'pointer', + nonblocking: false, + }, + webui_show: { + // bool webui_show(void* window, const char* content) + parameters: ['pointer', 'buffer'], + result: 'i32', + nonblocking: false, + }, + webui_show_browser: { + // bool webui_show_browser(void* window, const char* content, unsigned int browser) + parameters: ['pointer', 'buffer', 'u32'], + result: 'i32', + nonblocking: false, + }, + webui_interface_bind: { + // unsigned int webui_interface_bind(void* window, const char* element, void (*func)(void*, int, char*, char*, char*)) + parameters: ['pointer', 'buffer', 'function'], + result: 'u32', + nonblocking: false, + }, + webui_script: { + // bool webui_script(void* window, const char* script, unsigned int timeout, char* buffer, size_t buffer_length) + parameters: ['pointer', 'buffer', 'u32', 'buffer', 'i32'], + result: 'i32', + nonblocking: false, + }, + webui_run: { + // bool webui_run(void* window, const char* script) + parameters: ['pointer', 'buffer'], + result: 'i32', + nonblocking: false, + }, + webui_interface_set_response: { + // void webui_interface_set_response(char* ptr, const char* response) + parameters: ['pointer', 'buffer'], + result: 'void', + nonblocking: false, + }, + webui_exit: { + // void webui_exit(void) + parameters: [], + result: 'void', + nonblocking: false, + } + } as const, + ); + + // Make sure we don't load twice + lib_loaded = true; +} + +export function set_lib_path(path: string) { + lib_path = path; +} + +export function new_window(): Deno.Pointer { + load_lib(); + return webui_lib.symbols.webui_new_window(); +} + +export function show(win: Deno.Pointer, content: string): number { + load_lib(); + return webui_lib.symbols.webui_show(win, string_to_uint8array(content)); +} + +export function show_browser(win: Deno.Pointer, content: string, browser: number): number { + load_lib(); + return webui_lib.symbols.webui_show_browser(win, string_to_uint8array(content), browser); +} + +export function exit() { + load_lib(); + webui_lib.symbols.webui_exit(); +} + +export function script(win: Deno.Pointer, js, script: string): boolean { + load_lib(); + + // Response Buffer + const size: number = (js.BufferSize > 0 ? js.BufferSize: (1024 * 8)); + const buffer = new Uint8Array(size); + + // Execute the script + const status = webui_lib.symbols.webui_script(win, string_to_uint8array(script), js.timeout, buffer, size); + + // Update + js.response = String(uint8array_to_string(buffer)); + + return Boolean(status); +} + +export function run(win: Deno.Pointer, script: string): boolean { + load_lib(); + + // Execute the script + const status = webui_lib.symbols.webui_run(win, string_to_uint8array(script)); + + return Boolean(status); +} + +export function bind(win: Deno.Pointer, element: string, func: Function) { + load_lib(); + const callbackResource = new Deno.UnsafeCallback( + { + parameters: ['pointer', 'u32', 'pointer', 'pointer', 'pointer'], + result: 'void', + } as const, + ( + param_window: Deno.Pointer, + param_type: Deno.u32, + param_element: Deno.Pointer, + param_data: Deno.Pointer, + param_response: Deno.Pointer + ) => { + + // Create elements + const win = param_window; + const type = parseInt(param_type); + const element = new Deno.UnsafePointerView(param_element).getCString(); + const data = new Deno.UnsafePointerView(param_data).getCString(); + + // Create struct + const e: event = { + win: win, + type: type, + element: element, + data: data, + }; + + // Call the user callback + const result = String(func(e)); + + // Send back the response + webui_lib.symbols.webui_interface_set_response(param_response, string_to_uint8array(result)); + }, + ); + + webui_lib.symbols.webui_interface_bind(win, string_to_uint8array(element), callbackResource.pointer); +} + +// TODO: We should use the Non-blocking FFI to call +// `webui_lib.symbols.webui_wait()`. but it breaks +// the main thread. Lets do it in another way for now. +const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms)); +export async function wait() { + load_lib(); + while(true) { + await sleep(10); + if(!webui_lib.symbols.webui_interface_is_app_running()) + break; + } +} diff --git a/examples/TypeScript/Nodejs/README.md b/examples/TypeScript/Nodejs/README.md index 30acec57..33a3bb98 100644 --- a/examples/TypeScript/Nodejs/README.md +++ b/examples/TypeScript/Nodejs/README.md @@ -1,4 +1,7 @@ -# WebUI Examples - Node.js +# WebUI Examples - Nodejs -WebUI can create a web server and use [Node.js](https://nodejs.org/) runtimes to parse `JavaScript` files. Another way is simply create a web server using Node.js and run a simple system command to run a basic WebUI application to open a window. +Unfortunately, Nodejs does not support FFI natively (only trough unofficial addon). We recommend using Deno instead. + + +The only way to use Nodejs with WebUI is to create a simple app in another language like C, C++, Nim, Go, Python, V, Zig, Rust... and use `webui_set_runtime()` to interpret the Nodejs files. diff --git a/examples/TypeScript/Nodejs/nodejs_server/README.md b/examples/TypeScript/Nodejs/nodejs_server/README.md deleted file mode 100644 index e9610a48..00000000 --- a/examples/TypeScript/Nodejs/nodejs_server/README.md +++ /dev/null @@ -1,18 +0,0 @@ -### Node.js Server - -This example shows how to create a web server using Node.js and run a simple system command to run a basic WebUI application to open a window. - - 1. Download and [Install Node.js](https://nodejs.org/en/download/) - 2. Build WebUI Library (*[instructions](https://github.com/alifcommunity/webui/tree/main/build)*) - 3. Build `window.c` using any C compiler (*[instructions](https://github.com/alifcommunity/webui/tree/main/examples/C)*) - 3. Run `node server.js` - -Folder structure example (*Windows*) - - [My Folder] - * node.exe (if not installed) - * window.exe - * server.js - * file1.js - * file2.js - * ... diff --git a/examples/TypeScript/Nodejs/nodejs_server/server.js b/examples/TypeScript/Nodejs/nodejs_server/server.js deleted file mode 100644 index ed274859..00000000 --- a/examples/TypeScript/Nodejs/nodejs_server/server.js +++ /dev/null @@ -1,30 +0,0 @@ - -// Modules -const http = require('http'); - -// Config -const port = 8080; -const url = "http://localhost:" + 8080; - -// Server Listener -const requestListener = function (req, res) { - - res.writeHead(200); - res.end('Hello, World!\nNode.js will automatically stop when you close this window.\nNo need for Ctrl + D!'); -} - -// Create a web server using Nodejs -const server = http.createServer(requestListener); -server.listen(port); - -// Run the WebUI example application to open a new window -const { spawn } = require("child_process"); -const ls = spawn("window", [url]); -ls.on('error', (error) => { - console.log('Failed to start the window'); - process.exit(); -}); -ls.on("close", code => { - console.log('Thank you.'); - process.exit(); -}); diff --git a/examples/TypeScript/Nodejs/nodejs_server/window.c b/examples/TypeScript/Nodejs/nodejs_server/window.c deleted file mode 100644 index 0a955b87..00000000 --- a/examples/TypeScript/Nodejs/nodejs_server/window.c +++ /dev/null @@ -1,27 +0,0 @@ - - - -#include "webui.h" - -int main(int argc, char* argv[]) { - - // argc should be exactly 2 - if(argc != 2) - return 1; - - // Get the Nodejs server url - const char *url = argv[1]; - - // Create a new window - webui_window_t* my_window; - my_window = webui_new_window(); - - // Show the window - if(!webui_open(my_window, url, webui.browser.chrome)) // Run the window on Chrome - webui_open(my_window, url, webui.browser.any); // If not, run on any other installed web browser - - // Wait until all windows get closed - webui_wait(); - - return 0; -} diff --git a/examples/TypeScript/Nodejs/webui_server/README.md b/examples/TypeScript/Nodejs/webui_server/README.md deleted file mode 100644 index 142e8eb5..00000000 --- a/examples/TypeScript/Nodejs/webui_server/README.md +++ /dev/null @@ -1,18 +0,0 @@ -### WebUI Server - -This example shows how to create a WebUI web server and use Node.js runtimes to parse JavaScript files. - - 1. Download and [Install Node.js](https://nodejs.org/en/download/) - 2. Build WebUI Library (*[instructions](https://github.com/alifcommunity/webui/tree/main/build)*) - 3. Build `example.c` using any C compiler (*[instructions](https://github.com/alifcommunity/webui/tree/main/examples/C)*) - -Folder structure example (*Windows*) - - [My Folder] - * node.exe (if not installed) - * example.exe - * index.js or index.html - * style.css - * file1.js - * file2.js - * ... diff --git a/examples/TypeScript/Nodejs/webui_server/example.c b/examples/TypeScript/Nodejs/webui_server/example.c deleted file mode 100644 index 1fe85ea8..00000000 --- a/examples/TypeScript/Nodejs/webui_server/example.c +++ /dev/null @@ -1,34 +0,0 @@ - - - -#include "webui.h" - -int main() { - - // Create a new window - webui_window_t* my_window; - my_window = webui_new_window(); - - // Chose your preferable runtime for .js files - // Deno: webui.runtime.deno - // Node.js: webui.runtime.nodejs - webui_script_runtime(my_window, webui.runtime.nodejs); - - // Create a new web server using WebUI - const char* url = webui_new_server(my_window, ""); - - // Show the window - if(!webui_open(my_window, url, webui.browser.chrome)) // Run the window on Chrome - webui_open(my_window, url, webui.browser.any); // If not, run on any other installed web browser - - // Wait until all windows get closed - webui_wait(); - - return 0; -} - -#if defined(_MSC_VER) - int APIENTRY WinMain(HINSTANCE hInst, HINSTANCE hInstPrev, PSTR cmdline, int cmdshow) { - main(); - } -#endif diff --git a/examples/TypeScript/Nodejs/webui_server/index.js b/examples/TypeScript/Nodejs/webui_server/index.js deleted file mode 100644 index fa3e1df2..00000000 --- a/examples/TypeScript/Nodejs/webui_server/index.js +++ /dev/null @@ -1,6 +0,0 @@ - -var MyModule = require('./my_module'); - -console.log( - "Welcome!, You are using a WebUI server.\nToday is " + MyModule.date_time() -); diff --git a/examples/TypeScript/Nodejs/webui_server/my_module.js b/examples/TypeScript/Nodejs/webui_server/my_module.js deleted file mode 100644 index 2bf3972f..00000000 --- a/examples/TypeScript/Nodejs/webui_server/my_module.js +++ /dev/null @@ -1,5 +0,0 @@ - -exports.date_time = function () { - - return Date(); -}; diff --git a/examples/Zig/hello_world/src/main.zig b/examples/Zig/hello_world/src/main.zig index cdfb9116..098762ac 100644 --- a/examples/Zig/hello_world/src/main.zig +++ b/examples/Zig/hello_world/src/main.zig @@ -65,13 +65,13 @@ export fn check_the_password(e_opt: ?*c.webui_event_t) callconv(.C) void { c.webui_script(e.window, &js); // Check if there is a JavaScript error - if (js.result.@"error") { - std.log.err("JavaScript Error: {s}\n", .{js.result.data}); + if (js.@"error") { + std.log.err("JavaScript Error: {s}\n", .{js.data}); return; } // Get the password - const password = std.mem.span(js.result.data); + const password = std.mem.span(js.data); std.log.info("Password: {s}", .{password}); // Check the password diff --git a/examples/Zig/serve_folder/src/main.zig b/examples/Zig/serve_folder/src/main.zig index 1816530b..e50d2638 100644 --- a/examples/Zig/serve_folder/src/main.zig +++ b/examples/Zig/serve_folder/src/main.zig @@ -1,4 +1,4 @@ -// WebUI Library 2.1.1 +// WebUI Library 2.2.0 // Serve a Folder Example pub fn main() void { diff --git a/include/webui.h b/include/webui.h index 4cb043e1..07b08828 100644 --- a/include/webui.h +++ b/include/webui.h @@ -1,46 +1,29 @@ /* - WebUI Library 2.1.1 - - http://webui.me - https://github.com/alifcommunity/webui - - Copyright (c) 2020-2023 Hassan Draga. - Licensed under GNU General Public License v2.0. - All rights reserved. - Canada. + WebUI Library 2.2.0 + http://webui.me + https://github.com/alifcommunity/webui + Copyright (c) 2020-2023 Hassan Draga. + Licensed under GNU General Public License v2.0. + All rights reserved. + Canada. */ #ifndef _WEBUI_H #define _WEBUI_H +#define WEBUI_VERSION "2.2.0" + +// Dynamic Library Exports #if defined(_MSC_VER) || defined(__TINYC__) - #define EXPORT __declspec(dllexport) + #ifndef WEBUI_EXPORT + #define WEBUI_EXPORT __declspec(dllexport) + #endif #else - #define EXPORT extern + #ifndef WEBUI_EXPORT + #define WEBUI_EXPORT extern + #endif #endif -#define WEBUI_VERSION "2.1.1" // Version -#define WEBUI_HEADER_SIGNATURE 0xFF // All packets should start with this 8bit -#define WEBUI_HEADER_JS 0xFE // Javascript result in frontend -#define WEBUI_HEADER_CLICK 0xFD // Click event -#define WEBUI_HEADER_SWITCH 0xFC // Frontend refresh -#define WEBUI_HEADER_CLOSE 0xFB // Close window -#define WEBUI_HEADER_CALL_FUNC 0xFA // Call a backend function -#define WEBUI_MAX_ARRAY (1024) // Max threads, servers, windows, pointers.. -#define WEBUI_MIN_PORT (10000) // Minimum socket port -#define WEBUI_MAX_PORT (65500) // Should be less than 65535 -#define WEBUI_MAX_BUF (1024000) // 1024 Kb max dynamic memory allocation -#define WEBUI_DEFAULT_PATH "." // Default root path -#define WEBUI_DEF_TIMEOUT (8) // Default startup timeout in seconds - -#define WEBUI_EVENT_CONNECTED (1) // Window connected -#define WEBUI_EVENT_MULTI_CONNECTION (2) // Multi clients connected -#define WEBUI_EVENT_UNWANTED_CONNECTION (3) // Unwanted client connected -#define WEBUI_EVENT_DISCONNECTED (4) // Window disconnected -#define WEBUI_EVENT_MOUSE_CLICK (5) // Mouse Click -#define WEBUI_EVENT_NAVIGATION (6) // The window URL changed -#define WEBUI_EVENT_CALLBACK (7) // Function call - // -- C STD --------------------------- #include #include @@ -63,11 +46,9 @@ #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif - // #include // Fix _WIN32_WINNT warning #include #include #include - // #include #include #include #include @@ -77,24 +58,27 @@ #define WEBUI_PCLOSE _pclose #define WEBUI_MAX_PATH MAX_PATH #endif + // -- Linux --------------------------- #ifdef __linux__ - #include // POSIX threading + #include #include #include #include #include #include #include + #include #define WEBUI_GET_CURRENT_DIR getcwd #define WEBUI_FILE_EXIST access #define WEBUI_POPEN popen #define WEBUI_PCLOSE pclose #define WEBUI_MAX_PATH PATH_MAX #endif + // -- Apple --------------------------- #ifdef __APPLE__ - #include // POSIX threading + #include #include #include #include @@ -102,6 +86,7 @@ #include #include #include // PATH_MAX + #include #define WEBUI_GET_CURRENT_DIR getcwd #define WEBUI_FILE_EXIST access #define WEBUI_POPEN popen @@ -109,232 +94,96 @@ #define WEBUI_MAX_PATH PATH_MAX #endif +// -- Enums --------------------------- +enum webui_browsers { + AnyBrowser = 0, // 0. Default recommended web browser + Chrome, // 1. Google Chrome + Firefox, // 2. Mozilla Firefox + Edge, // 3. Microsoft Edge + Safari, // 4. Apple Safari + Chromium, // 5. The Chromium Project + Opera, // 6. Opera Browser + Brave, // 7. The Brave Browser + Vivaldi, // 8. The Vivaldi Browser + Epic, // 9. The Epic Browser + Yandex, // 10. The Yandex Browser +}; + +enum webui_runtimes { + None = 0, // 0. Prevent WebUI from using any runtime for .js and .ts files + Deno, // 1. Use Deno runtime for .js and .ts files + NodeJS, // 2. Use Nodejs runtime for .js files +}; + +enum webui_events { + WEBUI_EVENT_DISCONNECTED = 0, // 0. Window disconnection event + WEBUI_EVENT_CONNECTED, // 1. Window connection event + WEBUI_EVENT_MULTI_CONNECTION, // 2. New window connection event + WEBUI_EVENT_UNWANTED_CONNECTION, // 3. New unwanted window connection event + WEBUI_EVENT_MOUSE_CLICK, // 4. Mouse click event + WEBUI_EVENT_NAVIGATION, // 5. Window navigation event + WEBUI_EVENT_CALLBACK, // 6. Function call event +}; + // -- Structs ------------------------- -struct webui_event_t; -typedef struct webui_timer_t { - struct timespec start; - struct timespec now; -} webui_timer_t; -typedef struct webui_window_core_t { - unsigned int window_number; - bool server_running; - bool connected; - bool server_handled; - bool multi_access; - bool server_root; - unsigned int server_port; - char* url; - const char* html; - const char* html_cpy; - const char* icon; - const char* icon_type; - unsigned int CurrentBrowser; - char* browser_path; - char* profile_path; - unsigned int connections; - unsigned int runtime; - bool detect_process_close; - bool has_events; - #ifdef _WIN32 - HANDLE server_thread; - #else - pthread_t server_thread; - #endif -} webui_window_core_t; -typedef struct webui_window_t { - webui_window_core_t core; - char* path; -} webui_window_t; typedef struct webui_event_t { - unsigned int window_id; - unsigned int element_id; - char* element_name; - webui_window_t* window; - void* data; - void* response; - int type; + void* window; // Pointer to the window object + unsigned int type; // Event type + char* element; // HTML element ID + char* data; // JavaScript data + char* response; // Callback response } webui_event_t; -typedef struct webui_javascript_result_t { - bool error; - unsigned int length; - const char* data; -} webui_javascript_result_t; -typedef struct webui_script_t { - const char* script; - unsigned int timeout; - webui_javascript_result_t result; -} webui_script_t; -typedef struct webui_cb_t { - webui_window_t* win; - char* webui_internal_id; - char* element_name; - void* data; - unsigned int data_len; - int event_type; -} webui_cb_t; -typedef struct webui_cmd_async_t { - webui_window_t* win; - char* cmd; -} webui_cmd_async_t; -typedef struct webui_custom_browser_t { - char* app; - char* arg; - bool auto_link; -} webui_custom_browser_t; -typedef struct webui_browser_t { - unsigned int any; // 0 - unsigned int chrome; // 1 - unsigned int firefox; // 2 - unsigned int edge; // 3 - unsigned int safari; // 4 - unsigned int chromium; // 5 - unsigned int opera; // 6 - unsigned int brave; // 7 - unsigned int vivaldi; // 8 - unsigned int epic; // 9 - unsigned int yandex; // 10 - unsigned int current; // x - unsigned int custom; // 99 -} webui_browser_t; -typedef struct webui_runtime_t { - unsigned int none; // 0 - unsigned int deno; // 1 - unsigned int nodejs; // 2 -} webui_runtime_t; -typedef struct webui_t { - unsigned int servers; - unsigned int connections; - unsigned int process; - webui_custom_browser_t *custom_browser; - bool wait_for_socket_window; - char* html_elements[WEBUI_MAX_ARRAY]; - unsigned int used_ports[WEBUI_MAX_ARRAY]; - unsigned int last_window; - unsigned int startup_timeout; - bool use_timeout; - bool timeout_extra; - bool exit_now; - const char* run_responses[WEBUI_MAX_ARRAY]; - bool run_done[WEBUI_MAX_ARRAY]; - bool run_error[WEBUI_MAX_ARRAY]; - unsigned int run_last_id; - struct mg_mgr* mg_mgrs[WEBUI_MAX_ARRAY]; - struct mg_connection* mg_connections[WEBUI_MAX_ARRAY]; - webui_browser_t browser; - webui_runtime_t runtime; - bool initialized; - void (*cb[WEBUI_MAX_ARRAY])(webui_event_t* e); - void (*cb_interface[WEBUI_MAX_ARRAY])(unsigned int, unsigned int, char*, webui_window_t*, char*, char**); - char* executable_path; - void *ptr_list[WEBUI_MAX_ARRAY]; - unsigned int ptr_position; - size_t ptr_size[WEBUI_MAX_ARRAY]; -} webui_t; // -- Definitions --------------------- -EXPORT webui_t webui; -// Create a new window object -EXPORT webui_window_t* webui_new_window(void); -// Bind a specific html element click event with a function -EXPORT unsigned int webui_bind(webui_window_t* win, const char* element, void (*func)(webui_event_t* e)); -// Show a window using a static HTML script, or a file name in the same working directory. If the window is already opened then it will be refreshed with the new content -EXPORT bool webui_show(webui_window_t* win, const char* content); -// Wait until all opened windows get closed -EXPORT void webui_wait(void); -// Close a specific window -EXPORT void webui_close(webui_window_t* win); -// Close all opened windows -EXPORT void webui_exit(void); +// Create a new webui window object. +WEBUI_EXPORT void* webui_new_window(void); +// Bind a specific html element click event with a function. Empty element means all events. +WEBUI_EXPORT unsigned int webui_bind(void* window, const char* element, void (*func)(webui_event_t* e)); +// Show a window using a embedded HTML, or a file. If the window is already opened then it will be refreshed. +WEBUI_EXPORT bool webui_show(void* window, const char* content); +// Same as webui_show(). But with a specific web browser. +WEBUI_EXPORT bool webui_show_browser(void* window, const char* content, unsigned int browser); +// Wait until all opened windows get closed. +WEBUI_EXPORT void webui_wait(void); +// Close a specific window. +WEBUI_EXPORT void webui_close(void* window); +// Close all opened windows. webui_wait() will break. +WEBUI_EXPORT void webui_exit(void); -// JavaScript -EXPORT void webui_script(webui_window_t* win, webui_script_t* script); -EXPORT void webui_script_cleanup(webui_script_t* script); -EXPORT void webui_script_runtime(webui_window_t* win, unsigned int runtime); -EXPORT long long int webui_get_int(webui_event_t* e); -EXPORT const char* webui_get_string(webui_event_t* e); -EXPORT bool webui_get_bool(webui_event_t* e); -EXPORT void webui_return_int(webui_event_t* e, long long int n); -EXPORT void webui_return_string(webui_event_t* e, char* s); -EXPORT void webui_return_bool(webui_event_t* e, bool b); +// -- Other --------------------------- +WEBUI_EXPORT bool webui_is_shown(void* window); +WEBUI_EXPORT void webui_set_timeout(unsigned int second); +WEBUI_EXPORT void webui_set_icon(void* window, const char* icon, const char* type); +WEBUI_EXPORT void webui_set_multi_access(void* window, bool status); -// Other -EXPORT const char* webui_new_server(webui_window_t* win, const char* path); -EXPORT bool webui_open(webui_window_t* win, const char* url, unsigned int browser); -EXPORT bool webui_is_any_window_running(void); -EXPORT bool webui_is_app_running(void); -EXPORT bool webui_is_shown(webui_window_t* win); -EXPORT void webui_set_timeout(unsigned int second); -EXPORT void webui_set_icon(webui_window_t* win, const char* icon_s, const char* type_s); -EXPORT void webui_multi_access(webui_window_t* win, bool status); -EXPORT void webui_clean_mem(void* p); +// -- JavaScript ---------------------- +// Quickly run a JavaScript. +WEBUI_EXPORT bool webui_run(void* window, const char* script); +// Run a JavaScript, and get the response back (Make sure your local buffer can hold the response). +WEBUI_EXPORT bool webui_script(void* window, const char* script, unsigned int timeout, char* buffer, size_t buffer_length); +// Chose between Deno and Nodejs runtime for .js and .ts files. +WEBUI_EXPORT void webui_set_runtime(void* window, unsigned int runtime); +// Parse argument as integer. +WEBUI_EXPORT long long int webui_get_int(webui_event_t* e); +// Parse argument as string. +WEBUI_EXPORT const char* webui_get_string(webui_event_t* e); +// Parse argument as boolean. +WEBUI_EXPORT bool webui_get_bool(webui_event_t* e); +// Return the response to JavaScript as integer. +WEBUI_EXPORT void webui_return_int(webui_event_t* e, long long int n); +// Return the response to JavaScript as string. +WEBUI_EXPORT void webui_return_string(webui_event_t* e, char* s); +// Return the response to JavaScript as boolean. +WEBUI_EXPORT void webui_return_bool(webui_event_t* e, bool b); // -- Interface ----------------------- -// Used by other languages to create WebUI wrappers -typedef struct webui_script_interface_t { - char* script; - unsigned int timeout; - bool error; - unsigned int length; - const char* data; -} webui_script_interface_t; -EXPORT unsigned int webui_bind_interface(webui_window_t* win, const char* element, void (*func)(unsigned int, unsigned int, char*, webui_window_t*, char*, char**)); -EXPORT void webui_script_interface(webui_window_t* win, const char* script, unsigned int timeout, bool* error, unsigned int* length, char** data); -EXPORT void webui_script_interface_struct(webui_window_t* win, webui_script_interface_t* js_int); - -// Core -EXPORT void _webui_init(void); -EXPORT unsigned int _webui_get_cb_index(char* webui_internal_id); -EXPORT unsigned int _webui_set_cb_index(char* webui_internal_id); -EXPORT unsigned int _webui_get_free_port(void); -EXPORT unsigned int _webui_get_new_window_number(void); -EXPORT void _webui_wait_for_startup(void); -EXPORT void _webui_free_port(unsigned int port); -EXPORT void _webui_set_custom_browser(webui_custom_browser_t* p); -EXPORT char* _webui_get_current_path(void); -EXPORT void _webui_window_receive(webui_window_t* win, const char* packet, size_t len); -EXPORT void _webui_window_send(webui_window_t* win, char* packet, size_t packets_size); -EXPORT void _webui_window_event(webui_window_t* win, char* element_id, char* element, void* data, unsigned int data_len, int event_type); -EXPORT unsigned int _webui_window_get_number(webui_window_t* win); -EXPORT void _webui_window_open(webui_window_t* win, char* link, unsigned int browser); -EXPORT int _webui_cmd_sync(char* cmd, bool show); -EXPORT int _webui_cmd_async(char* cmd, bool show); -EXPORT int _webui_run_browser(webui_window_t* win, char* cmd); -EXPORT void _webui_clean(void); -EXPORT bool _webui_browser_exist(webui_window_t* win, unsigned int browser); -EXPORT const char* _webui_browser_get_temp_path(unsigned int browser); -EXPORT bool _webui_folder_exist(char* folder); -EXPORT bool _webui_browser_create_profile_folder(webui_window_t* win, unsigned int browser); -EXPORT bool _webui_browser_start_chrome(webui_window_t* win, const char* address); -EXPORT bool _webui_browser_start_edge(webui_window_t* win, const char* address); -EXPORT bool _webui_browser_start_epic(webui_window_t* win, const char* address); -EXPORT bool _webui_browser_start_vivaldi(webui_window_t* win, const char* address); -EXPORT bool _webui_browser_start_brave(webui_window_t* win, const char* address); -EXPORT bool _webui_browser_start_firefox(webui_window_t* win, const char* address); -EXPORT bool _webui_browser_start_yandex(webui_window_t* win, const char* address); -EXPORT bool _webui_browser_start_chromium(webui_window_t* win, const char* address); -EXPORT bool _webui_browser_start_custom(webui_window_t* win, const char* address); -EXPORT bool _webui_browser_start(webui_window_t* win, const char* address, unsigned int browser); -EXPORT long _webui_timer_diff(struct timespec *start, struct timespec *end); -EXPORT void _webui_timer_start(webui_timer_t* t); -EXPORT bool _webui_timer_is_end(webui_timer_t* t, unsigned int ms); -EXPORT void _webui_timer_clock_gettime(struct timespec *spec); -EXPORT bool _webui_set_root_folder(webui_window_t* win, const char* path); -EXPORT void _webui_wait_process(webui_window_t* win, bool status); -EXPORT const char* _webui_generate_js_bridge(webui_window_t* win); -EXPORT void _webui_print_hex(const char* data, size_t len); -EXPORT void _webui_free_mem(void **p); -EXPORT bool _webui_file_exist_mg(void *ev_data); -EXPORT bool _webui_file_exist(char* file); -EXPORT void _webui_free_all_mem(void); -EXPORT bool _webui_show_window(webui_window_t* win, const char* html, unsigned int browser); -EXPORT char* _webui_generate_internal_id(webui_window_t* win, const char* element); -#ifdef _WIN32 - EXPORT DWORD WINAPI _webui_cb(LPVOID _arg); - EXPORT DWORD WINAPI _webui_run_browser_task(LPVOID _arg); - EXPORT int _webui_system_win32(char* cmd, bool show); -#else - EXPORT void* _webui_cb(void* _arg); - EXPORT void* _webui_run_browser_task(void* _arg); -#endif +// Bind a specific html element click event with a function. Empty element means all events. This replace webui_bind(). +WEBUI_EXPORT unsigned int webui_interface_bind(void* window, const char* element, void (*func)(void*, unsigned int, char*, char*, char*)); +// When using `webui_interface_bind()` you need this function to easily set your response. +WEBUI_EXPORT void webui_interface_set_response(char* ptr, const char* response); +// Check if the app still running or not. This replace webui_wait(). +WEBUI_EXPORT bool webui_interface_is_app_running(void); +// Get window unique ID +WEBUI_EXPORT unsigned int webui_interface_get_window_id(void* window); #endif /* _WEBUI_H */ diff --git a/scripts/build.bat b/scripts/build.bat new file mode 100644 index 00000000..56e3c987 --- /dev/null +++ b/scripts/build.bat @@ -0,0 +1,70 @@ +@echo off + +set ARG1=%1 +IF "%ARG1%"=="debug" ( + set MSVC_CMD=nmake debug + set GCC_CMD=mingw32-make debug +) else ( + set MSVC_CMD=nmake + set GCC_CMD=mingw32-make +) + +echo. +echo - - - - - - - - - - - - - - - - - - - - - - - +echo WebUI v2.2.0 Build Script +echo - - - - - - - - - - - - - - - - - - - - - - - +echo. +echo OS: Micsoroft Windows x64 +echo Compiler: Microsoft Visual C + +Set RootPath=%CD%\..\ +cd "%RootPath%" + +echo. +echo - - [Build] - - - - - - - - - - - - - - - - - + +REM Build WebUI Library using MSVC +cd "%RootPath%" +cd "build\Windows\MSVC" +%MSVC_CMD% + +REM Build WebUI Library using GCC +cd "%RootPath%" +cd "build\Windows\GCC" +%GCC_CMD% + +echo. +echo - - [Copy] - - - - - - - - - - - - - - - - - +echo. + +cd "%RootPath%" + +REM Golang +copy /Y "build\Windows\GCC\libwebui-2-static-x64.a" "examples\Go\hello_world\webui\libwebui-2-static-x64.a" +copy /Y "include\webui.h" "examples\Go\hello_world\webui\webui.h" + +REM Deno +copy /Y "build\Windows\MSVC\webui-2-x64.dll" "examples\TypeScript\Deno\webui-2-x64.dll" + +REM Python +copy /Y "build\Windows\MSVC\webui-2-x64.dll" "examples\Python\PyPI\Package\src\webui\webui-2-x64.dll" + +echo. +IF "%ARG1%"=="" ( + + echo - - [Clean] - - - - - - - - - - - - - - - - - + + DEL /Q /F /S "*.exe" >nul 2>&1 + DEL /Q /F /S "*.o" >nul 2>&1 + DEL /Q /F /S "*.a" >nul 2>&1 + DEL /Q /F /S "*.def" >nul 2>&1 + DEL /Q /F /S "*.exp" >nul 2>&1 + DEL /Q /F /S "*.pdb" >nul 2>&1 + DEL /Q /F /S "*.ilk" >nul 2>&1 + DEL /Q /F /S "*.obj" >nul 2>&1 + DEL /Q /F /S "*.res" >nul 2>&1 + DEL /Q /F /S "*.bak" >nul 2>&1 + DEL /Q /F /S "*.DS_Store" >nul 2>&1 +) + +cd "scripts" diff --git a/scripts/clean_all.bat b/scripts/clean_all.bat deleted file mode 100644 index 5c130268..00000000 --- a/scripts/clean_all.bat +++ /dev/null @@ -1,22 +0,0 @@ -@echo off - -Set RootPath=%CD%\..\ -cd "%RootPath%" - -echo. -echo Clean all... - -DEL /Q /F /S "*.exe" >nul 2>&1 -DEL /Q /F /S "*.dll" >nul 2>&1 -DEL /Q /F /S "*.lib" >nul 2>&1 -DEL /Q /F /S "*.o" >nul 2>&1 -DEL /Q /F /S "*.a" >nul 2>&1 -DEL /Q /F /S "*.def" >nul 2>&1 -DEL /Q /F /S "*.exp" >nul 2>&1 -DEL /Q /F /S "*.pdb" >nul 2>&1 -DEL /Q /F /S "*.ilk" >nul 2>&1 -DEL /Q /F /S "*.obj" >nul 2>&1 -DEL /Q /F /S "*.res" >nul 2>&1 -DEL /Q /F /S "*.bak" >nul 2>&1 - -DEL /Q /F /S "*.DS_Store" >nul 2>&1 diff --git a/src/mongoose.c b/src/mongoose.c index f18d1c4f..ee65b18d 100644 --- a/src/mongoose.c +++ b/src/mongoose.c @@ -117,6 +117,7 @@ int mg_base64_decode(const char *src, int n, char *dst) { + struct dns_data { struct dns_data *next; struct mg_connection *c; @@ -411,9 +412,7 @@ void mg_error(struct mg_connection *c, const char *fmt, ...) { -static bool is_digit(int c) { - return c >= '0' && c <= '9'; -} +static bool is_digit(int c) { return c >= '0' && c <= '9'; } static int addexp(char *buf, int e, int sign) { int n = 0; @@ -446,7 +445,7 @@ static int xisnan(double x) { 0x7ff00000; } -static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width) { +static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width, bool tz) { char buf[40]; int i, s = 0, n = 0, e = 0; double t, mul, saved; @@ -468,13 +467,13 @@ static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width) { while (d < 1.0 && d / mul < 1.0) mul /= 10.0, e--; // printf(" --> %g %d %g %g\n", saved, e, t, mul); - if (e >= width) { - n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width); + if (e >= width && width > 1) { + n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); // printf(" --> %.*g %d [%.*s]\n", 10, d / t, e, n, buf); n += addexp(buf + s + n, e, '+'); return mg_snprintf(dst, dstlen, "%.*s", n, buf); - } else if (e <= -width) { - n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width); + } else if (e <= -width && width > 1) { + n = (int) mg_dtoa(buf, sizeof(buf), saved / mul, width, tz); // printf(" --> %.*g %d [%.*s]\n", 10, d / mul, e, n, buf); n += addexp(buf + s + n, -e, '-'); return mg_snprintf(dst, dstlen, "%.*s", n, buf); @@ -497,8 +496,8 @@ static size_t mg_dtoa(char *dst, size_t dstlen, double d, int width) { t /= 10.0; } } - while (n > 0 && buf[s + n - 1] == '0') n--; // Trim trailing zeros - if (n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot + while (tz && n > 0 && buf[s + n - 1] == '0') n--; // Trim trailing zeroes + if (n > 0 && buf[s + n - 1] == '.') n--; // Trim trailing dot n += s; if (n >= (int) sizeof(buf)) n = (int) sizeof(buf) - 1; buf[n] = '\0'; @@ -542,9 +541,7 @@ static char mg_esc(int c, bool esc) { return 0; } -static char mg_escape(int c) { - return mg_esc(c, true); -} +static char mg_escape(int c) { return mg_esc(c, true); } static size_t qcpy(void (*out)(char, void *), void *ptr, char *buf, size_t len) { @@ -631,7 +628,7 @@ size_t mg_vxprintf(void (*out)(char, void *), void *param, const char *fmt, if (c == 'g' || c == 'f') { double v = va_arg(*ap, double); if (pr == ~0U) pr = 6; - k = mg_dtoa(tmp, sizeof(tmp), v, (int) pr); + k = mg_dtoa(tmp, sizeof(tmp), v, (int) pr, c == 'g'); } else if (is_long == 2) { int64_t v = va_arg(*ap, int64_t); k = mg_lld(tmp, v, s, h); @@ -1309,6 +1306,7 @@ struct mg_fs mg_fs_posix = {p_stat, p_list, p_open, p_close, p_read, + // Chunk deletion marker is the MSB in the "processed" counter #define MG_DMARK ((size_t) 1 << (sizeof(size_t) * 8 - 1)) @@ -3247,6 +3245,7 @@ struct mg_connection *mg_mqtt_listen(struct mg_mgr *mgr, const char *url, + size_t mg_vprintf(struct mg_connection *c, const char *fmt, va_list *ap) { size_t old = c->send.len; mg_vxprintf(mg_pfn_iobuf, &c->send, fmt, ap); @@ -3495,7 +3494,6 @@ void mg_mgr_init(struct mg_mgr *mgr) { - size_t mg_queue_vprintf(struct mg_queue *q, const char *fmt, va_list *ap) { size_t len = mg_snprintf(NULL, 0, fmt, ap); char *buf; @@ -4107,6 +4105,7 @@ struct mg_connection *mg_sntp_connect(struct mg_mgr *mgr, const char *url, + #if MG_ENABLE_SOCKET #ifndef closesocket @@ -4669,7 +4668,7 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) { } } #else - struct timeval tv = {ms / 1000, (ms % 1000) * 1000}, tv_zero = {0, 0}; + struct timeval tv = {ms / 1000, (ms % 1000) * 1000}, tv_zero = {0, 0}, *tvp; struct mg_connection *c; fd_set rset, wset, eset; MG_SOCKET_TYPE maxfd = 0; @@ -4678,17 +4677,18 @@ static void mg_iotest(struct mg_mgr *mgr, int ms) { FD_ZERO(&rset); FD_ZERO(&wset); FD_ZERO(&eset); + tvp = ms < 0 ? NULL : &tv; for (c = mgr->conns; c != NULL; c = c->next) { c->is_readable = c->is_writable = 0; if (skip_iotest(c)) continue; FD_SET(FD(c), &eset); if (can_read(c)) FD_SET(FD(c), &rset); if (can_write(c)) FD_SET(FD(c), &wset); - if (mg_tls_pending(c) > 0) tv = tv_zero; + if (mg_tls_pending(c) > 0) tvp = &tv_zero; if (FD(c) > maxfd) maxfd = FD(c); } - if ((rc = select((int) maxfd + 1, &rset, &wset, &eset, &tv)) < 0) { + if ((rc = select((int) maxfd + 1, &rset, &wset, &eset, tvp)) < 0) { #if MG_ARCH == MG_ARCH_WIN32 if (maxfd == 0) Sleep(ms); // On Windows, select fails if no sockets #else @@ -5779,6 +5779,7 @@ uint64_t mg_millis(void) { + struct ws_msg { uint8_t flags; size_t header_len; diff --git a/include/mongoose.h b/src/mongoose.h similarity index 99% rename from include/mongoose.h rename to src/mongoose.h index 00f9910b..ee7e8287 100644 --- a/include/mongoose.h +++ b/src/mongoose.h @@ -862,14 +862,17 @@ void mg_queue_del(struct mg_queue *, size_t); // Delete oldest message - - typedef void (*mg_pfn_t)(char, void *); // Output function typedef size_t (*mg_pm_t)(mg_pfn_t, void *, va_list *); // %M printer size_t mg_vxprintf(void (*)(char, void *), void *, const char *fmt, va_list *); size_t mg_xprintf(void (*fn)(char, void *), void *, const char *fmt, ...); + + + + + // Convenience wrappers around mg_xprintf size_t mg_vsnprintf(char *buf, size_t len, const char *fmt, va_list *ap); size_t mg_snprintf(char *, size_t, const char *fmt, ...); @@ -989,7 +992,6 @@ bool mg_file_printf(struct mg_fs *fs, const char *path, const char *fmt, ...); - #if MG_ENABLE_ASSERT #include #elif !defined(assert) diff --git a/src/webui.c b/src/webui.c index 80422763..8a87b004 100644 --- a/src/webui.c +++ b/src/webui.c @@ -1,36 +1,30 @@ /* - WebUI Library 2.1.1 - - http://webui.me - https://github.com/alifcommunity/webui - - Copyright (c) 2020-2023 Hassan Draga. - Licensed under GNU General Public License v2.0. - All rights reserved. - Canada. + WebUI Library 2.2.0 + http://_webui_core.me + https://github.com/alifcommunity/webui + Copyright (c) 2020-2023 Hassan Draga. + Licensed under GNU General Public License v2.0. + All rights reserved. + Canada. */ // -- Third-party --------------------- #include "mongoose.h" // -- WebUI --------------------------- -#include "webui.h" - -// -- Log ----------------------------- -// #define WEBUI_LOG +#include "webui_core.h" // -- Heap ---------------------------- -webui_t webui; +static _webui_core_t _webui_core; +// -- WebUI JS-Bridge --------- +// This is a uncompressed version to make the debugging +// more easy in the browser using the builtin dev-tools #ifdef WEBUI_LOG #define WEBUI_JS_LOG "true" #else #define WEBUI_JS_LOG "false" #endif - -// -- WebUI JS-Bridge --------- -// This is a uncompressed version to make the debugging -// more easy in the browser using the builtin dev-tools static const char* webui_javascript_bridge = "var _webui_log = " WEBUI_JS_LOG "; \n" "var _webui_ws; \n" @@ -39,12 +33,13 @@ static const char* webui_javascript_bridge = "var _webui_close_reason = 0; \n" "var _webui_close_value; \n" "var _webui_has_events = false; \n" -"const _WEBUI_SIGNATURE = 255; \n" +"const _WEBUI_SIGNATURE = 221; \n" "const _WEBUI_JS = 254; \n" -"const _WEBUI_CLICK = 253; \n" -"const _WEBUI_SWITCH = 252; \n" -"const _WEBUI_CLOSE = 251; \n" -"const _WEBUI_FUNCTION = 250; \n" +"const _WEBUI_JS_QUICK = 253; \n" +"const _WEBUI_CLICK = 252; \n" +"const _WEBUI_SWITCH = 251; \n" +"const _WEBUI_CLOSE = 250; \n" +"const _WEBUI_FUNCTION = 249; \n" "function _webui_close(reason = 0, value = 0) { \n" " _webui_send_event_navigation(value); \n" " _webui_ws_status = false; \n" @@ -58,7 +53,7 @@ static const char* webui_javascript_bridge = "function _webui_start() { \n" " if('WebSocket' in window) { \n" " if(_webui_bind_list.includes(_webui_win_num + '/')) _webui_has_events = true; \n" -" _webui_ws = new WebSocket('ws://localhost:' + _webui_port + '/_ws'); \n" +" _webui_ws = new WebSocket('ws://localhost:' + _webui_port + '/_webui_ws_connect'); \n" " _webui_ws.binaryType = 'arraybuffer'; \n" " _webui_ws.onopen = function () { \n" " _webui_ws.binaryType = 'arraybuffer'; \n" @@ -102,13 +97,14 @@ static const char* webui_javascript_bridge = " _webui_close(_WEBUI_SWITCH, data8utf8); \n" " } else if(buffer8[1] === _WEBUI_CLOSE) { \n" " _webui_close(_WEBUI_CLOSE); \n" -" } else if(buffer8[1] === _WEBUI_JS) { \n" +" } else if(buffer8[1] === _WEBUI_JS_QUICK || buffer8[1] === _WEBUI_JS) { \n" " data8utf8 = data8utf8.replace(/(?:\\r\\n|\\r|\\n)/g, \"\\\\n\"); \n" " if(_webui_log) \n" " console.log('WebUI -> JS [' + data8utf8 + ']'); \n" " var FunReturn = 'undefined'; \n" " var FunError = false; \n" " try { FunReturn = eval('(() => {' + data8utf8 + '})()'); } catch (e) { FunError = true; FunReturn = e.message } \n" +" if(buffer8[1] === _WEBUI_JS_QUICK) return; \n" " if(typeof FunReturn === 'undefined' || FunReturn === undefined) FunReturn = 'undefined'; \n" " if(_webui_log && !FunError) console.log('WebUI -> JS -> Return [' + FunReturn + ']'); \n" " if(_webui_log && FunError) console.log('WebUI -> JS -> Error [' + FunReturn + ']'); \n" @@ -117,8 +113,8 @@ static const char* webui_javascript_bridge = " Return8[0] = _WEBUI_SIGNATURE; \n" " Return8[1] = _WEBUI_JS; \n" " Return8[2] = buffer8[2]; \n" -" if(FunError) Return8[3] = 1; \n" -" else Return8[3] = 0; \n" +" if(FunError) Return8[3] = 0; \n" +" else Return8[3] = 1; \n" " var p = -1; \n" " for (i = 4; i < FunReturn8.length + 4; i++) Return8[i] = FunReturn8[++p]; \n" " if(_webui_ws_status) _webui_ws.send(Return8.buffer); \n" @@ -175,6 +171,8 @@ static const char* webui_javascript_bridge = "function webui_fn(fn, value) { \n" " if(!_webui_has_events && !_webui_bind_list.includes(_webui_win_num + '/' + fn)) \n" " return; \n" +" if(typeof value == 'undefined') \n" +" var value = ''; \n" " var data = ''; \n" " if(_webui_ws_status && fn !== '') { \n" " if(_webui_log) \n" @@ -207,7 +205,7 @@ static const char* webui_javascript_bridge = " } \n" "}); \n" "window.onbeforeunload = function () { \n" -" //_webui_ws.close(); \n" +" _webui_ws.close(); \n" "}; \n" "setTimeout(function () { \n" " if(!_webui_ws_status_once) { \n" @@ -230,10 +228,15 @@ static const char* webui_javascript_bridge = " _webui_close(_WEBUI_SWITCH, link); \n" " } \n" "}); \n" -"navigation.addEventListener('navigate', (event) => { \n" -" const url = new URL(event.destination.url); \n" -" _webui_send_event_navigation(url); \n" -"}); \n" +"if(typeof navigation !== 'undefined') { \n" +" navigation.addEventListener('navigate', (event) => { \n" +" const url = new URL(event.destination.url); \n" +" _webui_send_event_navigation(url); \n" +" }); \n" +"} \n" +"document.body.addEventListener('contextmenu', function(event){ event.preventDefault(); }); \n" +"var inputs = document.getElementsByTagName('input'); \n" +"for(var i = 0; i < inputs.length; i++){ inputs[i].addEventListener('contextmenu', function(event){ event.stopPropagation(); });} \n" "// Load \n" "window.addEventListener('load', _webui_start()); \n"; @@ -242,126 +245,707 @@ static const char* webui_html_served = "Access Denied static const char* webui_html_res_not_available = "Resource Not Available

⚠ Resource Not Available

The requested resource is not available.


WebUI v" WEBUI_VERSION ""; static const char* webui_deno_not_found = "Deno Not Found

⚠ Deno Not Found

Deno is not found on this system.
Please download it from https://github.com/denoland/deno/releases


WebUI v" WEBUI_VERSION ""; static const char* webui_nodejs_not_found = "Node.js Not Found

⚠ Node.js Not Found

Node.js is not found on this system.
Please download it from https://nodejs.org/en/download/


WebUI v" WEBUI_VERSION ""; -static const char* webui_def_icon = ""; -static const char* webui_def_icon_type = "image/svg+xml"; -static const char* webui_js_empty = "WEBUI_JS_EMPTY"; -static const char* webui_js_timeout = "WEBUI_JS_TIMEOUT"; +static const char* webui_def_icon = ""; +static const char* webui_def_icon_type = "Content-Type: image/svg+xml\r\n"; +static const char* webui_js_empty = "ERR_WEBUI_NO_SCRIPT_FOUND"; +static const char* webui_js_timeout = "ERR_WEBUI_TIMEOUT"; static const char* const webui_empty_string = ""; // In case the compiler optimization is disabled -#ifdef _WIN32 - static const char* webui_sep = "\\"; -#else - static const char* webui_sep = "/"; -#endif - -#define WEBUI_NON_EXIST_BROWSER (100) - // -- Functions ----------------------- -bool _webui_ptr_exist(void *p) { - +bool webui_run(void* window, const char* script) { + #ifdef WEBUI_LOG - // printf("[0] _webui_ptr_exist()... \n"); + printf("[User] webui_run([%s])... \n", script); #endif - if(p == NULL) + size_t js_len = strlen(script); + + if(js_len < 1) return false; - for(unsigned int i = 0; i < webui.ptr_position; i++) { + // Dereference + _webui_window_t* win = (_webui_window_t*)window; - if(webui.ptr_list[i] == p) + // Initializing pipe + unsigned char run_id = _webui_get_run_id(); + _webui_core.run_done[run_id] = false; + _webui_core.run_error[run_id] = false; + if((void *)_webui_core.run_responses[run_id] != NULL) + _webui_free_mem((void *)_webui_core.run_responses[run_id]); + + // Prepare the packet + size_t packet_len = 3 + js_len; // [header][js] + char* packet = (char*) _webui_malloc(packet_len); + packet[0] = WEBUI_HEADER_SIGNATURE; // Signature + packet[1] = WEBUI_HEADER_JS_QUICK; // Type + packet[2] = run_id; // ID + for(unsigned int i = 0; i < js_len; i++) // Data + packet[i + 3] = script[i]; + + // Send packets + _webui_window_send(win, packet, packet_len); + _webui_free_mem((void *)packet); + + return true; +} + +bool webui_script(void* window, const char* script, unsigned int timeout_second, char* buffer, size_t buffer_length) { + + // Dereference + _webui_window_t* win = (_webui_window_t*)window; + + #ifdef WEBUI_LOG + printf("[User] webui_script()... \n", script); + printf("[User] webui_script() -> Script [%s] \n", script); + printf("[User] webui_script() -> Response Buffer @ 0x%p \n", buffer); + printf("[User] webui_script() -> Response Buffer Size %lld bytes \n", buffer_length); + #endif + + _webui_init(); + + // Initializing response buffer + if(buffer_length > 0) + memset(buffer, 0x00, buffer_length); + + size_t js_len = strlen(script); + + if(js_len < 1) { + + if(buffer != NULL && buffer_length > 1) + snprintf(buffer, buffer_length, "%s", webui_js_empty); + return false; + } + + // Initializing pipe + unsigned char run_id = _webui_get_run_id(); + _webui_core.run_done[run_id] = false; + _webui_core.run_error[run_id] = false; + if((void *)_webui_core.run_responses[run_id] != NULL) + _webui_free_mem((void *)_webui_core.run_responses[run_id]); + + // Prepare the packet + size_t packet_len = 3 + js_len; // [header][js] + char* packet = (char*) _webui_malloc(packet_len); + packet[0] = WEBUI_HEADER_SIGNATURE; // Signature + packet[1] = WEBUI_HEADER_JS; // Type + packet[2] = run_id; // ID + for(unsigned int i = 0; i < js_len; i++) // Data + packet[i + 3] = script[i]; + + // Send packets + _webui_window_send(win, packet, packet_len); + _webui_free_mem((void *)packet); + + // Wait for UI response + if(timeout_second < 1 || timeout_second > 86400) { + + // Wait forever + for(;;) { + + if(_webui_core.run_done[run_id]) + break; + + _webui_sleep(1); + } + } + else { + + // Using timeout + for(unsigned int n = 0; n <= (timeout_second * 1000); n++) { + + if(_webui_core.run_done[run_id]) + break; + + _webui_sleep(1); + } + } + + if(_webui_core.run_responses[run_id] != NULL) { + + #ifdef WEBUI_LOG + printf("[User] webui_script -> Response found [%s] \n", _webui_core.run_responses[run_id]); + #endif + + // Response found + if(buffer != NULL && buffer_length > 1) { + + // Copy response to the user's response buffer + size_t response_len = strlen(_webui_core.run_responses[run_id]); + size_t bytes_to_cpy = (response_len <= buffer_length ? response_len : buffer_length); + snprintf(buffer, bytes_to_cpy, "%s", _webui_core.run_responses[run_id]); + } + + _webui_free_mem((void *)_webui_core.run_responses[run_id]); + + return _webui_core.run_error[run_id]; + } + + return false; +} + +void* webui_new_window(void) { + + #ifdef WEBUI_LOG + printf("[User] webui_new_window()... \n"); + #endif + + _webui_init(); + + _webui_window_t* win = (_webui_window_t*) _webui_malloc(sizeof(_webui_window_t)); + + // Initialisation + win->window_number = _webui_get_new_window_number(); + win->browser_path = (char*) _webui_malloc(WEBUI_MAX_PATH); + win->profile_path = (char*) _webui_malloc(WEBUI_MAX_PATH); + win->server_root_path = (char*) _webui_malloc(WEBUI_MAX_PATH); + sprintf(win->server_root_path, "%s", WEBUI_DEFAULT_PATH); + + #ifdef WEBUI_LOG + printf("[User] webui_new_window() -> New window @ 0x%p\n", win); + #endif + + return (void*)win; +} + +void webui_close(void* window) { + + // Dereference + _webui_window_t* win = (_webui_window_t*)window; + + #ifdef WEBUI_LOG + printf("[User] webui_close()... \n"); + #endif + + _webui_init(); + + if(win->connected) { + + // Prepare packets + char* packet = (char*) _webui_malloc(4); + packet[0] = WEBUI_HEADER_SIGNATURE; // Signature + packet[1] = WEBUI_HEADER_CLOSE; // Type + packet[2] = 0; // ID + packet[3] = 0; // Data + + // Send packets + _webui_window_send(win, packet, 4); + _webui_free_mem((void *)packet); + } +} + +bool webui_is_shown(void* window) { + + // Dereference + _webui_window_t* win = (_webui_window_t*)window; + + #ifdef WEBUI_LOG + printf("[User] webui_is_shown()... \n"); + #endif + + return win->connected; +} + +void webui_set_multi_access(void* window, bool status) { + + // Dereference + _webui_window_t* win = (_webui_window_t*)window; + + #ifdef WEBUI_LOG + printf("[User] webui_set_multi_access([%d])... \n", status); + #endif + + win->multi_access = status; +} + +void webui_set_icon(void* window, const char* icon, const char* type) { + + // Dereference + _webui_window_t* win = (_webui_window_t*)window; + + #ifdef WEBUI_LOG + printf("[User] webui_set_icon([%s], [%s])... \n", icon, type); + #endif + + win->icon = icon; + win->icon_type = type; +} + +bool webui_show(void* window, const char* content) { + + // Dereference + _webui_window_t* win = (_webui_window_t*)window; + + #ifdef WEBUI_LOG + printf("[User] webui_show()... \n"); + #endif + + return _webui_show(win, content, AnyBrowser); +} + +bool webui_show_browser(void* window, const char* content, unsigned int browser) { + + // Dereference + _webui_window_t* win = (_webui_window_t*)window; + + #ifdef WEBUI_LOG + printf("[User] webui_show_browser([%d])... \n", browser); + #endif + + return _webui_show(win, content, browser); +} + +unsigned int webui_bind(void* window, const char* element, void (*func)(webui_event_t* e)) { + + // Dereference + _webui_window_t* win = (_webui_window_t*)window; + + #ifdef WEBUI_LOG + printf("[User] webui_bind([%s], [0x%p])... \n", element, func); + #endif + + _webui_init(); + + int len = 0; + if(_webui_is_empty(element)) + win->has_events = true; + else + len = strlen(element); + + // [win num][/][element] + char* webui_internal_id = _webui_malloc(3 + 1 + len); + sprintf(webui_internal_id, "%d/%s", win->window_number, element); + + unsigned int cb_index = _webui_get_cb_index(webui_internal_id); + + if(cb_index > 0) { + + // Replace a reference + _webui_core.cb[cb_index] = func; + + _webui_free_mem((void *)webui_internal_id); + } + else { + + // New reference + cb_index = _webui_set_cb_index(webui_internal_id); + + if(cb_index > 0) + _webui_core.cb[cb_index] = func; + else + _webui_free_mem((void *)webui_internal_id); + } + + return cb_index; +} + +const char* webui_get_string(webui_event_t* e) { + + #ifdef WEBUI_LOG + printf("[User] webui_get_string()... \n"); + #endif + + if(e->data != NULL) { + size_t len = strlen(e->data); + if(len > 0 && len <= WEBUI_MAX_BUF) + return (const char *) e->data; + } + + return webui_empty_string; +} + +long long int webui_get_int(webui_event_t* e) { + + #ifdef WEBUI_LOG + printf("[User] webui_get_int()... \n"); + #endif + + char *endptr; + + if(e->data != NULL) { + size_t len = strlen(e->data); + if(len > 0 && len <= 20) // 64-bit max is -9,223,372,036,854,775,808 (20 character) + return strtoll((const char *) e->data, &endptr, 10); + } + + return 0; +} + +bool webui_get_bool(webui_event_t* e) { + + #ifdef WEBUI_LOG + printf("[User] webui_get_bool()... \n"); + #endif + + const char* str = webui_get_string(e); + if(str[0] == 't' || str[0] == 'T') // true || True + return true; + + return false; +} + +void webui_return_int(webui_event_t* e, long long int n) { + + #ifdef WEBUI_LOG + printf("[User] webui_return_int([%lld])... \n", n); + #endif + + // Free + if(e->response != NULL) + _webui_free_mem(e->response); + + // Int to Str + // 64-bit max is -9,223,372,036,854,775,808 (20 character) + char* buf = (char*) _webui_malloc(20); + sprintf(buf, "%lld", n); + + // Set response + e->response = buf; +} + +void webui_return_string(webui_event_t* e, char* s) { + + #ifdef WEBUI_LOG + printf("[User] webui_return_string([%s])... \n", s); + #endif + + if(_webui_is_empty(s)) + return; + + // Free + if(e->response != NULL) + _webui_free_mem(e->response); + + // Copy Str + int len = strlen(s); + char* buf = (char*) _webui_malloc(len); + memcpy(buf, s, len); + + // Set response + e->response = buf; +} + +void webui_return_bool(webui_event_t* e, bool b) { + + #ifdef WEBUI_LOG + printf("[User] webui_return_bool([%d])... \n", b); + #endif + + // Free + if(e->response != NULL) + _webui_free_mem(e->response); + + // Bool to Str + int len = 1; + char* buf = (char*) _webui_malloc(len); + sprintf(buf, "%d", b); + + // Set response + e->response = buf; +} + +void webui_exit(void) { + + #ifdef WEBUI_LOG + printf("[User] webui_exit()... \n"); + #endif + + _webui_core.exit_now = true; + + // Let's give other threads more time to + // safely exit and finish their cleaning up. + _webui_sleep(100); +} + +void webui_wait(void) { + + #ifdef WEBUI_LOG + printf("[Loop] webui_wait()... \n"); + #endif + + _webui_init(); + + if(_webui_core.startup_timeout > 0) { + + #ifdef WEBUI_LOG + printf("[Loop] webui_wait() -> Using timeout %d second\n", _webui_core.startup_timeout); + #endif + + // Wait for browser to start + _webui_wait_for_startup(); + + #ifdef WEBUI_LOG + printf("[Loop] webui_wait() -> Wait for connected UI...\n"); + #endif + + while(_webui_core.servers > 0) { + + #ifdef WEBUI_LOG + // printf("[%d/%d]", _webui_core.servers, _webui_core.connections); + #endif + _webui_sleep(50); + } + } + else { + + #ifdef WEBUI_LOG + printf("[Loop] webui_wait() -> Infinite wait...\n"); + #endif + + // Infinite wait + while(!_webui_core.exit_now) + _webui_sleep(50); + } + + #ifdef WEBUI_LOG + printf("[Loop] webui_wait() -> Wait finished.\n"); + #endif + + // Final cleaning + _webui_clean(); +} + +void webui_set_timeout(unsigned int second) { + + #ifdef WEBUI_LOG + printf("[User] webui_set_timeout([%d])... \n", second); + #endif + + _webui_init(); + + if(second > WEBUI_MAX_TIMEOUT) + second = WEBUI_MAX_TIMEOUT; + + _webui_core.startup_timeout = second; +} + +void webui_set_runtime(void* window, unsigned int runtime) { + + // Dereference + _webui_window_t* win = (_webui_window_t*)window; + + #ifdef WEBUI_LOG + printf("[User] webui_script_runtime(%d)... \n", runtime); + #endif + + _webui_init(); + + if(runtime != Deno && runtime != NodeJS) + win->runtime = None; + else + win->runtime = runtime; +} + +// -- Interface's Functions ---------------- +void _webui_interface_bind_handler(webui_event_t* e) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_interface_bind_handler()... \n"); + #endif + + // Generate WebUI internal id + char* webui_internal_id = _webui_generate_internal_id(e->window, e->element); + unsigned int cb_index = _webui_get_cb_index(webui_internal_id); + + if(cb_index > 0 && _webui_core.cb_interface[cb_index] != NULL) + _webui_core.cb_interface[cb_index](e->window, e->type, e->element, e->data, (char*)&e->response); + + if(_webui_is_empty((const char *)e->response)) + e->response = (char*)webui_empty_string; + + // Free + _webui_free_mem((void *)webui_internal_id); + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_interface_bind_handler()... user-callback response [%s]\n", (const char *)e->response); + #endif +} + +unsigned int webui_interface_bind(void* window, const char* element, void (*func)(void*, unsigned int, char*, char*, char*)) { + + // Dereference + _webui_window_t* win = (_webui_window_t*)window; + + #ifdef WEBUI_LOG + printf("[User] webui_interface_bind()... \n"); + #endif + + // Bind + unsigned int cb_index = webui_bind(win, element, _webui_interface_bind_handler); + _webui_core.cb_interface[cb_index] = func; + return cb_index; +} + +void webui_interface_set_response(char* ptr, const char* response) { + + #ifdef WEBUI_LOG + printf("[User] webui_interface_set_response()... \n"); + printf("[User] webui_interface_set_response() -> Response [%s] \n", response); + #endif + + printf("[User] webui_interface_set_response() -> ptr 1 0x%p \n", ptr); + printf("[User] webui_interface_set_response() -> ptr 2 0x%p \n", *((char**)ptr)); + + size_t len = strlen(response); + + if(len < 1) + return; + + char* buf = (char*) _webui_malloc(len); + char** _ptr = (char**)ptr; + *_ptr = buf; + strcpy(*_ptr, response); + + #ifdef WEBUI_LOG + printf("[User] webui_interface_set_response() -> Internal buffer @ 0x%p \n", (*_ptr)); + printf("[User] webui_interface_set_response() -> Internal buffer [%s] \n", (*_ptr)); + #endif + + printf("[User] webui_interface_set_response() -> ptr 1 0x%p \n", ptr); + printf("[User] webui_interface_set_response() -> ptr 2 0x%p \n", *((char**)ptr)); +} + +bool webui_interface_is_app_running(void) { + + #ifdef WEBUI_LOG + // printf("[User] webui_is_app_running()... \n"); + #endif + + static bool app_is_running = true; + + // Stop if already flagged + if(!app_is_running) return false; + + // Initialization + if(!_webui_core.initialized) + _webui_init(); + + // Get app status + if(_webui_core.exit_now) { + app_is_running = false; + } + else if(_webui_core.startup_timeout > 0) { + if(_webui_core.servers < 1) + app_is_running = false; + } + + // Final cleaning + if(!app_is_running) { + #ifdef WEBUI_LOG + printf("[User] webui_is_app_running() -> App Stopped.\n"); + #endif + _webui_clean(); + } + + return app_is_running; +} + +unsigned int webui_interface_get_window_id(void* window) { + + #ifdef WEBUI_LOG + printf("[User] webui_interface_get_window_id()... \n"); + #endif + + // Dereference + _webui_window_t* win = (_webui_window_t*)window; + + return win->window_number; +} + +// -- Core's Functions ---------------- +bool _webui_ptr_exist(void* ptr) { + + #ifdef WEBUI_LOG + // printf("[Core]\t\t_webui_ptr_exist()... \n"); + #endif + + if(ptr == NULL) + return false; + + for(unsigned int i = 0; i < _webui_core.ptr_position; i++) { + + if(_webui_core.ptr_list[i] == ptr) return true; } return false; } -void _webui_ptr_add(void *p, size_t size) { +static void _webui_ptr_add(void* ptr, size_t size) { #ifdef WEBUI_LOG - // printf("[0] _webui_ptr_add(0x%p)... \n", p); + // printf("[Core]\t\t_webui_ptr_add(0x%p)... \n", ptr); #endif - if(p == NULL) + if(ptr == NULL) return; - if(!_webui_ptr_exist(p)) { + if(!_webui_ptr_exist(ptr)) { - for(unsigned int i = 0; i < webui.ptr_position; i++) { + for(unsigned int i = 0; i < _webui_core.ptr_position; i++) { - if(webui.ptr_list[i] == NULL) { + if(_webui_core.ptr_list[i] == NULL) { #ifdef WEBUI_LOG - printf("[0] _webui_ptr_add(0x%p)... Allocate %d bytes\n", p, (int)size); + printf("[Core]\t\t_webui_ptr_add(0x%p)... Allocate %d bytes\n", ptr, (int)size); #endif - webui.ptr_list[i] = p; - webui.ptr_size[i] = size; + _webui_core.ptr_list[i] = ptr; + _webui_core.ptr_size[i] = size; return; } } #ifdef WEBUI_LOG - printf("[0] _webui_ptr_add(0x%p)... Allocate %d bytes\n", p, (int)size); + printf("[Core]\t\t_webui_ptr_add(0x%p)... Allocate %d bytes\n", ptr, (int)size); #endif - webui.ptr_list[webui.ptr_position] = p; - webui.ptr_size[webui.ptr_position] = size; - webui.ptr_position++; - if(webui.ptr_position >= WEBUI_MAX_ARRAY) - webui.ptr_position = (WEBUI_MAX_ARRAY - 1); + _webui_core.ptr_list[_webui_core.ptr_position] = ptr; + _webui_core.ptr_size[_webui_core.ptr_position] = size; + _webui_core.ptr_position++; + if(_webui_core.ptr_position >= WEBUI_MAX_ARRAY) + _webui_core.ptr_position = (WEBUI_MAX_ARRAY - 1); } } -void webui_clean_mem(void* p) { - - #ifdef WEBUI_LOG - printf("[0] webui_clean_mem(0x%p)... \n", p); - #endif - - _webui_free_mem((void *) &p); -} - -void _webui_free_mem(void **p) { +static void _webui_free_mem(void* ptr) { #ifdef WEBUI_LOG - // printf("[0] _webui_free_mem(0x%p)... \n", *p); + printf("[Core]\t\t_webui_free_mem(0x%p)... \n", ptr); #endif - if(p == NULL || *p == NULL) + if(ptr == NULL) return; - for(unsigned int i = 0; i < webui.ptr_position; i++) { + for(unsigned int i = 0; i < _webui_core.ptr_position; i++) { - if(webui.ptr_list[i] == *p) { + if(_webui_core.ptr_list[i] == ptr) { #ifdef WEBUI_LOG - printf("[0] _webui_free_mem(0x%p)... Free %d bytes\n", *p, (int)webui.ptr_size[i]); + printf("[Core]\t\t_webui_free_mem(0x%p)... Free %d bytes\n", ptr, (int)_webui_core.ptr_size[i]); #endif - memset(*p, 0x00, webui.ptr_size[i]); - free(*p); + memset(ptr, 0x00, _webui_core.ptr_size[i]); + free(ptr); - webui.ptr_size[i] = 0; - webui.ptr_list[i] = NULL; + _webui_core.ptr_size[i] = 0; + _webui_core.ptr_list[i] = NULL; } } - for(int i = webui.ptr_position; i >= 0; i--) { + for(int i = _webui_core.ptr_position; i >= 0; i--) { - if(webui.ptr_list[i] == NULL) { + if(_webui_core.ptr_list[i] == NULL) { - webui.ptr_position = i; + _webui_core.ptr_position = i; break; } } - - *p = NULL; } -void _webui_free_all_mem(void) { +static void _webui_free_all_mem(void) { #ifdef WEBUI_LOG - printf("[0] _webui_free_all_mem()... \n"); + printf("[Core]\t\t_webui_free_all_mem()... \n"); #endif // Makes sure we run this once @@ -370,26 +954,26 @@ void _webui_free_all_mem(void) { freed = true; void* ptr = NULL; - for(unsigned int i = 0; i < webui.ptr_position; i++) { + for(unsigned int i = 0; i < _webui_core.ptr_position; i++) { - ptr = webui.ptr_list[i]; + ptr = _webui_core.ptr_list[i]; if(ptr != NULL) { #ifdef WEBUI_LOG - printf("[0] _webui_free_all_mem()... Free %d bytes @ 0x%p\n", (int)webui.ptr_size[i], ptr); + printf("[Core]\t\t_webui_free_all_mem()... Free %d bytes @ 0x%p\n", (int)_webui_core.ptr_size[i], ptr); #endif - memset(ptr, 0x00, webui.ptr_size[i]); + memset(ptr, 0x00, _webui_core.ptr_size[i]); free(ptr); } } } -void _webui_panic(void) { +static void _webui_panic(void) { #ifdef WEBUI_LOG - printf("[0] _webui_panic()... \n"); + printf("[Core]\t\t_webui_panic()... \n"); #endif webui_exit(); @@ -416,7 +1000,7 @@ size_t _webui_round_to_memory_block(int size) { void* _webui_malloc(int size) { #ifdef WEBUI_LOG - // printf("[0] _webui_malloc([%d])... \n", size); + printf("[Core]\t\t_webui_malloc([%d])... \n", size); #endif // Make sure we have the null @@ -452,10 +1036,10 @@ void* _webui_malloc(int size) { return block; } -void _webui_sleep(long unsigned int ms) { +static void _webui_sleep(long unsigned int ms) { #ifdef WEBUI_LOG - // printf("[0] _webui_sleep([%d])... \n", ms); + // printf("[Core]\t\t_webui_sleep([%d])... \n", ms); #endif #ifdef _WIN32 @@ -468,7 +1052,7 @@ void _webui_sleep(long unsigned int ms) { long _webui_timer_diff(struct timespec *start, struct timespec *end) { #ifdef WEBUI_LOG - // printf("[0] _webui_timer_diff()... \n"); + // printf("[Core]\t\t_webui_timer_diff()... \n"); #endif return ( @@ -479,10 +1063,10 @@ long _webui_timer_diff(struct timespec *start, struct timespec *end) { ); } -void _webui_timer_clock_gettime(struct timespec *spec) { +static void _webui_timer_clock_gettime(struct timespec *spec) { #ifdef WEBUI_LOG - // printf("[0] _webui_timer_clock_gettime()... \n"); + // printf("[Core]\t\t_webui_timer_clock_gettime()... \n"); #endif #ifdef _WIN32 @@ -496,19 +1080,19 @@ void _webui_timer_clock_gettime(struct timespec *spec) { #endif } -void _webui_timer_start(webui_timer_t* t) { +static void _webui_timer_start(_webui_timer_t* t) { #ifdef WEBUI_LOG - // printf("[0] _webui_timer_start()... \n"); + // printf("[Core]\t\t_webui_timer_start()... \n"); #endif _webui_timer_clock_gettime(&t->start); } -bool _webui_timer_is_end(webui_timer_t* t, unsigned int ms) { +bool _webui_timer_is_end(_webui_timer_t* t, unsigned int ms) { #ifdef WEBUI_LOG - // printf("[0] _webui_timer_is_end()... \n"); + // printf("[Core]\t\t_webui_timer_is_end()... \n"); #endif _webui_timer_clock_gettime(&t->now); @@ -519,21 +1103,10 @@ bool _webui_timer_is_end(webui_timer_t* t, unsigned int ms) { return false; } -#ifdef WEBUI_LOG - void _webui_print_hex(const char* data, size_t len) { - - for(size_t i = 0; i < len; i++) { - - printf("0x%02X ", (unsigned char) *data); - data++; - } - } -#endif - bool _webui_is_empty(const char* s) { #ifdef WEBUI_LOG - // printf("[0] _webui_is_empty()... \n"); + printf("[Core]\t\t_webui_is_empty()... \n"); #endif if((s != NULL) && (s[0] != '\0')) @@ -544,7 +1117,7 @@ bool _webui_is_empty(const char* s) { bool _webui_file_exist_mg(void *ev_data) { #ifdef WEBUI_LOG - printf("[0] _webui_file_exist_mg()... \n"); + printf("[Core]\t\t_webui_file_exist_mg()... \n"); #endif char* file; @@ -560,13 +1133,13 @@ bool _webui_file_exist_mg(void *ev_data) { // Get full path // [current folder][/][file] - full_path = (char*) _webui_malloc(strlen(webui.executable_path) + 1 + strlen(file)); - sprintf(full_path, "%s%s%s", webui.executable_path, webui_sep, file); + full_path = (char*) _webui_malloc(strlen(_webui_core.executable_path) + 1 + strlen(file)); + sprintf(full_path, "%s%s%s", _webui_core.executable_path, webui_sep, file); bool exist = _webui_file_exist(full_path); - _webui_free_mem((void *) &file); - _webui_free_mem((void *) &full_path); + _webui_free_mem((void *)file); + _webui_free_mem((void *)full_path); return exist; } @@ -574,7 +1147,7 @@ bool _webui_file_exist_mg(void *ev_data) { bool _webui_file_exist(char* file) { #ifdef WEBUI_LOG - printf("[0] _webui_file_exist([%s])... \n", file); + printf("[Core]\t\t_webui_file_exist([%s])... \n", file); #endif if(_webui_is_empty(file)) @@ -588,7 +1161,7 @@ bool _webui_file_exist(char* file) { const char* _webui_get_extension(const char*f) { #ifdef WEBUI_LOG - printf("[0] _webui_get_extension()... \n"); + printf("[Core]\t\t_webui_get_extension()... \n"); #endif if(f == NULL) @@ -601,19 +1174,19 @@ const char* _webui_get_extension(const char*f) { return ext + 1; } -unsigned int _webui_get_run_id(void) { +unsigned char _webui_get_run_id(void) { #ifdef WEBUI_LOG - printf("[0] _webui_get_run_id()... \n"); + printf("[Core]\t\t_webui_get_run_id()... \n"); #endif - return ++webui.run_last_id; + return ++_webui_core.run_last_id; } bool _webui_socket_test_listen_mg(unsigned int port_num) { #ifdef WEBUI_LOG - printf("[0] _webui_socket_test_listen_mg([%d])... \n", port_num); + printf("[Core]\t\t_webui_socket_test_listen_mg([%d])... \n", port_num); #endif struct mg_connection *c; @@ -636,11 +1209,2739 @@ bool _webui_socket_test_listen_mg(unsigned int port_num) { return true; } +bool _webui_port_is_used(unsigned int port_num) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_port_is_used([%d])... \n", port_num); + #endif + + #ifdef _WIN32 + // Listener test + if(!_webui_socket_test_listen_win32(port_num)) + return true; // Port is already used + return false; // Port is not in use + #else + // Listener test MG + if(!_webui_socket_test_listen_mg(port_num)) + return true; // Port is already used + return false; // Port is not in use + #endif +} + +static void _webui_serve_file(_webui_window_t* win, struct mg_connection *c, void *ev_data) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_serve_file()... \n"); + #endif + + // Serve a normal text based file + // send with HTTP 200 status code + + struct mg_http_serve_opts opts = { + + .root_dir = win->server_root_path + }; + + mg_http_serve_dir(c, ev_data, &opts); +} + +bool _webui_deno_exist(void) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_deno_exist()... \n"); + #endif + + static bool found = false; + + if(found) + return true; + + if(_webui_cmd_sync("deno --version", false) == 0) { + + found = true; + return true; + } + else + return false; +} + +bool _webui_nodejs_exist(void) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_nodejs_exist()... \n"); + #endif + + static bool found = false; + + if(found) + return true; + + if(_webui_cmd_sync("node -v", false) == 0) { + + found = true; + return true; + } + else + return false; +} + +const char* _webui_interpret_command(const char* cmd) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_interpret_command()... \n"); + #endif + + // Redirect stderr to stdout + char cmd_redirected[1024]; + sprintf(cmd_redirected, "%s 2>&1", cmd); + + FILE *runtime = WEBUI_POPEN(cmd_redirected, "r"); + + if(runtime == NULL) + return NULL; + + // Get STDOUT length + // int c; + // while ((c = fgetc(runtime)) != EOF) + // len++; + int len = 1024 * 8; + + // Read STDOUT + char* out = (char*) _webui_malloc(len); + char* line = (char*) _webui_malloc(4000); + while(fgets(line, 4000, runtime) != NULL) + strcat(out, line); + + WEBUI_PCLOSE(runtime); + _webui_free_mem((void *)line); + + return (const char*) out; +} + +static void _webui_interpret_file(_webui_window_t* win, struct mg_connection *c, void *ev_data, char* index) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_interpret_file()... \n"); + #endif + + // Run the JavaScript / TypeScript runtime + // and send back the output with HTTP 200 status code + // otherwise, send the file as a normal text based one + + char* file; + char* full_path; + + if(index != NULL && !_webui_is_empty(index)) { + + // Parse index file + file = index; + full_path = index; + } + else { + + // Parse other files + + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + + // Get file name + file = (char*) _webui_malloc(hm->uri.len); + const char* p = hm->uri.ptr; + p++; // Skip "/" + sprintf(file, "%.*s", (int)(hm->uri.len - 1), p); + + // Get full path + // [current folder][/][file] + full_path = (char*) _webui_malloc(strlen(_webui_core.executable_path) + 1 + strlen(file)); + sprintf(full_path, "%s%s%s", _webui_core.executable_path, webui_sep, file); + + if(!_webui_file_exist(full_path)) { + + // File not exist - 404 + _webui_serve_file(win, c, ev_data); + + _webui_free_mem((void *)file); + _webui_free_mem((void *)full_path); + return; + } + } + + // Get file extension + const char* extension = _webui_get_extension(file); + + if(strcmp(extension, "ts") == 0 || strcmp(extension, "js") == 0) { + + // TypeScript / JavaScript + + if(win->runtime == Deno) { + + // Use Deno + + if(_webui_deno_exist()) { + + // Set command + // [disable coloring][file] + char* cmd = (char*) _webui_malloc(64 + strlen(full_path)); + #ifdef _WIN32 + sprintf(cmd, "Set NO_COLOR=1 & deno run --allow-all \"%s\"", full_path); + #else + sprintf(cmd, "NO_COLOR=1 & deno run --allow-all \"%s\"", full_path); + #endif + + // Run command + const char* out = _webui_interpret_command(cmd); + + if(out != NULL) { + + // Send deno output + mg_http_reply( + c, 200, + "", + out + ); + } + else { + + // Deno failed. + // Serve as a normal text-based file + _webui_serve_file(win, c, ev_data); + } + + _webui_free_mem((void *)cmd); + _webui_free_mem((void *)out); + } + else { + + // Deno not installed + + mg_http_reply( + c, 200, + "", + webui_deno_not_found + ); + } + } + else if(win->runtime == NodeJS) { + + // Use Nodejs + + if(_webui_nodejs_exist()) { + + // Set command + // [node][file] + char* cmd = (char*) _webui_malloc(16 + strlen(full_path)); + sprintf(cmd, "node \"%s\"", full_path); + + // Run command + const char* out = _webui_interpret_command(cmd); + + if(out != NULL) { + + // Send Node.js output + mg_http_reply( + c, 200, + "", + out + ); + } + else { + + // Node.js failed. + // Serve as a normal text-based file + _webui_serve_file(win, c, ev_data); + } + + _webui_free_mem((void *)cmd); + _webui_free_mem((void *)out); + } + else { + + // Node.js not installed + + mg_http_reply( + c, 200, + "", + webui_nodejs_not_found + ); + } + } + else { + + // Unknown runtime + // Serve as a normal text-based file + _webui_serve_file(win, c, ev_data); + } + } + else { + + // Unknown file extension + // Serve as a normal text-based file + _webui_serve_file(win, c, ev_data); + } + + _webui_free_mem((void *)file); + _webui_free_mem((void *)full_path); +} + +const char* _webui_generate_js_bridge(_webui_window_t* win) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_generate_js_bridge()... \n"); + #endif + + // Calculate the cb size + size_t cb_mem_size = 64; // To hold 'const _webui_bind_list = ["elem1", "elem2",];' + for(unsigned int i = 1; i < WEBUI_MAX_ARRAY; i++) + if(_webui_core.html_elements[i] != NULL && !_webui_is_empty(_webui_core.html_elements[i])) + cb_mem_size += strlen(_webui_core.html_elements[i]) + 3; + + // Generate the cb array + char* event_cb_js_array = (char*) _webui_malloc(cb_mem_size); + strcat(event_cb_js_array, "const _webui_bind_list = ["); + for(unsigned int i = 1; i < WEBUI_MAX_ARRAY; i++) { + if(_webui_core.html_elements[i] != NULL && !_webui_is_empty(_webui_core.html_elements[i])) { + strcat(event_cb_js_array, "\""); + strcat(event_cb_js_array, _webui_core.html_elements[i]); + strcat(event_cb_js_array, "\","); + } + } + strcat(event_cb_js_array, "]; \n"); + + // Generate the full WebUI JS-Bridge + size_t len = cb_mem_size + strlen(webui_javascript_bridge); + char* js = (char*) _webui_malloc(len); + sprintf(js, + "_webui_port = %d; \n_webui_win_num = %d; \n%s \n%s \n", + win->server_port, win->window_number, event_cb_js_array, webui_javascript_bridge + ); + + return js; +} + +static void _webui_server_event_handler(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { + + #ifdef WEBUI_LOG + // printf("[Core]\t\t_webui_server_event_handler()... \n"); + #endif + + _webui_window_t* win = (_webui_window_t *) fn_data; + + if(ev == MG_EV_HTTP_MSG) { + + struct mg_http_message *hm = (struct mg_http_message *) ev_data; + + if(mg_http_match_uri(hm, "/_webui_ws_connect")) { + + // WebSocket + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_server_event_handler()... HTML Upgrade to WebSocket\n"); + #endif + + mg_ws_upgrade(c, hm, NULL); + } + else if(mg_http_match_uri(hm, "/webui.js")) { + + // WebUI JS-Bridge + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_server_event_handler()... HTML WebUI JS\n"); + #endif + + // Generate JavaScript bridge + const char* js = _webui_generate_js_bridge(win); + + // Header + // Content-Type: text/javascript + + // Send + mg_http_reply( + c, 200, + "", + js + ); + + _webui_free_mem((void *)js); + } + else if(strncmp(hm->uri.ptr, "/WEBUI/FUNC/", 12) == 0 && hm->uri.len >= 15) { + + // Function Call (With response) + + // [/WEBUI/FUNC/ELEMENT_ID/DATA] + // 0 12 + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_server_event_handler()... CB start\n"); + #endif + + // Copy packet + size_t len = hm->uri.len; + char* packet = (char*) _webui_malloc(len); + memcpy(packet, hm->uri.ptr, len); + + // Get html element id + char* element = &packet[12]; + size_t element_len = 0; + for (size_t i = 12; i < len; i++) { + if(packet[i] == '/') { + packet[i] = '\0'; + break; + } + element_len++; + } + + // [/WEBUI/FUNC/ELEMENT_ID DATA] + // 0 12 + + // Get data + void* data = &packet[11 + element_len + 2]; + size_t data_len = strlen(data); + + // Generate WebUI internal id + char* webui_internal_id = _webui_generate_internal_id(win, element); + + // Call user function + webui_event_t e; + e.element = element; + e.window = win; + e.data = data; + e.response = NULL; + e.type = WEBUI_EVENT_CALLBACK; + + unsigned int cb_index = _webui_get_cb_index(webui_internal_id); + + // Check for bind + if(cb_index > 0 && _webui_core.cb[cb_index] != NULL) { + + // Call user cb + _webui_core.cb[cb_index](&e); + } + + if(_webui_is_empty(e.response)) + e.response = (char*)webui_empty_string; + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_server_event_handler()... user-callback response [%s]\n", (const char*)e.response); + #endif + + // Send response + mg_http_reply( + c, 200, + "", + e.response + ); + + // Free + _webui_free_mem((void *)packet); + _webui_free_mem((void *)webui_internal_id); + _webui_free_mem((void *)e.response); + } + else if(mg_http_match_uri(hm, "/")) { + + // [/] + + if(win->is_embedded_html) { + + // Main HTML + + if(!win->multi_access && win->html_handled) { + + // Main HTML already handled. + // Forbidden 403 + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_server_event_handler()... HTML Main Already Handled (403)\n"); + #endif + + // Header + // text/html; charset=utf-8 + + mg_http_reply( + c, 403, + "", + webui_html_served + ); + } + else { + + // Send main HTML + + win->html_handled = true; + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_server_event_handler()... HTML Main\n"); + #endif + + char* html = (char*) webui_empty_string; + + if(win->html != NULL) { + + // Generate the full WebUI JS-Bridge + const char* js = _webui_generate_js_bridge(win); + + // Inject WebUI JS-Bridge into HTML + size_t len = strlen(win->html) + strlen(js) + 128; + html = (char*) _webui_malloc(len); + sprintf(html, + "%s \n ", + win->html, js + ); + + _webui_free_mem((void *)js); + } + + // // HTTP Header + // char header[512]; + // memset(header, 0x00, 512); + // sprintf(header, + // "HTTP/1.1 200 OK\r\n" + // "Content-Type: text/html; charset=utf-8\r\n" + // "Host: localhost:%d\r\n" + // "Cache-Control: no-cache\r\n" + // "Content-Length: %d\r\n" + // "Connection: close\r\n\r\n", + // win->server_port, strlen(html) + // ); + + // Send + mg_http_reply( + c, 200, + "", + html + ); + + _webui_free_mem((void *)html); + } + } + else { + + // Serve local files + + win->html_handled = true; + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_server_event_handler()... HTML Root Index\n"); + #endif + + // Set full path + // [Path][Sep][File Name] + char* index = (char*) _webui_malloc(strlen(_webui_core.executable_path) + 1 + 8); + + // Index.ts + sprintf(index, "%s%sindex.ts", _webui_core.executable_path, webui_sep); + if(_webui_file_exist(index)) { + + // TypeScript Index + _webui_interpret_file(win, c, ev_data, index); + + _webui_free_mem((void *)index); + return; + } + + // Index.js + sprintf(index, "%s%sindex.js", _webui_core.executable_path, webui_sep); + if(_webui_file_exist(index)) { + + // JavaScript Index + _webui_interpret_file(win, c, ev_data, index); + + _webui_free_mem((void *)index); + return; + } + + _webui_free_mem((void *)index); + + // Index.html + // Serve as a normal HTML text-based file + _webui_serve_file(win, c, ev_data); + } + } + else if(mg_http_match_uri(hm, "/favicon.ico") || mg_http_match_uri(hm, "/favicon.svg")) { + + // Favicon + + if(win->icon != NULL && win->icon_type != NULL) { + + // Custom user icon + + char* icon_header = (char*) _webui_malloc(strlen(win->icon_type) + 32); + sprintf(icon_header, "Content-Type: %s\r\n", win->icon_type); + + // User icon + mg_http_reply( + c, 200, + icon_header, + win->icon + ); + } + else if(_webui_file_exist_mg(ev_data)) { + + // Local icon file + _webui_serve_file(win, c, ev_data); + } + else { + + // Default embedded icon + + if(mg_http_match_uri(hm, "/favicon.ico")) { + + mg_http_reply(c, 302, "Location: /favicon.svg\r\n", ""); + } + else { + + // TODO: Use webui_def_icon_type + + // Header + // Content-Type: image/svg+xml + + // Default icon + mg_http_reply( + c, 200, + webui_def_icon_type, + webui_def_icon + ); + } + } + } + else { + + // [/file] + + if(win->is_embedded_html) { + + if(win->runtime != None) { + + // Interpret file + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_server_event_handler()... HTML Interpret file\n"); + #endif + + _webui_interpret_file(win, c, ev_data, NULL); + } + else { + + // Serve local files + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_server_event_handler()... HTML Root file\n"); + #endif + + // Serve as a normal text-based file + _webui_serve_file(win, c, ev_data); + } + } + else { + + // This is a non-server-folder mode + // but the HTML body request a local file + // this request can be css, js, image, etc... + + if(_webui_file_exist_mg(ev_data)) { + + // Serve as a normal text-based file + _webui_serve_file(win, c, ev_data); + } + else { + + // 404 + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_server_event_handler()... HTML 404\n"); + #endif + + // Header + // text/html; charset=utf-8 + + mg_http_reply( + c, 404, + "", + webui_html_res_not_available + ); + } + } + } + } + else if(ev == MG_EV_WS_MSG) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_server_event_handler()... WebSocket Data\n"); + #endif + + struct mg_ws_message *wm = (struct mg_ws_message *) ev_data; + + // Parse the packet + _webui_window_receive(win, wm->data.ptr, wm->data.len); + } + else if(ev == MG_EV_WS_OPEN) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_server_event_handler()... WebSocket Connected\n"); + #endif + + int event_type = WEBUI_EVENT_CONNECTED; + + if(!win->connected) { + + // First connection + + win->connected = true; // server thread + _webui_core.connections++; // main loop + _webui_core.mg_connections[win->window_number] = c; // websocket send func + } + else { + + if(win->multi_access) { + + // Multi connections + win->connections++; + event_type = WEBUI_EVENT_MULTI_CONNECTION; + } + else { + + // UNWANTED Multi connections + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_server_event_handler() -> UNWANTED Multi Connections\n"); + #endif + + mg_close_conn(c); + event_type = WEBUI_EVENT_UNWANTED_CONNECTION; + } + } + + // Events + if(win->has_events) { + + // Generate WebUI internal id + char* webui_internal_id = _webui_generate_internal_id(win, ""); + + _webui_window_event( + win, // Window + webui_internal_id, // WebUI Internal ID + "", // User HTML ID + NULL, // User Custom Data + 0, // User Data Len + event_type // Type of this event + ); + } + } + else if(ev == MG_EV_WS_CTL) { + + win->html_handled = false; + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_server_event_handler()... WebSocket Closed\n"); + #endif + + if(win->connected) { + + if(win->multi_access && win->connections > 0) { + + // Multi connections close + win->connections--; + } + else { + + // Main connection close + _webui_core.connections--; // main loop + win->connected = false; // server thread + } + } + + // Events + if(win->has_events) { + + // Generate WebUI internal id + char* webui_internal_id = _webui_generate_internal_id(win, ""); + + _webui_window_event( + win, // Window + webui_internal_id, // WebUI Internal ID + "", // User HTML ID + NULL, // User Custom Data + 0, // User Data Len + WEBUI_EVENT_DISCONNECTED // Type of this event + ); + } + } +} + +bool _webui_browser_create_profile_folder(_webui_window_t* win, unsigned int browser) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_browser_create_profile_folder(%d)... \n", browser); + #endif + + const char* temp = _webui_browser_get_temp_path(browser); + + if(browser == Chrome) { + + // Google Chrome + sprintf(win->profile_path, "%s%s.WebUI%sWebUIChromeProfile", temp, webui_sep, webui_sep); + return true; + } + else if(browser == Edge) { + + // Edge + sprintf(win->profile_path, "%s%s.WebUI%sWebUIEdgeProfile", temp, webui_sep, webui_sep); + return true; + } + else if(browser == Epic) { + + // Epic + sprintf(win->profile_path, "%s%s.WebUI%sWebUIEpicProfile", temp, webui_sep, webui_sep); + return true; + } + else if(browser == Vivaldi) { + + // Vivaldi + sprintf(win->profile_path, "%s%s.WebUI%sWebUIVivaldiProfile", temp, webui_sep, webui_sep); + return true; + } + else if(browser == Brave) { + + // Brave + sprintf(win->profile_path, "%s%s.WebUI%sWebUIBraveProfile", temp, webui_sep, webui_sep); + return true; + } + else if(browser == Yandex) { + + // Yandex + sprintf(win->profile_path, "%s%s.WebUI%sWebUIYandexProfile", temp, webui_sep, webui_sep); + return true; + } + else if(browser == Chromium) { + + // Chromium + sprintf(win->profile_path, "%s%s.WebUI%sWebUIChromiumProfile", temp, webui_sep, webui_sep); + return true; + } + else if(browser == Firefox) { + + // Firefox (We need to create a folder) + + char* profile_name = "WebUIFirefoxProfile"; + + char firefox_profile_path[1024]; + sprintf(firefox_profile_path, "%s%s.WebUI%s%s", temp, webui_sep, webui_sep, profile_name); + + if(!_webui_folder_exist(firefox_profile_path)) { + + char buf[2048]; + + sprintf(buf, "%s -CreateProfile \"WebUI %s\"", win->browser_path, firefox_profile_path); + _webui_cmd_sync(buf, false); + + // Creating the browser profile + for(unsigned int n = 0; n <= (_webui_core.startup_timeout * 4); n++) { + + if(_webui_folder_exist(firefox_profile_path)) + break; + + _webui_sleep(250); + } + + if(!_webui_folder_exist(firefox_profile_path)) + return false; + + // prefs.js + FILE *file; + sprintf(buf, "%s%sprefs.js", firefox_profile_path, webui_sep); + file = fopen(buf, "a"); + if(file == NULL) + return false; + fputs("user_pref(\"toolkit.legacyUserProfileCustomizations.stylesheets\", true); ", file); + fputs("user_pref(\"browser.shell.checkDefaultBrowser\", false); ", file); + fputs("user_pref(\"browser.tabs.warnOnClose\", false); ", file); + fclose(file); + + // userChrome.css + sprintf(buf, "\"%s%schrome%s\"", firefox_profile_path, webui_sep, webui_sep); + if(!_webui_folder_exist(buf)) { + + sprintf(buf, "mkdir \"%s%schrome%s\"", firefox_profile_path, webui_sep, webui_sep); + _webui_cmd_sync(buf, false); // Create directory + } + sprintf(buf, "%s%schrome%suserChrome.css", firefox_profile_path, webui_sep, webui_sep); + file = fopen(buf, "a"); + if(file == NULL) + return false; + #ifdef _WIN32 + fputs(":root{--uc-toolbar-height:32px}:root:not([uidensity=\"compact\"]) {--uc-toolbar-height:38px}#TabsToolbar{visibility:collapse!important}:root:not([inFullscreen]) #nav-bar{margin-top:calc(0px - var(--uc-toolbar-height))}#toolbar-menubar{min-height:unset!important;height:var(--uc-toolbar-height)!important;position:relative}#main-menubar{-moz-box-flex:1;background-color:var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor);background-clip:padding-box;border-right:30px solid transparent;border-image:linear-gradient(to left,transparent,var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor) 30px) 20 / 30px}#toolbar-menubar:not([inactive]) {z-index:2}#toolbar-menubar[inactive] > #menubar-items{opacity:0;pointer-events:none;margin-left:var(--uc-window-drag-space-width,0px)}#nav-bar{visibility:collapse}@-moz-document url(chrome://browser/content/browser.xhtml) {:root:not([sizemode=\"fullscreen\"]) > head{display: block;position: fixed;width: calc(200vw - 440px);text-align: left;z-index: 9;pointer-events: none;}head > *{ display: none }head > title{display: -moz-inline-box;padding: 4px;max-width: 50vw;overflow-x: hidden;text-overflow: ellipsis;}}", file); + #elif __APPLE__ + fputs(":root{--uc-toolbar-height:32px}:root:not([uidensity=\"compact\"]) {--uc-toolbar-height:38px}#TabsToolbar{visibility:collapse!important}:root:not([inFullscreen]) #nav-bar{margin-top:calc(0px - var(--uc-toolbar-height))}#toolbar-menubar{min-height:unset!important;height:var(--uc-toolbar-height)!important;position:relative}#main-menubar{-moz-box-flex:1;background-color:var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor);background-clip:padding-box;border-right:30px solid transparent;border-image:linear-gradient(to left,transparent,var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor) 30px) 20 / 30px}#toolbar-menubar:not([inactive]) {z-index:2}#toolbar-menubar[inactive] > #menubar-items{opacity:0;pointer-events:none;margin-left:var(--uc-window-drag-space-width,0px)}#nav-bar{visibility:collapse}@-moz-document url(chrome://browser/content/browser.xhtml) {:root:not([sizemode=\"fullscreen\"]) > head{display: block;position: fixed;width: calc(200vw - 440px);text-align: left;z-index: 9;pointer-events: none;}head > *{ display: none }head > title{display: -moz-inline-box;padding: 4px;max-width: 50vw;overflow-x: hidden;text-overflow: ellipsis;}}", file); + #else + fputs(":root{--uc-toolbar-height:32px}:root:not([uidensity=\"compact\"]) {--uc-toolbar-height:38px}#TabsToolbar{visibility:collapse!important}:root:not([inFullscreen]) #nav-bar{margin-top:calc(0px - var(--uc-toolbar-height))}#toolbar-menubar{min-height:unset!important;height:var(--uc-toolbar-height)!important;position:relative}#main-menubar{-moz-box-flex:1;background-color:var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor);background-clip:padding-box;border-right:30px solid transparent;border-image:linear-gradient(to left,transparent,var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor) 30px) 20 / 30px}#toolbar-menubar:not([inactive]) {z-index:2}#toolbar-menubar[inactive] > #menubar-items{opacity:0;pointer-events:none;margin-left:var(--uc-window-drag-space-width,0px)}#nav-bar{visibility:collapse}@-moz-document url(chrome://browser/content/browser.xhtml) {:root:not([sizemode=\"fullscreen\"]) > head{display: block;position: fixed;width: calc(200vw - 440px);text-align: left;z-index: 9;pointer-events: none;}head > *{ display: none }head > title{display: -moz-inline-box;padding: 4px;max-width: 50vw;overflow-x: hidden;text-overflow: ellipsis;}}", file); + #endif + fclose(file); + + sprintf(win->profile_path, "%s%s%s", temp, webui_sep, profile_name); + } + + return true; + } + + return false; +} + +bool _webui_folder_exist(char* folder) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_folder_exist([%s])... \n", folder); + #endif + + #if defined(_MSC_VER) + if(GetFileAttributesA(folder) != INVALID_FILE_ATTRIBUTES) + return true; + #else + DIR* dir = opendir(folder); + if(dir) { + closedir(dir); + return true; + } + #endif + + return false; +} + +char* _webui_generate_internal_id(_webui_window_t* win, const char* element) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_generate_internal_id([%s])... \n", element); + #endif + + // Generate WebUI internal id + size_t element_len = strlen(element); + size_t internal_id_size = 3 + 1 + element_len; // [win num][/][name] + char* webui_internal_id = (char*) _webui_malloc(internal_id_size); + sprintf(webui_internal_id, "%d/%s", win->window_number, element); + + return webui_internal_id; +} + +const char* _webui_browser_get_temp_path(unsigned int browser) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_browser_get_temp_path([%d])... \n", browser); + #endif + + #ifdef _WIN32 + // Resolve %USERPROFILE% + #ifdef _MSC_VER + char* WinUserProfile = NULL; + size_t sz = 0; + if(_dupenv_s(&WinUserProfile, &sz, "USERPROFILE") != 0 || WinUserProfile == NULL) + return webui_empty_string; + #else + char* WinUserProfile = getenv("USERPROFILE"); + if(WinUserProfile == NULL) + return webui_empty_string; + #endif + #elif __APPLE__ + // Resolve $HOME + char* MacUserProfile = getenv("HOME"); + if(MacUserProfile == NULL) + return webui_empty_string; + #else + // Resolve $HOME + char* LinuxUserProfile = getenv("HOME"); + if(LinuxUserProfile == NULL) + return webui_empty_string; + #endif + + #ifdef _WIN32 + return WinUserProfile; + #elif __APPLE__ + return MacUserProfile; + #else + return LinuxUserProfile; + #endif +} + +bool _webui_is_google_chrome_folder(const char* folder) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_is_google_chrome_folder([%s])... \n", folder); + #endif + + char browser_full_path[WEBUI_MAX_PATH]; + + // Make sure this folder is Google Chrome setup and not Chromium + // by checking if `master_preferences` file exist or `initial_preferences` + // Ref: https://support.google.com/chrome/a/answer/187948?hl=en + + sprintf(browser_full_path, "%s\\master_preferences", folder); + if(!_webui_file_exist(browser_full_path)) { + + sprintf(browser_full_path, "%s\\initial_preferences", folder); + if(!_webui_file_exist(browser_full_path)) + return false; // This is Chromium or something else + } + + // Make sure the browser executable file exist + sprintf(browser_full_path, "%s\\chrome.exe", folder); + if(!_webui_file_exist(browser_full_path)) + return false; + + return true; +} + +bool _webui_browser_exist(_webui_window_t* win, unsigned int browser) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_browser_exist([%d])... \n", browser); + #endif + + // Check if a web browser is installed on this machine + + if(browser == Chrome) { + + // Google Chrome + + #ifdef _WIN32 + + // Google Chrome on Windows + + char browser_folder[WEBUI_MAX_PATH]; + + // Search in `HKEY_LOCAL_MACHINE` (If Google Chrome installed for multi-user) + if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe", "Path", browser_folder)) { + + // Make sure its Google Chrome and not Chromium + if(_webui_is_google_chrome_folder(browser_folder)) { + + // Google Chrome Found (multi-user) + sprintf(win->browser_path, "\"%s\\chrome.exe\"", browser_folder); + return true; + } + } + + // Search in `HKEY_CURRENT_USER` (If Google Chrome installed for one user) + if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe", "Path", browser_folder)) { + + // Make sure its Google Chrome and not Chromium + if(_webui_is_google_chrome_folder(browser_folder)) { + + // Google Chrome Found (one user) + sprintf(win->browser_path, "\"%s\\chrome.exe\"", browser_folder); + return true; + } + } + + return false; + + #elif __APPLE__ + + // Google Chrome on macOS + if(_webui_cmd_sync("open -R -a \"Google Chrome\"", false) == 0) { + + sprintf(win->browser_path, "open -W \"/Applications/Google Chrome.app\" --args"); + return true; + } + else + return false; + #else + + // Google Chrome on Linux + if(_webui_cmd_sync("google-chrome --version", false) == 0) { + + sprintf(win->browser_path, "google-chrome"); + return true; + } + else if(_webui_cmd_sync("google-chrome-stable --version", false) == 0) { + + sprintf(win->core.browser_path, "google-chrome-stable"); + return true; + } + else + return false; + + #endif + } + else if(browser == Edge) { + + // Edge + + #ifdef _WIN32 + + // Edge on Windows + + char browser_fullpath[WEBUI_MAX_PATH]; + + // Search in `HKEY_LOCAL_MACHINE` (If Edge installed for multi-user) + if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\msedge.exe", "", browser_fullpath)) { + + // Make sure the browser executable file exist + if(_webui_file_exist(browser_fullpath)) { + + // Edge Found (multi-user) + sprintf(win->browser_path, "\"%s\"", browser_fullpath); + return true; + } + } + + // Search in `HKEY_CURRENT_USER` (If Edge installed for one user) + if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\msedge.exe", "", browser_fullpath)) { + + // Make sure the browser executable file exist + if(_webui_file_exist(browser_fullpath)) { + + // Edge Found (one user) + sprintf(win->browser_path, "\"%s\"", browser_fullpath); + return true; + } + } + + return false; + + #elif __APPLE__ + + // Edge on macOS + return false; + + #else + + // Edge on Linux + return false; + + #endif + } + else if(browser == Epic) { + + // Epic Privacy Browser + + #ifdef _WIN32 + + // Epic on Windows + + char browser_fullpath[WEBUI_MAX_PATH]; + + // Search in `HKEY_CURRENT_USER` (If Epic installed for one user) + if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\epic.exe", "", browser_fullpath)) { + + // Make sure the browser executable file exist + if(_webui_file_exist(browser_fullpath)) { + + // Epic Found (one user) + sprintf(win->browser_path, "\"%s\"", browser_fullpath); + return true; + } + } + + // Search in `HKEY_LOCAL_MACHINE` (If Epic installed for multi-user) + if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\epic.exe", "", browser_fullpath)) { + + // Make sure the browser executable file exist + if(_webui_file_exist(browser_fullpath)) { + + // Epic Found (multi-user) + sprintf(win->browser_path, "\"%s\"", browser_fullpath); + return true; + } + } + + return false; + + #elif __APPLE__ + + // Epic on macOS + if(_webui_cmd_sync("open -R -a \"Epic\"", false) == 0) { + + sprintf(win->browser_path, "open -W \"/Applications/Epic.app\" --args"); + return true; + } + else + return false; + #else + + // Epic on Linux + if(_webui_cmd_sync("epic --version", false) == 0) { + + sprintf(win->browser_path, "epic"); + return true; + } + else + return false; + #endif + } + else if(browser == Vivaldi) { + + // Vivaldi Browser + + #ifdef _WIN32 + + // Vivaldi on Windows + + char browser_fullpath[WEBUI_MAX_PATH]; + + // Search in `HKEY_LOCAL_MACHINE` (If Vivaldi installed for multi-user) + if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vivaldi.exe", "", browser_fullpath)) { + + // Make sure the browser executable file exist + if(_webui_file_exist(browser_fullpath)) { + + // Vivaldi Found (multi-user) + sprintf(win->browser_path, "\"%s\"", browser_fullpath); + return true; + } + } + + // Search in `HKEY_CURRENT_USER` (If Vivaldi installed for one user) + if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vivaldi.exe", "", browser_fullpath)) { + + // Make sure the browser executable file exist + if(_webui_file_exist(browser_fullpath)) { + + // Vivaldi Found (one user) + sprintf(win->browser_path, "\"%s\"", browser_fullpath); + return true; + } + } + + return false; + + #elif __APPLE__ + + // Vivaldi on macOS + if(_webui_cmd_sync("open -R -a \"Vivaldi\"", false) == 0) { + + sprintf(win->browser_path, "open -W \"/Applications/Vivaldi.app\" --args"); + return true; + } + else + return false; + #else + + // Vivaldi on Linux + if(_webui_cmd_sync("vivaldi --version", false) == 0) { + + sprintf(win->browser_path, "vivaldi"); + return true; + } + else + return false; + #endif + } + else if(browser == Brave) { + + // Brave Browser + + #ifdef _WIN32 + + // Brave on Windows + + char browser_fullpath[WEBUI_MAX_PATH]; + + // Search in `HKEY_LOCAL_MACHINE` (If Brave installed for multi-user) + if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\brave.exe", "", browser_fullpath)) { + + // Make sure the browser executable file exist + if(_webui_file_exist(browser_fullpath)) { + + // Brave Found (multi-user) + sprintf(win->browser_path, "\"%s\"", browser_fullpath); + return true; + } + } + + // Search in `HKEY_CURRENT_USER` (If Brave installed for one user) + if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\brave.exe", "", browser_fullpath)) { + + // Make sure the browser executable file exist + if(_webui_file_exist(browser_fullpath)) { + + // Brave Found (one user) + sprintf(win->browser_path, "\"%s\"", browser_fullpath); + return true; + } + } + + return false; + + #elif __APPLE__ + + // Brave on macOS + if(_webui_cmd_sync("open -R -a \"Brave\"", false) == 0) { + + sprintf(win->browser_path, "open -W \"/Applications/Brave.app\" --args"); + return true; + } + else + return false; + #else + + // Brave on Linux + if(_webui_cmd_sync("brave-browser --version", false) == 0) { + + sprintf(win->browser_path, "brave-browser"); + return true; + } + else + return false; + #endif + } + else if(browser == Firefox) { + + // Firefox + + #ifdef _WIN32 + + // Firefox on Windows + + char browser_fullpath[WEBUI_MAX_PATH]; + + // Search in `HKEY_LOCAL_MACHINE` (If Firefox installed for multi-user) + if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\firefox.exe", "", browser_fullpath)) { + + // Make sure the browser executable file exist + if(_webui_file_exist(browser_fullpath)) { + + // Firefox Found (multi-user) + sprintf(win->browser_path, "\"%s\"", browser_fullpath); + return true; + } + } + + // Search in `HKEY_CURRENT_USER` (If Firefox installed for one user) + if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\firefox.exe", "", browser_fullpath)) { + + // Make sure the browser executable file exist + if(_webui_file_exist(browser_fullpath)) { + + // Firefox Found (one user) + sprintf(win->browser_path, "\"%s\"", browser_fullpath); + return true; + } + } + + return false; + + #elif __APPLE__ + + // Firefox on macOS + if(_webui_cmd_sync("open -R -a \"firefox\"", false) == 0) { + + sprintf(win->browser_path, "open -W \"/Applications/Firefox.app\" --args"); + return true; + } + else + return false; + #else + + // Firefox on Linux + + if(_webui_cmd_sync("firefox -v", false) == 0) { + + sprintf(win->browser_path, "firefox"); + return true; + } + else + return false; + + #endif + + } + else if(browser == Yandex) { + + // Yandex Browser + + #ifdef _WIN32 + + // Yandex on Windows + + char browser_fullpath[WEBUI_MAX_PATH]; + + // Search in `HKEY_CURRENT_USER` (If Yandex installed for one user) + if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\browser.exe", "", browser_fullpath)) { + + // Make sure the browser executable file exist + if(_webui_file_exist(browser_fullpath)) { + + // Yandex Found (one user) + sprintf(win->browser_path, "\"%s\"", browser_fullpath); + return true; + } + } + + // Search in `HKEY_LOCAL_MACHINE` (If Yandex installed for multi-user) + if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\browser.exe", "", browser_fullpath)) { + + // Make sure the browser executable file exist + if(_webui_file_exist(browser_fullpath)) { + + // Yandex Found (multi-user) + sprintf(win->browser_path, "\"%s\"", browser_fullpath); + return true; + } + } + + return false; + + #elif __APPLE__ + + // Yandex on macOS + if(_webui_cmd_sync("open -R -a \"Yandex\"", false) == 0) { + + sprintf(win->browser_path, "open -W \"/Applications/Yandex.app\" --args"); + return true; + } + else + return false; + #else + + // Yandex on Linux + if(_webui_cmd_sync("yandex-browser --version", false) == 0) { + + sprintf(win->browser_path, "yandex-browser"); + return true; + } + else + return false; + #endif + } + else if(browser == Chromium) { + + // The Chromium Projects + + #ifdef _WIN32 + + // Chromium on Windows + + char browser_folder[WEBUI_MAX_PATH]; + + // Search in `HKEY_CURRENT_USER` (If Chromium installed for one user) + if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe", "Path", browser_folder)) { + + // Make sure its Chromium and not Google Chrome + if(!_webui_is_google_chrome_folder(browser_folder)) { + + // Chromium Found (one user) + sprintf(win->browser_path, "\"%s\\chrome.exe\"", browser_folder); + return true; + } + } + + // Search in `HKEY_LOCAL_MACHINE` (If Chromium installed for multi-user) + if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe", "Path", browser_folder)) { + + // Make sure its Chromium and not Google Chrome + if(!_webui_is_google_chrome_folder(browser_folder)) { + + // Chromium Found (multi-user) + sprintf(win->browser_path, "\"%s\\chrome.exe\"", browser_folder); + return true; + } + } + + return false; + + #elif __APPLE__ + + // Chromium on macOS + if(_webui_cmd_sync("open -R -a \"Chromium\"", false) == 0) { + + sprintf(win->browser_path, "open -W \"/Applications/Chromium.app\" --args"); + return true; + } + else + return false; + #else + + // Chromium on Linux + if(_webui_cmd_sync("chromium-browser --version", false) == 0) { + + sprintf(win->browser_path, "chromium-browser"); + return true; + } + else + return false; + #endif + } + + return false; +} + +static void _webui_clean(void) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_clean()... \n"); + #endif + + static bool cleaned = false; + if(cleaned) return; + cleaned = true; + + // Let's give other threads more time to safely exit + // and finish their cleaning up. + _webui_sleep(120); + + // TODO: Add option to let the user decide if + // WebUI should delete the web browser profile + // folder or not. + + // Free all non-freed memory allocations + _webui_free_all_mem(); +} + +int _webui_cmd_sync(char* cmd, bool show) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_cmd_sync()... \n"); + #endif + + // Run sync command and + // return the exit code + + char buf[1024]; + + #ifdef _WIN32 + sprintf(buf, "cmd /c \"%s\" > nul 2>&1", cmd); + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_cmd_sync() -> Running [%s] \n", buf); + #endif + return _webui_system_win32(buf, show); + #else + sprintf(buf, "%s >>/dev/null 2>>/dev/null ", cmd); + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_cmd_sync() -> Running [%s] \n", buf); + #endif + int r = system(buf); + r = (r != -1 && r != 127 && WIFEXITED(r)) ? WEXITSTATUS(r) : -1; + return r; + #endif +} + +int _webui_cmd_async(char* cmd, bool show) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_cmd_async()... \n"); + #endif + + // Run a async command + // and return immediately + + char buf[1024]; + int res = 0; + + // Asynchronous command + #ifdef _WIN32 + sprintf(buf, "START \"\" %s", cmd); + res = _webui_cmd_sync(buf, show); + #else + sprintf(buf, "%s >>/dev/null 2>>/dev/null &", cmd); + res = _webui_cmd_sync(buf, show); + #endif + + return res; +} + +int _webui_run_browser(_webui_window_t* win, char* cmd) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_run_browser()... \n"); + #endif + + // Run a async command + return _webui_cmd_async(cmd, false); +} + +bool _webui_browser_start_chrome(_webui_window_t* win, const char* address) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_browser_start_chrome([%s])... \n", address); + #endif + + // -- Google Chrome ---------------------- + + if(win->current_browser != 0 && win->current_browser != Chrome) + return false; + + if(!_webui_browser_exist(win, Chrome)) + return false; + + if(!_webui_browser_create_profile_folder(win, Chrome)) + return false; + + char arg[1024]; + sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --disable-sync-preferences --app=", win->profile_path); + + char full[1024]; + sprintf(full, "%s%s%s", win->browser_path, arg, address); + + if(_webui_run_browser(win, full) == 0) { + + win->current_browser = Chrome; + _webui_core.current_browser = Chrome; + return true; + } + else + return false; +} + +bool _webui_browser_start_edge(_webui_window_t* win, const char* address) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_browser_start_edge([%s])... \n", address); + #endif + + // -- Microsoft Edge ---------------------- + + if(win->current_browser != 0 && win->current_browser != Edge) + return false; + + if(!_webui_browser_exist(win, Edge)) + return false; + + if(!_webui_browser_create_profile_folder(win, Edge)) + return false; + + char arg[1024]; + sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --disable-sync-preferences --app=", win->profile_path); + + char full[1024]; + sprintf(full, "%s%s%s", win->browser_path, arg, address); + + if(_webui_run_browser(win, full) == 0) { + + win->current_browser = Edge; + _webui_core.current_browser = Edge; + return true; + } + else + return false; +} + +bool _webui_browser_start_epic(_webui_window_t* win, const char* address) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_browser_start_epic([%s])... \n", address); + #endif + + // -- Epic Privacy Browser ---------------------- + + if(win->current_browser != 0 && win->current_browser != Epic) + return false; + + if(!_webui_browser_exist(win, Epic)) + return false; + + if(!_webui_browser_create_profile_folder(win, Epic)) + return false; + + char arg[1024]; + sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --disable-sync-preferences --app=", win->profile_path); + + char full[1024]; + sprintf(full, "%s%s%s", win->browser_path, arg, address); + + if(_webui_run_browser(win, full) == 0) { + + win->current_browser = Epic; + _webui_core.current_browser = Epic; + return true; + } + else + return false; +} + +bool _webui_browser_start_vivaldi(_webui_window_t* win, const char* address) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_browser_start_vivaldi([%s])... \n", address); + #endif + + // -- Vivaldi Browser ---------------------- + + if(win->current_browser != 0 && win->current_browser != Vivaldi) + return false; + + if(!_webui_browser_exist(win, Vivaldi)) + return false; + + if(!_webui_browser_create_profile_folder(win, Vivaldi)) + return false; + + char arg[1024]; + sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --disable-sync-preferences --app=", win->profile_path); + + char full[1024]; + sprintf(full, "%s%s%s", win->browser_path, arg, address); + + if(_webui_run_browser(win, full) == 0) { + + win->current_browser = Vivaldi; + _webui_core.current_browser = Vivaldi; + return true; + } + else + return false; +} + +bool _webui_browser_start_brave(_webui_window_t* win, const char* address) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_browser_start_brave([%s])... \n", address); + #endif + + // -- Brave Browser ---------------------- + + if(win->current_browser != 0 && win->current_browser != Brave) + return false; + + if(!_webui_browser_exist(win, Brave)) + return false; + + if(!_webui_browser_create_profile_folder(win, Brave)) + return false; + + char arg[1024]; + sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --disable-sync-preferences --app=", win->profile_path); + + char full[1024]; + sprintf(full, "%s%s%s", win->browser_path, arg, address); + + if(_webui_run_browser(win, full) == 0) { + + win->current_browser = Brave; + _webui_core.current_browser = Brave; + return true; + } + else + return false; +} + +bool _webui_browser_start_firefox(_webui_window_t* win, const char* address) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_browser_start_firefox([%s])... \n", address); + #endif + + // -- Mozilla Firefox ---------------------- + + if(win->current_browser != 0 && win->current_browser != Firefox) + return false; + + if(!_webui_browser_exist(win, Firefox)) + return false; + + if(!_webui_browser_create_profile_folder(win, Firefox)) + return false; + + char full[1024]; + sprintf(full, "%s -P WebUI -purgecaches -new-window %s", win->browser_path, address); + + if(_webui_run_browser(win, full) == 0) { + + win->current_browser = Firefox; + _webui_core.current_browser = Firefox; + return true; + } + else + return false; +} + +bool _webui_browser_start_yandex(_webui_window_t* win, const char* address) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_browser_start_yandex([%s])... \n", address); + #endif + + // -- Yandex Browser ---------------------- + + if(win->current_browser != 0 && win->current_browser != Yandex) + return false; + + if(!_webui_browser_exist(win, Yandex)) + return false; + + if(!_webui_browser_create_profile_folder(win, Yandex)) + return false; + + char arg[1024]; + sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --disable-sync-preferences --app=", win->profile_path); + + char full[1024]; + sprintf(full, "%s%s%s", win->browser_path, arg, address); + + if(_webui_run_browser(win, full) == 0) { + + win->current_browser = Yandex; + _webui_core.current_browser = Yandex; + return true; + } + else + return false; +} + +bool _webui_browser_start_chromium(_webui_window_t* win, const char* address) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_browser_start_chromium([%s])... \n", address); + #endif + + // -- The Chromium Projects ------------------- + + if (win->current_browser != 0 && win->current_browser != Chromium) + return false; + + if (!_webui_browser_exist(win, Chromium)) + return false; + + if (!_webui_browser_create_profile_folder(win, Chromium)) + return false; + + char arg[1024]; + sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --disable-sync-preferences --app=", win->profile_path); + + char full[1024]; + sprintf(full, "%s%s%s", win->browser_path, arg, address); + + if (_webui_run_browser(win, full) == 0) { + + win->current_browser = Chromium; + _webui_core.current_browser = Chromium; + return true; + } + else + return false; +} + +bool _webui_browser_start(_webui_window_t* win, const char* address, unsigned int browser) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_browser_start([%s], [%d])... \n", address, browser); + #endif + + // Non existing browser + if(browser > 10) + return false; + + // Current browser + if(browser == AnyBrowser && _webui_core.current_browser != 0) + browser = _webui_core.current_browser; + + // TODO: Convert address from [/...] to [file://...] + + if(browser != 0) { + + // User specified browser + + if(browser == Chrome) + return _webui_browser_start_chrome(win, address); + else if(browser == Edge) + return _webui_browser_start_edge(win, address); + else if(browser == Epic) + return _webui_browser_start_epic(win, address); + else if(browser == Vivaldi) + return _webui_browser_start_vivaldi(win, address); + else if(browser == Brave) + return _webui_browser_start_brave(win, address); + else if(browser == Firefox) + return _webui_browser_start_firefox(win, address); + else if(browser == Yandex) + return _webui_browser_start_yandex(win, address); + else if(browser == Chromium) + return _webui_browser_start_chromium(win, address); + else + return false; + } + else if(win->current_browser != 0) { + + // Already used browser + + if(win->current_browser == Chrome) + return _webui_browser_start_chrome(win, address); + else if(win->current_browser == Edge) + return _webui_browser_start_edge(win, address); + else if(win->current_browser == Epic) + return _webui_browser_start_epic(win, address); + else if(win->current_browser == Vivaldi) + return _webui_browser_start_vivaldi(win, address); + else if(win->current_browser == Brave) + return _webui_browser_start_brave(win, address); + else if(win->current_browser == Firefox) + return _webui_browser_start_firefox(win, address); + else if(win->current_browser == Yandex) + return _webui_browser_start_yandex(win, address); + else if(browser == Chromium) + return _webui_browser_start_chromium(win, address); + else + return false; + } + else { + + // Default OS browser + + #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) + // Windows + if(!_webui_browser_start_chrome(win, address)) + if(!_webui_browser_start_edge(win, address)) + if(!_webui_browser_start_epic(win, address)) + if(!_webui_browser_start_vivaldi(win, address)) + if(!_webui_browser_start_brave(win, address)) + if(!_webui_browser_start_firefox(win, address)) + if(!_webui_browser_start_yandex(win, address)) + if(!_webui_browser_start_chromium(win, address)) + return false; + #elif __APPLE__ + // macOS + if(!_webui_browser_start_chrome(win, address)) + if(!_webui_browser_start_edge(win, address)) + if(!_webui_browser_start_epic(win, address)) + if(!_webui_browser_start_vivaldi(win, address)) + if(!_webui_browser_start_brave(win, address)) + if(!_webui_browser_start_firefox(win, address)) + if(!_webui_browser_start_yandex(win, address)) + if(!_webui_browser_start_chromium(win, address)) + return false; + #else + // Linux + if(!_webui_browser_start_chrome(win, address)) + if(!_webui_browser_start_edge(win, address)) + if(!_webui_browser_start_epic(win, address)) + if(!_webui_browser_start_vivaldi(win, address)) + if(!_webui_browser_start_brave(win, address)) + if(!_webui_browser_start_firefox(win, address)) + if(!_webui_browser_start_yandex(win, address)) + if(!_webui_browser_start_chromium(win, address)) + return false; + #endif + } + + return true; +} + +bool _webui_set_root_folder(_webui_window_t* win, const char* path) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_set_root_folder([%s])... \n", path); + #endif + + if((path == NULL) || (strlen(path) > WEBUI_MAX_PATH)) + return false; + + win->is_embedded_html = true; + + if(_webui_is_empty(path)) + sprintf(win->server_root_path, "%s", WEBUI_DEFAULT_PATH); + else + sprintf(win->server_root_path, "%s", path); + + webui_set_multi_access(win, true); + + return true; +} + +bool _webui_show(_webui_window_t* win, const char* content, unsigned int browser) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_show([%d])... \n", browser); + #endif + + if(_webui_is_empty(content)) + return false; + + // Some wrappers does not guarantee `content` to + // stay valid, so, let's make our copy right now + size_t content_len = strlen(content); + const char* content_cpy = (const char*)_webui_malloc(content_len); + memcpy((char*)content_cpy, content, content_len); + + if(strstr(content_cpy, " Embedded HTML:\n"); + printf("- - -[HTML]- - - - - - - - - -\n%s\n- - - - - - - - - - - - - - - -\n", content_cpy); + #endif + + return _webui_show_window(win, content_cpy, true, browser); + } + else { + + // File + #ifdef WEBUI_LOG + printf("[User] webui_show() -> File: [%s]\n", content_cpy); + #endif + + if(content_len > WEBUI_MAX_PATH || strstr(content_cpy, "<")) + return false; + + return _webui_show_window(win, content_cpy, false, browser); + } +} + +bool _webui_show_window(_webui_window_t* win, const char* content, bool is_embedded_html, unsigned int browser) { + + #ifdef WEBUI_LOG + if(is_embedded_html) + printf("[Core]\t\t_webui_show_window(HTML, [%d])... \n", browser); + else + printf("[Core]\t\t_webui_show_window(FILE, [%d])... \n", browser); + #endif + + _webui_init(); + + char* url = NULL; + unsigned int port = (win->server_port == 0 ? _webui_get_free_port() : win->server_port); + + // Initialization + if(win->html != NULL) + _webui_free_mem((void *)win->html); + if(win->url != NULL) + _webui_free_mem((void *)win->url); + + if(is_embedded_html) { + + // Show a window using the embedded HTML + win->is_embedded_html = true; + win->html = (content == NULL ? webui_empty_string : content); + + // Generate the URL + size_t url_len = 20; // [domain][port] + url = (char*) _webui_malloc(url_len); + sprintf(url, "http://localhost:%d", port); + } + else { + + // Show a window using a local file + win->is_embedded_html = false; + win->html = webui_empty_string; + + // Generate the URL + size_t url_len = 20 + strlen(content); // [domain][port][file] + url = (char*) _webui_malloc(url_len); + sprintf(url, "http://localhost:%d/%s", port, content); + } + + // Set URL + win->url = url; + win->server_port = port; + + if(!webui_is_shown(win)) { + + // Start a new window + + // Run browser + if(!_webui_browser_start(win, win->url, browser)) { + + // Browser not available + _webui_free_mem((void *)win->html); + _webui_free_mem((void *)win->url); + _webui_free_port(win->server_port); + return false; + } + + // New server thread + #ifdef _WIN32 + HANDLE thread = CreateThread(NULL, 0, _webui_server_start, (void *)win, 0, NULL); + win->server_thread = thread; + if(thread != NULL) + CloseHandle(thread); + #else + pthread_t thread; + pthread_create(&thread, NULL, &_webui_server_start, (void *)win); + pthread_detach(thread); + win->server_thread = thread; + #endif + } + else { + + // Refresh an existing running window + + // Prepare packets + size_t packet_len = 3 + strlen(url); // [header][url] + char* packet = (char*) _webui_malloc(packet_len); + packet[0] = WEBUI_HEADER_SIGNATURE; // Signature + packet[1] = WEBUI_HEADER_SWITCH; // Type + packet[2] = 0; // ID + for(unsigned int i = 0; i < strlen(win->url); i++) // URL + packet[i + 3] = win->url[i]; + + // Send the packet + _webui_window_send(win, packet, packet_len); + _webui_free_mem((void *)packet); + } + + return true; +} + +static void _webui_window_event(_webui_window_t* win, char* webui_internal_id, char* element, void* data, unsigned int data_len, int event_type) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_window_event([%s], [%s])... \n", webui_internal_id, element); + #endif + + // Create a thread, and call the used cb function + _webui_cb_t* arg = (_webui_cb_t*) _webui_malloc(sizeof(_webui_cb_t)); + arg->win = win; + arg->webui_internal_id = webui_internal_id; + arg->element_name = element; + arg->event_type = event_type; + if(data != NULL) { + arg->data = data; + arg->data_len = data_len; + } + else { + arg->data = (void*) webui_empty_string; + arg->data_len = 0; + } + + #ifdef _WIN32 + HANDLE user_fun_thread = CreateThread(NULL, 0, _webui_cb, (void *) arg, 0, NULL); + if(user_fun_thread != NULL) + CloseHandle(user_fun_thread); + #else + pthread_t thread; + pthread_create(&thread, NULL, &_webui_cb, (void *) arg); + pthread_detach(thread); + #endif +} + +static void _webui_window_send(_webui_window_t* win, char* packet, size_t packets_size) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_window_send()... \n"); + printf("[Core]\t\t_webui_window_send() -> %d bytes \n", (int)packets_size); + printf("[Core]\t\t_webui_window_send() -> [ "); + _webui_print_hex(packet, 3); + printf("]\n"); + printf("[Core]\t\t_webui_window_send() -> [%.*s] \n", (int)(packets_size - 3), (const char*)&packet[3]); + #endif + + if(!win->connected || + _webui_core.mg_connections[win->window_number] == NULL || + packet == NULL || + packets_size < 4) + return; + + struct mg_connection* c = _webui_core.mg_connections[win->window_number]; + mg_ws_send( + c, + packet, + packets_size, + WEBSOCKET_OP_BINARY + ); +} + +bool _webui_get_data(const char* packet, size_t packet_len, unsigned int pos, size_t* data_len, char** data) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_get_data()... \n"); + #endif + + if((pos + 1) > packet_len) { + + *data = NULL; + data_len = 0; + return false; + } + + *data = (char*) _webui_malloc((packet_len - pos)); + + // Check mem + if(*data == NULL) { + + data_len = 0; + return false; + } + + // Copy data part + char* p = *data; + for(unsigned int i = pos; i < packet_len; i++) { + + memcpy(p, &packet[i], 1); + p++; + } + + // Check data size + *data_len = strlen(*data); + if(*data_len < 1) { + + _webui_free_mem((void *) data); + *data = NULL; + data_len = 0; + return false; + } + + return true; +} + +static void _webui_window_receive(_webui_window_t* win, const char* packet, size_t len) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_window_receive()... \n"); + #endif + + if((unsigned char) packet[0] != WEBUI_HEADER_SIGNATURE || len < 4) + return; + + if((unsigned char) packet[1] == WEBUI_HEADER_CLICK) { + + // Click Event + + // 0: [Signature] + // 1: [Type] + // 2: + // 3: [Data] + + // Get html element id + char* element; + size_t element_len; + if(!_webui_get_data(packet, len, 3, &element_len, &element)) + return; + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_window_receive() -> %d bytes \n", (int)element_len); + printf("[Core]\t\t_webui_window_receive() -> [%s] \n", element); + #endif + + // Generate WebUI internal id + char* webui_internal_id = _webui_generate_internal_id(win, element); + + _webui_window_event( + win, // Window + webui_internal_id, // WebUI Internal ID + element, // User HTML ID + NULL, // User Custom Data + 0, // User Data Len + WEBUI_EVENT_MOUSE_CLICK // Type of this event + ); + } + else if((unsigned char) packet[1] == WEBUI_HEADER_JS) { + + // JS Result + + // 0: [Signature] + // 1: [Type] + // 2: [ID] + // 3: [Error] + // 4: [Data] + + // Get pipe id + unsigned char run_id = packet[2]; + if(run_id < 0x01) { + + // Fatal. + // The pipe ID is not valid + // we can't send the ready signal to webui_script() + return; + } + + // Get data part + char* data; + size_t data_len; + bool data_status = _webui_get_data(packet, len, 4, &data_len, &data); + + // Get js-error + bool error = true; + if((unsigned char) packet[3] == 0x00) + error = false; + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_window_receive() -> run_id = 0x%02x \n", run_id); + printf("[Core]\t\t_webui_window_receive() -> error = 0x%02x \n", error); + printf("[Core]\t\t_webui_window_receive() -> %d bytes of data\n", (int)data_len); + printf("[Core]\t\t_webui_window_receive() -> data = [%s] @ 0x%p\n", data, data); + #endif + + // Initialize pipe + if((void *)_webui_core.run_responses[run_id] != NULL) + _webui_free_mem((void *)_webui_core.run_responses[run_id]); + + // Set pipe + if(data_status && data_len > 0) { + + _webui_core.run_error[run_id] = error; + _webui_core.run_responses[run_id] = data; + } + else { + + // Empty Result + _webui_core.run_error[run_id] = error; + _webui_core.run_responses[run_id] = webui_empty_string; + } + + // Send ready signal to webui_script() + _webui_core.run_done[run_id] = true; + } + else if((unsigned char) packet[1] == WEBUI_HEADER_CALL_FUNC) { + + // Function Call (No response) + + // 0: [Signature] + // 1: [Type] + // 2: + // 3: [ID, Null, Data] + + // Get html element id + char* element; + size_t element_len; + if(!_webui_get_data(packet, len, 3, &element_len, &element)) + return; + + // Get data + void* data; + size_t data_len; + if(!_webui_get_data(packet, len, (3 + element_len + 1), &data_len, (char **) &data)) + return; + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_window_receive() -> %d bytes \n", (int)element_len); + printf("[Core]\t\t_webui_window_receive() -> [%s] \n", element); + #endif + + // Generate WebUI internal id + char* webui_internal_id = _webui_generate_internal_id(win, element); + + _webui_window_event( + win, // Window + webui_internal_id, // WebUI Internal ID + element, // User HTML ID + data, // User Custom Data + data_len, // User Data Len + WEBUI_EVENT_CALLBACK // Type of this event + ); + } + else if((unsigned char) packet[1] == WEBUI_HEADER_SWITCH) { + + // Navigation Event + + // 0: [Signature] + // 1: [Type] + // 2: + // 3: [URL] + + // Events + if(win->has_events) { + + // Get URL + char* url; + size_t url_len; + if(!_webui_get_data(packet, len, 3, &url_len, &url)) + return; + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_window_receive() -> %d bytes \n", (int)url_len); + printf("[Core]\t\t_webui_window_receive() -> [%s] \n", url); + #endif + + // Generate WebUI internal id + char* webui_internal_id = _webui_generate_internal_id(win, ""); + + _webui_window_event( + win, // Window + webui_internal_id, // WebUI Internal ID + "", // HTML ID + url, // URL + url_len, // URL Len + WEBUI_EVENT_NAVIGATION // Type of this event + ); + } + } +} + +char* _webui_get_current_path(void) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_get_current_path()... \n"); + #endif + + char* path = (char*) _webui_malloc(WEBUI_MAX_PATH); + if(WEBUI_GET_CURRENT_DIR (path, WEBUI_MAX_PATH) == NULL) + path[0] = 0x00; + + return path; +} + +static void _webui_free_port(unsigned int port) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_free_port([%d])... \n", port); + #endif + + for(unsigned int i = 0; i < WEBUI_MAX_ARRAY; i++) { + if(_webui_core.used_ports[i] == port) { + _webui_core.used_ports[i] = 0; + break; + } + } +} + +static void _webui_wait_for_startup(void) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_wait_for_startup()... \n"); + #endif + + if(_webui_core.connections > 0) + return; + + // Wait for the first connection + for(unsigned int n = 0; n <= (_webui_core.startup_timeout * 10); n++) { + + if(_webui_core.connections > 0) + break; + + _webui_sleep(50); + } + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_wait_for_startup() -> Finish.\n"); + #endif +} + +unsigned int _webui_get_new_window_number(void) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_get_new_window_number()... \n"); + #endif + + return ++_webui_core.last_window; +} + +unsigned int _webui_get_free_port(void) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_get_free_port()... \n"); + #endif + + #ifdef _WIN32 + srand((unsigned int)time(NULL)); + #else + srand(time(NULL)); + #endif + + unsigned int port = (rand() % (WEBUI_MAX_PORT + 1 - WEBUI_MIN_PORT)) + WEBUI_MIN_PORT; + + for(unsigned int i = WEBUI_MIN_PORT; i <= WEBUI_MAX_PORT; i++) { + + // Search [port] in [_webui_core.used_ports] + bool found = false; + for(unsigned int j = 0; j < WEBUI_MAX_ARRAY; j++) { + if(_webui_core.used_ports[j] == port) { + found = true; + break; + } + } + + if(found) + // Port used by local window + port = (rand() % (WEBUI_MAX_PORT + 1 - WEBUI_MIN_PORT)) + WEBUI_MIN_PORT; + else { + if(_webui_port_is_used(port)) + // Port used by an external app + port = (rand() % (WEBUI_MAX_PORT + 1 - WEBUI_MIN_PORT)) + WEBUI_MIN_PORT; + else + // Port is free + break; + } + } + + // Add + for(unsigned int i = 0; i < WEBUI_MAX_ARRAY; i++) { + if(_webui_core.used_ports[i] == 0) { + _webui_core.used_ports[i] = port; + break; + } + } + + return port; +} + +static void _webui_init(void) { + + if(_webui_core.initialized) + return; + + #ifdef WEBUI_LOG + printf("[Core]\t\tWebUI v%s \n", WEBUI_VERSION); + printf("[Core]\t\t_webui_init()... \n"); + #endif + + // Initializing + memset(&_webui_core, 0x0, sizeof(_webui_core_t)); + + _webui_core.initialized = true; + _webui_core.startup_timeout = WEBUI_DEF_TIMEOUT; + _webui_core.executable_path = _webui_get_current_path(); +} + +unsigned int _webui_get_cb_index(char* webui_internal_id) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_get_cb_index([%s])... \n", webui_internal_id); + #endif + + if(webui_internal_id != NULL) { + + for(unsigned int i = 1; i < WEBUI_MAX_ARRAY; i++) { + + if(_webui_core.html_elements[i] != NULL && !_webui_is_empty(_webui_core.html_elements[i])) + if(strcmp(_webui_core.html_elements[i], webui_internal_id) == 0) + return i; + } + } + + return 0; +} + +unsigned int _webui_set_cb_index(char* webui_internal_id) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t_webui_set_cb_index([%s])... \n", webui_internal_id); + #endif + + // Add + for(unsigned int i = 1; i < WEBUI_MAX_ARRAY; i++) { + + if(_webui_is_empty(_webui_core.html_elements[i])) { + + _webui_core.html_elements[i] = webui_internal_id; + + return i; + } + } + + return 0; +} + +#ifdef WEBUI_LOG + static void _webui_print_hex(const char* data, size_t len) { + + for(size_t i = 0; i < len; i++) { + + printf("0x%02X ", (unsigned char) *data); + data++; + } + } +#endif + +WEBUI_SERVER_START +{ + _webui_window_t* win = (_webui_window_t*) arg; + + #ifdef WEBUI_LOG + printf("[Core]\t\t[Thread] _webui_server_start(%s)... \n", win->url); + #endif + + // Initialization + _webui_core.servers++; + win->server_running = true; + if(_webui_core.startup_timeout < 1) + _webui_core.startup_timeout = 0; + if(_webui_core.startup_timeout > 30) + _webui_core.startup_timeout = 30; + + // Start Server + struct mg_mgr mgr; + mg_mgr_init(&mgr); + _webui_core.mg_mgrs[win->window_number] = &mgr; + + if(mg_http_listen(&mgr, win->url, _webui_server_event_handler, (void *)win) != NULL) { + + if(_webui_core.startup_timeout > 0) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t[Thread] _webui_server_start()... Listening Success\n"); + printf("[Core]\t\t[Thread] _webui_server_start()... Timeout is %d seconds\n", _webui_core.startup_timeout); + #endif + + bool stop = false; + + while(!stop) { + + if(!win->connected) { + + // Wait for first connection + _webui_timer_t timer_1; + _webui_timer_start(&timer_1); + for(;;) { + + // Stop if window is connected + mg_mgr_poll(&mgr, 1); + if(win->connected) + break; + + // Stop if timer is finished + if(_webui_timer_is_end(&timer_1, (_webui_core.startup_timeout * 1000))) + break; + } + + if(!win->connected && win->html_handled) { + + // At this moment the browser is already started and HTML + // is already handled, so, let's wait more time to give + // the WebSocket an extra one second to connect. + + _webui_timer_t timer_2; + _webui_timer_start(&timer_2); + for(;;) { + + // Stop if window is connected + mg_mgr_poll(&mgr, 1); + if(win->connected) + break; + + // Stop if timer is finished + if(_webui_timer_is_end(&timer_2, 1000)) + break; + } + } + + if(!win->connected) + stop = true; // First run failed + } + else { + + // UI is connected + + while(!stop) { + + // Wait forever for disconnection + + mg_mgr_poll(&mgr, 1); + + // Exit signal + if(_webui_core.exit_now) { + stop = true; + break; + } + + if(!win->connected) { + + // The UI is just get disconnected + // probably the user did a refresh + // let's wait for re-connection... + + _webui_timer_t timer_3; + _webui_timer_start(&timer_3); + for(;;) { + + // Stop if window is re-connected + mg_mgr_poll(&mgr, 1); + if(win->connected) + break; + + // Stop if timer is finished + if(_webui_timer_is_end(&timer_3, 600)) + break; + } + + if(!win->connected) { + + stop = true; + break; + } + } + } + } + } + } + + // Let's check the flag again, there is a change that + // the flag has ben changed during the first loop for + // example when set_timeout() get called later + if(_webui_core.startup_timeout == 0) { + + #ifdef WEBUI_LOG + printf("[Core]\t\t[Thread] _webui_server_start(%s)... Listening Success -> Infinite Loop... \n", win->url); + #endif + + // Wait forever + for(;;) { + + mg_mgr_poll(&mgr, 1); + if(_webui_core.exit_now) + break; + } + } + } + else { + + #ifdef WEBUI_LOG + printf("[Core]\t\t[Thread] _webui_server_start(%s)... Listening failed\n", win->url); + #endif + } + + // Stop server + mg_mgr_free(&mgr); + _webui_core.servers--; + + #ifdef WEBUI_LOG + printf("[Core]\t\t[Thread] _webui_server_start()... Server Stop.\n"); + #endif + + // Clean + win->server_running = false; + win->html_handled = false; + win->connected = false; + _webui_core.mg_mgrs[win->window_number] = NULL; + _webui_core.mg_connections[win->window_number] = NULL; + _webui_free_port(win->server_port); + + THREAD_RETURN +} + +WEBUI_CB +{ + _webui_cb_t* arg = (_webui_cb_t*) _arg; + + #ifdef WEBUI_LOG + printf("[Core]\t\t[Thread] _webui_cb()... \n"); + #endif + + webui_event_t e; + e.element = arg->element_name; + e.window = arg->win; + e.data = arg->data; + e.response = NULL; + e.type = arg->event_type; + + // Check for the events-bind function + if(arg->win->has_events) { + + char* events_id = _webui_generate_internal_id(arg->win, ""); + unsigned int events_cb_index = _webui_get_cb_index(events_id); + _webui_free_mem((void *)events_id); + + if(events_cb_index > 0 && _webui_core.cb[events_cb_index] != NULL) { + + // Call user events cb + _webui_core.cb[events_cb_index](&e); + } + } + + // Check for the bind function + if(arg->element_name != NULL && !_webui_is_empty(arg->element_name)) { + + unsigned int cb_index = _webui_get_cb_index(arg->webui_internal_id); + if(cb_index > 0 && _webui_core.cb[cb_index] != NULL) { + + // Call user cb + _webui_core.cb[cb_index](&e); + } + } + + #ifdef WEBUI_LOG + printf("[Core]\t\t[Thread] _webui_cb()... Stop.\n"); + #endif + + // Free + _webui_free_mem((void *)e.response); + _webui_free_mem((void *)arg->webui_internal_id); + _webui_free_mem((void *)arg->element_name); + _webui_free_mem((void *)arg); + + THREAD_RETURN +} + #ifdef _WIN32 + bool _webui_socket_test_listen_win32(unsigned int port_num) { #ifdef WEBUI_LOG - printf("[0] _webui_socket_test_listen_win32([%d])... \n", port_num); + printf("[Core]\t\t_webui_socket_test_listen_win32([%d])... \n", port_num); #endif WSADATA wsaData; @@ -697,1715 +3998,11 @@ bool _webui_socket_test_listen_mg(unsigned int port_num) { // Listening Success return true; } -#endif -bool _webui_port_is_used(unsigned int port_num) { - - #ifdef WEBUI_LOG - printf("[0] _webui_port_is_used([%d])... \n", port_num); - #endif - - #ifdef _WIN32 - // Listener test - if(!_webui_socket_test_listen_win32(port_num)) - return true; // Port is already used - return false; // Port is not in use - #else - // Listener test MG - if(!_webui_socket_test_listen_mg(port_num)) - return true; // Port is already used - return false; // Port is not in use - #endif -} - -void _webui_serve_file(webui_window_t* win, struct mg_connection *c, void *ev_data) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_serve_file()... \n", win->core.window_number); - #endif - - // Serve a normal text based file - // send with HTTP 200 status code - - struct mg_http_serve_opts opts = { - - .root_dir = win->path - }; - - mg_http_serve_dir(c, ev_data, &opts); -} - -bool _webui_deno_exist(void) { - - #ifdef WEBUI_LOG - printf("[0] _webui_deno_exist()... \n"); - #endif - - static bool found = false; - - if(found) - return true; - - if(_webui_cmd_sync("deno --version", false) == 0) { - - found = true; - return true; - } - else - return false; -} - -bool _webui_nodejs_exist(void) { - - #ifdef WEBUI_LOG - printf("[0] _webui_nodejs_exist()... \n"); - #endif - - static bool found = false; - - if(found) - return true; - - if(_webui_cmd_sync("node -v", false) == 0) { - - found = true; - return true; - } - else - return false; -} - -const char* _webui_interpret_command(const char* cmd) { - - #ifdef WEBUI_LOG - printf("[0] _webui_interpret_command()... \n"); - #endif - - // Redirect stderr to stdout - char cmd_redirected[1024]; - sprintf(cmd_redirected, "%s 2>&1", cmd); - - FILE *runtime = WEBUI_POPEN(cmd_redirected, "r"); - - if(runtime == NULL) - return NULL; - - // Get STDOUT length - // int c; - // while ((c = fgetc(runtime)) != EOF) - // len++; - int len = 1024 * 8; - - // Read STDOUT - char* out = (char*) _webui_malloc(len); - char* line = (char*) _webui_malloc(4000); - while(fgets(line, 4000, runtime) != NULL) - strcat(out, line); - - WEBUI_PCLOSE(runtime); - _webui_free_mem((void *) &line); - - return (const char*) out; -} - -void _webui_interpret_file(webui_window_t* win, struct mg_connection *c, void *ev_data, char* index) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_interpret_file()... \n", win->core.window_number); - #endif - - // Run the JavaScript / TypeScript runtime - // and send back the output with HTTP 200 status code - // otherwise, send the file as a normal text based one - - char* file; - char* full_path; - - if(!_webui_is_empty(index)) { - - // Parse index file - file = index; - full_path = index; - } - else { - - // Parse other files - - struct mg_http_message *hm = (struct mg_http_message *) ev_data; - - // Get file name - file = (char*) _webui_malloc(hm->uri.len); - const char* p = hm->uri.ptr; - p++; // Skip "/" - sprintf(file, "%.*s", (int)(hm->uri.len - 1), p); - - // Get full path - // [current folder][/][file] - full_path = (char*) _webui_malloc(strlen(webui.executable_path) + 1 + strlen(file)); - sprintf(full_path, "%s%s%s", webui.executable_path, webui_sep, file); - - if(!_webui_file_exist(full_path)) { - - // File not exist - 404 - _webui_serve_file(win, c, ev_data); - - _webui_free_mem((void *) &file); - _webui_free_mem((void *) &full_path); - return; - } - } - - // Get file extension - const char* extension = _webui_get_extension(file); - - if(strcmp(extension, "ts") == 0 || strcmp(extension, "js") == 0) { - - // TypeScript / JavaScript - - if(win->core.runtime == webui.runtime.deno) { - - // Use Deno - - if(_webui_deno_exist()) { - - // Set command - // [disable coloring][file] - char* cmd = (char*) _webui_malloc(64 + strlen(full_path)); - #ifdef _WIN32 - sprintf(cmd, "Set NO_COLOR=1 & deno run --allow-all \"%s\"", full_path); - #else - sprintf(cmd, "NO_COLOR=1 & deno run --allow-all \"%s\"", full_path); - #endif - - // Run command - const char* out = _webui_interpret_command(cmd); - - if(out != NULL) { - - // Send deno output - mg_http_reply( - c, 200, - "", - out - ); - } - else { - - // Deno failed. - // Serve as a normal text-based file - _webui_serve_file(win, c, ev_data); - } - - _webui_free_mem((void *) &cmd); - _webui_free_mem((void *) &out); - } - else { - - // Deno not installed - - mg_http_reply( - c, 200, - "", - webui_deno_not_found - ); - } - } - else if(win->core.runtime == webui.runtime.nodejs) { - - // Use Nodejs - - if(_webui_nodejs_exist()) { - - // Set command - // [node][file] - char* cmd = (char*) _webui_malloc(16 + strlen(full_path)); - sprintf(cmd, "node \"%s\"", full_path); - - // Run command - const char* out = _webui_interpret_command(cmd); - - if(out != NULL) { - - // Send Node.js output - mg_http_reply( - c, 200, - "", - out - ); - } - else { - - // Node.js failed. - // Serve as a normal text-based file - _webui_serve_file(win, c, ev_data); - } - - _webui_free_mem((void *) &cmd); - _webui_free_mem((void *) &out); - } - else { - - // Node.js not installed - - mg_http_reply( - c, 200, - "", - webui_nodejs_not_found - ); - } - } - else { - - // Unknown runtime - // Serve as a normal text-based file - _webui_serve_file(win, c, ev_data); - } - } - else { - - // Unknown file extension - // Serve as a normal text-based file - _webui_serve_file(win, c, ev_data); - } - - _webui_free_mem((void *) &file); - _webui_free_mem((void *) &full_path); -} - -const char* _webui_generate_js_bridge(webui_window_t* win) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_generate_js_bridge()... \n", win->core.window_number); - #endif - - // Calculate the cb size - size_t cb_mem_size = 64; // To hold 'const _webui_bind_list = ["elem1", "elem2",];' - for(unsigned int i = 1; i < WEBUI_MAX_ARRAY; i++) - if(!_webui_is_empty(webui.html_elements[i])) - cb_mem_size += strlen(webui.html_elements[i]) + 3; - - // Generate the cb array - char* event_cb_js_array = (char*) _webui_malloc(cb_mem_size); - strcat(event_cb_js_array, "const _webui_bind_list = ["); - for(unsigned int i = 1; i < WEBUI_MAX_ARRAY; i++) { - if(!_webui_is_empty(webui.html_elements[i])) { - strcat(event_cb_js_array, "\""); - strcat(event_cb_js_array, webui.html_elements[i]); - strcat(event_cb_js_array, "\","); - } - } - strcat(event_cb_js_array, "]; \n"); - - // Generate the full WebUI JS-Bridge - size_t len = cb_mem_size + strlen(webui_javascript_bridge) + 1; - char* js = (char*) _webui_malloc(len); - sprintf(js, - "_webui_port = %d; \n _webui_win_num = %d; \n %s \n %s \n", - win->core.server_port, win->core.window_number, event_cb_js_array, webui_javascript_bridge - ); - - return js; -} - -static void _webui_server_event_handler(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { - - webui_window_t* win = (webui_window_t *) fn_data; - - #ifdef WEBUI_LOG - // printf("[%d] _webui_server_event_handler()... \n", win->core.window_number); - #endif - - if(ev == MG_EV_OPEN) { - - // c->is_hexdumping = 1; - } - else if(ev == MG_EV_HTTP_MSG) { - - struct mg_http_message *hm = (struct mg_http_message *) ev_data; - - if(mg_http_match_uri(hm, "/_ws")) { - - // WebSocket - - #ifdef WEBUI_LOG - printf("[%d] _webui_server_event_handler()... HTML Upgrade to WebSocket\n", win->core.window_number); - #endif - - mg_ws_upgrade(c, hm, NULL); - } - else if(mg_http_match_uri(hm, "/webui.js")) { - - // WebUI JS-Bridge - - #ifdef WEBUI_LOG - printf("[%d] _webui_server_event_handler()... HTML WebUI JS\n", win->core.window_number); - #endif - - // Generate JavaScript bridge - const char* js = _webui_generate_js_bridge(win); - - // Header - // Content-Type: text/javascript - - // Send - mg_http_reply( - c, 200, - "", - js - ); - - _webui_free_mem((void *) &js); - } - else if(mg_http_match_uri(hm, "/favicon.ico") || - mg_http_match_uri(hm, "/favicon.svg") || - mg_http_match_uri(hm, "/favicon.png")) { - - // Send favicon - - if(win->core.icon) { - - // Custom icon - - // TODO: Add core.icon_type to the header - - // Header - // ... - - // User icon - mg_http_reply( - c, 200, - "", - win->core.icon - ); - } - else if(win->core.server_root) { - - // Local icon file - _webui_serve_file(win, c, ev_data); - } - else { - - // Default embedded icon - - // TODO: Use webui_def_icon_type - - // Header - // Content-Type: image/svg+xml - - // Default icon - mg_http_reply( - c, 200, - "", - webui_def_icon - ); - } - } - else if(mg_http_match_uri(hm, "/")) { - - // [/] - - if(win->core.server_root) { - - // Serve local files - - #ifdef WEBUI_LOG - printf("[%d] _webui_server_event_handler()... HTML Root Index\n", win->core.window_number); - #endif - - win->core.server_handled = true; - - // Set full path - // [Path][Sep][File Name] - char* index = (char*) _webui_malloc(strlen(webui.executable_path) + 1 + 8); - - // Index.ts - sprintf(index, "%s%sindex.ts", webui.executable_path, webui_sep); - if(_webui_file_exist(index)) { - - // TypeScript Index - _webui_interpret_file(win, c, ev_data, index); - - _webui_free_mem((void *) &index); - return; - } - - // Index.js - sprintf(index, "%s%sindex.js", webui.executable_path, webui_sep); - if(_webui_file_exist(index)) { - - // JavaScript Index - _webui_interpret_file(win, c, ev_data, index); - - _webui_free_mem((void *) &index); - return; - } - - _webui_free_mem((void *) &index); - - // Index.html - // Serve as a normal HTML text-based file - _webui_serve_file(win, c, ev_data); - } - else { - - // Main HTML - - if(!win->core.multi_access && win->core.server_handled) { - - // Main HTML already handled. - // Forbidden 403 - - #ifdef WEBUI_LOG - printf("[%d] _webui_server_event_handler()... HTML Main Already Handled (403)\n", win->core.window_number); - #endif - - // Header - // text/html; charset=utf-8 - - mg_http_reply( - c, 403, - "", - webui_html_served - ); - } - else { - - // Send main HTML - - #ifdef WEBUI_LOG - printf("[%d] _webui_server_event_handler()... HTML Main\n", win->core.window_number); - #endif - - win->core.server_handled = true; - char* html = (char*) webui_empty_string; - - if(win->core.html != NULL) { - - // Generate the full WebUI JS-Bridge - const char* js = _webui_generate_js_bridge(win); - - // Inject WebUI JS-Bridge into HTML - size_t len = strlen(win->core.html) + strlen(js) + 128; - html = (char*) _webui_malloc(len); - sprintf(html, - "%s \n ", - win->core.html, js - ); - - _webui_free_mem((void *) &js); - } - - // // HTTP Header - // char header[512]; - // memset(header, 0x00, 512); - // sprintf(header, - // "HTTP/1.1 200 OK\r\n" - // "Content-Type: text/html; charset=utf-8\r\n" - // "Host: localhost:%d\r\n" - // "Cache-Control: no-cache\r\n" - // "Content-Length: %d\r\n" - // "Connection: close\r\n\r\n", - // win->core.server_port, strlen(html) - // ); - - // Send - mg_http_reply( - c, 200, - "", - html - ); - - _webui_free_mem((void *) &html); - } - } - } - else if(strncmp(hm->uri.ptr, "/WEBUI/FUNC/", 12) == 0 && hm->uri.len >= 15) { - - // Function Call (With response) - - // [/WEBUI/FUNC/ELEMENT_ID/DATA] - // 0 12 - - #ifdef WEBUI_LOG - printf("[%d] _webui_server_event_handler()... CB start\n", win->core.window_number); - #endif - - // Copy packet - size_t len = hm->uri.len; - char* packet = (char*) _webui_malloc(len); - memcpy(packet, hm->uri.ptr, len); - - // Get html element id - char* element = &packet[12]; - size_t element_len = 0; - for (size_t i = 12; i < len; i++) { - if(packet[i] == '/') { - packet[i] = '\0'; - break; - } - element_len++; - } - - // [/WEBUI/FUNC/ELEMENT_ID DATA] - // 0 12 - - // Get data - void* data = &packet[11 + element_len + 2]; - size_t data_len = strlen(data); - - // Generate WebUI internal id - char* webui_internal_id = _webui_generate_internal_id(win, element); - - // Call user function - webui_event_t e; - e.window_id = win->core.window_number; - e.element_name = element; - e.window = win; - e.data = data; - e.response = NULL; - e.type = WEBUI_EVENT_CALLBACK; - - unsigned int cb_index = _webui_get_cb_index(webui_internal_id); - - // Check for bind - if(cb_index > 0 && webui.cb[cb_index] != NULL) { - - // Call user cb - e.element_id = cb_index; - webui.cb[cb_index](&e); - } - - if(_webui_is_empty(e.response)) - e.response = (char*)webui_empty_string; - - #ifdef WEBUI_LOG - printf("[%d] _webui_server_event_handler()... user-callback response [%s]\n", win->core.window_number, (const char*)e.response); - #endif - - // Send response - mg_http_reply( - c, 200, - "", - e.response - ); - - // Free - _webui_free_mem((void *) &packet); - _webui_free_mem((void *) &webui_internal_id); - - // Free data allocated by user callback - if(e.response != NULL) { - if(_webui_ptr_exist(e.response)) { - // This block of memory is allocated by WebUI - // for example the user callback used webui_return_int() - // It's totally safe to free it right now - _webui_free_mem((void *) &e.response); - } - else { - // This block of memory is allocated by - // the user-callback in another language - // for example Python, Rust, Golang... - // We should not free it, it's unsafe. - e.response = NULL; - } - } - } - else { - - // [/file] - - if(win->core.server_root) { - - if(win->core.runtime != webui.runtime.none) { - - // Interpret file - - #ifdef WEBUI_LOG - printf("[%d] _webui_server_event_handler()... HTML Interpret file\n", win->core.window_number); - #endif - - _webui_interpret_file(win, c, ev_data, NULL); - } - else { - - // Serve local files - - #ifdef WEBUI_LOG - printf("[%d] _webui_server_event_handler()... HTML Root file\n", win->core.window_number); - #endif - - // Serve as a normal text-based file - _webui_serve_file(win, c, ev_data); - } - } - else { - - // This is a non-server-folder mode - // but the HTML body request a local file - // this request can be css, js, image, etc... - - if(_webui_file_exist_mg(ev_data)) { - - // Serve as a normal text-based file - _webui_serve_file(win, c, ev_data); - } - else { - - // 404 - - #ifdef WEBUI_LOG - printf("[%d] _webui_server_event_handler()... HTML 404\n", win->core.window_number); - #endif - - // Header - // text/html; charset=utf-8 - - mg_http_reply( - c, 404, - "", - webui_html_res_not_available - ); - } - } - } - } - else if(ev == MG_EV_WS_MSG) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_server_event_handler()... WebSocket Data\n", win->core.window_number); - #endif - - struct mg_ws_message *wm = (struct mg_ws_message *) ev_data; - - // Parse the packet - _webui_window_receive(win, wm->data.ptr, wm->data.len); - } - else if(ev == MG_EV_WS_OPEN) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_server_event_handler()... WebSocket Connected\n", win->core.window_number); - #endif - - int event_type = WEBUI_EVENT_CONNECTED; - - if(!win->core.connected) { - - // First connection - - win->core.connected = true; // server thread - webui.connections++; // main loop - webui.mg_connections[win->core.window_number] = c; // websocket send func - } - else { - - if(win->core.multi_access) { - - // Multi connections - win->core.connections++; - event_type = WEBUI_EVENT_MULTI_CONNECTION; - } - else { - - // UNWANTED Multi connections - - #ifdef WEBUI_LOG - printf("[%d] _webui_server_event_handler() -> UNWANTED Multi Connections\n", win->core.window_number); - #endif - - mg_close_conn(c); - event_type = WEBUI_EVENT_UNWANTED_CONNECTION; - } - } - - // Generate WebUI internal id - char* webui_internal_id = _webui_generate_internal_id(win, ""); - - _webui_window_event( - win, // Window - webui_internal_id, // WebUI Internal ID - "", // User HTML ID - NULL, // User Custom Data - 0, // User Data Len - event_type // Type of this event - ); - } - else if(ev == MG_EV_WS_CTL) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_server_event_handler()... WebSocket Closed\n", win->core.window_number); - #endif - - if(win->core.connected) { - - if(win->core.multi_access && win->core.connections > 0) { - - // Multi connections close - win->core.connections--; - } - else { - - // Main connection close - webui.connections--; // main loop - win->core.connected = false; // server thread - } - } - - // Generate WebUI internal id - char* webui_internal_id = _webui_generate_internal_id(win, ""); - - _webui_window_event( - win, // Window - webui_internal_id, // WebUI Internal ID - "", // User HTML ID - NULL, // User Custom Data - 0, // User Data Len - WEBUI_EVENT_DISCONNECTED // Type of this event - ); - } -} - -#ifdef _WIN32 - DWORD WINAPI webui_server_start(LPVOID arg) -#else - void* webui_server_start(void* arg) -#endif -{ - webui_window_t* win = (webui_window_t*) arg; - - #ifdef WEBUI_LOG - printf("[%d] [Thread] webui_server_start(%s)... \n", win->core.window_number, win->core.url); - #endif - - // Initialization - webui.servers++; - win->core.server_running = true; - unsigned int timeout = webui.startup_timeout; - if(timeout < 1) - timeout = 1; - - // Start Server - struct mg_mgr mgr; - mg_mgr_init(&mgr); - webui.mg_mgrs[win->core.window_number] = &mgr; - - if(mg_http_listen(&mgr, win->core.url, _webui_server_event_handler, (void *)win) != NULL) { - - if(webui.use_timeout) { - - #ifdef WEBUI_LOG - printf("[%d] [Thread] webui_server_start(%s)... Listening Success -> Loop (%d seconds timeout)... \n", win->core.window_number, win->core.url, timeout); - #endif - - bool stop = false; - - for(;;) { - - if(!win->core.server_handled) { - - // Wait for first connection - webui_timer_t timer; - _webui_timer_start(&timer); - for(;;) { - - // Stop if window is connected - mg_mgr_poll(&mgr, 1); - if(win->core.connected) - break; - - // Stop if timer is finished - if(_webui_timer_is_end(&timer, (timeout * 1000))) - break; - } - - if(!win->core.connected && webui.timeout_extra && win->core.server_handled) { - - // At this moment the browser is already started and HTML - // is already handled, so, let's wait more time to give - // the WebSocket an extra time to connect - - _webui_timer_start(&timer); - for(;;) { - - // Stop if window is connected - mg_mgr_poll(&mgr, 1); - if(win->core.connected) - break; - - // Stop if timer is finished - if(_webui_timer_is_end(&timer, (timeout * 1000))) - break; - } - } - - if(!win->core.connected) - stop = true; - } - else { - - for(;;) { - - // Wait forever for disconnection - - mg_mgr_poll(&mgr, 1); - - // Exit signal - if(webui.exit_now) { - stop = true; - break; - } - - if(!win->core.connected) { - - // The UI is just get disconnected - // let's wait for re-connection... - - webui_timer_t timer; - _webui_timer_start(&timer); - for(;;) { - - // Stop if window is re-connected - mg_mgr_poll(&mgr, 1); - if(win->core.connected) - break; - - // Stop if all process get closed - if(webui.process < 1) { - - stop = true; - break; - } - - // Stop if timer is finished - if(_webui_timer_is_end(&timer, 2500)) - break; - } - - if(!win->core.connected) - break; - } - } - - if(win->core.server_handled) - stop = true; - } - - if(stop) - break; - } - } - - // Let's check the flag again, there is a change - // that the flag is changed during the first loop - // for example when set_timeout() get called later - if(!webui.use_timeout) { - - #ifdef WEBUI_LOG - printf("[%d] [Thread] webui_server_start(%s)... Listening Success -> Infinite Loop... \n", win->core.window_number, win->core.url); - #endif - - // Wait forever - for(;;) { - - mg_mgr_poll(&mgr, 1); - if(webui.exit_now) - break; - } - } - } - else { - - #ifdef WEBUI_LOG - printf("[%d] [Thread] webui_server_start(%s)... Listening failed\n", win->core.window_number, win->core.url); - #endif - } - - // Stop server - mg_mgr_free(&mgr); - webui.servers--; - - #ifdef WEBUI_LOG - printf("[%d] [Thread] webui_server_start()... Server Stop.\n", win->core.window_number); - #endif - - // Clean - win->core.server_running = false; - win->core.server_handled = false; - win->core.connected = false; - webui.mg_mgrs[win->core.window_number] = NULL; - webui.mg_connections[win->core.window_number] = NULL; - _webui_free_port(win->core.server_port); - - #ifdef _WIN32 - return 0; - #else - pthread_exit(NULL); - #endif -} - -bool _webui_browser_create_profile_folder(webui_window_t* win, unsigned int browser) { - - #ifdef WEBUI_LOG - printf("[0] _webui_browser_create_profile_folder(%d)... \n", browser); - #endif - - // Custom Browser - if(browser == webui.browser.custom) { - // Check the struct pointer - if(webui.custom_browser == NULL) - return false; - return true; - } - - const char* temp = _webui_browser_get_temp_path(browser); - - if(browser == webui.browser.chrome) { - - // Google Chrome - sprintf(win->core.profile_path, "%s%s.WebUI%sWebUIChromeProfile", temp, webui_sep, webui_sep); - return true; - } - else if(browser == webui.browser.edge) { - - // Edge - sprintf(win->core.profile_path, "%s%s.WebUI%sWebUIEdgeProfile", temp, webui_sep, webui_sep); - return true; - } - else if(browser == webui.browser.epic) { - - // Epic - sprintf(win->core.profile_path, "%s%s.WebUI%sWebUIEpicProfile", temp, webui_sep, webui_sep); - return true; - } - else if(browser == webui.browser.vivaldi) { - - // Vivaldi - sprintf(win->core.profile_path, "%s%s.WebUI%sWebUIVivaldiProfile", temp, webui_sep, webui_sep); - return true; - } - else if(browser == webui.browser.brave) { - - // Brave - sprintf(win->core.profile_path, "%s%s.WebUI%sWebUIBraveProfile", temp, webui_sep, webui_sep); - return true; - } - else if(browser == webui.browser.yandex) { - - // Yandex - sprintf(win->core.profile_path, "%s%s.WebUI%sWebUIYandexProfile", temp, webui_sep, webui_sep); - return true; - } - else if(browser == webui.browser.chromium) { - - // Chromium - sprintf(win->core.profile_path, "%s%s.WebUI%sWebUIChromiumProfile", temp, webui_sep, webui_sep); - return true; - } - else if(browser == webui.browser.firefox) { - - // Firefox (We need to create a folder) - - char* profile_name = "WebUIFirefoxProfile"; - - char firefox_profile_path[1024]; - sprintf(firefox_profile_path, "%s%s.WebUI%s%s", temp, webui_sep, webui_sep, profile_name); - - if(!_webui_folder_exist(firefox_profile_path)) { - - char buf[2048]; - - sprintf(buf, "%s -CreateProfile \"WebUI %s\"", win->core.browser_path, firefox_profile_path); - _webui_cmd_sync(buf, false); - - // Creating the browser profile folders timeout... - for(unsigned int n = 0; n <= (webui.startup_timeout * 4); n++) { - - if(_webui_folder_exist(firefox_profile_path)) - break; - - _webui_sleep(250); - } - - if(!_webui_folder_exist(firefox_profile_path)) - return false; - - // prefs.js - FILE *file; - sprintf(buf, "%s%sprefs.js", firefox_profile_path, webui_sep); - file = fopen(buf, "a"); - if(file == NULL) - return false; - fputs("user_pref(\"toolkit.legacyUserProfileCustomizations.stylesheets\", true); ", file); - fputs("user_pref(\"browser.shell.checkDefaultBrowser\", false); ", file); - fputs("user_pref(\"browser.tabs.warnOnClose\", false); ", file); - fclose(file); - - // userChrome.css - sprintf(buf, "\"%s%schrome%s\"", firefox_profile_path, webui_sep, webui_sep); - if(!_webui_folder_exist(buf)) { - - sprintf(buf, "mkdir \"%s%schrome%s\"", firefox_profile_path, webui_sep, webui_sep); - _webui_cmd_sync(buf, false); // Create directory - } - sprintf(buf, "%s%schrome%suserChrome.css", firefox_profile_path, webui_sep, webui_sep); - file = fopen(buf, "a"); - if(file == NULL) - return false; - #ifdef _WIN32 - fputs(":root{--uc-toolbar-height:32px}:root:not([uidensity=\"compact\"]) {--uc-toolbar-height:38px}#TabsToolbar{visibility:collapse!important}:root:not([inFullscreen]) #nav-bar{margin-top:calc(0px - var(--uc-toolbar-height))}#toolbar-menubar{min-height:unset!important;height:var(--uc-toolbar-height)!important;position:relative}#main-menubar{-moz-box-flex:1;background-color:var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor);background-clip:padding-box;border-right:30px solid transparent;border-image:linear-gradient(to left,transparent,var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor) 30px) 20 / 30px}#toolbar-menubar:not([inactive]) {z-index:2}#toolbar-menubar[inactive] > #menubar-items{opacity:0;pointer-events:none;margin-left:var(--uc-window-drag-space-width,0px)}#nav-bar{visibility:collapse}@-moz-document url(chrome://browser/content/browser.xhtml) {:root:not([sizemode=\"fullscreen\"]) > head{display: block;position: fixed;width: calc(200vw - 440px);text-align: left;z-index: 9;pointer-events: none;}head > *{ display: none }head > title{display: -moz-inline-box;padding: 4px;max-width: 50vw;overflow-x: hidden;text-overflow: ellipsis;}}", file); - #elif __APPLE__ - fputs(":root{--uc-toolbar-height:32px}:root:not([uidensity=\"compact\"]) {--uc-toolbar-height:38px}#TabsToolbar{visibility:collapse!important}:root:not([inFullscreen]) #nav-bar{margin-top:calc(0px - var(--uc-toolbar-height))}#toolbar-menubar{min-height:unset!important;height:var(--uc-toolbar-height)!important;position:relative}#main-menubar{-moz-box-flex:1;background-color:var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor);background-clip:padding-box;border-right:30px solid transparent;border-image:linear-gradient(to left,transparent,var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor) 30px) 20 / 30px}#toolbar-menubar:not([inactive]) {z-index:2}#toolbar-menubar[inactive] > #menubar-items{opacity:0;pointer-events:none;margin-left:var(--uc-window-drag-space-width,0px)}#nav-bar{visibility:collapse}@-moz-document url(chrome://browser/content/browser.xhtml) {:root:not([sizemode=\"fullscreen\"]) > head{display: block;position: fixed;width: calc(200vw - 440px);text-align: left;z-index: 9;pointer-events: none;}head > *{ display: none }head > title{display: -moz-inline-box;padding: 4px;max-width: 50vw;overflow-x: hidden;text-overflow: ellipsis;}}", file); - #else - fputs(":root{--uc-toolbar-height:32px}:root:not([uidensity=\"compact\"]) {--uc-toolbar-height:38px}#TabsToolbar{visibility:collapse!important}:root:not([inFullscreen]) #nav-bar{margin-top:calc(0px - var(--uc-toolbar-height))}#toolbar-menubar{min-height:unset!important;height:var(--uc-toolbar-height)!important;position:relative}#main-menubar{-moz-box-flex:1;background-color:var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor);background-clip:padding-box;border-right:30px solid transparent;border-image:linear-gradient(to left,transparent,var(--toolbar-bgcolor,--toolbar-non-lwt-bgcolor) 30px) 20 / 30px}#toolbar-menubar:not([inactive]) {z-index:2}#toolbar-menubar[inactive] > #menubar-items{opacity:0;pointer-events:none;margin-left:var(--uc-window-drag-space-width,0px)}#nav-bar{visibility:collapse}@-moz-document url(chrome://browser/content/browser.xhtml) {:root:not([sizemode=\"fullscreen\"]) > head{display: block;position: fixed;width: calc(200vw - 440px);text-align: left;z-index: 9;pointer-events: none;}head > *{ display: none }head > title{display: -moz-inline-box;padding: 4px;max-width: 50vw;overflow-x: hidden;text-overflow: ellipsis;}}", file); - #endif - fclose(file); - - sprintf(win->core.profile_path, "%s%s%s", temp, webui_sep, profile_name); - } - - return true; - } - - return false; -} - -bool _webui_folder_exist(char* folder) { - - #ifdef WEBUI_LOG - printf("[0] _webui_folder_exist([%s])... \n", folder); - #endif - - #if defined(_MSC_VER) - if(GetFileAttributesA(folder) != INVALID_FILE_ATTRIBUTES) - return true; - #else - DIR* dir = opendir(folder); - if(dir) { - closedir(dir); - return true; - } - #endif - - return false; -} - -char* _webui_generate_internal_id(webui_window_t* win, const char* element) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_generate_internal_id([%s])... \n", win->core.window_number, element); - #endif - - // Generate WebUI internal id - size_t element_len = strlen(element); - size_t internal_id_size = 3 + 1 + element_len; // [win num][/][name] - char* webui_internal_id = (char*) _webui_malloc(internal_id_size); - sprintf(webui_internal_id, "%d/%s", win->core.window_number, element); - - return webui_internal_id; -} - -const char* _webui_browser_get_temp_path(unsigned int browser) { - - #ifdef WEBUI_LOG - printf("[0] _webui_browser_get_temp_path([%d])... \n", browser); - #endif - - #ifdef _WIN32 - // Resolve %USERPROFILE% - #ifdef _MSC_VER - char* WinUserProfile = NULL; - size_t sz = 0; - if(_dupenv_s(&WinUserProfile, &sz, "USERPROFILE") != 0 || WinUserProfile == NULL) - return webui_empty_string; - #else - char* WinUserProfile = getenv("USERPROFILE"); - if(WinUserProfile == NULL) - return webui_empty_string; - #endif - #elif __APPLE__ - // Resolve $HOME - char* MacUserProfile = getenv("HOME"); - if(MacUserProfile == NULL) - return webui_empty_string; - #else - // Resolve $HOME - char* LinuxUserProfile = getenv("HOME"); - if(LinuxUserProfile == NULL) - return webui_empty_string; - #endif - - #ifdef _WIN32 - // WinUserProfile is already a complete path - return WinUserProfile; - #elif __APPLE__ - return MacUserProfile; - #else - return LinuxUserProfile; - #endif -} - -#ifdef _WIN32 - bool _webui_get_windows_reg_value(HKEY key, const char* reg, const char* value_name, char value[WEBUI_MAX_PATH]) { - - #ifdef WEBUI_LOG - printf("[0] _webui_get_windows_reg_value([%s])... \n", reg); - #endif - - HKEY hKey; - - if(RegOpenKeyEx(key, reg, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { - - DWORD valueSize = WEBUI_MAX_PATH; - // If `value_name` is empty then - // will read the "(default)" reg-key - if(RegQueryValueEx(hKey, value_name, NULL, NULL, (LPBYTE)value, &valueSize) == ERROR_SUCCESS) { - - RegCloseKey(hKey); - return true; - } - } - - return false; - } -#endif - -bool _webui_is_google_chrome_folder(const char* folder) { - - #ifdef WEBUI_LOG - printf("[0] _webui_is_google_chrome_folder([%s])... \n", folder); - #endif - - char browser_full_path[WEBUI_MAX_PATH]; - - // Make sure this folder is Google Chrome setup and not Chromium - // by checking if `master_preferences` file exist or `initial_preferences` - // Ref: https://support.google.com/chrome/a/answer/187948?hl=en - - sprintf(browser_full_path, "%s\\master_preferences", folder); - if(!_webui_file_exist(browser_full_path)) { - - sprintf(browser_full_path, "%s\\initial_preferences", folder); - if(!_webui_file_exist(browser_full_path)) - return false; // This is Chromium or something else - } - - // Make sure the browser executable file exist - sprintf(browser_full_path, "%s\\chrome.exe", folder); - if(!_webui_file_exist(browser_full_path)) - return false; - - return true; -} - -bool _webui_browser_exist(webui_window_t* win, unsigned int browser) { - - #ifdef WEBUI_LOG - printf("[0] _webui_browser_exist([%d])... \n", browser); - #endif - - // Check if a web browser is installed on this machine - - // Custom Browser - if(browser == webui.browser.custom) { - // Check the struct pointer - if(webui.custom_browser == NULL) - return false; - return true; - } - - if(browser == webui.browser.chrome) { - - // Google Chrome - - #ifdef _WIN32 - - // Google Chrome on Windows - - char browser_folder[WEBUI_MAX_PATH]; - - // Search in `HKEY_LOCAL_MACHINE` (If Google Chrome installed for multi-user) - if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe", "Path", browser_folder)) { - - // Make sure its Google Chrome and not Chromium - if(_webui_is_google_chrome_folder(browser_folder)) { - - // Google Chrome Found (multi-user) - sprintf(win->core.browser_path, "\"%s\\chrome.exe\"", browser_folder); - return true; - } - } - - // Search in `HKEY_CURRENT_USER` (If Google Chrome installed for one user) - if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe", "Path", browser_folder)) { - - // Make sure its Google Chrome and not Chromium - if(_webui_is_google_chrome_folder(browser_folder)) { - - // Google Chrome Found (one user) - sprintf(win->core.browser_path, "\"%s\\chrome.exe\"", browser_folder); - return true; - } - } - - return false; - - #elif __APPLE__ - - // Google Chrome on macOS - if(_webui_cmd_sync("open -R -a \"Google Chrome\"", false) == 0) { - - sprintf(win->core.browser_path, "open -W \"/Applications/Google Chrome.app\" --args"); - return true; - } - else - return false; - #else - - // Google Chrome on Linux - if(_webui_cmd_sync("google-chrome --version", false) == 0) { - - sprintf(win->core.browser_path, "google-chrome"); - return true; - } - else if(_webui_cmd_sync("google-chrome-stable --version", false) == 0) { - - sprintf(win->core.browser_path, "google-chrome-stable"); - return true; - } - else - return false; - - #endif - } - else if(browser == webui.browser.edge) { - - // Edge - - #ifdef _WIN32 - - // Edge on Windows - - char browser_fullpath[WEBUI_MAX_PATH]; - - // Search in `HKEY_LOCAL_MACHINE` (If Edge installed for multi-user) - if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\msedge.exe", "", browser_fullpath)) { - - // Make sure the browser executable file exist - if(_webui_file_exist(browser_fullpath)) { - - // Edge Found (multi-user) - sprintf(win->core.browser_path, "\"%s\"", browser_fullpath); - return true; - } - } - - // Search in `HKEY_CURRENT_USER` (If Edge installed for one user) - if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\msedge.exe", "", browser_fullpath)) { - - // Make sure the browser executable file exist - if(_webui_file_exist(browser_fullpath)) { - - // Edge Found (one user) - sprintf(win->core.browser_path, "\"%s\"", browser_fullpath); - return true; - } - } - - return false; - - #elif __APPLE__ - - // Edge on macOS - return false; - - #else - - // Edge on Linux - return false; - - #endif - } - else if(browser == webui.browser.epic) { - - // Epic Privacy Browser - - #ifdef _WIN32 - - // Epic on Windows - - char browser_fullpath[WEBUI_MAX_PATH]; - - // Search in `HKEY_CURRENT_USER` (If Epic installed for one user) - if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\epic.exe", "", browser_fullpath)) { - - // Make sure the browser executable file exist - if(_webui_file_exist(browser_fullpath)) { - - // Epic Found (one user) - sprintf(win->core.browser_path, "\"%s\"", browser_fullpath); - return true; - } - } - - // Search in `HKEY_LOCAL_MACHINE` (If Epic installed for multi-user) - if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\epic.exe", "", browser_fullpath)) { - - // Make sure the browser executable file exist - if(_webui_file_exist(browser_fullpath)) { - - // Epic Found (multi-user) - sprintf(win->core.browser_path, "\"%s\"", browser_fullpath); - return true; - } - } - - return false; - - #elif __APPLE__ - - // Epic on macOS - if(_webui_cmd_sync("open -R -a \"Epic\"", false) == 0) { - - sprintf(win->core.browser_path, "open -W \"/Applications/Epic.app\" --args"); - return true; - } - else - return false; - #else - - // Epic on Linux - if(_webui_cmd_sync("epic --version", false) == 0) { - - sprintf(win->core.browser_path, "epic"); - return true; - } - else - return false; - #endif - } - else if(browser == webui.browser.vivaldi) { - - // Vivaldi Browser - - #ifdef _WIN32 - - // Vivaldi on Windows - - char browser_fullpath[WEBUI_MAX_PATH]; - - // Search in `HKEY_LOCAL_MACHINE` (If Vivaldi installed for multi-user) - if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vivaldi.exe", "", browser_fullpath)) { - - // Make sure the browser executable file exist - if(_webui_file_exist(browser_fullpath)) { - - // Vivaldi Found (multi-user) - sprintf(win->core.browser_path, "\"%s\"", browser_fullpath); - return true; - } - } - - // Search in `HKEY_CURRENT_USER` (If Vivaldi installed for one user) - if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\vivaldi.exe", "", browser_fullpath)) { - - // Make sure the browser executable file exist - if(_webui_file_exist(browser_fullpath)) { - - // Vivaldi Found (one user) - sprintf(win->core.browser_path, "\"%s\"", browser_fullpath); - return true; - } - } - - return false; - - #elif __APPLE__ - - // Vivaldi on macOS - if(_webui_cmd_sync("open -R -a \"Vivaldi\"", false) == 0) { - - sprintf(win->core.browser_path, "open -W \"/Applications/Vivaldi.app\" --args"); - return true; - } - else - return false; - #else - - // Vivaldi on Linux - if(_webui_cmd_sync("vivaldi --version", false) == 0) { - - sprintf(win->core.browser_path, "vivaldi"); - return true; - } - else - return false; - #endif - } - else if(browser == webui.browser.brave) { - - // Brave Browser - - #ifdef _WIN32 - - // Brave on Windows - - char browser_fullpath[WEBUI_MAX_PATH]; - - // Search in `HKEY_LOCAL_MACHINE` (If Brave installed for multi-user) - if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\brave.exe", "", browser_fullpath)) { - - // Make sure the browser executable file exist - if(_webui_file_exist(browser_fullpath)) { - - // Brave Found (multi-user) - sprintf(win->core.browser_path, "\"%s\"", browser_fullpath); - return true; - } - } - - // Search in `HKEY_CURRENT_USER` (If Brave installed for one user) - if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\brave.exe", "", browser_fullpath)) { - - // Make sure the browser executable file exist - if(_webui_file_exist(browser_fullpath)) { - - // Brave Found (one user) - sprintf(win->core.browser_path, "\"%s\"", browser_fullpath); - return true; - } - } - - return false; - - #elif __APPLE__ - - // Brave on macOS - if(_webui_cmd_sync("open -R -a \"Brave\"", false) == 0) { - - sprintf(win->core.browser_path, "open -W \"/Applications/Brave.app\" --args"); - return true; - } - else - return false; - #else - - // Brave on Linux - if(_webui_cmd_sync("brave-browser --version", false) == 0) { - - sprintf(win->core.browser_path, "brave-browser"); - return true; - } - else - return false; - #endif - } - else if(browser == webui.browser.firefox) { - - // Firefox - - #ifdef _WIN32 - - // Firefox on Windows - - char browser_fullpath[WEBUI_MAX_PATH]; - - // Search in `HKEY_LOCAL_MACHINE` (If Firefox installed for multi-user) - if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\firefox.exe", "", browser_fullpath)) { - - // Make sure the browser executable file exist - if(_webui_file_exist(browser_fullpath)) { - - // Firefox Found (multi-user) - sprintf(win->core.browser_path, "\"%s\"", browser_fullpath); - return true; - } - } - - // Search in `HKEY_CURRENT_USER` (If Firefox installed for one user) - if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\firefox.exe", "", browser_fullpath)) { - - // Make sure the browser executable file exist - if(_webui_file_exist(browser_fullpath)) { - - // Firefox Found (one user) - sprintf(win->core.browser_path, "\"%s\"", browser_fullpath); - return true; - } - } - - return false; - - #elif __APPLE__ - - // Firefox on macOS - if(_webui_cmd_sync("open -R -a \"firefox\"", false) == 0) { - - sprintf(win->core.browser_path, "open -W \"/Applications/Firefox.app\" --args"); - return true; - } - else - return false; - #else - - // Firefox on Linux - - if(_webui_cmd_sync("firefox -v", false) == 0) { - - sprintf(win->core.browser_path, "firefox"); - return true; - } - else - return false; - - #endif - - } - else if(browser == webui.browser.yandex) { - - // Yandex Browser - - #ifdef _WIN32 - - // Yandex on Windows - - char browser_fullpath[WEBUI_MAX_PATH]; - - // Search in `HKEY_CURRENT_USER` (If Yandex installed for one user) - if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\browser.exe", "", browser_fullpath)) { - - // Make sure the browser executable file exist - if(_webui_file_exist(browser_fullpath)) { - - // Yandex Found (one user) - sprintf(win->core.browser_path, "\"%s\"", browser_fullpath); - return true; - } - } - - // Search in `HKEY_LOCAL_MACHINE` (If Yandex installed for multi-user) - if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\browser.exe", "", browser_fullpath)) { - - // Make sure the browser executable file exist - if(_webui_file_exist(browser_fullpath)) { - - // Yandex Found (multi-user) - sprintf(win->core.browser_path, "\"%s\"", browser_fullpath); - return true; - } - } - - return false; - - #elif __APPLE__ - - // Yandex on macOS - if(_webui_cmd_sync("open -R -a \"Yandex\"", false) == 0) { - - sprintf(win->core.browser_path, "open -W \"/Applications/Yandex.app\" --args"); - return true; - } - else - return false; - #else - - // Yandex on Linux - if(_webui_cmd_sync("yandex-browser --version", false) == 0) { - - sprintf(win->core.browser_path, "yandex-browser"); - return true; - } - else - return false; - #endif - } - else if(browser == webui.browser.chromium) { - - // The Chromium Projects - - #ifdef _WIN32 - - // Chromium on Windows - - char browser_folder[WEBUI_MAX_PATH]; - - // Search in `HKEY_CURRENT_USER` (If Chromium installed for one user) - if(_webui_get_windows_reg_value(HKEY_CURRENT_USER, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe", "Path", browser_folder)) { - - // Make sure its Chromium and not Google Chrome - if(!_webui_is_google_chrome_folder(browser_folder)) { - - // Chromium Found (one user) - sprintf(win->core.browser_path, "\"%s\\chrome.exe\"", browser_folder); - return true; - } - } - - // Search in `HKEY_LOCAL_MACHINE` (If Chromium installed for multi-user) - if(_webui_get_windows_reg_value(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\chrome.exe", "Path", browser_folder)) { - - // Make sure its Chromium and not Google Chrome - if(!_webui_is_google_chrome_folder(browser_folder)) { - - // Chromium Found (multi-user) - sprintf(win->core.browser_path, "\"%s\\chrome.exe\"", browser_folder); - return true; - } - } - - return false; - - #elif __APPLE__ - - // Chromium on macOS - if(_webui_cmd_sync("open -R -a \"Chromium\"", false) == 0) { - - sprintf(win->core.browser_path, "open -W \"/Applications/Chromium.app\" --args"); - return true; - } - else - return false; - #else - - // Chromium on Linux - if(_webui_cmd_sync("chromium-browser --version", false) == 0) { - - sprintf(win->core.browser_path, "chromium-browser"); - return true; - } - else - return false; - #endif - } - - return false; -} - -void _webui_clean(void) { - - #ifdef WEBUI_LOG - printf("[0] _webui_clean()... \n"); - #endif - - static bool cleaned = false; - if(cleaned) return; - cleaned = true; - - // Let's give other threads more time to safely exit - // and finish their cleaning up. - _webui_sleep(120); - - // TODO: Add option to let the user decide if - // WebUI should delete the web browser profile - // folder or not. - - // Free all non-freed memory allocations - _webui_free_all_mem(); -} - -#ifdef _WIN32 int _webui_system_win32(char* cmd, bool show) { #ifdef WEBUI_LOG - printf("[0] _webui_system_win32()... \n"); + printf("[Core]\t\t_webui_system_win32()... \n"); #endif DWORD Return = 0; @@ -2462,1810 +4059,32 @@ void _webui_clean(void) { else return -1; } -#endif -int _webui_cmd_sync(char* cmd, bool show) { - - #ifdef WEBUI_LOG - printf("[0] _webui_cmd_sync()... \n"); - #endif - - // Run sync command and - // return the exit code - - char buf[1024]; - - #ifdef _WIN32 - sprintf(buf, "cmd /c \"%s\" > nul 2>&1", cmd); - #ifdef WEBUI_LOG - printf("[0] _webui_cmd_sync() -> Running [%s] \n", buf); - #endif - return _webui_system_win32(buf, show); - #else - sprintf(buf, "%s >>/dev/null 2>>/dev/null ", cmd); - #ifdef WEBUI_LOG - printf("[0] _webui_cmd_sync() -> Running [%s] \n", buf); - #endif - int r = system(buf); - r = (r != -1 && r != 127 && WIFEXITED(r)) ? WEXITSTATUS(r) : -1; - return r; - #endif -} - -int _webui_cmd_async(char* cmd, bool show) { - - #ifdef WEBUI_LOG - printf("[0] _webui_cmd_async()... \n"); - #endif - - // Run a async command - // and return immediately - - char buf[1024]; - int res = 0; - - // Asynchronous command - #ifdef _WIN32 - sprintf(buf, "START \"\" %s", cmd); - res = _webui_cmd_sync(buf, show); - #else - sprintf(buf, "%s >>/dev/null 2>>/dev/null &", cmd); - res = _webui_cmd_sync(buf, show); - #endif - - return res; -} - -#ifdef _WIN32 - DWORD WINAPI _webui_run_browser_detect_proc_task(LPVOID _arg) -#else - void* _webui_run_browser_detect_proc_task(void* _arg) -#endif -{ - webui_cmd_async_t* arg = (webui_cmd_async_t*) _arg; - - #ifdef WEBUI_LOG - printf("[%d] _webui_run_browser_detect_proc_task()... \n", arg->win->core.window_number); - #endif - - // Prevent the main loop from closing - webui.process++; - - // Run command - _webui_cmd_sync(arg->cmd, false); - - #ifdef WEBUI_LOG - printf("[%d] _webui_run_browser_detect_proc_task() -> Process closed.\n", arg->win->core.window_number); - #endif - - // Free memory - _webui_free_mem((void *) &arg->cmd); - - // The browser process just get closed - // let the main loop break if there is - // no other running browser process. - webui.process--; - _webui_sleep(250); - if((webui.process < 1) && (!arg->win->core.connected)) - webui.exit_now = true; - - #ifdef _WIN32 - return 0; - #else - pthread_exit(NULL); - #endif -} - -int _webui_run_browser(webui_window_t* win, char* cmd) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_run_browser()... \n", win->core.window_number); - #endif - - int res = 0; - - if(win->core.detect_process_close) { - - // Detect window closing event by the browser - // process, instead of the WS connection status - - webui_cmd_async_t* arg = (webui_cmd_async_t*) _webui_malloc(sizeof(webui_cmd_async_t)); - arg->win = win; - arg->cmd = (char*) _webui_malloc(strlen(cmd)); - strcpy(arg->cmd, cmd); - - #ifdef _WIN32 - HANDLE user_fun_thread = CreateThread(NULL, 0, _webui_run_browser_detect_proc_task, (void *) arg, 0, NULL); - if(user_fun_thread != NULL) - CloseHandle(user_fun_thread); - #else - pthread_t thread; - pthread_create(&thread, NULL, &_webui_run_browser_detect_proc_task, (void *) arg); - pthread_detach(thread); - #endif - - // TODO: We need to set 'res = -1' when _webui_run_browser_detect_proc_task() fails. - } - else { - - // Run a async command - res = _webui_cmd_async(cmd, false); - } - - return res; -} - -bool _webui_browser_start_chrome(webui_window_t* win, const char* address) { - - #ifdef WEBUI_LOG - printf("[0] _webui_browser_start_chrome([%s])... \n", address); - #endif - - // -- Google Chrome ---------------------- - - if(win->core.CurrentBrowser != 0 && win->core.CurrentBrowser != webui.browser.chrome) - return false; - - if(!_webui_browser_exist(win, webui.browser.chrome)) - return false; - - if(!_webui_browser_create_profile_folder(win, webui.browser.chrome)) - return false; - - char arg[1024]; - sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --incognito --app=", win->core.profile_path); - - char full[1024]; - sprintf(full, "%s%s%s", win->core.browser_path, arg, address); - - if(_webui_run_browser(win, full) == 0) { - - win->core.CurrentBrowser = webui.browser.chrome; - webui.browser.current = webui.browser.chrome; - return true; - } - else - return false; -} - -bool _webui_browser_start_edge(webui_window_t* win, const char* address) { - - #ifdef WEBUI_LOG - printf("[0] _webui_browser_start_edge([%s])... \n", address); - #endif - - // -- Microsoft Edge ---------------------- - - if(win->core.CurrentBrowser != 0 && win->core.CurrentBrowser != webui.browser.edge) - return false; - - if(!_webui_browser_exist(win, webui.browser.edge)) - return false; - - if(!_webui_browser_create_profile_folder(win, webui.browser.edge)) - return false; - - // TODO: We need to disable the Sync message in the first run, - // we fix it using `--inprivate`, but it add "" in the title bar. - - char arg[1024]; - sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --inprivate --app=", win->core.profile_path); - - char full[1024]; - sprintf(full, "%s%s%s", win->core.browser_path, arg, address); - - if(_webui_run_browser(win, full) == 0) { - - win->core.CurrentBrowser = webui.browser.edge; - webui.browser.current = webui.browser.edge; - return true; - } - else - return false; -} - -bool _webui_browser_start_epic(webui_window_t* win, const char* address) { - - #ifdef WEBUI_LOG - printf("[0] _webui_browser_start_epic([%s])... \n", address); - #endif - - // -- Epic Privacy Browser ---------------------- - - if(win->core.CurrentBrowser != 0 && win->core.CurrentBrowser != webui.browser.epic) - return false; - - if(!_webui_browser_exist(win, webui.browser.epic)) - return false; - - if(!_webui_browser_create_profile_folder(win, webui.browser.epic)) - return false; - - char arg[1024]; - sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --incognito --app=", win->core.profile_path); - - char full[1024]; - sprintf(full, "%s%s%s", win->core.browser_path, arg, address); - - if(_webui_run_browser(win, full) == 0) { - - win->core.CurrentBrowser = webui.browser.epic; - webui.browser.current = webui.browser.epic; - return true; - } - else - return false; -} - -bool _webui_browser_start_vivaldi(webui_window_t* win, const char* address) { - - #ifdef WEBUI_LOG - printf("[0] _webui_browser_start_vivaldi([%s])... \n", address); - #endif - - // -- Vivaldi Browser ---------------------- - - if(win->core.CurrentBrowser != 0 && win->core.CurrentBrowser != webui.browser.vivaldi) - return false; - - if(!_webui_browser_exist(win, webui.browser.vivaldi)) - return false; - - if(!_webui_browser_create_profile_folder(win, webui.browser.vivaldi)) - return false; - - char arg[1024]; - sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --incognito --app=", win->core.profile_path); - - char full[1024]; - sprintf(full, "%s%s%s", win->core.browser_path, arg, address); - - if(_webui_run_browser(win, full) == 0) { - - win->core.CurrentBrowser = webui.browser.vivaldi; - webui.browser.current = webui.browser.vivaldi; - return true; - } - else - return false; -} - -bool _webui_browser_start_brave(webui_window_t* win, const char* address) { - - #ifdef WEBUI_LOG - printf("[0] _webui_browser_start_brave([%s])... \n", address); - #endif - - // -- Brave Browser ---------------------- - - if(win->core.CurrentBrowser != 0 && win->core.CurrentBrowser != webui.browser.brave) - return false; - - if(!_webui_browser_exist(win, webui.browser.brave)) - return false; - - if(!_webui_browser_create_profile_folder(win, webui.browser.brave)) - return false; - - char arg[1024]; - sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --incognito --app=", win->core.profile_path); - - char full[1024]; - sprintf(full, "%s%s%s", win->core.browser_path, arg, address); - - if(_webui_run_browser(win, full) == 0) { - - win->core.CurrentBrowser = webui.browser.brave; - webui.browser.current = webui.browser.brave; - return true; - } - else - return false; -} - -bool _webui_browser_start_firefox(webui_window_t* win, const char* address) { - - #ifdef WEBUI_LOG - printf("[0] _webui_browser_start_firefox([%s])... \n", address); - #endif - - // -- Mozilla Firefox ---------------------- - - if(win->core.CurrentBrowser != 0 && win->core.CurrentBrowser != webui.browser.firefox) - return false; - - if(!_webui_browser_exist(win, webui.browser.firefox)) - return false; - - if(!_webui_browser_create_profile_folder(win, webui.browser.firefox)) - return false; - - char full[1024]; - sprintf(full, "%s -P WebUI -purgecaches -new-window -private-window %s", win->core.browser_path, address); - - if(_webui_run_browser(win, full) == 0) { - - win->core.CurrentBrowser = webui.browser.firefox; - webui.browser.current = webui.browser.firefox; - return true; - } - else - return false; -} - -bool _webui_browser_start_yandex(webui_window_t* win, const char* address) { - - #ifdef WEBUI_LOG - printf("[0] _webui_browser_start_yandex([%s])... \n", address); - #endif - - // -- Yandex Browser ---------------------- - - if(win->core.CurrentBrowser != 0 && win->core.CurrentBrowser != webui.browser.yandex) - return false; - - if(!_webui_browser_exist(win, webui.browser.yandex)) - return false; - - if(!_webui_browser_create_profile_folder(win, webui.browser.yandex)) - return false; - - char arg[1024]; - sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --incognito --app=", win->core.profile_path); - - char full[1024]; - sprintf(full, "%s%s%s", win->core.browser_path, arg, address); - - if(_webui_run_browser(win, full) == 0) { - - win->core.CurrentBrowser = webui.browser.yandex; - webui.browser.current = webui.browser.yandex; - return true; - } - else - return false; -} - -bool _webui_browser_start_chromium(webui_window_t* win, const char* address) { - - #ifdef WEBUI_LOG - printf("[0] _webui_browser_start_chromium([%s])... \n", address); - #endif - - // -- The Chromium Projects ------------------- - - if (win->core.CurrentBrowser != 0 && win->core.CurrentBrowser != webui.browser.chromium) - return false; - - if (!_webui_browser_exist(win, webui.browser.chromium)) - return false; - - if (!_webui_browser_create_profile_folder(win, webui.browser.chromium)) - return false; - - char arg[1024]; - sprintf(arg, " --user-data-dir=\"%s\" --no-first-run --disable-gpu --disable-software-rasterizer --no-proxy-server --safe-mode --disable-extensions --disable-background-mode --disable-plugins --disable-plugins-discovery --disable-translate --bwsi --disable-sync --incognito --app=", win->core.profile_path); - - char full[1024]; - sprintf(full, "%s%s%s", win->core.browser_path, arg, address); - - if (_webui_run_browser(win, full) == 0) { - - win->core.CurrentBrowser = webui.browser.chromium; - webui.browser.current = webui.browser.chromium; - return true; - } - else - return false; -} - -bool _webui_browser_start_custom(webui_window_t* win, const char* address) { - - #ifdef WEBUI_LOG - printf("[0] _webui_browser_start_custom([%s])... \n", address); - #endif - - // -- Custom Browser ---------------------- - - if(win->core.CurrentBrowser != 0 && win->core.CurrentBrowser != webui.browser.custom) - return false; - - if(!_webui_browser_exist(win, webui.browser.custom)) - return false; - - if(!_webui_browser_create_profile_folder(win, webui.browser.custom)) - return false; - - char full[1024]; - if(webui.custom_browser->auto_link) - sprintf(full, "%s %s%s", webui.custom_browser->app, webui.custom_browser->arg, address); - else - sprintf(full, "%s %s", webui.custom_browser->app, webui.custom_browser->arg); - - if(_webui_run_browser(win, full) == 0) { - - win->core.CurrentBrowser = webui.browser.custom; - webui.browser.current = webui.browser.custom; - return true; - } - else - return false; -} - -bool _webui_browser_start(webui_window_t* win, const char* address, unsigned int browser) { - - #ifdef WEBUI_LOG - printf("[0] _webui_browser_start([%s], [%d])... \n", address, browser); - #endif - - // Non existing browser - if(browser > 10) - return false; - - // Current browser - if(browser == webui.browser.any && webui.browser.current != 0) - browser = webui.browser.current; - - // TODO: Convert address from [/...] to [file://...] - - if(browser != 0) { - - // User specified browser - - if(browser == webui.browser.chrome) - return _webui_browser_start_chrome(win, address); - else if(browser == webui.browser.edge) - return _webui_browser_start_edge(win, address); - else if(browser == webui.browser.epic) - return _webui_browser_start_epic(win, address); - else if(browser == webui.browser.vivaldi) - return _webui_browser_start_vivaldi(win, address); - else if(browser == webui.browser.brave) - return _webui_browser_start_brave(win, address); - else if(browser == webui.browser.firefox) - return _webui_browser_start_firefox(win, address); - else if(browser == webui.browser.yandex) - return _webui_browser_start_yandex(win, address); - else if(browser == webui.browser.chromium) - return _webui_browser_start_chromium(win, address); - else if(browser == webui.browser.custom) - return _webui_browser_start_custom(win, address); - else - return false; - } - else if(win->core.CurrentBrowser != 0) { - - // Already used browser - - if(win->core.CurrentBrowser == webui.browser.chrome) - return _webui_browser_start_chrome(win, address); - else if(win->core.CurrentBrowser == webui.browser.edge) - return _webui_browser_start_edge(win, address); - else if(win->core.CurrentBrowser == webui.browser.epic) - return _webui_browser_start_epic(win, address); - else if(win->core.CurrentBrowser == webui.browser.vivaldi) - return _webui_browser_start_vivaldi(win, address); - else if(win->core.CurrentBrowser == webui.browser.brave) - return _webui_browser_start_brave(win, address); - else if(win->core.CurrentBrowser == webui.browser.firefox) - return _webui_browser_start_firefox(win, address); - else if(win->core.CurrentBrowser == webui.browser.yandex) - return _webui_browser_start_yandex(win, address); - else if(browser == webui.browser.chromium) - return _webui_browser_start_chromium(win, address); - else if(win->core.CurrentBrowser == webui.browser.custom) - return _webui_browser_start_custom(win, address); - else - return false; - } - else { - - // Default OS browser - - #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__NT__) - // Windows - if(!_webui_browser_start_chrome(win, address)) - if(!_webui_browser_start_edge(win, address)) - if(!_webui_browser_start_epic(win, address)) - if(!_webui_browser_start_vivaldi(win, address)) - if(!_webui_browser_start_brave(win, address)) - if(!_webui_browser_start_firefox(win, address)) - if(!_webui_browser_start_yandex(win, address)) - if(!_webui_browser_start_chromium(win, address)) - if(!_webui_browser_start_custom(win, address)) - return false; - #elif __APPLE__ - // macOS - if(!_webui_browser_start_chrome(win, address)) - if(!_webui_browser_start_edge(win, address)) - if(!_webui_browser_start_epic(win, address)) - if(!_webui_browser_start_vivaldi(win, address)) - if(!_webui_browser_start_brave(win, address)) - if(!_webui_browser_start_firefox(win, address)) - if(!_webui_browser_start_yandex(win, address)) - if(!_webui_browser_start_chromium(win, address)) - if(!_webui_browser_start_custom(win, address)) - return false; - #else - // Linux - if(!_webui_browser_start_chrome(win, address)) - if(!_webui_browser_start_edge(win, address)) - if(!_webui_browser_start_epic(win, address)) - if(!_webui_browser_start_vivaldi(win, address)) - if(!_webui_browser_start_brave(win, address)) - if(!_webui_browser_start_firefox(win, address)) - if(!_webui_browser_start_yandex(win, address)) - if(!_webui_browser_start_chromium(win, address)) - if(!_webui_browser_start_custom(win, address)) - return false; - #endif - } - - return true; -} - -void _webui_window_open(webui_window_t* win, char* link, unsigned int browser) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_window_open([%s], [%d])... \n", win->core.window_number, link, browser); - #endif - - // Just open an app-mode window using this link - _webui_browser_start(win, link, browser); -} - -void webui_script_cleanup(webui_script_t* script) { - - _webui_free_mem((void *) &script->result.data); - _webui_free_mem((void *) &script->script); -} - -void webui_script(webui_window_t* win, webui_script_t* script) { - - #ifdef WEBUI_LOG - printf("[%d] webui_script([%s])... \n", win->core.window_number, script->script); - #endif - - _webui_init(); - - size_t js_len = strlen(script->script); - - if(js_len < 1) { - - _webui_free_mem((void *) &script->result.data); - script->result.data = webui_js_empty; - script->result.length = (unsigned int) strlen(webui_js_empty); - script->result.error = true; - return; - } - - // Initializing js result - _webui_free_mem((void *) &script->result.data); - script->result.data = webui_js_timeout; - script->result.length = (unsigned int) strlen(webui_js_timeout); - script->result.error = true; - - // Initializing pipe - unsigned int run_id = _webui_get_run_id(); - webui.run_done[run_id] = false; - webui.run_error[run_id] = false; - _webui_free_mem((void *) &webui.run_responses[run_id]); - - // Prepare the packet - size_t packet_len = 3 + js_len + 1; // [header][js] - char* packet = (char*) _webui_malloc(packet_len); - packet[0] = WEBUI_HEADER_SIGNATURE; // Signature - packet[1] = WEBUI_HEADER_JS; // Type - packet[2] = run_id; // ID - for(unsigned int i = 0; i < js_len; i++) // Data - packet[i + 3] = script->script[i]; - - // Send packets - _webui_window_send(win, packet, packet_len); - _webui_free_mem((void *) &packet); - - // Wait for UI response - if(script->timeout < 1 || script->timeout > 86400) { - - for(;;) { - - if(webui.run_done[run_id]) - break; - - _webui_sleep(1); - } - } - else { - - for(unsigned int n = 0; n <= (script->timeout * 1000); n++) { - - if(webui.run_done[run_id]) - break; - - _webui_sleep(1); - } - } - - if(webui.run_responses[run_id] != NULL) { - - script->result.data = webui.run_responses[run_id]; - script->result.length = (unsigned int) strlen(webui.run_responses[run_id]); - script->result.error = webui.run_error[run_id]; - } -} - -webui_window_t* webui_new_window(void) { - - #ifdef WEBUI_LOG - printf("[0] webui_new_window()... \n"); - #endif - - _webui_init(); - - webui_window_t* win = (webui_window_t*) _webui_malloc(sizeof(webui_window_t)); - - // Initialisation - win->core.window_number = _webui_get_new_window_number(); - win->core.browser_path = (char*) _webui_malloc(WEBUI_MAX_PATH); - win->core.profile_path = (char*) _webui_malloc(WEBUI_MAX_PATH); - win->path = (char*) _webui_malloc(WEBUI_MAX_PATH); - sprintf(win->path, "%s", WEBUI_DEFAULT_PATH); - - #ifdef WEBUI_LOG - printf("[0] webui_new_window() -> New window @ 0x%p\n", win); - #endif - - return win; -} - -void webui_close(webui_window_t* win) { - - #ifdef WEBUI_LOG - printf("[%d] webui_close()... \n", win->core.window_number); - #endif - - _webui_init(); - - if(win->core.connected) { - - // Prepare packets - char* packet = (char*) _webui_malloc(4); - packet[0] = WEBUI_HEADER_SIGNATURE; // Signature - packet[1] = WEBUI_HEADER_CLOSE; // Type - packet[2] = 0; // ID - packet[3] = 0; // Data - - // Send packets - _webui_window_send(win, packet, 4); - _webui_free_mem((void *) &packet); - } -} - -bool webui_is_shown(webui_window_t* win) { - - #ifdef WEBUI_LOG - printf("[%d] webui_is_shown()... \n", win->core.window_number); - #endif - - return win->core.connected; -} - -bool webui_is_any_window_running(void) { - - #ifdef WEBUI_LOG - printf("[0] webui_is_any_window_running()... \n"); - #endif - - _webui_init(); - - if(webui.connections > 0) - return true; - - return false; -} - -unsigned int _webui_window_get_number(webui_window_t* win) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_window_get_number()... \n", win->core.window_number); - #endif - - return win->core.window_number; -} - -const char* webui_new_server(webui_window_t* win, const char* path) { - - #ifdef WEBUI_LOG - printf("[%d] webui_new_server()... \n", win->core.window_number); - #endif - - _webui_init(); - - // Root folder to serve - if(!_webui_set_root_folder(win, path)) - return webui_empty_string; - - // Prevent the server from using the - // timeout-mode while waiting for connections - webui_set_timeout(0); - - // WEBUI_NON_EXIST_BROWSER is to prevent - // any browser from running. Because we want - // to only to run a web-server this time. - _webui_show_window(win, NULL, WEBUI_NON_EXIST_BROWSER); - - // Wait for server to start - for(unsigned int n = 0; n < 500; n++) { - - if(win->core.server_running) - break; - - _webui_sleep(1); - } - - return (const char*) win->core.url; -} - -bool _webui_set_root_folder(webui_window_t* win, const char* path) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_set_root_folder([%s])... \n", win->core.window_number, path); - #endif - - if((path == NULL) || (strlen(path) > WEBUI_MAX_PATH)) - return false; - - win->core.server_root = true; - - if(_webui_is_empty(path)) - sprintf(win->path, "%s", WEBUI_DEFAULT_PATH); - else - sprintf(win->path, "%s", path); - - webui_multi_access(win, true); - - return true; -} - -void webui_multi_access(webui_window_t* win, bool status) { - - #ifdef WEBUI_LOG - printf("[%d] webui_multi_access([%d])... \n", win->core.window_number, status); - #endif - - win->core.multi_access = status; -} - -void webui_set_icon(webui_window_t* win, const char* icon_s, const char* type_s) { - - #ifdef WEBUI_LOG - printf("[%d] webui_set_icon([%s], [%s])... \n", win->core.window_number, icon_s, type_s); - #endif - - win->core.icon = icon_s; - win->core.icon_type = type_s; -} - -bool webui_open(webui_window_t* win, const char* url, unsigned int browser) { - - #ifdef WEBUI_LOG - printf("[%d] webui_open()... \n", win->core.window_number); - #endif - - // Just open an app-mode window using the link - // webui_set_timeout(0); - _webui_wait_process(win, true); - - if(webui_is_shown(win)) { - - // Refresh an existing running window + bool _webui_get_windows_reg_value(HKEY key, const char* reg, const char* value_name, char value[WEBUI_MAX_PATH]) { #ifdef WEBUI_LOG - printf("[%d] webui_open()... Refresh the running window to [%s]...\n", win->core.window_number, url); + printf("[Core]\t\t_webui_get_windows_reg_value([%s])... \n", reg); #endif - // Prepare packets - size_t packet_len = 3 + strlen(url) + 1; // [header][url] - char* packet = (char*) _webui_malloc(packet_len); - packet[0] = WEBUI_HEADER_SIGNATURE; // Signature - packet[1] = WEBUI_HEADER_SWITCH; // Type - packet[2] = 0; // ID - for(unsigned int i = 0; i < strlen(url); i++) // Data - packet[i + 3] = url[i]; + HKEY hKey; - // Send the packet - _webui_window_send(win, packet, packet_len); - _webui_free_mem((void *) &packet); + if(RegOpenKeyEx(key, reg, 0, KEY_READ, &hKey) == ERROR_SUCCESS) { - return true; - } - - // New window - return _webui_browser_start(win, url, browser); -} + DWORD valueSize = WEBUI_MAX_PATH; + // If `value_name` is empty then + // will read the "(default)" reg-key + if(RegQueryValueEx(hKey, value_name, NULL, NULL, (LPBYTE)value, &valueSize) == ERROR_SUCCESS) { -bool webui_show(webui_window_t* win, const char* content) { - - #ifdef WEBUI_LOG - printf("[%d] webui_show()... \n", win->core.window_number); - #endif - - size_t content_len = strlen(content); - - // Some wrappers does not guarantee `content` to - // stay valid, so, let's make a copy right now. - char* content_cpy = (char*) webui_empty_string; - if(content_len > 1) { - content_cpy = _webui_malloc(content_len); - memcpy(content_cpy, content, content_len); - } - - // Check if this is an HTML script or a file name - if(strstr(content_cpy, " Static HTML Script:\n", win->core.window_number); - printf("- - -[HTML]- - - - - - - - - -\n%s\n- - - - - - - - - - - - - - - -\n", content_cpy); - #endif - win->core.server_root = false; - return _webui_show_window(win, content_cpy, webui.browser.any); - } - - // Handel the file - #ifdef WEBUI_LOG - printf("[%d] webui_show()... -> File: [%s]\n", win->core.window_number, content_cpy); - #endif - if(content_len > WEBUI_MAX_PATH || strstr(content_cpy, "<")) - return false; - if(win->core.url == NULL) - webui_new_server(win, ""); - // URL: [localhost:port][/][filename] - char* url = (char*) _webui_malloc(strlen(win->core.url) + 1 + content_len); - sprintf(url, "%s/%s", win->core.url, content_cpy); - return webui_open(win, url, win->core.CurrentBrowser); -} - -bool _webui_show_window(webui_window_t* win, const char* html, unsigned int browser) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_show_window(html, [%d])... \n", win->core.window_number, browser); - #endif - - _webui_init(); - - // Initializing - win->core.html = (html == NULL ? webui_empty_string : html); - win->core.server_handled = false; - webui.wait_for_socket_window = true; - - // Detect window closing event by the browser - // process, instead of the WS connection status - _webui_wait_process(win, true); - - if(!webui_is_shown(win)) { - - // Start a new window - - // Get new port - unsigned int port = _webui_get_free_port(); - win->core.server_port = port; - _webui_free_mem((void *) &win->core.url); - win->core.url = (char*) _webui_malloc(2000); - sprintf(win->core.url, "http://localhost:%d", port); - - // Run browser - bool browser_started = _webui_browser_start(win, win->core.url, browser); - if(browser != WEBUI_NON_EXIST_BROWSER && !browser_started) { - - // Browser not available - _webui_free_port(win->core.server_port); - return false; - } - - // New Server Thread - #ifdef _WIN32 - HANDLE thread = CreateThread(NULL, 0, webui_server_start, (void *) win, 0, NULL); - win->core.server_thread = thread; - if(thread != NULL) - CloseHandle(thread); - #else - pthread_t thread; - pthread_create(&thread, NULL, &webui_server_start, (void *) win); - pthread_detach(thread); - win->core.server_thread = thread; - #endif - } - else { - - // Refresh an existing running window - - // Prepare packets - size_t packet_len = 3 + strlen(win->core.url) + 1; // [header][url] - char* packet = (char*) _webui_malloc(packet_len); - packet[0] = WEBUI_HEADER_SIGNATURE; // Signature - packet[1] = WEBUI_HEADER_SWITCH; // Type - packet[2] = 0; // ID - for(unsigned int i = 0; i < strlen(win->core.url); i++) // Data - packet[i + 3] = win->core.url[i]; - - // Send the packet - _webui_window_send(win, packet, packet_len); - _webui_free_mem((void *) &packet); - } - - return true; -} - -unsigned int webui_bind(webui_window_t* win, const char* element, void (*func)(webui_event_t* e)) { - - #ifdef WEBUI_LOG - printf("[%d] webui_bind([%s], [0x%p])... \n", win->core.window_number, element, func); - #endif - - _webui_init(); - - int len = 0; - if(_webui_is_empty(element)) - win->core.has_events = true; - else - len = strlen(element); - - // [win num][/][element] - char* webui_internal_id = _webui_malloc(3 + 1 + len); - sprintf(webui_internal_id, "%d/%s", win->core.window_number, element); - - unsigned int cb_index = _webui_get_cb_index(webui_internal_id); - - if(cb_index > 0) { - - // Replace a reference - webui.cb[cb_index] = func; - - _webui_free_mem((void *) &webui_internal_id); - } - else { - - // New reference - cb_index = _webui_set_cb_index(webui_internal_id); - - if(cb_index > 0) - webui.cb[cb_index] = func; - else - _webui_free_mem((void *) &webui_internal_id); - } - - return cb_index; -} - -#ifdef _WIN32 - DWORD WINAPI _webui_cb(LPVOID _arg) -#else - void* _webui_cb(void* _arg) -#endif -{ - webui_cb_t* arg = (webui_cb_t*) _arg; - - #ifdef WEBUI_LOG - printf("[%d] [Thread] _webui_cb()... \n", arg->win->core.window_number); - #endif - - webui_event_t e; - e.window_id = arg->win->core.window_number; - e.element_name = arg->element_name; - e.window = arg->win; - e.data = arg->data; - e.response = NULL; - e.type = arg->event_type; - - // Check for the events-bind function - if(arg->win->core.has_events) { - - char* events_id = _webui_generate_internal_id(arg->win, ""); - unsigned int events_cb_index = _webui_get_cb_index(events_id); - _webui_free_mem((void *) &events_id); - - if(events_cb_index > 0 && webui.cb[events_cb_index] != NULL) { - - // Call user events cb - e.element_id = 0; - webui.cb[events_cb_index](&e); - } - } - - // Check for the bind function - if(!_webui_is_empty(arg->element_name)) { - - unsigned int cb_index = _webui_get_cb_index(arg->webui_internal_id); - if(cb_index > 0 && webui.cb[cb_index] != NULL) { - - // Call user cb - e.element_id = cb_index; - webui.cb[cb_index](&e); - } - } - - #ifdef WEBUI_LOG - printf("[%d] [Thread] _webui_cb()... Stop.\n", arg->win->core.window_number); - #endif - - // Free - _webui_free_mem((void *) &arg->webui_internal_id); - _webui_free_mem((void *) &arg->element_name); - _webui_free_mem((void *) &arg); - - #ifdef _WIN32 - return 0; - #else - pthread_exit(NULL); - #endif -} - -void _webui_window_event(webui_window_t* win, char* webui_internal_id, char* element, void* data, unsigned int data_len, int event_type) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_window_event([%s], [%s])... \n", win->core.window_number, webui_internal_id, element); - #endif - - // Create a thread, and call the used cb function - webui_cb_t* arg = (webui_cb_t*) _webui_malloc(sizeof(webui_cb_t)); - arg->win = win; - arg->webui_internal_id = webui_internal_id; - arg->element_name = element; - arg->event_type = event_type; - if(data != NULL) { - arg->data = data; - arg->data_len = data_len; - } - else { - arg->data = (void*) webui_empty_string; - arg->data_len = 0; - } - - #ifdef _WIN32 - HANDLE user_fun_thread = CreateThread(NULL, 0, _webui_cb, (void *) arg, 0, NULL); - if(user_fun_thread != NULL) - CloseHandle(user_fun_thread); - #else - pthread_t thread; - pthread_create(&thread, NULL, &_webui_cb, (void *) arg); - pthread_detach(thread); - #endif -} - -void _webui_window_send(webui_window_t* win, char* packet, size_t packets_size) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_window_send([%.*s], [%d])... [ ", win->core.window_number, (int)packets_size, packet, (int)packets_size); - _webui_print_hex(packet, packets_size); - printf("]\n"); - #endif - - if(!win->core.connected || - webui.mg_connections[win->core.window_number] == NULL || - packet == NULL || - packets_size < 4) - return; - - struct mg_connection* c = webui.mg_connections[win->core.window_number]; - mg_ws_send( - c, - packet, - packets_size, - WEBSOCKET_OP_BINARY - ); -} - -bool _webui_get_data(const char* packet, size_t packet_len, unsigned int pos, size_t* data_len, char** data) { - - #ifdef WEBUI_LOG - printf("[0] _webui_get_data()... \n"); - #endif - - if((pos + 1) > packet_len) { - - *data = NULL; - data_len = 0; - return false; - } - - *data = (char*) _webui_malloc((packet_len - pos)); - - // Check mem - if(*data == NULL) { - - data_len = 0; - return false; - } - - // Copy data part - char* p = *data; - for(unsigned int i = pos; i < packet_len; i++) { - - memcpy(p, &packet[i], 1); - p++; - } - - // Check data size - *data_len = strlen(*data); - if(*data_len < 1) { - - _webui_free_mem((void *) data); - *data = NULL; - data_len = 0; - return false; - } - - return true; -} - -void _webui_window_receive(webui_window_t* win, const char* packet, size_t len) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_window_receive([%.*s], [%d])... [ ", win->core.window_number, (int)len, packet, (int)len); - _webui_print_hex(packet, len); - printf("]\n"); - #endif - - if((unsigned char) packet[0] != WEBUI_HEADER_SIGNATURE || len < 4) - return; - - if((unsigned char) packet[1] == WEBUI_HEADER_CLICK) { - - // Click Event - - // 0: [Signature] - // 1: [Type] - // 2: - // 3: [Data] - - // Get html element id - char* element; - size_t element_len; - if(!_webui_get_data(packet, len, 3, &element_len, &element)) - return; - - // Generate WebUI internal id - char* webui_internal_id = _webui_generate_internal_id(win, element); - - _webui_window_event( - win, // Window - webui_internal_id, // WebUI Internal ID - element, // User HTML ID - NULL, // User Custom Data - 0, // User Data Len - WEBUI_EVENT_MOUSE_CLICK // Type of this event - ); - } - else if((unsigned char) packet[1] == WEBUI_HEADER_JS) { - - // JS Result - - // 0: [Signature] - // 1: [Type] - // 2: [ID] - // 3: [Error] - // 4: [Data] - - // Get pipe id - unsigned char run_id = packet[2]; - if(run_id < 0x01) { - - // Fatal. - // The pipe ID is not valid - // we can't send the ready signal to webui_script() - return; - } - - // Get data part - char* data; - size_t data_len; - bool data_status = _webui_get_data(packet, len, 4, &data_len, &data); - - // Get js-error - bool error = true; - if((unsigned char) packet[3] == 0x00) - error = false; - - // Initialize pipe - _webui_free_mem((void *) &webui.run_responses[run_id]); - - // Set pipe - if(data_status && data_len > 0) { - - webui.run_error[run_id] = error; - webui.run_responses[run_id] = data; - } - else { - - // Empty Result - webui.run_error[run_id] = error; - webui.run_responses[run_id] = webui_empty_string; - } - - // Send ready signal to webui_script() - webui.run_done[run_id] = true; - } - else if((unsigned char) packet[1] == WEBUI_HEADER_CALL_FUNC) { - - // Function Call (No response) - - // 0: [Signature] - // 1: [Type] - // 2: - // 3: [ID, Null, Data] - - // Get html element id - char* element; - size_t element_len; - if(!_webui_get_data(packet, len, 3, &element_len, &element)) - return; - - // Get data - void* data; - size_t data_len; - if(!_webui_get_data(packet, len, (3 + element_len + 1), &data_len, (char **) &data)) - return; - - // Generate WebUI internal id - char* webui_internal_id = _webui_generate_internal_id(win, element); - - _webui_window_event( - win, // Window - webui_internal_id, // WebUI Internal ID - element, // User HTML ID - data, // User Custom Data - data_len, // User Data Len - WEBUI_EVENT_CALLBACK // Type of this event - ); - } - else if((unsigned char) packet[1] == WEBUI_HEADER_SWITCH) { - - // Navigation Event - - // 0: [Signature] - // 1: [Type] - // 2: - // 3: [URL] - - // Get URL - char* url; - size_t url_len; - if(!_webui_get_data(packet, len, 3, &url_len, &url)) - return; - - // Generate WebUI internal id - char* webui_internal_id = _webui_generate_internal_id(win, ""); - - _webui_window_event( - win, // Window - webui_internal_id, // WebUI Internal ID - "", // HTML ID - url, // URL - url_len, // URL Len - WEBUI_EVENT_NAVIGATION // Type of this event - ); - } -} - -const char* webui_get_string(webui_event_t* e) { - - #ifdef WEBUI_LOG - printf("[0] webui_get_string()... \n"); - #endif - - if(e->data != NULL) { - size_t len = strlen(e->data); - if(len > 0 && len <= WEBUI_MAX_BUF) - return (const char *) e->data; - } - - return webui_empty_string; -} - -long long int webui_get_int(webui_event_t* e) { - - #ifdef WEBUI_LOG - printf("[0] webui_get_int()... \n"); - #endif - - char *endptr; - - if(e->data != NULL) { - size_t len = strlen(e->data); - if(len > 0 && len <= 20) // 64-bit max is -9,223,372,036,854,775,808 (20 character) - return strtoll((const char *) e->data, &endptr, 10); - } - - return 0; -} - -bool webui_get_bool(webui_event_t* e) { - - #ifdef WEBUI_LOG - printf("[0] webui_get_bool()... \n"); - #endif - - const char* str = webui_get_string(e); - if(str[0] == 't' || str[0] == 'T') // true || True - return true; - - return false; -} - -void webui_return_int(webui_event_t* e, long long int n) { - - #ifdef WEBUI_LOG - printf("[%d] webui_return_int([%lld])... \n", e->window_id, n); - #endif - - // Int to Str - // 64-bit max is -9,223,372,036,854,775,808 (20 character) - char* buf = (char*) _webui_malloc(20); - sprintf(buf, "%lld", n); - - // Set response - e->response = buf; -} - -void webui_return_string(webui_event_t* e, char* s) { - - #ifdef WEBUI_LOG - printf("[%d] webui_return_string([%s])... \n", e->window_id, s); - #endif - - if(_webui_is_empty(s)) - return; - - // Copy Str - int len = strlen(s); - char* buf = (char*) _webui_malloc(len); - memcpy(buf, s, len); - - // Set response - e->response = buf; -} - -void webui_return_bool(webui_event_t* e, bool b) { - - #ifdef WEBUI_LOG - printf("[%d] webui_return_bool([%d])... \n", e->window_id, b); - #endif - - // Bool to Str - int len = 1; - char* buf = (char*) _webui_malloc(len); - sprintf(buf, "%d", b); - - // Set response - e->response = buf; -} - -void _webui_wait_process(webui_window_t* win, bool status) { - - #ifdef WEBUI_LOG - printf("[%d] _webui_wait_process()... \n", win->core.window_number); - #endif - - win->core.detect_process_close = status; -} - -char* _webui_get_current_path(void) { - - #ifdef WEBUI_LOG - printf("[0] _webui_get_current_path()... \n"); - #endif - - char* path = (char*) _webui_malloc(WEBUI_MAX_PATH); - if(WEBUI_GET_CURRENT_DIR (path, WEBUI_MAX_PATH) == NULL) - path[0] = 0x00; - - return path; -} - -void _webui_set_custom_browser(webui_custom_browser_t* p) { - - #ifdef WEBUI_LOG - printf("[0] _webui_set_custom_browser()... \n"); - #endif - - webui.custom_browser = p; -} - -void webui_exit(void) { - - #ifdef WEBUI_LOG - printf("[0] webui_exit()... \n"); - #endif - - webui.wait_for_socket_window = false; - webui.exit_now = true; - - // Let's give other threads more time to - // safely exit and finish their cleaning up. - _webui_sleep(100); -} - -bool webui_is_app_running(void) { - - #ifdef WEBUI_LOG - // printf("[0] webui_is_app_running()... \n"); - #endif - - static bool app_is_running = true; - - // Stop if already flagged - if(!app_is_running) return false; - - // Initialization - if(!webui.initialized) - _webui_init(); - - // Get app status - if(webui.exit_now) { - app_is_running = false; - } - else if(webui.use_timeout) { - if(webui.wait_for_socket_window) { - if(webui.servers < 1) - app_is_running = false; - } - } - - // Final cleaning - if(!app_is_running) { - #ifdef WEBUI_LOG - printf("[0] webui_is_app_running()... -> App Stopped.\n"); - #endif - _webui_clean(); - } - - return app_is_running; -} - -void webui_wait(void) { - - #ifdef WEBUI_LOG - printf("[L] webui_wait()... \n"); - #endif - - _webui_init(); - - if(webui.use_timeout) { - - #ifdef WEBUI_LOG - printf("[L] webui_wait() -> Using timeout %d second\n", webui.startup_timeout); - #endif - - // Wait for browser to start - _webui_wait_for_startup(); - - if(webui.wait_for_socket_window) { - - #ifdef WEBUI_LOG - printf("[L] webui_wait() -> Wait for connected UI...\n"); - #endif - - while(webui.servers > 0) { - - #ifdef WEBUI_LOG - // printf("[%d/%d]", webui.servers, webui.connections); - #endif - _webui_sleep(50); - } - } - else { - - // Probably the app didn't use the show() function - // so, there is no window running. - - #ifdef WEBUI_LOG - printf("[L] webui_wait() -> Ignoring connected UI.\n"); - #endif - } - } - else { - - #ifdef WEBUI_LOG - printf("[L] webui_wait() -> Infinite wait...\n"); - #endif - - // Infinite wait - while(!webui.exit_now) - _webui_sleep(50); - } - - #ifdef WEBUI_LOG - printf("[L] webui_wait() -> Wait finished.\n"); - #endif - - // Final cleaning - _webui_clean(); -} - -void _webui_free_port(unsigned int port) { - - #ifdef WEBUI_LOG - printf("[0] _webui_free_port([%d])... \n", port); - #endif - - for(unsigned int i = 0; i < WEBUI_MAX_ARRAY; i++) { - if(webui.used_ports[i] == port) { - webui.used_ports[i] = 0; - break; - } - } -} - -void _webui_wait_for_startup(void) { - - #ifdef WEBUI_LOG - printf("[0] _webui_wait_for_startup()... \n"); - #endif - - if(webui.connections > 0) - return; - - // Wait for a specific time - for(unsigned int n = 0; n <= (webui.startup_timeout * 10); n++) { - - if(webui.connections > 0) - break; - - // We should wait 100ms, but the server thread - // may add an extras 3 second to the main loop. - _webui_sleep(50); - } - - #ifdef WEBUI_LOG - printf("[0] _webui_wait_for_startup() -> Finish.\n"); - #endif -} - -void webui_set_timeout(unsigned int second) { - - #ifdef WEBUI_LOG - printf("[0] webui_set_timeout([%d])... \n", second); - #endif - - _webui_init(); - - if(second < 1) { - - webui.use_timeout = false; - } - else { - - webui.use_timeout = true; - webui.startup_timeout = second; - webui.timeout_extra = false; - } -} - -unsigned int _webui_get_new_window_number(void) { - - #ifdef WEBUI_LOG - printf("[0] _webui_get_new_window_number()... \n"); - #endif - - return ++webui.last_window; -} - -unsigned int _webui_get_free_port(void) { - - #ifdef WEBUI_LOG - printf("[0] _webui_get_free_port()... \n"); - #endif - - #ifdef _WIN32 - srand((unsigned int)time(NULL)); - #else - srand(time(NULL)); - #endif - - unsigned int port = (rand() % (WEBUI_MAX_PORT + 1 - WEBUI_MIN_PORT)) + WEBUI_MIN_PORT; - - for(unsigned int i = WEBUI_MIN_PORT; i <= WEBUI_MAX_PORT; i++) { - - // Search [port] in [webui.used_ports] - bool found = false; - for(unsigned int j = 0; j < WEBUI_MAX_ARRAY; j++) { - if(webui.used_ports[j] == port) { - found = true; - break; + RegCloseKey(hKey); + return true; } } - if(found) - // Port used by local window - port = (rand() % (WEBUI_MAX_PORT + 1 - WEBUI_MIN_PORT)) + WEBUI_MIN_PORT; - else { - if(_webui_port_is_used(port)) - // Port used by an external app - port = (rand() % (WEBUI_MAX_PORT + 1 - WEBUI_MIN_PORT)) + WEBUI_MIN_PORT; - else - // Port is free - break; - } + return false; } - // Add - for(unsigned int i = 0; i < WEBUI_MAX_ARRAY; i++) { - if(webui.used_ports[i] == 0) { - webui.used_ports[i] = port; - break; - } - } - - return port; -} - -void webui_script_runtime(webui_window_t* win, unsigned int runtime) { - - #ifdef WEBUI_LOG - printf("[%d] webui_script_runtime(%d)... \n", win->core.window_number, runtime); - #endif - - _webui_init(); - - if(runtime != webui.runtime.deno && runtime != webui.runtime.nodejs) - win->core.runtime = webui.runtime.none; - else - win->core.runtime = runtime; -} - -void _webui_init(void) { - - if(webui.initialized) - return; - - #ifdef WEBUI_LOG - printf("[0] WebUI v%s \n", WEBUI_VERSION); - printf("[0] _webui_init()... \n"); - #endif - - // Initializing - memset(&webui, 0x0, sizeof(webui_t)); - webui.initialized = true; - webui.use_timeout = true; - webui.startup_timeout = WEBUI_DEF_TIMEOUT; - webui.timeout_extra = true; - webui.browser.chrome = 1; - webui.browser.firefox = 2; - webui.browser.edge = 3; - webui.browser.safari = 4; - webui.browser.chromium = 5; - webui.browser.opera = 6; - webui.browser.brave = 7; - webui.browser.vivaldi = 8; - webui.browser.epic = 9; - webui.browser.yandex = 10; - webui.browser.custom = 99; - webui.runtime.deno = 1; - webui.runtime.nodejs = 2; - webui.executable_path = _webui_get_current_path(); -} - -unsigned int _webui_get_cb_index(char* webui_internal_id) { - - #ifdef WEBUI_LOG - printf("[0] _webui_get_cb_index([%s])... \n", webui_internal_id); - #endif - - if(webui_internal_id != NULL) { - - for(unsigned int i = 1; i < WEBUI_MAX_ARRAY; i++) { - - if(!_webui_is_empty(webui.html_elements[i])) - if(strcmp(webui.html_elements[i], webui_internal_id) == 0) - return i; - } - } - - return 0; -} - -unsigned int _webui_set_cb_index(char* webui_internal_id) { - - #ifdef WEBUI_LOG - printf("[0] _webui_set_cb_index([%s])... \n", webui_internal_id); - #endif - - // Add - for(unsigned int i = 1; i < WEBUI_MAX_ARRAY; i++) { - - if(_webui_is_empty(webui.html_elements[i])) { - - webui.html_elements[i] = webui_internal_id; - - return i; - } - } - - return 0; -} - -// --[Interface]--------------- - -void webui_bind_interface_handler(webui_event_t* e) { - - #ifdef WEBUI_LOG - printf("[%d] webui_bind_interface_handler()... \n", e->window_id); - #endif - - unsigned int cb_index = e->element_id; - - if(cb_index > 0 && webui.cb_interface[cb_index] != NULL) - webui.cb_interface[cb_index](e->element_id, e->window_id, e->element_name, e->window, (char*)e->data, (char**)&e->response); - - if(_webui_is_empty((const char *)e->response)) { - e->response = (void*)webui_empty_string; - } - else { - - // The response pointer is not guaranteed to stay live - // so let's make our own copy. - size_t len = strlen((const char *)e->response); - char* new_cpy = (char*)_webui_malloc(len); - memcpy(new_cpy, e->response, len); - e->response = new_cpy; - } - - #ifdef WEBUI_LOG - printf("[%d] webui_bind_interface_handler()... user-callback response [%s]\n", e->window_id, (const char *)e->response); - #endif -} - -unsigned int webui_bind_interface(webui_window_t* win, const char* element, void (*func)(unsigned int, unsigned int, char*, webui_window_t*, char*, char**)) { - - #ifdef WEBUI_LOG - printf("[%d] webui_bind_interface()... \n", win->core.window_number); - #endif - - // Bind - unsigned int cb_index = webui_bind(win, element, webui_bind_interface_handler); - webui.cb_interface[cb_index] = func; - return cb_index; -} - -void webui_script_interface(webui_window_t* win, const char* script, unsigned int timeout, bool* error, unsigned int* length, char** data) { - - #ifdef WEBUI_LOG - printf("[%d] webui_script_interface()... \n", win->core.window_number); - #endif - - webui_script_t js = { - .script = script, - .timeout = timeout - }; - - webui_script(win, &js); - - *data = (char*) js.result.data; - *error = js.result.error; - *length = js.result.length; -} - -void webui_script_interface_struct(webui_window_t* win, webui_script_interface_t* js_int) { - - #ifdef WEBUI_LOG - printf("[%d] webui_script_interface_struct()... \n", win->core.window_number); - #endif - - webui_script_t js = { - .script = js_int->script, - .timeout = js_int->timeout - }; - - webui_script(win, &js); - - js_int->data = js.result.data; - js_int->error = js.result.error; - js_int->length = js.result.length; -} - -#ifdef _WIN32 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved) { return true; } + #endif diff --git a/src/webui_core.h b/src/webui_core.h new file mode 100644 index 00000000..198ebeec --- /dev/null +++ b/src/webui_core.h @@ -0,0 +1,181 @@ +/* + WebUI Library 2.2.0 + http://webui.me + https://github.com/alifcommunity/webui + Copyright (c) 2020-2023 Hassan Draga. + Licensed under GNU General Public License v2.0. + All rights reserved. + Canada. +*/ + +#ifndef _WEBUI_CORE_H +#define _WEBUI_CORE_H + +#include "webui.h" + +#define WEBUI_HEADER_SIGNATURE 0xDD // All packets should start with this 8bit +#define WEBUI_HEADER_JS 0xFE // JavaScript result in frontend +#define WEBUI_HEADER_JS_QUICK 0xFD // JavaScript result in frontend +#define WEBUI_HEADER_CLICK 0xFC // Click event +#define WEBUI_HEADER_SWITCH 0xFB // Frontend refresh +#define WEBUI_HEADER_CLOSE 0xFA // Close window +#define WEBUI_HEADER_CALL_FUNC 0xF9 // Call a backend function +#define WEBUI_MAX_ARRAY (128) // Max threads, servers, windows, pointers.. +#define WEBUI_MIN_PORT (10000) // Minimum socket port +#define WEBUI_MAX_PORT (65500) // Should be less than 65535 +#define WEBUI_MAX_BUF (1024000) // 1024 Kb max dynamic memory allocation +#define WEBUI_DEFAULT_PATH "." // Default root path +#define WEBUI_DEF_TIMEOUT (10) // Default startup timeout in seconds +#define WEBUI_MAX_TIMEOUT (30) // Maximum startup timeout in seconds + +typedef struct _webui_timer_t { + struct timespec start; + struct timespec now; +} _webui_timer_t; + +typedef struct _webui_window_t { + unsigned int window_number; + bool server_running; + bool connected; + bool html_handled; + bool multi_access; + bool is_embedded_html; + unsigned int server_port; + char* url; + const char* html; + const char* icon; + const char* icon_type; + unsigned int current_browser; + char* browser_path; + char* profile_path; + unsigned int connections; + unsigned int runtime; + bool has_events; + char* server_root_path; + #ifdef _WIN32 + HANDLE server_thread; + #else + pthread_t server_thread; + #endif +} _webui_window_t; + +typedef struct _webui_core_t { + unsigned int servers; + unsigned int connections; + char* html_elements[WEBUI_MAX_ARRAY]; + unsigned int used_ports[WEBUI_MAX_ARRAY]; + unsigned int last_window; + unsigned int startup_timeout; + bool exit_now; + const char* run_responses[WEBUI_MAX_ARRAY]; + bool run_done[WEBUI_MAX_ARRAY]; + bool run_error[WEBUI_MAX_ARRAY]; + unsigned char run_last_id; + struct mg_mgr* mg_mgrs[WEBUI_MAX_ARRAY]; + struct mg_connection* mg_connections[WEBUI_MAX_ARRAY]; + bool initialized; + void (*cb[WEBUI_MAX_ARRAY])(webui_event_t* e); + void (*cb_interface[WEBUI_MAX_ARRAY])(void*, unsigned int, char*, char*, char*); + char* executable_path; + void *ptr_list[WEBUI_MAX_ARRAY]; + unsigned int ptr_position; + size_t ptr_size[WEBUI_MAX_ARRAY]; + unsigned int current_browser; +} _webui_core_t; + +typedef struct _webui_cb_t { + _webui_window_t* win; + char* webui_internal_id; + char* element_name; + void* data; + unsigned int data_len; + int event_type; +} _webui_cb_t; + +typedef struct _webui_mg_handler_t { + struct mg_connection* c; + int ev; + void* ev_data; + void* fn_data; +} _webui_mg_handler_t; + +typedef struct _webui_cmd_async_t { + _webui_window_t* win; + char* cmd; +} _webui_cmd_async_t; + +// -- Definitions --------------------- +#ifdef _WIN32 + static const char* webui_sep = "\\"; + DWORD WINAPI _webui_cb(LPVOID _arg); + DWORD WINAPI _webui_run_browser_task(LPVOID _arg); + int _webui_system_win32(char* cmd, bool show); + bool _webui_socket_test_listen_win32(unsigned int port_num); + bool _webui_get_windows_reg_value(HKEY key, const char* reg, const char* value_name, char value[WEBUI_MAX_PATH]); + + #define WEBUI_CB DWORD WINAPI _webui_cb(LPVOID _arg) + #define WEBUI_SERVER_START DWORD WINAPI _webui_server_start(LPVOID arg) + #define THREAD_RETURN return 0; +#else + static const char* webui_sep = "/"; + void* _webui_cb(void* _arg); + void* _webui_run_browser_task(void* _arg); + + #define WEBUI_CB void* _webui_cb(void* _arg) + #define WEBUI_SERVER_START void* _webui_server_start(void* arg) + #define THREAD_RETURN pthread_exit(NULL); +#endif + +static void _webui_init(void); +bool _webui_show(_webui_window_t* window, const char* content, unsigned int browser); +unsigned int _webui_get_cb_index(char* webui_internal_id); +unsigned int _webui_set_cb_index(char* webui_internal_id); +unsigned int _webui_get_free_port(void); +unsigned int _webui_get_new_window_number(void); +static void _webui_wait_for_startup(void); +static void _webui_free_port(unsigned int port); +char* _webui_get_current_path(void); +static void _webui_window_receive(_webui_window_t* win, const char* packet, size_t len); +static void _webui_window_send(_webui_window_t* win, char* packet, size_t packets_size); +static void _webui_window_event(_webui_window_t* win, char* element_id, char* element, void* data, unsigned int data_len, int event_type); +unsigned int _webui_window_get_number(_webui_window_t* win); +static void _webui_window_open(_webui_window_t* win, char* link, unsigned int browser); +int _webui_cmd_sync(char* cmd, bool show); +int _webui_cmd_async(char* cmd, bool show); +int _webui_run_browser(_webui_window_t* win, char* cmd); +static void _webui_clean(void); +bool _webui_browser_exist(_webui_window_t* win, unsigned int browser); +const char* _webui_browser_get_temp_path(unsigned int browser); +bool _webui_folder_exist(char* folder); +bool _webui_browser_create_profile_folder(_webui_window_t* win, unsigned int browser); +bool _webui_browser_start_chrome(_webui_window_t* win, const char* address); +bool _webui_browser_start_edge(_webui_window_t* win, const char* address); +bool _webui_browser_start_epic(_webui_window_t* win, const char* address); +bool _webui_browser_start_vivaldi(_webui_window_t* win, const char* address); +bool _webui_browser_start_brave(_webui_window_t* win, const char* address); +bool _webui_browser_start_firefox(_webui_window_t* win, const char* address); +bool _webui_browser_start_yandex(_webui_window_t* win, const char* address); +bool _webui_browser_start_chromium(_webui_window_t* win, const char* address); +bool _webui_browser_start(_webui_window_t* win, const char* address, unsigned int browser); +long _webui_timer_diff(struct timespec *start, struct timespec *end); +static void _webui_timer_start(_webui_timer_t* t); +bool _webui_timer_is_end(_webui_timer_t* t, unsigned int ms); +static void _webui_timer_clock_gettime(struct timespec *spec); +bool _webui_set_root_folder(_webui_window_t* win, const char* path); +const char* _webui_generate_js_bridge(_webui_window_t* win); +static void _webui_print_hex(const char* data, size_t len); +static void _webui_free_mem(void* ptr); +bool _webui_file_exist_mg(void *ev_data); +bool _webui_file_exist(char* file); +static void _webui_free_all_mem(void); +bool _webui_show_window(_webui_window_t* win, const char* content, bool is_embedded_html, unsigned int browser); +char* _webui_generate_internal_id(_webui_window_t* win, const char* element); +bool _webui_is_empty(const char* s); +unsigned char _webui_get_run_id(void); +void* _webui_malloc(int size); +static void _webui_sleep(long unsigned int ms); + +WEBUI_SERVER_START; +WEBUI_CB; + +#endif /* _WEBUI_CORE_H */ diff --git a/website/docs/c_api.md b/website/docs/c_api.md index 3fe885e2..129039fa 100644 --- a/website/docs/c_api.md +++ b/website/docs/c_api.md @@ -87,7 +87,7 @@ Please visit [C Examples](https://github.com/alifcommunity/webui/tree/main/examp To create a new window object, you can use `webui_new_window()`, which returns a pointer to a struct `webui_window_t`. This pointer does *NOT* need to be freed. ```c -webui_window_t* my_window = webui_new_window(); +void* my_window = webui_new_window(); ``` --- @@ -328,10 +328,10 @@ void my_function(webui_event_t* e){ webui_script(e->window, &js); // Check if there is any JavaScript error - if(js.result.error) - printf("Error: %s\n", js.result.data); + if(js.error) + printf("Error: %s\n", js.data); else - printf("Output: %s\n", js.result.data); + printf("Output: %s\n", js.data); // Free resources webui_script_cleanup(&js); diff --git a/website/index.html b/website/index.html index 251aa352..2d478adb 100644 --- a/website/index.html +++ b/website/index.html @@ -151,8 +151,8 @@ @@ -172,8 +172,8 @@