Files
racket-webview/private/racket-webview.rkt
2026-03-03 22:52:22 +01:00

196 lines
6.0 KiB
Racket

#lang racket/base
(require "racket-webview-qt.rkt"
"utils.rkt"
web-server/servlet
web-server/servlet-env
web-server/http
net/url
racket/runtime-path
racket/file
racket/string
racket/path
racket/port
json
)
(provide webview-create
webview-devtools
webview-close
webview-run-js
webview-call-js
webview-move
webview-resize
webview-bind!
webview-standard-file-getter
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Web server
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define current-servlet-port 8083)
(define current-window-nr 1)
(define-runtime-path js-path "../js")
(define (default-boilerplate-js)
(let ((file (build-path js-path "boilerplate.js")))
(file->string file)))
(define-struct wv
([handle #:mutable]
port
[window-nr #:mutable]
[file-getter #:mutable]
[boilerplate-js #:mutable]
[webserver-thread #:mutable]))
(define (process-html wv-handle path out)
(let ((html (file->string path))
(boilerplate-js (wv-boilerplate-js wv-handle)))
(set! html (string-replace html "<head>"
(string-append "<head>" "\n"
"<script>" "\n"
boilerplate-js "\n"
"</script>" "\n")))
(display html out)))
(define (process-file wv-handle ext path out)
(let ((content (file->bytes path)))
(write bytes out)))
(define (web-serve wv-handle req)
(let* ((path (url->string (request-uri req)))
(file-getter (wv-file-getter wv-handle)))
(let* ((file-to-serve (build-path (file-getter path)))
(ext-bytes (path-get-extension file-to-serve))
(ext (if (eq? ext-bytes #f) #f
(string->symbol (string-downcase (substring (bytes->string/utf-8 ext-bytes) 1)))))
)
(if (file-exists? file-to-serve)
(response/output
(λ (out)
(if (or (eq? ext 'html) (eq? ext 'htm))
(process-html wv-handle file-to-serve out)
(process-file wv-handle ext file-to-serve out))
))
(response/output
#:code 404
(λ (out)
(displayln (format "~a not found" path) out)))
)
)
)
)
(define (start-web-server h)
(thread (λ ()
(serve/servlet
(λ (req) (web-serve h req))
#:listen-ip "127.0.0.1"
#:port (wv-port h)
#:command-line? #t
#:servlet-path ""
#:stateless? #t
;#:launch-browser #f
#:servlet-regexp #rx""))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Utilities
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (util-parse-event evt)
(let ((wv-d0 (with-input-from-string evt read-json)))
wv-d0))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Webview functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define (webview-create file-getter event-callback
#:boilerplate-js [bj (default-boilerplate-js)]
#:parent [p 0])
(let* ((h (make-wv #f current-servlet-port -1 file-getter bj #f))
(server (let ((s (start-web-server h)))
(sleep 1) ;;; TODO: Check if web server is up.
s))
(event-processor (λ (wv evt)
(event-callback h (util-parse-event evt))))
(wv (rkt-webview-create p event-processor))
(base-req (format "http://127.0.0.1:~a"
(wv-port h)))
)
(set-wv-handle! h wv)
(set-wv-window-nr! h (rkt-wv-win wv))
(set-wv-webserver-thread! h server)
(rkt-webview-set-url! (wv-handle h) base-req)
(set! current-servlet-port (+ current-servlet-port 1))
h))
(define (webview-devtools wv)
(rkt-webview-open-devtools (wv-handle wv)))
(define (webview-move wv x y)
(rkt-webview-move (wv-handle wv) x y))
(define (webview-resize wv w h)
(rkt-webview-resize (wv-handle wv) w h))
(define (webview-close wv)
(rkt-webview-close (wv-handle wv))
(kill-thread (wv-webserver-thread wv))
)
(define (webview-bind! wv selector event)
(let ((sel (if (symbol? selector)
(format "#~a" selector)
selector))
(evt (format "~a" event)))
(webview-run-js wv
(format "window.rkt_bind_evt_ids(~a, '~a', '~a')"
(wv-window-nr wv) sel evt))))
(define (webview-run-js wv js)
(rkt-webview-run-js (wv-handle wv) js))
(define (webview-standard-file-getter base-path
#:not-exist [on-not-exist (λ (file base-path path) path)]
)
(λ (file)
(let ((f (if (string=? file "/") "index.html" file)))
(when (string-prefix? f "/")
(set! f (substring f 1)))
(let ((p (build-path base-path f)))
(if (not (file-exists? p))
(on-not-exist file base-path p)
p)))))
(define (webview-call-js wv js)
(let ((result (rkt_webview_call_js (wv-handle wv) js)))
result))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; testing
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-runtime-path example-path "../example")
(define file-getter (webview-standard-file-getter example-path))
(define (test)
(let* ((cb (λ (handle evt)
(displayln evt)))
(h (webview-create file-getter cb)))
h))
; (while (not (webview-has-events? h))
; (displayln "Waiting...")
; (sleep 1))
; (let ((evt (webview-get-event h)))
; (when (string=? (hash-ref evt 'evt) "html-loaded")
; (webview-bind h "button" "click")))
; h))