Omzetting naar stdio.

This commit is contained in:
2026-08-03 08:20:56 +02:00
parent 93453c351f
commit 1ecb678e38
9 changed files with 989 additions and 1218 deletions
+44 -84
View File
@@ -2,23 +2,24 @@
@defmodule{racket-webview/racket-webview-qt}
@title{Racket FFI Interface for @tt{rktwebview_qt}}
@title{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 a Racket FFI wrapper around the
native @tt{rktwebview_qt} library. It loads the shared library, initializes the
native runtime, and exposes Racket functions for creating and controlling
webview windows.
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.
If the Qt backend is available locally, it is loaded directly. Otherwise the
module attempts to resolve and download the backend. If that is not possible,
the module continues in a degraded mode in which a limited subset of the
FFI entry points will only display a warning and perform a no-op. All others will fail.
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.
The wrapper translates the low-level C interface into a Racket-oriented API
based on structures, callbacks, and ordinary Racket values.
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:
@@ -30,77 +31,35 @@ The module provides:
@item{window geometry and visibility control}
@item{native dialogs}
@item{asynchronous event delivery}
@item{version and cleanup utilities}
]
@section{Requirements}
The native backend requires Qt version @tt{6.10.2} or newer.
The shared library @tt{rktwebview_qt} must therefore be built against Qt
@tt{6.10.2} or a compatible later release.
Earlier Qt versions are not supported.
@item{version and cleanup utilities}]
@section{Backend Availability}
The module first checks whether the expected @tt{racket-webview-qt} backend is
already installed.
The environment variable @tt{RKT_WEBVIEW_PRG} may name an explicit backend
executable. This is useful during development and testing.
If it is not installed, the module attempts to resolve the configured download
site. If the site can be resolved and the configured archive is downloadable,
the backend is downloaded automatically.
If the download site cannot be resolved, if no archive is available for the
current operating system and machine architecture, or if the download fails, the
module does not immediately abort module loading. Instead it switches to a
degraded mode in which native FFI loading is disabled.
In that degraded mode, a textual reason is stored internally and selected FFI
entry points are replaced by fallback implementations.
When the backend cannot be loaded, the module defines fallback implementations
for missing FFI entry points through @racket[define-ffi-definer] and
@racket[#:default-make-fail].
These fallbacks behave in two different ways.
For a small set of initialization and shutdown functions, a non-failing fallback
is installed:
@itemlist[#:style 'compact
@item{@racket[rkt_webview_env] returns @racket[#t]}
@item{@racket[rkt_webview_events_waiting] returns @racket[0]}
@item{@racket[rkt_webview_init] returns @racket[#t]}
@item{@racket[rkt_webview_cleanup] returns @racket[#t]}]
All other missing FFI functions raise an exception when called.
Fallback warnings are emitted at most once per function. If native loading was
disabled because the backend was unavailable, the warning message includes the
recorded reason. If native loading was enabled but a specific symbol could not
be loaded from the library, the error names the library file.
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}
Loading the module performs several initialization steps automatically.
Starting the backend performs the following steps:
@itemlist[#:style 'compact
@item{determines the operating system and architecture}
@item{sets Qt runtime environment variables}
@item{loads the @tt{rktwebview_qt} shared library}
@item{initializes the native runtime}
@item{starts a background thread that processes native events}
]
@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}]
Currently the wrapper supports the following platforms:
Stdout is reserved for protocol records. Human-readable backend logging is
written only to stderr.
@itemlist[#:style 'compact
@item{@tt{'linux}}
@item{@tt{'windows}}
]
If the current system is unsupported, loading the module raises an error.
Currently the wrapper supports @tt{'linux} and @tt{'windows} release layouts.
@section{Data Model}
@@ -200,8 +159,9 @@ Events generated by the native layer are delivered asynchronously through
Requests that the webview window be closed.
The wrapper forwards the request to the native backend and schedules cleanup of
the event-processing loop.
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].
}
@@ -370,7 +330,7 @@ Opens the browser developer tools window.
@section{Native Dialogs}
Dialog functions return immediately with a status code.
Dialog functions return immediately with a status code.
The users choice is delivered asynchronously through the event callback.
@defproc[(rkt-webview-choose-dir
@@ -450,14 +410,14 @@ The wrapper does not parse the JSON payload.
@defproc[(rkt-webview-version)
(list/c list? list?)]{
Returns version information for the native backend.
Returns the stdio protocol version and the expected Qt backend release.
Example result:
@racketblock[
(list
(list 'webview-c-api 1 0 0)
(list 'qt 6 10 2))
(list 'webview-stdio-api 1 0 0)
(list 'racket-webview-qt 0 2 3))
]
}
@@ -482,14 +442,14 @@ Example result:
@section{Summary}
The FFI module provides a thin Racket interface to the native
@tt{rktwebview_qt} backend.
The module provides a thin Racket interface to the separate
@tt{rktwebview_prg} process.
Key characteristics:
@itemlist[#:style 'compact
@item{thin wrapper around the native C API}
@item{asynchronous event delivery}
@item{JSON-based event payloads}
@item{simple Racket structures for webviews}
]
@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}]