Files
racket-webview/scrbl/racket-webview-qt.scrbl
T
2026-08-03 14:15:19 +02:00

456 lines
11 KiB
Racket
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#lang scribble/manual
@defmodule{racket-webview/racket-webview-qt}
@title[#:tag "rktwebview-process"]{Racket Process Interface for @tt{rktwebview_prg}}
@author[@author+email["Hans Dijkema" "hans@dijkewijk.nl"]]
@section{Overview}
The module @tt{racket-webview-qt.rkt} provides the low-level Racket interface to
the Qt helper process @tt{rktwebview_prg}. It keeps the existing command numbers,
JSON command payloads, result symbols, event strings, and public Racket
procedures, but no longer loads a shared library through FFI.
The helper process is started lazily when the first backend operation is
requested. Commands are written as one JSON object per line to the process
stdin. Results and asynchronous events are read from stdout. Backend diagnostics
are copied from stderr to the current Racket error port.
Each command has a request identifier. The identifier is transport metadata and
does not alter the existing command payload. It makes it possible to associate
a result with the correct caller when multiple Racket threads use the backend.
The module provides:
@itemlist[#:style 'compact
@item{creation of HTTP(S) contexts}
@item{creation and management of webview windows}
@item{navigation and HTML loading}
@item{JavaScript execution}
@item{window geometry and visibility control}
@item{native dialogs}
@item{asynchronous event delivery}
@item{version and cleanup utilities}]
@section{Backend Availability}
The environment variable @tt{RKT_WEBVIEW_PRG} may name an explicit backend
executable. This is useful during development and testing.
Without that variable, the module uses the existing downloader and installation
directory. Availability is checked when the first backend operation is made,
not while the module is loaded. If the configured backend cannot be found or
downloaded, that operation raises an exception with the reason.
@section{Module Initialization}
Starting the backend performs the following steps:
@itemlist[#:style 'compact
@item{locates or downloads @tt{rktwebview_prg}}
@item{sets the Qt runtime environment for the child process}
@item{starts the child with separate stdin, stdout, and stderr pipes}
@item{starts a stdout protocol reader}
@item{starts a stderr forwarding thread}
@item{waits for the protocol-version handshake}
@item{starts an independent Racket event-dispatch thread}]
Stdout is reserved for protocol records. Human-readable backend logging is
written only to stderr.
Currently the wrapper supports @tt{'linux} and @tt{'windows} release layouts.
@section{Data Model}
@subsection{The @tt{rkt-wv} Structure}
Each webview window is represented by a transparent Racket structure.
@defstruct*[rkt-wv
([win exact-integer?]
[evt-queue any/c]
[callback procedure?]
[valid boolean?]
[close-callback procedure?])]{
Represents a webview instance managed by the Racket wrapper.
Fields:
@itemlist[#:style 'compact
@item{@tt{win}: the native integer window handle}
@item{@tt{evt-queue}: internal queue of pending event strings}
@item{@tt{callback}: user event callback}
@item{@tt{valid}: mutable flag indicating whether the wrapper considers the
window active}
@item{@tt{close-callback}: procedure invoked when the window is closed}
]
Although the structure is transparent, user code should normally treat it as
an opaque handle.
}
@;@defproc[(rkt-wv-win [wv rkt-wv?]) exact-integer?]{
@;Returns the native window handle associated with @racket[wv].
@;}
@section{HTTP(S) Contexts}
A context represents the shared HTTP(S) environment used by webviews.
Contexts correspond to native WebEngine profiles and determine properties such
as:
@itemlist[#:style 'compact
@item{cookies and cache}
@item{injected JavaScript}
@item{trusted certificates}
]
@defproc[(rkt-webview-new-context
[boilerplate-js string?]
[server-cert bytes?])
exact-integer?]{
Creates a new HTTP(S) context and returns its native identifier.
Arguments:
@itemlist[#:style 'compact
@item{@racket[boilerplate-js]: JavaScript source injected into pages}
@item{@racket[server-cert]: optional certificate data}
]
The returned context identifier can be passed to
@racket[rkt-webview-create] to create webviews within that context.
}
@section{Creating Webviews}
@defproc[(rkt-webview-create
[context exact-integer?]
[parent (or/c #f rkt-wv?)]
[evt-callback (-> rkt-wv? string? any)]
[close-callback (-> any)])
rkt-wv?]{
Creates a new webview window in the given HTTP(S) context.
Arguments:
@itemlist[#:style 'compact
@item{@racket[context]: context identifier returned by
@racket[rkt-webview-new-context]}
@item{@racket[parent]: optional parent webview}
@item{@racket[evt-callback]: procedure invoked for each event}
@item{@racket[close-callback]: procedure invoked when the window is closed}
]
The result is a new @racket[rkt-wv] structure.
Events generated by the native layer are delivered asynchronously through
@racket[evt-callback].
}
@section{Window Lifecycle}
@defproc[(rkt-webview-close [wv rkt-wv?]) boolean?]{
Requests that the webview window be closed.
The wrapper forwards the request to the helper process. The Qt-generated
@tt{"closed"} event is delivered asynchronously, after which the Racket handle
is removed.
Returns @racket[#t].
}
@defproc[(rkt-webview-valid? [wv rkt-wv?]) boolean?]{
Returns whether the webview still exists.
The wrapper first checks its internal validity flag and then queries the native
runtime.
}
@defproc[(rkt-webview-exit) void?]{
Closes all webviews and stops the background event-processing thread.
This function is also registered with the Racket plumber so that cleanup occurs
automatically when the process exits.
}
@section{Window Configuration}
@defproc[(rkt-webview-set-title! [wv rkt-wv?] [title string?])
symbol?]{
Sets the native window title.
Returns a result symbol such as:
@itemlist[#:style 'compact
@item{@racket['oke]}
@item{@racket['failed]}
]
}
@defproc[(rkt-webview-set-ou-token [wv rkt-wv?] [token string?])
boolean?]{
Associates an Organizational Unit token with the window.
This token may be used by the native layer when accepting certain
self-signed certificates.
}
@section{Navigation}
@defproc[(rkt-webview-set-url! [wv rkt-wv?] [url string?]) symbol?]{
Navigates the webview to the given URL.
Returns a result symbol such as:
@itemlist[#:style 'compact
@item{@racket['oke]}
@item{@racket['set_navigate_failed]}
]
}
@defproc[(rkt-webview-set-html! [wv rkt-wv?] [html string?]) symbol?]{
Loads HTML directly into the webview.
Returns a result symbol such as:
@itemlist[#:style 'compact
@item{@racket['oke]}
@item{@racket['set_html_failed]}
]
}
@section{JavaScript Execution}
@defproc[(rkt-webview-run-js [wv rkt-wv?] [js string?]) symbol?]{
Executes JavaScript without returning a value.
Returns a result symbol such as:
@itemlist[#:style 'compact
@item{@racket['oke]}
@item{@racket['eval_js_failed]}
]
}
@defproc[(rkt-webview-call-js [wv rkt-wv?] [js string?])
(list/c symbol? string?)]{
Executes JavaScript and returns:
@racketblock[
(list result value)
]
where:
@itemlist[#:style 'compact
@item{@racket[result] is the native result code}
@item{@racket[value] is a JSON string containing the returned data}
]
The JSON structure is generated by the native backend.
}
@section{Window Geometry}
@defproc[(rkt-webview-move [wv rkt-wv?] [x exact-integer?] [y exact-integer?])
symbol?]{
Moves the webview window to the given screen coordinates.
}
@defproc[(rkt-webview-resize
[wv rkt-wv?]
[width exact-integer?]
[height exact-integer?])
symbol?]{
Resizes the window.
}
@defproc[(rkt-webview-show [wv rkt-wv?]) symbol?]{
Shows the window.
}
@defproc[(rkt-webview-hide [wv rkt-wv?]) symbol?]{
Hides the window.
}
@defproc[(rkt-webview-show-normal [wv rkt-wv?]) symbol?]{
Restores the window to its normal state.
}
@defproc[(rkt-webview-maximize [wv rkt-wv?]) symbol?]{
Maximizes the window.
}
@defproc[(rkt-webview-minimize [wv rkt-wv?]) symbol?]{
Minimizes the window.
}
@defproc[(rkt-webview-present [wv rkt-wv?]) symbol?]{
Requests that the window be presented to the user.
}
@defproc[(rkt-webview-window-state [wv rkt-wv?]) symbol?]{
Returns the current window state.
Possible results:
@itemlist[#:style 'compact
@item{@racket['normal]}
@item{@racket['minimized]}
@item{@racket['maximized]}
@item{@racket['hidden]}
@item{@racket['normal_active]}
@item{@racket['maximized_active]}
]
}
@section{Developer Tools}
@defproc[(rkt-webview-open-devtools [wv rkt-wv?]) symbol?]{
Opens the browser developer tools window.
}
@section{Native Dialogs}
Dialog functions return immediately with a status code.
The users choice is delivered asynchronously through the event callback.
@defproc[(rkt-webview-choose-dir
[wv rkt-wv?]
[title string?]
[base-dir string?])
symbol?]{
Requests a directory-selection dialog.
}
@defproc[(rkt-webview-file-open
[wv rkt-wv?]
[title string?]
[base-dir string?]
[extensions string?])
symbol?]{
Requests a file-open dialog.
}
@defproc[(rkt-webview-file-save
[wv rkt-wv?]
[title string?]
[base-dir string?]
[extensions string?])
symbol?]{
Requests a file-save dialog.
}
@defproc[(rkt-webview-messagebox
[wv rkt-wv?]
[title string?]
[message string?]
[submessage string?]
[type symbol?])
symbol?]{
Shows a native message box.
}
@section{Event Delivery}
Each webview has an associated event callback.
The callback has the form:
@racketblock[
(λ (wv event-json) ...)
]
Arguments:
@itemlist[#:style 'compact
@item{@racket[wv]: the webview handle}
@item{@racket[event-json]: JSON event string from the native backend}
]
Typical event types include:
@itemlist[#:style 'compact
@item{@tt{"show"}}
@item{@tt{"hide"}}
@item{@tt{"move"}}
@item{@tt{"resize"}}
@item{@tt{"closed"}}
@item{@tt{"page-loaded"}}
@item{@tt{"navigation-request"}}
@item{@tt{"js-evt"}}
]
The wrapper does not parse the JSON payload.
@section{Version Information}
@defproc[(rkt-webview-version)
(list/c list? list?)]{
Returns the stdio protocol version and the expected Qt backend release.
Example result:
@racketblock[
(list
(list 'webview-stdio-api 1 0 0)
(list 'racket-webview-qt 0 2 3))
]
}
@section{Example}
@racketblock[
(define ctx
(rkt-webview-new-context "" #""))
(define wv
(rkt-webview-create
ctx
#f
(λ (wv evt)
(displayln evt))
(λ ()
(displayln "closed"))))
(rkt-webview-set-title! wv "Example")
(rkt-webview-set-url! wv "https://example.org")
]
@section{Summary}
The module provides a thin Racket interface to the separate
@tt{rktwebview_prg} process.
Key characteristics:
@itemlist[#:style 'compact
@item{line-delimited JSON over stdin and stdout}
@item{request identifiers for concurrent synchronous calls}
@item{asynchronous event delivery on a separate Racket thread}
@item{unchanged JSON event payloads}
@item{simple Racket structures for webviews}]