447 lines
10 KiB
Racket
447 lines
10 KiB
Racket
#lang scribble/manual
|
||
|
||
@title{Racket FFI Interface for @tt{rktwebview_qt}}
|
||
@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 wrapper translates the low-level C interface into a Racket-oriented API
|
||
based on structures, callbacks, and ordinary Racket values.
|
||
|
||
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{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.
|
||
|
||
@section{Module Initialization}
|
||
|
||
Loading the module performs several initialization steps automatically.
|
||
|
||
@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}
|
||
]
|
||
|
||
Currently the wrapper supports the following platforms:
|
||
|
||
@itemlist[#:style 'compact
|
||
@item{@tt{'linux}}
|
||
@item{@tt{'windows}}
|
||
]
|
||
|
||
If the current system is unsupported, loading the module raises an error.
|
||
|
||
@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 native backend and schedules cleanup of
|
||
the event-processing loop.
|
||
|
||
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 include:
|
||
|
||
@itemlist[#:style 'compact
|
||
@item{@racket['normal]}
|
||
@item{@racket['minimized]}
|
||
@item{@racket['maximized]}
|
||
@item{@racket['hidden]}
|
||
]
|
||
}
|
||
|
||
@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 version information for the native backend.
|
||
|
||
Example result:
|
||
|
||
@racketblock[
|
||
(list
|
||
(list 'webview-c-api 1 0 0)
|
||
(list 'qt 6 10 2))
|
||
]
|
||
}
|
||
|
||
@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 FFI module provides a thin Racket interface to the native
|
||
@tt{rktwebview_qt} backend.
|
||
|
||
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}
|
||
] |