From 25ca1ec4a46ef1f79404a11d1604ca3922eb1967 Mon Sep 17 00:00:00 2001 From: Hans Dijkema Date: Mon, 2 Mar 2026 23:10:29 +0100 Subject: [PATCH] - --- racket-webview-ffi.rkt | 6 +- racket-webview-qt.rkt | 76 +++++++++++ racket-webview.rkt | 6 +- rktwebview/.gitignore | 1 + rktwebview/CMakeLists.txt | 105 +++++++++++++- rktwebview/json.h | 4 + rktwebview/main.cpp | 14 +- rktwebview/rktwebview.cpp | 57 ++++++-- rktwebview/rktwebview.h | 24 +++- rktwebview_qt/.gitignore | 74 ++++++++++ rktwebview_qt/CMakeLists.txt | 47 +++++++ rktwebview_qt/main.cpp | 37 +++++ rktwebview_qt/rktwebview.cpp | 157 +++++++++++++++++++++ rktwebview_qt/rktwebview.h | 57 ++++++++ rktwebview_qt/rktwebview_internal.h | 25 ++++ rktwebview_qt/rktwebview_qt.cpp | 196 +++++++++++++++++++++++++++ rktwebview_qt/rktwebview_qt.h | 68 ++++++++++ rktwebview_qt/rktwebview_qt_global.h | 12 ++ rktwebview_qt/webviewapp.cpp | 8 ++ rktwebview_qt/webviewapp.h | 13 ++ rktwebview_qt/webviewqt.cpp | 19 +++ rktwebview_qt/webviewqt.h | 22 +++ rktwebview_qt/webviewwindow.cpp | 27 ++++ rktwebview_qt/webviewwindow.h | 30 ++++ 24 files changed, 1054 insertions(+), 31 deletions(-) create mode 100644 racket-webview-qt.rkt create mode 100644 rktwebview/.gitignore create mode 100644 rktwebview_qt/.gitignore create mode 100644 rktwebview_qt/CMakeLists.txt create mode 100644 rktwebview_qt/main.cpp create mode 100644 rktwebview_qt/rktwebview.cpp create mode 100644 rktwebview_qt/rktwebview.h create mode 100644 rktwebview_qt/rktwebview_internal.h create mode 100644 rktwebview_qt/rktwebview_qt.cpp create mode 100644 rktwebview_qt/rktwebview_qt.h create mode 100644 rktwebview_qt/rktwebview_qt_global.h create mode 100644 rktwebview_qt/webviewapp.cpp create mode 100644 rktwebview_qt/webviewapp.h create mode 100644 rktwebview_qt/webviewqt.cpp create mode 100644 rktwebview_qt/webviewqt.h create mode 100644 rktwebview_qt/webviewwindow.cpp create mode 100644 rktwebview_qt/webviewwindow.h diff --git a/racket-webview-ffi.rkt b/racket-webview-ffi.rkt index a67e7c3..988292d 100644 --- a/racket-webview-ffi.rkt +++ b/racket-webview-ffi.rkt @@ -18,7 +18,7 @@ rkt_webview_set_html rkt_webview_valid rkt_webview_run_js - rkt_webview_call_js + ;rkt_webview_call_js rkt_webview_pending_events rkt_webview_get_event rkt_webview_set_event_callback! @@ -39,9 +39,11 @@ (define libname (let ((os (system-type 'os*))) (cond ((eq? os 'windows) (format "rktwebview.dll")) + ((eq? os 'linux) (format "librktwebview.so")) (else (error (format "OS ~a not supported" os))))) ) -(set! libname "../rtkwebview/build/Release/rktwebview.dll") +;(set! libname "../rktwebview/build/Release/rktwebview.dll") +(set! libname "../rktwebview/build/Release/librktwebview.so") (define webview-lib-file (build-path lib-dir libname)) (define webview-lib (ffi-lib webview-lib-file)) diff --git a/racket-webview-qt.rkt b/racket-webview-qt.rkt new file mode 100644 index 0000000..b9fb336 --- /dev/null +++ b/racket-webview-qt.rkt @@ -0,0 +1,76 @@ +#lang racket/base + +(require ffi/unsafe + ffi/unsafe/define + ffi/unsafe/atomic + ffi/unsafe/os-thread + racket/async-channel + racket/runtime-path + racket/port + data/queue + json + racket/string + racket/path + ) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; FFI Library +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(define lib-type 'release) + +(define-runtime-path lib-dir "lib") + +(define libname (let ((os (system-type 'os*))) + (cond ((eq? os 'windows) (format "rktwebview.dll")) + ((eq? os 'linux) (format "librktwebview.so")) + (else (error (format "OS ~a not supported" os))))) + ) +;(set! libname "../rktwebview/build/Release/rktwebview.dll") +;(set! libname "../rktwebview/build/Release/librktwebview.so") +(set! libname "../rktwebview_qt/rktwebview_qt/build/Desktop-Release/librktwebview_qt.so") +(define webview-lib-file (build-path lib-dir libname)) + +(define webview-lib (ffi-lib webview-lib-file)) +(define-ffi-definer define-rktwebview webview-lib) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Types +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;RKTWEBVIEW_QT_EXPORT void rkt_webview_init(int &argc, char **argv); +(define-rktwebview rkt_webview_init + (_fun -> _void)) + +;RKTWEBVIEW_QT_EXPORT void rkt_webview_process_events(int for_ms); +(define-rktwebview rkt_webview_process_events + (_fun _int -> _void)) + +;RKTWEBVIEW_QT_EXPORT int rkt_webview_create(int parent); +(define-rktwebview rkt_webview_create + (_fun _int -> _int)) + +;RKTWEBVIEW_QT_EXPORT void rkt_webview_close(int wv); +(define-rktwebview rkt_webview_close + (_fun _int -> _void)) + +;RKTWEBVIEW_QT_EXPORT result_t rkt_webview_set_url(int wv, const char *url); +(define-rktwebview rkt_webview_set_url + (_fun _int _string/utf-8 -> _int)) + +;RKTWEBVIEW_QT_EXPORT result_t rkt_webview_set_html(int wv, const char *html); + +(define process-events #t) +(define (start-event-processing) + (thread (λ () + (letrec ((f (λ () + (rkt_webview_process_events 1) + (sleep 0.001) + (if process-events + (f) + 'done)))) + (f))))) + +(rkt_webview_init) +(start-event-processing) + \ No newline at end of file diff --git a/racket-webview.rkt b/racket-webview.rkt index e1d1582..f58157e 100644 --- a/racket-webview.rkt +++ b/racket-webview.rkt @@ -164,9 +164,9 @@ (define (webview-run-js wv js) (rkt_webview_run_js (wv-handle wv) js)) -(define (webview-call-js wv js) - (let ((result (rkt_webview_call_js (wv-handle wv) js))) - result)) +;(define (webview-call-js wv js) +; (let ((result (rkt_webview_call_js (wv-handle wv) js))) +; result)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; testing diff --git a/rktwebview/.gitignore b/rktwebview/.gitignore new file mode 100644 index 0000000..8a9d35c --- /dev/null +++ b/rktwebview/.gitignore @@ -0,0 +1 @@ +*.user diff --git a/rktwebview/CMakeLists.txt b/rktwebview/CMakeLists.txt index eb5b5c7..a4dfdc7 100644 --- a/rktwebview/CMakeLists.txt +++ b/rktwebview/CMakeLists.txt @@ -4,10 +4,88 @@ project(rktwebview LANGUAGES CXX) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(GIT_EXECUTABLE git) +set(CMAKE_VERBOSE_MAKEFILE ON) -include_directories(../webview/core/include) -include_directories(../webview/build/Release/_deps/microsoft_web_webview2-src/build/native/include) -link_directories(../webview/build/Release/core) +set(WEBVIEW_BUILD_STATIC_LIBRARY ON) +set(WEBVIEW_WEBKITGTK_API "6.0") +#set(WEBVIEW_WEBKITGTK_PREFERRED_API_LIST webkitgtk-6.0) + +if("${CMAKE_BUILD_TYPE}" STREQUAL "") + set(CMAKE_BUILD_TYPE "Release") +endif() + +if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + set(LIBKIND "d") +else() + set(LIBKIND "r") +endif() + +if(CMAKE_SYSTEM_NAME STREQUAL "Windows") + set(LIBEXT lib) + set(LIBPRE ) +else() + set(LIBEXT a) + set(LIBPRE lib) +endif() + +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + # using Clang + add_compile_options(-fPIC) + set(CFLAGS_ENV "CFLAGS=-fPIC") +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # using GCC + add_compile_options(-fPIC) + set(CFLAGS_ENV "CFLAGS=-fPIC") +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "Intel") + # using Intel C++ + set(CFLAGS_ENV "") +elseif (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + # using Visual Studio C++ + set(CFLAGS_ENV "") +endif() + +if(NOT(EXISTS ./deps)) + file(MAKE_DIRECTORY ./deps) +endif() + +function(git_dep dir dep) + if(EXISTS ${rktwebview_SOURCE_DIR}/deps/${dir}) + execute_process( + COMMAND ${GIT_EXECUTABLE} pull + COMMAND_ECHO STDOUT + WORKING_DIRECTORY ${rktwebview_SOURCE_DIR}/deps/${dir} + ) + else() + message("Executing git clone...") + execute_process( + COMMAND ${GIT_EXECUTABLE} clone ${dep} ${rktwebview_SOURCE_DIR}/deps/${dir} + COMMAND_ECHO STDOUT + ) + endif() +endfunction() + +git_dep(webview https://github.com/webview/webview.git) +add_subdirectory(deps/webview) + + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + find_package(PkgConfig REQUIRED) + pkg_check_modules(GTK REQUIRED gtk+-3.0) + + include_directories(${GTK_INCLUDE_DIRS}) + message("${GTK_INCLUDE_DIRS}") + link_directories(${GTK_LIBRARY_DIRS}) + + pkg_check_modules(GTKWEBVIEW REQUIRED webkit2gtk-4.1) + include_directories(${GTKWEBVIEW_INCLUDE_DIRS}) + message("${GTKWEBVIEW_INCLUDE_DIRS}") + link_directories(${GTKWEBVIEW_LIBRARY_DIRS}) +endif() + +include_directories(deps/webview/core/include) +include_directories(deps/webview/build/Release/_deps/microsoft_web_webview2-src/build/native/include) +link_directories(build/Release/deps/webview/core) link_directories(build/Release) add_library(rktwebview SHARED @@ -15,18 +93,31 @@ add_library(rktwebview SHARED rktwebview.cpp rktwebview.h - json.h json.cpp - main.cpp ) add_executable(rktwebview_test main.cpp + + rktwebview_global.h + rktwebview.cpp + rktwebview.h + + json.h + json.cpp ) +#add_dependencies(rktwebview webview) -target_link_libraries(rktwebview webview_static) -target_link_libraries(rktwebview_test rktwebview) +target_link_libraries(rktwebview webview_core_static) +#target_link_libraries(rktwebview_test rktwebview) +target_link_libraries(rktwebview_test webview_core_static) target_compile_definitions(rktwebview PRIVATE RKTWEBVIEW_LIBRARY) + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + target_link_libraries(rktwebview ${GTK_LIBRARIES} ${GTKWEBVIEW_LIBRARIES}) + target_link_libraries(rktwebview_test ${GTK_LIBRARIES} ${GTKWEBVIEW_LIBRARIES}) +endif() + diff --git a/rktwebview/json.h b/rktwebview/json.h index 87f1b57..7f54c39 100644 --- a/rktwebview/json.h +++ b/rktwebview/json.h @@ -15,6 +15,10 @@ std::string json_escape( const std::string &str ); +#ifdef Bool +#undef Bool +#endif + class JSON { union BackingData { diff --git a/rktwebview/main.cpp b/rktwebview/main.cpp index 7c6ba58..eb0a3b6 100644 --- a/rktwebview/main.cpp +++ b/rktwebview/main.cpp @@ -5,12 +5,19 @@ int main() { rkt_webview_t *wv = rkt_create_webview(); result_t r = rkt_webview_navigate(wv, "https://wikipedia.org"); + + rkt_webview_t *wv1 = nullptr; //result_t r = rkt_set_html(wv, "He daar!

He daar!

"); printf("Navigate: result = %d\n", r); int i = 0; while(rkt_webview_valid(wv)) { - printf("Waiting...\n"); + printf("Waiting...%d\n", i); +#ifdef USE_WIN_THREADS Sleep(1000); +#endif +#ifdef USE_PTHREADS + usleep(1000 * 1000); +#endif i += 1; if (i > 5) { item_t item = rkt_webview_call_js(wv, "{ window.location = 'https://dijkewijk.nl'; return 42; }"); @@ -20,6 +27,11 @@ int main() printf("%d, %s\n", item1.context, item1.data); rkt_webview_destroy_item(item1); } + if (i == 16) { + wv1 = rkt_create_webview(); + result_t r = rkt_webview_navigate(wv, "https://www.cs.cmu.edu/afs/cs/academic/class/15492-f07/www/pthreads.html"); + } + } return 0; } diff --git a/rktwebview/rktwebview.cpp b/rktwebview/rktwebview.cpp index 6e127fe..b50d288 100644 --- a/rktwebview/rktwebview.cpp +++ b/rktwebview/rktwebview.cpp @@ -24,7 +24,7 @@ static result_t do_dispatch(rkt_webview_t *, item_t item); static DWORD webviewThread(LPVOID args) #endif #ifdef USE_PTHREADS -static int webviewThread(void *args) +static void *webviewThread(void *args) #endif { rkt_webview_t *wv = (rkt_webview_t *) args; @@ -41,14 +41,19 @@ static int webviewThread(void *args) wv->webview_handle = NULL; wv->handle_destroyed = true; mutex_unlock(wv); +#ifdef USE_WIN_THREADS return 0; +#endif +#ifdef USE_PTHREADS + return NULL; +#endif } #ifdef USE_WIN_THREADS static DWORD queueGuardThread(LPVOID args) #endif #ifdef USE_PTHREADS - static int queueGuardThread(void *args) +static void *queueGuardThread(void *args) #endif { rkt_webview_t *wv = (rkt_webview_t *) args; @@ -73,7 +78,12 @@ static DWORD queueGuardThread(LPVOID args) thread_sleep_ms(wv, 10); } +#ifdef USE_WIN_THREADS return 0; +#endif +#ifdef USE_PTHREADS + return NULL; +#endif } @@ -82,9 +92,12 @@ RKTWEBVIEW_EXPORT rkt_webview_t *rkt_create_webview() rkt_webview_t *wv = (rkt_webview_t *) malloc(sizeof(rkt_webview_t)); if (wv == NULL) { return NULL; } - #ifdef _WIN32 + #ifdef USE_WIN_THREADS wv->mutex = CreateMutex(NULL, FALSE, NULL); #endif + #ifdef USE_PTHREADS + wv->mutex = PTHREAD_MUTEX_INITIALIZER; + #endif wv->handle_set = false; wv->handle_destroyed = false; queue_init(&wv->to_webview); @@ -112,7 +125,9 @@ RKTWEBVIEW_EXPORT rkt_webview_t *rkt_create_webview() &wv->queue_guard_thread_id ); #endif - #ifdef USE_PTHREAD + #ifdef USE_PTHREADS + wv->webview_thread_id = pthread_create(&wv->webview_thread, NULL, webviewThread, wv); + wv->queue_guard_thread_id = pthread_create(&wv->queue_guard_thread, NULL, queueGuardThread, wv); #endif bool go_on = true; while(go_on) { @@ -123,6 +138,7 @@ RKTWEBVIEW_EXPORT rkt_webview_t *rkt_create_webview() thread_sleep_ms(wv, 10); } } + printf("handle_set = %d\n", wv->handle_set); return wv; } @@ -152,6 +168,7 @@ RKTWEBVIEW_EXPORT item_t rkt_webview_call_js(rkt_webview_t *wv, const char *js) mutex_unlock(wv); char buf[30]; sprintf(buf, "%d", call_nr); + std::string _js = std::string("{ let f = function() { ") + js + " };" + " let call_nr = " + buf + ";" + " try { let r = { result: f() };" + @@ -159,7 +176,7 @@ RKTWEBVIEW_EXPORT item_t rkt_webview_call_js(rkt_webview_t *wv, const char *js) " } catch(e) {" + " rkt_webview_call_js_result(call_nr, false, e.message); " + " }" + - "}"; ""; + "}"; item_t item = { CONTEXT_CALL_JS, strdup(_js.c_str()) }; @@ -232,7 +249,9 @@ RKTWEBVIEW_EXPORT result_t rkt_destroy_webview(rkt_webview_t *wv) CloseHandle(wv->queue_guard_thread); CloseHandle(wv->mutex); #endif -#ifdef USE_PTHREAD +#ifdef USE_PTHREADS + pthread_join(wv->webview_thread, NULL); + pthread_join(wv->queue_guard_thread, NULL); #endif delete wv->js_evaluated; free(wv); @@ -405,11 +424,24 @@ result_t do_dispatch(rkt_webview_t *wv, item_t item) } return r; } else { - lr = reason_set_html_failed; + switch(e) { + case WEBVIEW_ERROR_MISSING_DEPENDENCY: lr = reason_webview_missing_dependency; + break; + case WEBVIEW_ERROR_CANCELED: lr = reason_webview_canceled; + break; + case WEBVIEW_ERROR_INVALID_STATE: lr = reason_webview_invalid_state; + break; + case WEBVIEW_ERROR_INVALID_ARGUMENT: lr = reason_webview_invalid_argument; + break; + case WEBVIEW_ERROR_UNSPECIFIED: lr = reason_webview_unspecified; + break; + default: + lr = reason_webview_dispatch_failed; + } r = error; } - printf("error = %d, reason = %d", r, lr); + printf("error = %d, reason = %d\n", r, lr); return r; } @@ -490,7 +522,8 @@ void mutex_lock(rkt_webview_t *wv) #ifdef USE_WIN_THREADS WaitForSingleObject(wv->mutex, INFINITE); #endif -#ifdef USE_PTHREAD +#ifdef USE_PTHREADS + pthread_mutex_lock(&wv->mutex); #endif } @@ -500,7 +533,8 @@ void mutex_unlock(rkt_webview_t *wv) #ifdef USE_WIN_THREADS ReleaseMutex(wv->mutex); #endif -#ifdef USE_PTHREAD +#ifdef USE_PTHREADS + pthread_mutex_unlock(&wv->mutex); #endif } @@ -509,6 +543,7 @@ static void thread_sleep_ms(rkt_webview_t *wv, int ms) #ifdef USE_WIN_THREADS Sleep(ms); #endif -#ifdef USE_PTHREAD +#ifdef USE_PTHREADS + usleep(ms * 1000); #endif } diff --git a/rktwebview/rktwebview.h b/rktwebview/rktwebview.h index 939cefc..762fd0e 100644 --- a/rktwebview/rktwebview.h +++ b/rktwebview/rktwebview.h @@ -13,9 +13,13 @@ typedef DWORD thread_id_t; #define USE_WIN_THREADS #endif -#ifdef _linux -#define USE_PTHREADS +#ifdef __linux #include +#include +typedef pthread_t thread_t; +typedef pthread_mutex_t mutex_t; +typedef int thread_id_t; +#define USE_PTHREADS #endif #define CONTEXT_INVALID 0 @@ -38,11 +42,17 @@ typedef enum { typedef enum { reason_no_result_yet = -1, reason_oke = 0, - reason_set_html_failed, - reason_set_navigate_failed, - reason_eval_js_failed, - reason_no_devtools_on_platform, - reason_no_delegate_for_context + reason_set_html_failed = 1, + reason_set_navigate_failed = 2, + reason_eval_js_failed = 3, + reason_no_devtools_on_platform = 4, + reason_no_delegate_for_context = 5, + reason_webview_missing_dependency = 6, + reason_webview_canceled = 7, + reason_webview_invalid_state = 8, + reason_webview_invalid_argument = 9, + reason_webview_unspecified = 10, + reason_webview_dispatch_failed = 11 } reason_t; typedef struct { diff --git a/rktwebview_qt/.gitignore b/rktwebview_qt/.gitignore new file mode 100644 index 0000000..4a0b530 --- /dev/null +++ b/rktwebview_qt/.gitignore @@ -0,0 +1,74 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* +CMakeLists.txt.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/rktwebview_qt/CMakeLists.txt b/rktwebview_qt/CMakeLists.txt new file mode 100644 index 0000000..8b5d597 --- /dev/null +++ b/rktwebview_qt/CMakeLists.txt @@ -0,0 +1,47 @@ +cmake_minimum_required(VERSION 3.14) + +project(rktwebview_qt LANGUAGES CXX) + +set(CMAKE_AUTOUIC ON) +set(CMAKE_AUTOMOC ON) +set(CMAKE_AUTORCC ON) +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets WebEngineWidgets) +find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets WebEngineWidgets) + +add_library(rktwebview_qt SHARED + rktwebview_qt_global.h + rktwebview_qt.cpp + rktwebview_qt.h + rktwebview.h + rktwebview.cpp + webviewqt.h webviewqt.cpp + rktwebview_internal.h + webviewwindow.h webviewwindow.cpp + webviewapp.h webviewapp.cpp +) + +target_link_libraries(rktwebview_qt PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) +target_link_libraries(rktwebview_qt PRIVATE Qt${QT_VERSION_MAJOR}::WebEngineWidgets) + +target_compile_definitions(rktwebview_qt PRIVATE RKTWEBVIEW_QT_LIBRARY) + +add_executable(rktwebview_qt_test + main.cpp + rktwebview_qt_global.h + rktwebview_qt.cpp + rktwebview_qt.h + rktwebview.h + rktwebview.cpp + webviewqt.h webviewqt.cpp + webviewwindow.h webviewwindow.cpp + rktwebview_internal.h + webviewapp.h webviewapp.cpp +) + +target_compile_definitions(rktwebview_qt_test PRIVATE RKTWEBVIEW_QT_LIBRARY) + +target_link_libraries(rktwebview_qt_test PRIVATE Qt${QT_VERSION_MAJOR}::Widgets) +target_link_libraries(rktwebview_qt_test PRIVATE Qt${QT_VERSION_MAJOR}::WebEngineWidgets) diff --git a/rktwebview_qt/main.cpp b/rktwebview_qt/main.cpp new file mode 100644 index 0000000..e7840a6 --- /dev/null +++ b/rktwebview_qt/main.cpp @@ -0,0 +1,37 @@ +#include "rktwebview.h" + +#include + +static int _argc; +static char **_argv; + +int main(int argc, char *argv[]) +{ + int wv1; + int wv2; + + _argc = argc; + _argv = argv; + + rkt_webview_init(); + wv1 = rkt_webview_create(0); + rkt_webview_set_url(wv1, "https://wikipedia.org"); + + int i = 0; + while(i < 20) { + printf("Waiting...%d\n", i); + rkt_webview_process_events(1000); + + if (i == 10) { + wv2 = rkt_webview_create(0); + } + + if (i == 15) { + rkt_webview_close(wv1); + } + i += 1; + } + + rkt_webview_close(wv2); + +} diff --git a/rktwebview_qt/rktwebview.cpp b/rktwebview_qt/rktwebview.cpp new file mode 100644 index 0000000..c1380ec --- /dev/null +++ b/rktwebview_qt/rktwebview.cpp @@ -0,0 +1,157 @@ +#include "rktwebview_internal.h" + +#include +#include +#include +#include +#include +#include +#include "rktwebview_qt.h" +#include "webviewapp.h" + +uint64_t current_ms() { + using namespace std::chrono; + return duration_cast(system_clock::now().time_since_epoch()).count(); +} + +///////////////////////////////////////////////////////////////////// +// Main C Interface +///////////////////////////////////////////////////////////////////// + +static int pipefd[2]; +static bool started = false; +static pid_t webview_process; +static bool cannot_fork_or_pipe = false; + +Rktwebview_qt *handler = nullptr; + +void rkt_webview_init() +{ + /* + if (!started) { + if (pipe(pipefd) != -1) { + webview_process = fork(); + + if (webview_process < 0) + cannot_fork_or_pipe = true; + } else if (webview_process == 0) { + WebViewApp app; + + + + started = true; + } else { + cannot_fork_or_pipe = true; + } + } + */ + + if (handler == nullptr) { + handler = new Rktwebview_qt(&handler); + } +} + +int rkt_webview_create(int parent) +{ + //rkt_webview_init(); + return handler->rktWebViewCreate(parent); +} + +void rkt_webview_close(int wv) +{ + handler->rktWebViewClose(wv); +} + +result_t rkt_webview_set_url(int wv, const char *url) +{ + result_t r = handler->rktSetUrl(wv, url); + return r; +} + +///////////////////////////////////////////////////////////////////// +// Supporting functions +///////////////////////////////////////////////////////////////////// + +void queue_init(queue_t **q) +{ + *q = static_cast(malloc(sizeof(queue_t))); + queue_t *Q = *q; + Q->length = 0; + Q->first = nullptr; + Q->last = nullptr; +} + +void enqueue(queue_t *q, item_t item) +{ + queue_item_t *itm = (queue_item_t *) malloc(sizeof(queue_item_t)); + itm->item.context = item.context; + itm->item.data = strdup(item.data); + if (q->first == nullptr) { + q->first = itm; + q->last = itm; + itm->prev = nullptr; + itm->next = nullptr; + q->length = 1; + } else { + itm->prev = q->last; + itm->next = nullptr; + q->last->next = itm; + q->last = itm; + q->length += 1; + } +} + +bool dequeue(queue_t *q, item_t *item) +{ + if (q->length == 0) { + item->context = CONTEXT_INVALID; + item->data = nullptr; + return false; + } else { + queue_item_t *itm = q->first; + q->first = q->first->next; + q->length -= 1; + if (q->length == 0) { + q->first = nullptr; + q->last = nullptr; + } + item->context = itm->item.context; + item->data = itm->item.data; + free(itm); + return true; + } +} + +int queue_length(queue_t *q) +{ + return q->length; +} + +void queue_destroy(queue_t *q) +{ + item_t i; + while(dequeue(q, &i)) { + free(i.data); + } + free(q); +} + +void free_item(item_t item) +{ + free(item.data); +} + + + +void rkt_webview_process_events(int for_ms) +{ + //rkt_webview_init(); + + int64_t start_ms = current_ms(); + int64_t end_ms = start_ms + for_ms; + + while (current_ms() < end_ms) { + usleep(500); // sleep 0.5 ms + handler->doEvents(); + } +} diff --git a/rktwebview_qt/rktwebview.h b/rktwebview_qt/rktwebview.h new file mode 100644 index 0000000..61e22ec --- /dev/null +++ b/rktwebview_qt/rktwebview.h @@ -0,0 +1,57 @@ +#ifndef RKTWEBVIEW_H +#define RKTWEBVIEW_H + +#include "rktwebview_qt_global.h" + +#define CONTEXT_QUIT -2 +#define CONTEXT_NIL -1 +#define CONTEXT_INVALID 0 +#define CONTEXT_BOUND_EVENT 1 +#define CONTEXT_WINDOW_RESIZE 2 +#define CONTEXT_WINDOW_MOVE 3 +#define CONTEXT_WINDOW_CAN_CLOSE 4 +#define CONTEXT_WINDOW_CLOSED 5 +#define CONTEXT_SET_HTML 6 +#define CONTEXT_NAVIGATE 7 +#define CONTEXT_EVAL_JS 8 +#define CONTEXT_OPEN_DEVTOOLS 9 +#define CONTEXT_CALL_JS 10 +#define CONTEXT_CMD_CLOSE 11 +#define CONTEXT_CMD_CREATE_VIEW 12 + +extern "C" { + +typedef int rktwebview_t; + +typedef struct { + rktwebview_t wv; + int context; + char *data; +} item_t; + +typedef enum { + no_result_yet = -1, + oke = 0, + set_html_failed = 1, + set_navigate_failed = 2, + eval_js_failed = 3, + no_devtools_on_platform = 4, + no_delegate_for_context = 5, + webview_missing_dependency = 6, + webview_canceled = 7, + webview_invalid_state = 8, + webview_invalid_argument = 9, + webview_unspecified = 10, + webview_dispatch_failed = 11 +} result_t; + +RKTWEBVIEW_QT_EXPORT void rkt_webview_init(); +RKTWEBVIEW_QT_EXPORT void rkt_webview_process_events(int for_ms); +RKTWEBVIEW_QT_EXPORT int rkt_webview_create(int parent); +RKTWEBVIEW_QT_EXPORT void rkt_webview_close(int wv); +RKTWEBVIEW_QT_EXPORT result_t rkt_webview_set_url(int wv, const char *url); +RKTWEBVIEW_QT_EXPORT result_t rkt_webview_set_html(int wv, const char *html); + +} + +#endif // RKTWEBVIEW_H diff --git a/rktwebview_qt/rktwebview_internal.h b/rktwebview_qt/rktwebview_internal.h new file mode 100644 index 0000000..43aee0e --- /dev/null +++ b/rktwebview_qt/rktwebview_internal.h @@ -0,0 +1,25 @@ +#ifndef RKTWEBVIEW_INTERNAL_H +#define RKTWEBVIEW_INTERNAL_H + +#include "rktwebview.h" + +typedef struct _item { + item_t item; + struct _item *next; + struct _item *prev; +} queue_item_t; + +typedef struct { + queue_item_t *first; + queue_item_t *last; + int length; +} queue_t; + +void queue_init(queue_t **q); +void enqueue(queue_t *q, item_t item); +bool dequeue(queue_t *q, item_t *item); +int queue_length(queue_t *q); +void queue_destroy(queue_t *q); +void free_item(item_t i); + +#endif // RKTWEBVIEW_INTERNAL_H diff --git a/rktwebview_qt/rktwebview_qt.cpp b/rktwebview_qt/rktwebview_qt.cpp new file mode 100644 index 0000000..62b02f3 --- /dev/null +++ b/rktwebview_qt/rktwebview_qt.cpp @@ -0,0 +1,196 @@ +#include "rktwebview_qt.h" +#include "webviewqt.h" +#include "rktwebview.h" +#include "webviewwindow.h" +#include +#include +#include + +#define COMMAND_QUIT 1 +#define COMMAND_CLOSE 2 +#define COMMAND_CREATE 3 +#define COMMAND_SET_URL 4 + +class Command +{ +public: + int cmd; + QVector args; + QVariant result; + bool done; +public: + Command(int _cmd) { + cmd = _cmd; + done = false; + } +}; + +void Rktwebview_qt::processCommands() +{ + printf("COmmand processing\n"); + while(!_command_queue.empty()) { + Command *cmd = _command_queue.dequeue(); + + switch(cmd->cmd) { + case COMMAND_QUIT: { + _app->quit(); + cmd->done = true; + } + break; + case COMMAND_CREATE: { + int parent = cmd->args[0].toInt(); + QWidget *p; + if (_views.contains(parent)) { + p = _views[parent]; + } else { + p = nullptr; + } + + WebviewWindow *w = new WebviewWindow(p); + WebViewQt *view = new WebViewQt(nextHandle(), w); + w->addView(view, this); + + int id = view->id(); + + _views[id] = w; + + w->show(); + + cmd->result = id; + cmd->done = true; + } + break; + case COMMAND_CLOSE: { + int wv = cmd->args[0].toInt(); + if (_views.contains(wv)) { + WebviewWindow *w= _views[wv]; + _views.remove(wv); + w->close(); + w->deleteLater(); + cmd->result = true; + } else { + cmd->result = false; + } + cmd->done = true; + } + break; + case COMMAND_SET_URL: { + int wv = cmd->args[0].toInt(); + QString url = cmd->args[1].toString(); + if (_views.contains(wv)) { + WebviewWindow *w = _views[wv]; + WebViewQt *v = w->view(); + QUrl u(url); + v->setUrl(u); + cmd->result = true; + } else { + cmd->result = false; + } + cmd->done = true; + } + break; + default: { + cmd->result = false; + cmd->done = true; + } + break; + } + } +} + +void Rktwebview_qt::interruptEventLoop() +{ + printf("Exeting application event loop\n"); + //_app->quit(); +} + +void Rktwebview_qt::removeView(int id) +{ + _views.remove(id); +} + +int Rktwebview_qt::nextHandle() +{ + int h = ++_current_handle; + return h; +} + +int Rktwebview_qt::rktWebViewCreate(int parent) +{ + Command c(COMMAND_CREATE); + c.args.push_back(parent); + + _command_queue.enqueue(&c); + + while(!c.done) { doEvents(); } + + int id = c.result.toInt(); + return id; +} + +void Rktwebview_qt::rktWebViewClose(int wv) +{ + Command c(COMMAND_CLOSE); + c.args.push_back(wv); + + _command_queue.enqueue(&c); + + while(!c.done) { doEvents(); } +} + +result_t Rktwebview_qt::rktSetUrl(rktwebview_t wv, const char *url) +{ + Command c(COMMAND_SET_URL); + QString _url(url); + c.args.push_back(wv); + c.args.push_back(_url); + _command_queue.enqueue(&c); + + while(!c.done) { doEvents(); } + + bool r = c.result.toBool(); + + return r ? result_t::oke : result_t::set_navigate_failed; +} + +void Rktwebview_qt::rktQuit() +{ + QList keys = _views.keys(); + int i; + for(i = 0; i < keys.size(); i++) { + int view_handle = keys[i]; + rktWebViewClose(view_handle); + } + + Command c(COMMAND_QUIT); + _command_queue.enqueue(&c); + + while(!c.done) { doEvents(); } +} + +void Rktwebview_qt::doEvents() +{ + //_quit_event_loop.start(2000); + //_app->exec(); + //processCommands(); + _app->processEvents(); + +} + +Rktwebview_qt::Rktwebview_qt(Rktwebview_qt **handler) : + QObject() +{ + _argc = 1; + _argv[0] = const_cast("Rktwebview_qt"); + + + _current_handle = 0; + _handler = handler; + + _app = new QApplication(_argc, _argv); + connect(&_process_commands, &QTimer::timeout, this, &Rktwebview_qt::processCommands); + connect(&_quit_event_loop, &QTimer::timeout, this, &Rktwebview_qt::interruptEventLoop); + _process_commands.start(1000); + + *_handler = nullptr; +} diff --git a/rktwebview_qt/rktwebview_qt.h b/rktwebview_qt/rktwebview_qt.h new file mode 100644 index 0000000..fd92878 --- /dev/null +++ b/rktwebview_qt/rktwebview_qt.h @@ -0,0 +1,68 @@ +#ifndef RKTWEBVIEW_QT_H +#define RKTWEBVIEW_QT_H + +#include "rktwebview_qt_global.h" +#include "rktwebview_internal.h" + +#include +#include +#include +#include +#include +#include +#include + + +class WebViewQt; +class WebviewWindow; +class Command; + +class RKTWEBVIEW_QT_EXPORT Rktwebview_qt : public QObject +{ + Q_OBJECT +private: + QApplication *_app; + rktwebview_t _current_handle; + QHash _views; + + QQueue _command_queue; + QTimer _process_commands; + QTimer _quit_event_loop; + + Rktwebview_qt **_handler; + + int _argc; + char *_argv[1]; + +public slots: + void processCommands(); + void interruptEventLoop(); + +public: + void removeView(int id); + +public: + int nextHandle(); +public: + int rktWebViewCreate(int parent = 0); // threadsafe + void rktWebViewClose(int wv); + void rktQuit(); + + void runJs(rktwebview_t wv, const char *js); // threadsafe + item_t callJs(rktwebview_t wv, const char *js); // threadsafe + + void setHtml(rktwebview_t wv, const char *html); // threadsafe + void navigate(rktwebview_t wv, const char *url); // threadsafe + result_t rktSetUrl(rktwebview_t wv, const char *url); + +public: + void doEvents(); + +public: + void runCommandThread(); + +public: + Rktwebview_qt(Rktwebview_qt **handler); +}; + +#endif // RKTWEBVIEW_QT_H diff --git a/rktwebview_qt/rktwebview_qt_global.h b/rktwebview_qt/rktwebview_qt_global.h new file mode 100644 index 0000000..98ed696 --- /dev/null +++ b/rktwebview_qt/rktwebview_qt_global.h @@ -0,0 +1,12 @@ +#ifndef RKTWEBVIEW_QT_GLOBAL_H +#define RKTWEBVIEW_QT_GLOBAL_H + +#include + +#if defined(RKTWEBVIEW_QT_LIBRARY) +#define RKTWEBVIEW_QT_EXPORT Q_DECL_EXPORT +#else +#define RKTWEBVIEW_QT_EXPORT Q_DECL_IMPORT +#endif + +#endif // RKTWEBVIEW_QT_GLOBAL_H diff --git a/rktwebview_qt/webviewapp.cpp b/rktwebview_qt/webviewapp.cpp new file mode 100644 index 0000000..357bc2f --- /dev/null +++ b/rktwebview_qt/webviewapp.cpp @@ -0,0 +1,8 @@ +#include "webviewapp.h" + + +WebViewApp::WebViewApp(int &argc, char **argv) : + QApplication(argc, argv) +{ + +} diff --git a/rktwebview_qt/webviewapp.h b/rktwebview_qt/webviewapp.h new file mode 100644 index 0000000..a450b65 --- /dev/null +++ b/rktwebview_qt/webviewapp.h @@ -0,0 +1,13 @@ +#ifndef WEBVIEWAPP_H +#define WEBVIEWAPP_H + +#include + +class WebViewApp : public QApplication +{ + Q_OBJECT +public: + WebViewApp(int &argc, char **argv); +}; + +#endif // WEBVIEWAPP_H diff --git a/rktwebview_qt/webviewqt.cpp b/rktwebview_qt/webviewqt.cpp new file mode 100644 index 0000000..d7a0a89 --- /dev/null +++ b/rktwebview_qt/webviewqt.cpp @@ -0,0 +1,19 @@ +#include "webviewqt.h" + +WebViewQt::WebViewQt(int id, QWidget *parent) + : QWebEngineView(parent) +{ + _id = id; +} + +int WebViewQt::id() const +{ + return _id; +} + +void WebViewQt::runJs(const char *js) +{ + +} + + diff --git a/rktwebview_qt/webviewqt.h b/rktwebview_qt/webviewqt.h new file mode 100644 index 0000000..8e65c3f --- /dev/null +++ b/rktwebview_qt/webviewqt.h @@ -0,0 +1,22 @@ +#ifndef WEBVIEWQT_H +#define WEBVIEWQT_H + +#include + +class WebViewQt : public QWebEngineView +{ +private: + int _id; + +public: + int id() const; + +public: + void runJs(const char *js); + +public: + WebViewQt(int id, QWidget *parent = nullptr); + +}; + +#endif // WEBVIEWQT_H diff --git a/rktwebview_qt/webviewwindow.cpp b/rktwebview_qt/webviewwindow.cpp new file mode 100644 index 0000000..d97e9a7 --- /dev/null +++ b/rktwebview_qt/webviewwindow.cpp @@ -0,0 +1,27 @@ +#include "webviewwindow.h" + +#include "webviewqt.h" +#include "rktwebview_qt.h" + +void WebviewWindow::closeEvent(QCloseEvent *evt) +{ + _container->removeView(_view->id()); +} + +void WebviewWindow::addView(WebViewQt *v, Rktwebview_qt *c) +{ + _container = c; + _view = v; + this->setCentralWidget(v); +} + +WebViewQt *WebviewWindow::view() +{ + return _view; +} + +WebviewWindow::WebviewWindow(QWidget *parent) + : QMainWindow{parent} +{ + _view = nullptr; +} diff --git a/rktwebview_qt/webviewwindow.h b/rktwebview_qt/webviewwindow.h new file mode 100644 index 0000000..c54797f --- /dev/null +++ b/rktwebview_qt/webviewwindow.h @@ -0,0 +1,30 @@ +#ifndef WEBVIEWWINDOW_H +#define WEBVIEWWINDOW_H + +#include + +class WebViewQt; +class Rktwebview_qt; + +class WebviewWindow : public QMainWindow +{ + Q_OBJECT +private: + Rktwebview_qt *_container; + WebViewQt *_view; + +protected: + void closeEvent(QCloseEvent *evt); + +public: + void addView(WebViewQt *v, Rktwebview_qt *c); + + WebViewQt *view(); + +public: + explicit WebviewWindow(QWidget *parent = nullptr); + +signals: +}; + +#endif // WEBVIEWWINDOW_H