diff --git a/example/index.html b/example/index.html index 688829c..f2b9088 100644 --- a/example/index.html +++ b/example/index.html @@ -17,7 +17,7 @@ Some input text: Some input date:

-Some color input: +Some color input:

diff --git a/example1/example-1.html b/example1/example-1.html index 9b67683..bd07432 100644 --- a/example1/example-1.html +++ b/example1/example-1.html @@ -34,6 +34,7 @@
+

Menu responses

diff --git a/example1/example.rkt b/example1/example.rkt index 0d8b11c..5d11a26 100644 --- a/example1/example.rkt +++ b/example1/example.rkt @@ -11,13 +11,17 @@ example-1-window% ) +(define ww-debug displayln) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Example 1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define-runtime-path html-start "example-1.html") (define-runtime-path dialog-html "example-1-dialog.html") +(define-runtime-path cur-dir ".") +#| (define test-menu (menu 'main-menu (menu-item 'm-file "File" #:submenu @@ -46,33 +50,34 @@ ) ) )) +|# (define example-1-dialog% - (class ww-webview-dialog% + (class wv-dialog% (inherit-field settings) - (super-new [html-file dialog-html] - [width 400] - [height 300]) + (super-new [window-context 'example-1-dialog] + [file-not-found-handler (root-file-not-found-handler "example-1-dialog.html")] + ) - (define/override (html-loaded) - (super html-loaded) + (define/override (page-loaded oke) + (super page-loaded oke) - (ww-debug "html-loaded for example-1-dialog%") - (let* ((btn (send this element 'ok-btn))) - (send btn connect 'click (λ (data) - (send this close)))) + ;(ww-debug "html-loaded for example-1-dialog%") + (send this bind! 'ok-btn 'click (λ (el evt data) + (send this close))) - (let* ((inp1 (send this element 'inp1)) - (inp2 (send this element 'inp2)) - (inp3 (send this element 'inp3))) + (let* ((inp1 (send this element 'inp1 'text)) + (inp2 (send this element 'inp2 'text)) + (inp3 (send this element 'inp3 'text))) (send inp1 set! (send settings get 'inp1 "")) (send inp2 set! (send settings get 'inp2 "")) (send inp3 set! (send settings get 'inp3 "")) - (send inp1 on-change! - (λ (val) - (send settings set! 'inp1 val))) - (send inp2 on-change! (λ (val) (send settings set! 'inp2 val))) - (send inp3 on-change! (λ (val) (send settings set! 'inp3 val))) + (send this bind! 'inp1 'change (λ (el evt data) + (displayln (format "~a ~a ~a" el evt data)) + (displayln (format "get = ~a" (send el get))) + (send settings set! 'inp1 (send el get)))) + (send this bind! 'inp2 'change (λ (el evt data) (send settings set! 'inp2 (send el get)))) + (send this bind! 'inp3 'change (λ (el evt data) (send settings set! 'inp3 (send el get)))) ) ) ) @@ -86,10 +91,11 @@ var)))) (define example-1-window% - (class ww-webview% + (class wv-window% - (inherit-field settings) - (super-new [html-file html-start] + (inherit-field settings app-context) + (super-new [file-not-found-handler (root-file-not-found-handler "example-1.html")] + [window-context 'example-1-main] ) (define go-on-counter #f) @@ -97,14 +103,21 @@ (define counter-inc 1) (define counter-thread #f) (define div-counter #f) - (define my-dir (send settings get 'folder ".")) + (define my-dir (send settings get/global 'folder ".")) (define/override (can-close?) (eq? counter-thread #f)) - (define/public (reset-counter) + (define start-stop-btn #f) + + (define/public (stop-counter) + (send start-stop-btn set-innerHTML! "Start Counter") (set! go-on-counter #f) + ) + + (define/public (reset-counter) + (stop-counter) (set! counter-thread #f) ) @@ -117,25 +130,26 @@ (send this update-counter)) (define/public (update-counter) - (send div-counter set-inner-html! (format "Count = ~a" c-counter)) + (send div-counter set-innerHTML! (format "Count = ~a" c-counter)) (when (and (> c-counter 0) (<= c-counter 100)) (send div-counter set-style! - (css-style '((background white))))) + '((background white)))) (when (and (> c-counter 100) (<= c-counter 200)) (send div-counter set-style! - (css-style '((background green) (color white))))) + '((background green) (color white)))) (when (and (> c-counter 200) (<= c-counter 300)) (send div-counter set-style! - (css-style '((background yellow) (font-size: 120%))))) + '((background yellow) (color black) (font-size: 120%)))) (when (and (> c-counter 300) (<= c-counter 400)) (send div-counter set-style! - (css-style '((color white) (background orange) (font-size 110%))))) + '((color white) (background orange) (font-size 110%)))) (when (and (> c-counter 400)) (send div-counter set-style! - (css-style '((color white) (background red) (font-size 120%) (font-weight bold))))) + '((color white) (background red) (font-size 120%) (font-weight bold)))) ) (define/public (start-counter) + (send start-stop-btn set-innerHTML! "Stop Counter") (set! counter-thread (thread (λ () @@ -150,65 +164,68 @@ (define/public (set-folder new-dir) (set! my-dir new-dir) - (send settings set 'folder new-dir) + (send settings set/global! 'folder new-dir) (let ((el (send this element 'folder))) - (send el set-inner-html! (format "Selected folder: ~a" my-dir)) + (send el set-innerHTML! (format "Selected folder: ~a" my-dir)) ) ) (define/override (choose-dir) - (let ((handle (super choose-dir "Select a folder" my-dir))) - (displayln (format "choosen dir handle: ~a" handle)) + (let ((result (super choose-dir "Select a folder" my-dir))) + (displayln (format "choosen dir handle: ~a" result)) + (unless (eq? result 'canceled) + (send this set-folder result)) ) ) - (define/override (dir-choosen handle choosen dir) - (displayln (format "dir-choosen: ~a ~a ~a" handle choosen dir)) - (when choosen - (send this set-folder dir))) - (define/public (prefs) - (new example-1-dialog% [parent this] [settings (send this clone-settings 'example-1-dialog)])) + (stop-counter) + (set! go-on-counter #f) + (new example-1-dialog% + [parent this] + ) + ) ; (send this clone-settings 'example-1-dialog)])) - (define/override (handle-navigate url type kind) - (send this reset-counter) - (super handle-navigate url type kind)) + ;(define/override (handle-navigate url type kind) + ; (send this reset-counter) + ; (super handle-navigate url type kind)) - (define/override (html-loaded) + (define/override (page-loaded oke) (ww-debug "HTML LOADED") - (super html-loaded) + (super page-loaded oke) (set! div-counter (send this element 'div-counter)) (send this update-counter) (send this set-folder my-dir) (ww-debug "CONNECTING BUTTONS") - (let* ((dialog-btn (send this element 'dialog-button)) - (start-stop-btn (send this element 'start-stop-button)) - (choose-dir-btn (send this element 'select-dir-button)) - ) - (send dialog-btn connect 'click - (λ (data) (send this prefs))) - - (send start-stop-btn connect 'click - (λ (data) - (if (eq? counter-thread #f) - (begin - (send this start-counter) - (send start-stop-btn set-inner-html! "Stop Counter")) - (begin - (send this reset-counter) - (send start-stop-btn set-inner-html! "Start Counter")) + (send this bind! 'dialog-button 'click (λ (el evt data) + (send this prefs))) + + (set! start-stop-btn (send this element 'start-stop-button)) + (send this bind! 'start-stop-button 'click + (λ (el evt data) + (if (eq? counter-thread #f) + (begin + (send this start-counter) ) - ) + (begin + (send this reset-counter) + ) + ) ) - (send choose-dir-btn connect 'click - (λ (data) - (send this choose-dir))) - ) + ) + + (send this bind! 'devtools 'click + (λ (el evt data) (send this devtools))) + + (send this bind! 'select-dir-button 'click + (λ (el evt data) + (send this choose-dir))) - (ww-debug "SETTING MENU") - (let* ((div-open (send this element 'div-open)) + + ;(ww-debug "SETTING MENU") + #|(let* ((div-open (send this element 'div-open)) (c-open 0) (div-close (send this element 'div-close)) (c-close 0) @@ -250,7 +267,7 @@ (λ () (send this choose-dir))) ) - ) + )|# ) (begin @@ -261,7 +278,9 @@ (define (run-example) (let* ((ini (new ini% [file 'web-racket-example1])) - (settings (new ww-simple-ini% [ini ini] [section 'example-1-window])) - (window (new example-1-window% [settings settings])) + (window (new example-1-window% + [app-context 'example-1] + [ini ini] + [base-dir cur-dir])) ) window)) diff --git a/private/lib/linux/x86_64/librktwebview_qt.so b/private/lib/linux/x86_64/librktwebview_qt.so index 10af4ac..e0ff9e7 100755 Binary files a/private/lib/linux/x86_64/librktwebview_qt.so and b/private/lib/linux/x86_64/librktwebview_qt.so differ diff --git a/private/mimetypes.rkt b/private/mimetypes.rkt index f6e9aca..03f6214 100644 --- a/private/mimetypes.rkt +++ b/private/mimetypes.rkt @@ -722,4 +722,3 @@ (addMimeType "Zip Archive" "application/zip" '(zip) "Wikipedia: Zip") (addMimeType "ZVUE Media Manager" "application/vnd.handheld-entertainment+xml" '(zmm) "IANA: ZVUE Media Manager") (addMimeType "Zzazz Deck" "application/vnd.zzazz.deck+xml" '(zaz) "IANA: Zzazz") - diff --git a/private/racket-webview-qt.rkt b/private/racket-webview-qt.rkt index 003e56b..ac7418d 100644 --- a/private/racket-webview-qt.rkt +++ b/private/racket-webview-qt.rkt @@ -39,6 +39,7 @@ rkt-webview-choose-dir rkt-webview-file-open rkt-webview-file-save + rkt-webview-version ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -48,6 +49,7 @@ (define lib-type 'release) (define os (system-type 'os*)) +(define arch (system-type 'arch)) (define supported-os '(windows linux)) (unless (ormap (λ (o) (eq? os o)) supported-os) @@ -77,7 +79,27 @@ Qt6WebEngineWidgets.dll )) ([eq? os 'linux] - '()) + '(libQt6XcbQpa + ;libQt6WaylandClient + ;libQt6EglFSDeviceIntegration + libQt6Core + libQt6Positioning + libQt6Gui + libQt6Widgets + libQt6Svg + libQt6Network + libQt6OpenGL + libQt6PrintSupport + libQt6Qml + libQt6QmlModels + libQt6QmlWorkerScript + libQt6QmlMeta + libQt6Quick + libQt6QuickWidgets + libQt6WebChannel + libQt6WebEngineCore + libQt6WebEngineWidgets + )) ) ) @@ -88,26 +110,51 @@ ) ) +(define os-lib-dir (build-path lib-dir (symbol->string os) (symbol->string arch))) + (define (libname lib-symbol) - (build-path lib-dir (symbol->string os) (symbol->string lib-symbol))) + (build-path os-lib-dir (symbol->string lib-symbol))) ; c:\qt\6.10.2\msvc2022_64\bin\windeployqt.exe rktwebview_qt_test.exe -(when (eq? os 'windows) - (putenv "QT_PLUGIN_PATH" - (path->string (build-path lib-dir (symbol->string os)))) - (putenv "QTWEBENGINEPROCESS_PATH" - (path->string (build-path lib-dir (symbol->string os) "QtWebEngineProcess.exe"))) - (putenv "QTWEBENGINE_RESOURCES_PATH" - (path->string (build-path lib-dir (symbol->string os) "resources"))) - (putenv "QTWEBENGINE_LOCALES_PATH" - (path->string (build-path lib-dir (symbol->string os) "translations" "qtwebengine_locales"))) - ) +(define quiet-call #t) +(set! quiet-call + (when (or (eq? os 'windows) (eq? os 'linux)) + (putenv "QT_PLUGIN_PATH" + (path->string (build-path os-lib-dir))) + (putenv "QTWEBENGINEPROCESS_PATH" + (path->string (build-path os-lib-dir "QtWebEngineProcess.exe"))) + (putenv "QTWEBENGINE_RESOURCES_PATH" + (path->string (build-path os-lib-dir "resources"))) + (putenv "QTWEBENGINE_LOCALES_PATH" + (path->string (build-path os-lib-dir "translations" "qtwebengine_locales"))) + (when (eq? os 'linux) + (putenv "QT_QPA_PLATFORM" "xcb") + (putenv "LD_LIBRARY_PATH" + (string-append + (path->string (build-path os-lib-dir)) ":" + (path->string (build-path os-lib-dir "platforms")) + ) + ) + ) + ) + ) + ;;; Preload libraries (for-each (λ (lib-symbol) - (let ((load-lib (libname lib-symbol))) - (ffi-lib load-lib))) + (let* ((libn (if (list? lib-symbol) (car lib-symbol) lib-symbol)) + (versions (if (list? lib-symbol) (cons (cadr lib-symbol) '(#f)) (list #f))) + (load-lib (if (list? lib-symbol) + (if (eq? (caddr lib-symbol) #f) + (symbol->string libn) + (libname libn)) + (libname libn))) + ) + ;(displayln (format "loading ~a" load-lib)) + (ffi-lib load-lib versions) + ) + ) libraries-to-preload) ;;; Actual FFI integration @@ -130,7 +177,7 @@ ffi-library os exp)))) ) ]) - (ffi-lib webview-lib-file))) + (ffi-lib webview-lib-file '("6" #f) #:get-lib-dirs (list os-lib-dir)))) (define-ffi-definer define-rktwebview webview-lib) @@ -170,6 +217,24 @@ ) ) +(define _rkt_data_kind + (_enum '(version = -1 + event = 2 + js-result = 3 + ) + ) + ) + +(define-cstruct _rkt_version_t + ([qt-major _int] + [qt-minor _int] + [qt-patch _int] + [api-major _int] + [api-minor _int] + [api-patch _int] + ) + ) + (define-cstruct _rkt_evt_t ([w _int] [evt _pointer] @@ -180,6 +245,13 @@ [value _pointer] )) +(define-cstruct _rkt_data_t + ([kind _rkt_data_kind] + [data (_union _rkt_version_t _rkt_evt_t _rkt_js_result_t)] + ) + ) + + ;RKTWEBVIEW_QT_EXPORT void rkt_webview_init(int &argc, char **argv); (define-rktwebview rkt_webview_init (_fun -> _void)) @@ -188,10 +260,23 @@ (define-rktwebview rkt_webview_process_events (_fun _int -> _void)) -;RKTWEBVIEW_QT_EXPORT int rkt_webview_create(int parent); +;RKTWEBVIEW_QT_EXPORT void rkt_webview_free_data(rkt_data_t *d); +(define-rktwebview rkt_webview_free_data + (_fun _rkt_data_t-pointer -> _void)) + +;RKTWEBVIEW_QT_EXPORT rkt_data_t *rkt_webview_version(); +(define-rktwebview rkt_webview_version + (_fun -> _rkt_data_t-pointer)) + +;RKTWEBVIEW_QT_EXPORT int rkt_webview_create(rktwebview_t parent, +; event_cb_t js_event_cb, +; const char *optional_server_cert_pem); (define-rktwebview rkt_webview_create - (_fun _int (_fun #:keep callback-box #:async-apply applier - _rkt_evt_t-pointer -> _void) -> _int)) + (_fun _int + (_fun #:keep callback-box #:async-apply applier + _rkt_data_t-pointer -> _void) + _bytes + -> _int)) ;RKTWEBVIEW_QT_EXPORT void rkt_webview_close(int wv); (define-rktwebview rkt_webview_close @@ -215,20 +300,12 @@ ;RKTWEBVIEW_QT_EXPORT rkt_js_result_t *rkt_webview_call_js(rktwebview_t wv, const char *js); (define-rktwebview rkt_webview_call_js - (_fun _int _string/utf-8 -> _rkt_js_result_t-pointer)) + (_fun _int _string/utf-8 -> _rkt_data_t-pointer)) -;RKTWEBVIEW_QT_EXPORT result_t rkt_webview_destroy_js_result(rkt_js_result_t *r); -(define-rktwebview rkt_webview_destroy_js_result - (_fun _rkt_js_result_t-pointer -> _rkt_result_t)) - ;RKTWEBVIEW_QT_EXPORT result_t rkt_webview_open_devtools(int wv); (define-rktwebview rkt_webview_open_devtools (_fun _int -> _rkt_result_t)) -;RKTWEBVIEW_QT_EXPORT result_t rkt_webview_destroy_event(rkt_event_t e); -(define-rktwebview rkt_webview_destroy_event - (_fun _rkt_evt_t-pointer -> _rkt_result_t)) - ;RKTWEBVIEW_QT_EXPORT result_t rkt_webview_move(rktwebview_t w, int x, int y); (define-rktwebview rkt_webview_move (_fun _int _int _int -> _rkt_result_t)) @@ -275,15 +352,15 @@ ;RKTWEBVIEW_QT_EXPORT rkt_js_result_t *rkt_webview_choose_dir(rktwebview_t w, const char *title, const char *base_dir); (define-rktwebview rkt_webview_choose_dir - (_fun _int _string/utf-8 _string/utf-8 -> _rkt_js_result_t-pointer)) + (_fun _int _string/utf-8 _string/utf-8 -> _rkt_data_t-pointer)) ;RKTWEBVIEW_QT_EXPORT rkt_js_result_t *rkt_webview_file_open(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); (define-rktwebview rkt_webview_file_open - (_fun _int _string/utf-8 _string/utf-8 _string/utf-8 -> _rkt_js_result_t-pointer)) + (_fun _int _string/utf-8 _string/utf-8 _string/utf-8 -> _rkt_data_t-pointer)) ;RKTWEBVIEW_QT_EXPORT rkt_js_result_t *rkt_webview_file_save(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); (define-rktwebview rkt_webview_file_save - (_fun _int _string/utf-8 _string/utf-8 _string/utf-8 -> _rkt_js_result_t-pointer)) + (_fun _int _string/utf-8 _string/utf-8 _string/utf-8 -> _rkt_data_t-pointer)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -308,7 +385,7 @@ (f))))) (rkt_webview_init) -(start-event-processing) +(set! quiet-call (start-event-processing)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Provided features @@ -323,27 +400,29 @@ (define (rkt-process-events handle) (if (> (queue-length (rkt-wv-evt-queue handle)) 0) - (let ((e (dequeue! (rkt-wv-evt-queue handle)))) - (if (symbol? e) - (if (eq? e 'quit) + (let ((data (dequeue! (rkt-wv-evt-queue handle)))) + (if (symbol? data) + (if (eq? data 'quit) (begin (hash-remove! rkt-wv-store (rkt-wv-win handle)) 'quit) (rkt-process-events handle)) - (let ((evt (cast (rkt_evt_t-evt e) _pointer _string*/utf-8))) + (let* ((e (union-ref (rkt_data_t-data data) 1)) + (evt (cast (rkt_evt_t-evt e) _pointer _string*/utf-8))) ((rkt-wv-callback handle) handle evt) - (rkt_webview_destroy_event e) + (rkt_webview_free_data data) (rkt-process-events handle))) ) 'done)) -(define (rkt-webview-create parent evt-callback close-callback) +(define (rkt-webview-create parent evt-callback close-callback server-cert) (let* ((evt-queue (make-queue)) (parent-win (if (eq? parent #f) 0 (rkt-wv-win parent))) ) (let ((wv (rkt_webview_create parent-win (λ (rkt-evt) - (enqueue! evt-queue rkt-evt))))) + (enqueue! evt-queue rkt-evt)) + server-cert))) (let ((handle (make-rkt-wv wv evt-queue evt-callback #t close-callback))) (thread (λ () (sleep 1) @@ -385,10 +464,11 @@ (rkt_webview_run_js (rkt-wv-win wv) js)) (define (rkt-webview-call-js wv js) - (let* ((r (rkt_webview_call_js (rkt-wv-win wv) js)) + (let* ((d (rkt_webview_call_js (rkt-wv-win wv) js)) + (r (union-ref (rkt_data_t-data d) 2)) (value (cast (rkt_js_result_t-value r) _pointer _string*/utf-8)) (result (rkt_js_result_t-result r))) - (rkt_webview_destroy_js_result r) + (rkt_webview_free_data d) (list result value))) (define (rkt-webview-resize wv w h) @@ -415,26 +495,47 @@ (rkt_webview_open_devtools (rkt-wv-win wv))) (define (rkt-webview-choose-dir wv title base-dir) - (let* ((r (rkt_webview_choose_dir (rkt-wv-win wv) title base-dir)) + (let* ((d (rkt_webview_choose_dir (rkt-wv-win wv) title base-dir)) + (r (union-ref (rkt_data_t-data d) 2)) (value (cast (rkt_js_result_t-value r) _pointer _string*/utf-8)) (result (rkt_js_result_t-result r))) - (rkt_webview_destroy_js_result r) + (rkt_webview_free_data d) (list result value))) (define (rkt-webview-file-open wv title base-dir permitted-exts) - (let* ((r (rkt_webview_file_open (rkt-wv-win wv) title base-dir permitted-exts)) + (let* ((d (rkt_webview_file_open (rkt-wv-win wv) title base-dir permitted-exts)) + (r (union-ref (rkt_data_t-data d) 2)) (value (cast (rkt_js_result_t-value r) _pointer _string*/utf-8)) (result (rkt_js_result_t-result r))) - (rkt_webview_destroy_js_result r) + (rkt_webview_free_data d) (list result value))) (define (rkt-webview-file-save wv title base-dir permitted-exts) - (let* ((r (rkt_webview_file_save (rkt-wv-win wv) title base-dir permitted-exts)) + (let* ((d (rkt_webview_file_save (rkt-wv-win wv) title base-dir permitted-exts)) + (r (union-ref (rkt_data_t-data d) 2)) (value (cast (rkt_js_result_t-value r) _pointer _string*/utf-8)) (result (rkt_js_result_t-result r))) - (rkt_webview_destroy_js_result r) + (rkt_webview_free_data d) (list result value))) +(define (rkt-webview-version) + (let ((d (rkt_webview_version))) + (let ((v (union-ref (rkt_data_t-data d) 0))) + (let ((qt-major (rkt_version_t-qt-major v)) + (qt-minor (rkt_version_t-qt-minor v)) + (qt-patch (rkt_version_t-qt-patch v)) + (api-major (rkt_version_t-api-major v)) + (api-minor (rkt_version_t-api-minor v)) + (api-patch (rkt_version_t-api-patch v)) + ) + (rkt_webview_free_data d) + (list (list 'webview-c-api api-major api-minor api-patch) + (list 'qt qt-major qt-minor qt-patch)) + ) + ) + ) + ) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Administration ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -459,9 +560,10 @@ ;; Cleanup on exit ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(plumber-add-flush! (current-plumber) - (λ (handle) - (rkt-webview-exit))) +(set! quiet-call + (plumber-add-flush! (current-plumber) + (λ (handle) + (rkt-webview-exit)))) diff --git a/private/racket-webview.rkt b/private/racket-webview.rkt index 4100fcf..d9b35e6 100644 --- a/private/racket-webview.rkt +++ b/private/racket-webview.rkt @@ -1,6 +1,7 @@ #lang racket/base (require "racket-webview-qt.rkt" + "racket-webview-version.rkt" "utils.rkt" "mimetypes.rkt" "rgba.rkt" @@ -99,6 +100,11 @@ webview-standard-file-getter + webview-version + + wv-base-url + wv-window-nr + test ) @@ -393,12 +399,13 @@ (cert (generate-self-signed-cert 2048 365 '("127.0.0.1" "localhost") "NL" "Dijkema" #:ou (wv-cert-ou-token h))) + (server-cert (certificate cert)) (event-processor (λ (wv evt) (event-callback h (util-parse-event evt)))) (close-callback (λ () (remove-cert-files h))) (ph (if (wv? p) (wv-handle p) #f)) - (wv (let ((internal-handle (rkt-webview-create ph event-processor close-callback))) + (wv (let ((internal-handle (rkt-webview-create ph event-processor close-callback server-cert))) (set-wv-handle! h internal-handle) (set-wv-window-nr! h (rkt-wv-win internal-handle)) internal-handle)) @@ -870,7 +877,11 @@ (wva webview-attr/date g:date? string->date) (wva webview-attr/time g:time? string->time) (wva webview-attr/datetime g:datetime string->datetime) -(wvv webview-attr/boolean boolean? (λ (e) (if (string=? e "true") #t #f))) +(wva webview-attr/boolean boolean? (λ (e) (if (string=? e "true") #t #f))) + +(define (webview-version) + (cons (list 'webview webview-major webview-minor webview-patch) + (rkt-webview-version))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; testing diff --git a/rktwebview_qt/command.h b/rktwebview_qt/command.h index fea5d76..ca484de 100644 --- a/rktwebview_qt/command.h +++ b/rktwebview_qt/command.h @@ -26,6 +26,7 @@ #define COMMAND_FILE_OPEN 20 #define COMMAND_FILE_SAVE 21 #define COMMAND_SET_OU_TOKEN 22 +#define COMMAND_SET_CERTIFICATE 23 class Command { diff --git a/rktwebview_qt/main.cpp b/rktwebview_qt/main.cpp index 604e3d8..66a0e89 100644 --- a/rktwebview_qt/main.cpp +++ b/rktwebview_qt/main.cpp @@ -1,14 +1,15 @@ #include "rktwebview.h" #include +#include static int _argc; static char **_argv; -void eventCb(rkt_event_t *e) +void eventCb(rkt_data_t *e) { - printf("event: %s\n", e->event); - rkt_webview_destroy_event(e); + printf("event: %s\n", e->data.event.event); + rkt_webview_free_data(e); } int main(int argc, char *argv[]) @@ -20,7 +21,9 @@ int main(int argc, char *argv[]) _argv = argv; rkt_webview_init(); - wv1 = rkt_webview_create(0, eventCb); + + wv1 = rkt_webview_create(0, eventCb, nullptr); + rkt_webview_move(wv1, 200, 300); rkt_webview_resize(wv1, 800, 600); rkt_webview_set_url(wv1, "https://wikipedia.org"); //"http://127.0.0.1:8083"); @@ -35,23 +38,26 @@ int main(int argc, char *argv[]) } if (i == 3) { - rkt_js_result_t *r = rkt_webview_call_js(wv1, "{ let a = 7 * 6; console.log('a = ' + a); return a; }"); - printf("rkt_js_result: %d: %s\n", r->result, r->value); + rkt_data_t *r = rkt_webview_call_js(wv1, "{ let a = 7 * 6; console.log('a = ' + a); return a; }"); + printf("rkt_js_result: %d: %s\n", r->data.js_result.result, r->data.js_result.value); + rkt_webview_free_data(r); } if (i == 4) { - rkt_js_result_t *r = rkt_webview_call_js(wv1, "let el = document.getElementById('hi');el.value = '10';"); - printf("rkt_js_result: %d: %s\n", r->result, r->value); + rkt_data_t *r = rkt_webview_call_js(wv1, "let el = document.getElementById('hi');el.value = '10';"); + printf("rkt_js_result: %d: %s\n", r->data.js_result.result, r->data.js_result.value); + rkt_webview_free_data(r); } if (i == 6) { - rkt_js_result_t *r = rkt_webview_call_js(wv1, "document.body.innerHTML = '

