456 lines
11 KiB
Racket
456 lines
11 KiB
Racket
#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 user’s 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}]
|