Hi!

'; return document.body.innerHTML;"); - printf("rkt_js_result: %d: %s\n", r->result, r->value); + rkt_data_t *r = rkt_webview_call_js(wv1, "document.body.innerHTML = '

Hi!

'; return document.body.innerHTML;"); + printf("rkt_js_result: %d: %s\n", r->data.js_result.result, r->data.js_result.value); + rkt_webview_free_data(r); } if (i == 10) { - wv2 = rkt_webview_create(wv1, eventCb); + wv2 = rkt_webview_create(wv1, eventCb, nullptr); rkt_webview_move(wv2, 400, 200); rkt_webview_resize(wv2, 800, 600); rkt_webview_set_url(wv2, "https://127.0.0.1"); diff --git a/rktwebview_qt/rktwebview.cpp b/rktwebview_qt/rktwebview.cpp index 43366d4..d8a4cc6 100644 --- a/rktwebview_qt/rktwebview.cpp +++ b/rktwebview_qt/rktwebview.cpp @@ -32,10 +32,10 @@ void rkt_webview_init() } } -int rkt_webview_create(rktwebview_t parent, event_cb_t js_event_cb) +int rkt_webview_create(rktwebview_t parent, event_cb_t js_event_cb, const char *optional_server_cert_pem) { rkt_webview_init(); - return handler->rktWebViewCreate(parent, js_event_cb); + return handler->rktWebViewCreate(parent, js_event_cb, optional_server_cert_pem); } void rkt_webview_close(rktwebview_t wv) @@ -66,10 +66,10 @@ result_t rkt_webview_run_js(rktwebview_t wv, const char *js) return r; } -rkt_js_result_t *rkt_webview_call_js(rktwebview_t wv, const char *js) +rkt_data_t *rkt_webview_call_js(rktwebview_t wv, const char *js) { rkt_webview_init(); - rkt_js_result_t *r = handler->rktCallJs(wv, js); + rkt_data_t *r = handler->rktCallJs(wv, js); return r; } @@ -95,22 +95,6 @@ void rkt_webview_process_events(int for_ms) } } - -result_t rkt_webview_destroy_event(rkt_event_t *e) -{ - free(e->event); - free(e); - return result_t::oke; -} - -result_t rkt_webview_destroy_js_result(rkt_js_result_t *r) -{ - free(r->value); - free(r); - return result_t::oke; -} - - result_t rkt_webview_move(rktwebview_t wv, int x, int y) { rkt_webview_init(); @@ -184,19 +168,19 @@ result_t rkt_webview_set_title(rktwebview_t wv, const char *title) return handler->rktWindowSetTitle(wv, title); } -rkt_js_result_t *rkt_webview_choose_dir(rktwebview_t w, const char *title, const char *base_dir) +rkt_data_t *rkt_webview_choose_dir(rktwebview_t w, const char *title, const char *base_dir) { rkt_webview_init(); return handler->rktChooseDir(w, title, base_dir); } -rkt_js_result_t *rkt_webview_file_open(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts) +rkt_data_t *rkt_webview_file_open(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts) { rkt_webview_init(); return handler->rktFileOpen(w, title, base_dir, permitted_exts); } -rkt_js_result_t *rkt_webview_file_save(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts) +rkt_data_t *rkt_webview_file_save(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts) { rkt_webview_init(); return handler->rktFileSave(w, title, base_dir, permitted_exts); @@ -207,3 +191,31 @@ void rkt_webview_set_ou_token(rktwebview_t wv, const char *token) rkt_webview_init(); handler->rktSetOUToken(wv, token); } + +void rkt_webview_free_data(rkt_data_t *d) +{ + if (d->kind == version) { + free(d); + } else if (d->kind == event) { + free(d->data.event.event); + free(d); + } else if (d->kind == js_result) { + free(d->data.js_result.value); + free(d); + } else { + fprintf(stderr, "UNEXPECTED: data kind %d cannot be freed\n", d->kind); + } +} + +rkt_data_t *rkt_webview_version() +{ + rkt_data_t *d = static_cast(malloc(sizeof(rkt_data_t))); + d->kind = version; + d->data.version.api_major = RKT_WEBVIEW_API_MAJOR; + d->data.version.api_minor = RKT_WEBVIEW_API_MINOR; + d->data.version.api_patch = RKT_WEBVIEW_API_PATCH; + d->data.version.qt_major = QT_VERSION_MAJOR; + d->data.version.qt_minor = QT_VERSION_MINOR; + d->data.version.qt_patch = QT_VERSION_PATCH; + return d; +} diff --git a/rktwebview_qt/rktwebview.h b/rktwebview_qt/rktwebview.h index bf06380..6c19a44 100644 --- a/rktwebview_qt/rktwebview.h +++ b/rktwebview_qt/rktwebview.h @@ -3,6 +3,10 @@ #include "rktwebview_qt_global.h" +#define RKT_WEBVIEW_API_MAJOR 0 +#define RKT_WEBVIEW_API_MINOR 1 +#define RKT_WEBVIEW_API_PATCH 1 + extern "C" { typedef int rktwebview_t; @@ -12,8 +16,6 @@ typedef struct { char *event; } rkt_event_t; -typedef void (*event_cb_t)(rkt_event_t *); - typedef enum { no_result_yet = -1, oke = 0, @@ -51,27 +53,53 @@ typedef enum { maximized_active = 18 } window_state_t; +typedef struct { + int qt_major; + int qt_minor; + int qt_patch; + int api_major; + int api_minor; + int api_patch; +} rkt_version_t; + +typedef enum { + version = 1, + event = 2, + js_result = 3 +} rkt_data_kind_t; + +typedef struct { + rkt_data_kind_t kind; + union { + rkt_version_t version; + rkt_event_t event; + rkt_js_result_t js_result; + } data; +} rkt_data_t; + +typedef void (*event_cb_t)(rkt_data_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(rktwebview_t parent, event_cb_t js_event_cb); +RKTWEBVIEW_QT_EXPORT void rkt_webview_free_data(rkt_data_t *d); + +RKTWEBVIEW_QT_EXPORT rkt_data_t *rkt_webview_version(); + +RKTWEBVIEW_QT_EXPORT int rkt_webview_create(rktwebview_t parent, event_cb_t js_event_cb, const char *optional_server_cert_pem); RKTWEBVIEW_QT_EXPORT void rkt_webview_close(rktwebview_t wv); RKTWEBVIEW_QT_EXPORT bool rkt_webview_valid(rktwebview_t wv); RKTWEBVIEW_QT_EXPORT result_t rkt_webview_set_title(rktwebview_t wv, const char *title); RKTWEBVIEW_QT_EXPORT void rkt_webview_set_ou_token(rktwebview_t wv, const char *token); - RKTWEBVIEW_QT_EXPORT result_t rkt_webview_set_url(rktwebview_t wv, const char *url); RKTWEBVIEW_QT_EXPORT result_t rkt_webview_set_html(rktwebview_t wv, const char *html); RKTWEBVIEW_QT_EXPORT result_t rkt_webview_run_js(rktwebview_t wv, const char *js); -RKTWEBVIEW_QT_EXPORT rkt_js_result_t *rkt_webview_call_js(rktwebview_t wv, const char *js); -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_destroy_js_result(rkt_js_result_t *r); +RKTWEBVIEW_QT_EXPORT rkt_data_t *rkt_webview_call_js(rktwebview_t wv, const char *js); RKTWEBVIEW_QT_EXPORT result_t rkt_webview_open_devtools(rktwebview_t wv); -RKTWEBVIEW_QT_EXPORT result_t rkt_webview_destroy_event(rkt_event_t *e); - RKTWEBVIEW_QT_EXPORT result_t rkt_webview_move(rktwebview_t w, int x, int y); RKTWEBVIEW_QT_EXPORT result_t rkt_webview_resize(rktwebview_t w, int width, int height); RKTWEBVIEW_QT_EXPORT result_t rkt_webview_hide(rktwebview_t w); @@ -82,9 +110,9 @@ RKTWEBVIEW_QT_EXPORT result_t rkt_webview_maximize(rktwebview_t w); RKTWEBVIEW_QT_EXPORT result_t rkt_webview_minimize(rktwebview_t w); RKTWEBVIEW_QT_EXPORT window_state_t rkt_webview_window_state(rktwebview_t w); -RKTWEBVIEW_QT_EXPORT rkt_js_result_t *rkt_webview_choose_dir(rktwebview_t w, const char *title, const char *base_dir); -RKTWEBVIEW_QT_EXPORT rkt_js_result_t *rkt_webview_file_open(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); -RKTWEBVIEW_QT_EXPORT rkt_js_result_t *rkt_webview_file_save(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); +RKTWEBVIEW_QT_EXPORT rkt_data_t *rkt_webview_choose_dir(rktwebview_t w, const char *title, const char *base_dir); +RKTWEBVIEW_QT_EXPORT rkt_data_t *rkt_webview_file_open(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); +RKTWEBVIEW_QT_EXPORT rkt_data_t *rkt_webview_file_save(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); } diff --git a/rktwebview_qt/rktwebview_qt.cpp b/rktwebview_qt/rktwebview_qt.cpp index f4fa58c..0dc6ded 100644 --- a/rktwebview_qt/rktwebview_qt.cpp +++ b/rktwebview_qt/rktwebview_qt.cpp @@ -12,6 +12,14 @@ #include "command.h" #include +static inline char *copyString(const char *s) +{ + int l = strlen(s); + char *cpy = static_cast(malloc(l + 1)); + memcpy(cpy, s, l + 1); + return cpy; +} + void Rktwebview_qt::processCommand(Command *cmd) { switch(cmd->cmd) { @@ -26,6 +34,9 @@ void Rktwebview_qt::processCommand(Command *cmd) void *f = cmd->args[1].value(); event_cb_t js_event_cb = reinterpret_cast (f); + bool has_scp = cmd->args[2].toBool(); + QByteArray scp_pem = cmd->args[3].toByteArray(); + WebviewWindow *p; if (_views.contains(parent)) { p = _views[parent]; @@ -33,7 +44,7 @@ void Rktwebview_qt::processCommand(Command *cmd) p = nullptr; } - WebviewWindow *w = new WebviewWindow(p); + WebviewWindow *w = new WebviewWindow(p, has_scp, scp_pem); WebViewQt *view = new WebViewQt(nextHandle(), w); w->addView(view, this); @@ -359,13 +370,17 @@ int Rktwebview_qt::nextHandle() return h; } -int Rktwebview_qt::rktWebViewCreate(int parent, event_cb_t js_evt_cb) +int Rktwebview_qt::rktWebViewCreate(int parent, event_cb_t js_evt_cb, const char *optional_server_cert_pem) { Command c(COMMAND_CREATE); c.args.push_back(parent); void *function = reinterpret_cast(js_evt_cb); QVariant f(QVariant::fromValue(function)); c.args.push_back(f); + bool has_scp = (optional_server_cert_pem != nullptr); + c.args.push_back(has_scp); + QByteArray scp = (optional_server_cert_pem == nullptr) ? QByteArray("") : QByteArray(optional_server_cert_pem); + c.args.push_back(scp); postCommand(&c); @@ -427,7 +442,7 @@ result_t Rktwebview_qt::rktSetHtml(rktwebview_t wv, const char *html) return r ? result_t::oke : result_t::set_html_failed; } -rkt_js_result_t *Rktwebview_qt::rktCallJs(rktwebview_t wv, const char *js) +rkt_data_t *Rktwebview_qt::rktCallJs(rktwebview_t wv, const char *js) { Command c(COMMAND_CALL_JS); QString _js(js); @@ -436,9 +451,10 @@ rkt_js_result_t *Rktwebview_qt::rktCallJs(rktwebview_t wv, const char *js) postCommand(&c); while(!c.done) { doEvents(); } - rkt_js_result_t *r = static_cast(malloc(sizeof(rkt_js_result_t))); - r->result = c.js_result_ok ? result_t::oke : result_t::eval_js_failed; - r->value = strdup(c.result.toString().toUtf8()); + rkt_data_t *r = static_cast(malloc(sizeof(rkt_data_t))); + r->kind = js_result; + r->data.js_result.result = c.js_result_ok ? result_t::oke : result_t::eval_js_failed; + r->data.js_result.value = copyString(c.result.toString().toUtf8()); return r; } @@ -520,7 +536,7 @@ window_state_t Rktwebview_qt::rktWindowState(rktwebview_t w) return ws; } -rkt_js_result_t *Rktwebview_qt::rktChooseDir(rktwebview_t w, const char *title, const char *base_dir) +rkt_data_t *Rktwebview_qt::rktChooseDir(rktwebview_t w, const char *title, const char *base_dir) { Command c(COMMAND_CHOOSE_DIR); c.args.push_back(w); @@ -537,15 +553,15 @@ rkt_js_result_t *Rktwebview_qt::rktChooseDir(rktwebview_t w, const char *title, bool oke = c.js_result_ok; - rkt_js_result_t *r = static_cast(malloc(sizeof(rkt_js_result_t))); - r->result = c.js_result_ok ? result_t::oke : result_t::choose_dir_failed; - - r->value = strdup(c.result.toString().toUtf8()); + rkt_data_t *r = static_cast(malloc(sizeof(rkt_js_result_t))); + r->kind = js_result; + r->data.js_result.result = c.js_result_ok ? result_t::oke : result_t::choose_dir_failed; + r->data.js_result.value = copyString(c.result.toString().toUtf8()); return r; } -rkt_js_result_t *Rktwebview_qt::rktFileOpen(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts) +rkt_data_t *Rktwebview_qt::rktFileOpen(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts) { Command c(COMMAND_FILE_OPEN); c.args.push_back(w); @@ -565,15 +581,15 @@ rkt_js_result_t *Rktwebview_qt::rktFileOpen(rktwebview_t w, const char *title, c bool oke = c.js_result_ok; - rkt_js_result_t *r = static_cast(malloc(sizeof(rkt_js_result_t))); - r->result = c.js_result_ok ? result_t::oke : result_t::choose_dir_failed; - - r->value = strdup(c.result.toString().toUtf8()); + rkt_data_t *r = static_cast(malloc(sizeof(rkt_data_t))); + r->kind = js_result; + r->data.js_result.result = c.js_result_ok ? result_t::oke : result_t::choose_dir_failed; + r->data.js_result.value = copyString(c.result.toString().toUtf8()); return r; } -rkt_js_result_t *Rktwebview_qt::rktFileSave(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts) +rkt_data_t *Rktwebview_qt::rktFileSave(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts) { Command c(COMMAND_FILE_SAVE); c.args.push_back(w); @@ -593,10 +609,10 @@ rkt_js_result_t *Rktwebview_qt::rktFileSave(rktwebview_t w, const char *title, c bool oke = c.js_result_ok; - rkt_js_result_t *r = static_cast(malloc(sizeof(rkt_js_result_t))); - r->result = c.js_result_ok ? result_t::oke : result_t::choose_dir_failed; - - r->value = strdup(c.result.toString().toUtf8()); + rkt_data_t *r = static_cast(malloc(sizeof(rkt_data_t))); + r->kind = js_result; + r->data.js_result.result = c.js_result_ok ? result_t::oke : result_t::choose_dir_failed; + r->data.js_result.value = copyString(c.result.toString().toUtf8()); return r; } @@ -672,11 +688,12 @@ void Rktwebview_qt::triggerEvent(rktwebview_t wv, const QString &msg) { if (_view_js_callbacks.contains(wv)) { event_cb_t js_event_cb = _view_js_callbacks[wv]; - char *evt = strdup(msg.toUtf8().constData()); - rkt_event_t *e = static_cast(malloc(sizeof(rkt_event_t))); - e->wv = wv; - e->event = evt; - js_event_cb(e); + char *evt = copyString(msg.toUtf8().constData()); + rkt_data_t *d = static_cast(malloc(sizeof(rkt_data_t))); + d->kind = rkt_data_kind_t::event; + d->data.event.wv = wv; + d->data.event.event = evt; + js_event_cb(d); } } @@ -726,21 +743,29 @@ void Rktwebview_qt::customEvent(QEvent *event) void Rktwebview_qt::doEvents() { - //_app->processEvents(); - if (_evt_loop_depth == 0) { + //QTime ct = QTime::currentTime(); + //QTime expire = QTime::currentTime().addMSecs(2); + //while(QTime::currentTime() <= expire) { + _app->processEvents(); + //} + + // Qt 6.10 --> this leads to a core dump + // together with the stopEventloop stuff. + // Qt 6.4 seem stable. + /*if (_evt_loop_depth == 0) { _evt_loop_depth += 1; _evt_loop_timer.setSingleShot(true); _evt_loop_timer.start(2); //_evt_loop.exec(); _app->exec(); - } + }*/ } void Rktwebview_qt::stopEventloop() { //_evt_loop.exit(0); - _app->exit(0); - _evt_loop_depth -= 1; + //_app->exit(0); + //_evt_loop_depth -= 1; } @@ -760,15 +785,18 @@ Rktwebview_qt::Rktwebview_qt(Rktwebview_qt **handler) : connect(&_process_events, &QTimer::timeout, this, &Rktwebview_qt::processJsEventQueues); _process_events.start(5); - connect(&_evt_loop_timer, &QTimer::timeout, this, &Rktwebview_qt::stopEventloop); + // See Qt 6.10 remark at doEvents. + //connect(&_evt_loop_timer, &QTimer::timeout, this, &Rktwebview_qt::stopEventloop); - //const auto *eventDispatcher = QThread::currentThread()->eventDispatcher(); - //QObject::connect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock, - // QThread::currentThread(), []{ - // if (QThread::currentThread()->loopLevel() == 0) - // QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - // } - // ); + // Because we are using processEvents only (Qt 6.10), we need this dispatcher to + // handle deferred Deletes. + const auto *eventDispatcher = QThread::currentThread()->eventDispatcher(); + QObject::connect(eventDispatcher, &QAbstractEventDispatcher::aboutToBlock, + QThread::currentThread(), []{ + if (QThread::currentThread()->loopLevel() == 0) + QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); + } + ); *_handler = nullptr; } diff --git a/rktwebview_qt/rktwebview_qt.h b/rktwebview_qt/rktwebview_qt.h index abf088d..dcb7f72 100644 --- a/rktwebview_qt/rktwebview_qt.h +++ b/rktwebview_qt/rktwebview_qt.h @@ -64,7 +64,7 @@ public: public: int nextHandle(); public: - int rktWebViewCreate(int parent, event_cb_t js_evt_cb); + int rktWebViewCreate(int parent, event_cb_t js_evt_cb, const char *optional_server_cert_pem); void rktWebViewClose(int wv); void rktSetOUToken(rktwebview_t wv, const char *ou_token); void rktQuit(); @@ -73,7 +73,7 @@ public: result_t rktSetUrl(rktwebview_t wv, const char *url); result_t rktSetHtml(rktwebview_t wv, const char *html); result_t rktRunJs(rktwebview_t wv, const char *js); - rkt_js_result_t *rktCallJs(rktwebview_t wv, const char *js); + rkt_data_t *rktCallJs(rktwebview_t wv, const char *js); result_t rktMove(rktwebview_t wv, int x, int y); result_t rktResize(rktwebview_t wv, int w, int h); @@ -85,9 +85,9 @@ public: result_t rktShowNormalWindow(rktwebview_t w); window_state_t rktWindowState(rktwebview_t w); - rkt_js_result_t *rktChooseDir(rktwebview_t w, const char *title, const char *base_dir); - rkt_js_result_t * rktFileOpen(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); - rkt_js_result_t * rktFileSave(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); + rkt_data_t *rktChooseDir(rktwebview_t w, const char *title, const char *base_dir); + rkt_data_t *rktFileOpen(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); + rkt_data_t *rktFileSave(rktwebview_t w, const char *title, const char *base_dir, const char *permitted_exts); result_t rktWindowSetTitle(rktwebview_t wv, const char *title); @@ -110,7 +110,7 @@ public: public: Rktwebview_qt(Rktwebview_qt **handler); - }; +}; diff --git a/rktwebview_qt/webviewqt.cpp b/rktwebview_qt/webviewqt.cpp index 9526b5e..0d575ea 100644 --- a/rktwebview_qt/webviewqt.cpp +++ b/rktwebview_qt/webviewqt.cpp @@ -4,7 +4,7 @@ #include WebViewQt::WebViewQt(int id, WebviewWindow *window) - : QWebEngineView(window) + : QWebEngineView(window->profile(), window) { _id = id; _window = window; diff --git a/rktwebview_qt/webviewwindow.cpp b/rktwebview_qt/webviewwindow.cpp index 47c2870..8bcd6ea 100644 --- a/rktwebview_qt/webviewwindow.cpp +++ b/rktwebview_qt/webviewwindow.cpp @@ -10,13 +10,37 @@ #include #include #include +#include +#include +#include #include #include +#include -WebviewWindow::WebviewWindow(WebviewWindow *parent) +/* +static void displ(QSslCertificate & cert, QSslCertificate::SubjectInfo &a) { + QStringList issuerInfo = cert.issuerInfo(a); + int i, N; + printf("%d\n", a); + for(i = 0, N = issuerInfo.size(); i < N; i++) { + printf("%d[%d]: %s\n", a, i, issuerInfo[i].toUtf8().constData()); + } +}; + +static void displ1(QSslCertificate &cert, QList l) { + int i, N; + for(i = 0, N = l.size(); i < N; i++) { + displ(cert, l[i]); + } +}; +*/ + +WebviewWindow::WebviewWindow(WebviewWindow *parent, bool has_scp, QByteArray scp_pem) : QMainWindow{parent} { + static int profile_nr = 0; + _view = nullptr; _must_close = false; @@ -27,6 +51,22 @@ WebviewWindow::WebviewWindow(WebviewWindow *parent) _moved = 0; _resized = 0; + QWebEngineProfileBuilder b; + if (has_scp) { + profile_nr += 1; + char buf[100]; + sprintf(buf, "profile-%d", profile_nr); + QString name(buf); + QSslCertificate cert(scp_pem); + + QList certs; + certs.append(cert); + b.setAdditionalTrustedCertificates(certs); + _profile = b.createProfile(name); + } else { + _profile = QWebEngineProfile::defaultProfile(); + } + if (parent != nullptr) { setWindowModality(Qt::WindowModality::WindowModal); setWindowFlag(Qt::WindowType::Dialog, true); @@ -36,9 +76,63 @@ WebviewWindow::WebviewWindow(WebviewWindow *parent) connect(&_move_timer, &QTimer::timeout, this, &WebviewWindow::triggerMove); } +void WebviewWindow::navigationRequested(QWebEngineNavigationRequest &req) +{ + if (req.navigationType() == QWebEngineNavigationRequest::NavigationType::TypedNavigation) { + req.accept(); + } else { + EventContainer e("navigation-request"); + + e["url"] = req.url().toString(); + + QString type; + switch (req.navigationType()) { + case QWebEngineNavigationRequest::NavigationType::LinkClickedNavigation: type = "link-clicked"; + break; + case QWebEngineNavigationRequest::NavigationType::TypedNavigation: type = "typed"; + break; + case QWebEngineNavigationRequest::NavigationType::FormSubmittedNavigation: type = "form-submit"; + break; + case QWebEngineNavigationRequest::NavigationType::BackForwardNavigation: type = "back-or-forward"; + break; + case QWebEngineNavigationRequest::NavigationType::ReloadNavigation: type = "reload"; + break; + case QWebEngineNavigationRequest::NavigationType::RedirectNavigation: type = "redirect"; + break; + default: type = "other"; + break; + } + + e["type"] = type; + _container->triggerEvent(_view->id(), e); + + req.reject(); + } +} void WebviewWindow::handleCertificate(const QWebEngineCertificateError &certificateError) { + /* + QList certs = _view->page()->profile()->additionalTrustedCertificates(); + + auto dodisp = [](QList certs) + { + int i; + for(i = 0; i < certs.size(); i++) { + QSslCertificate cert = certs[i]; + QList attrs; + attrs.append(QSslCertificate::Organization); + attrs.append(QSslCertificate::OrganizationalUnitName); + attrs.append(QSslCertificate::CountryName); + attrs.append(QSslCertificate::CommonName); + displ1(cert, attrs); + } + }; + dodisp(certs); + dodisp(certificateError.certificateChain()); +*/ + + fprintf(stderr, "Certificate Error: %s\n", certificateError.description().toUtf8().constData()); QList chain = certificateError.certificateChain(); int i; for(i = 0; i < chain.size(); i++) { @@ -88,6 +182,7 @@ void WebviewWindow::closeEvent(QCloseEvent *evt) EventContainer e("closed"); _container->triggerEvent(_view->id(), e); _container->removeView(_view->id()); + _view->deleteLater(); this->deleteLater(); if (_devtools != nullptr) { _devtools->deleteLater(); @@ -131,7 +226,14 @@ void WebviewWindow::addView(WebViewQt *v, Rktwebview_qt *c) _view = v; this->setCentralWidget(v); - QWebEnginePage *page = _view->page(); + QWebEnginePage *page; + + if (_profile == nullptr) { + page = _view->page(); + } else { + page = new QWebEnginePage(_profile, this); + _view->setPage(page); + } // Inject event handling code for the javascript side QWebEngineScriptCollection &col = page->scripts(); @@ -160,6 +262,8 @@ void WebviewWindow::addView(WebViewQt *v, Rktwebview_qt *c) connect(page, &QWebEnginePage::loadStarted, this, [this]() { _container->onPageLoad(_view->id()); }); + connect(page, &QWebEnginePage::navigationRequested, this, &WebviewWindow::navigationRequested); + connect(page, &QWebEnginePage::certificateError, this, &WebviewWindow::handleCertificate); } @@ -231,6 +335,11 @@ void WebviewWindow::triggerMove() _container->triggerEvent(_view->id(), xy); } +QWebEngineProfile *WebviewWindow::profile() +{ + return _profile; +} + void WebviewWindow::resizeEvent(QResizeEvent *event) { _w = event->size().width(); diff --git a/rktwebview_qt/webviewwindow.h b/rktwebview_qt/webviewwindow.h index 94d27d6..d86a8af 100644 --- a/rktwebview_qt/webviewwindow.h +++ b/rktwebview_qt/webviewwindow.h @@ -4,6 +4,8 @@ #include #include #include +#include +#include class WebViewQt; class Rktwebview_qt; @@ -33,8 +35,11 @@ private: QString _ou_token; + QWebEngineProfile *_profile; + private slots: void handleCertificate(const QWebEngineCertificateError &certificateError); + void navigationRequested(QWebEngineNavigationRequest &req); public slots: void processJsEvents(); @@ -60,12 +65,15 @@ public: void openDevTools(); public: - explicit WebviewWindow(WebviewWindow *parent = nullptr); + explicit WebviewWindow(WebviewWindow *parent, bool has_scp, QByteArray scp_pem); private slots: void triggerResize(); void triggerMove(); +public: + QWebEngineProfile *profile(); + signals: // QWidget interface