34 Commits

Author SHA1 Message Date
hans a1a56cd08c Ja. 2026-08-04 13:00:56 +02:00
hans d9af82d230 Omzetting naar stdio. 2026-08-03 08:22:42 +02:00
hans fb1a293e88 QtWebEngine select popup rendering issue observed with older Qt 6.x.
Symptom: native HTML <select> popup keeps growing/repainting while open;
QtWebEngine process busy, Racket process idle.
Resolved by upgrading to Qt 6.11.1.
2026-07-07 19:23:17 +02:00
hans 803d6edd41 Merge branch 'main' of https://git.dijkewijk.nl/hans/racket-webview-qt 2026-07-07 17:43:46 +02:00
hans 35b7bfe2e9 Migration to Qt 6.11.1 2026-07-07 17:43:27 +02:00
hans c6db000d26 linefeet added 2026-05-06 09:22:44 +02:00
hans 85b5a29192 handle tray menu myself instead by tray icon 2026-04-30 13:42:57 +02:00
hans b53597396c trying to clear the context menu, but that does not work 2026-04-30 11:53:08 +02:00
hans be980dbffd tray 2026-04-29 23:35:40 +02:00
hans 8165ee20cc Adding a tray icon. 2026-04-29 14:33:43 +02:00
hans cacffbcc2a synchronization on finalization finally solved. 2026-04-20 19:07:03 +02:00
hans b4a4b2f554 mutex implemented for callback to racket.
Needs to be serialized and protected.
2026-04-20 18:31:21 +02:00
hans 2b107f8f73 Finalization & synchronization. Making sure racket does not get stuck on a mutex. 2026-04-20 18:19:58 +02:00
hans 490b89be38 Ja. 2026-04-20 14:01:20 +02:00
hans 2c57eb2ebf More robust cleanup handling 2026-04-20 09:52:48 +02:00
hans 06d2cdb68f finalizing 2026-04-19 21:19:37 +02:00
hans 431ba3dffa finalizer 2026-04-18 17:03:53 +02:00
hans 0786a8eb0e init with function name 2026-04-18 16:54:32 +02:00
hans debda73068 webview-exit 2026-04-18 16:43:19 +02:00
hans ce72177973 events on quit 2026-04-18 16:31:12 +02:00
hans 5214b9f925 Another attempt on quitting cleanly 2026-04-18 15:50:41 +02:00
hans f37e89d956 racket webview qt finalizer next 2026-04-18 15:32:15 +02:00
hans 89b084a6a7 Making sure no warnings are printed (except for getenv) on windows. 2026-04-17 13:58:28 +02:00
hans ee33c72915 small adjustments, many enhancements to rktplayer 2026-04-16 22:22:25 +02:00
hans 59c316a1f0 alive ack implemented and stopping 2026-04-11 16:27:45 +02:00
hans e9e295c3bc alive_ack_queue implemented 2026-04-11 16:10:39 +02:00
hans 52aa0cac10 Alive thread implemented 2026-04-11 09:50:59 +02:00
hans 8e5381fda2 Added callback for events. 2026-04-10 23:49:29 +02:00
hans 82f58bc24b Events for racket-webview-qt 2026-04-10 22:07:03 +02:00
hans ef3883ed15 threading / event callback 2026-04-10 10:16:13 +02:00
hans 79d18bdd5d - 2026-04-09 10:42:36 +02:00
hans 7468a16d63 icon 2026-04-08 16:39:23 +02:00
hans a5378e32b9 - 2026-04-07 13:42:21 +02:00
hans 8ab8cb5335 - 2026-04-07 11:18:15 +02:00
201 changed files with 2030 additions and 475 deletions
+2
View File
@@ -73,3 +73,5 @@ CMakeLists.txt.user*
*.exe
build
lib/linux-x86_64
lib/*.zip
+10 -20
View File
@@ -12,19 +12,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(QT NAMES Qt6 REQUIRED COMPONENTS Widgets WebEngineWidgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets WebEngineWidgets)
add_library(rktwebview SHARED
rktwebview_global.h
rktwebview.h
rktwebview.cpp
shm.h shm.cpp
shmqueue.h shmqueue.cpp
rkt_protocol.h
rktwebview_types.h
json.cpp json.h
utils.h
utils.cpp
)
add_executable(rktwebview_prg
main.cpp
@@ -36,22 +23,25 @@ add_executable(rktwebview_prg
rktutils.h rktutils.cpp
command.h command.cpp
shm.h shm.cpp
shmqueue.h shmqueue.cpp
rkt_protocol.h
rktwebview_types.h
utils.cpp utils.h
)
add_executable(rktwebview_test
rktwebview_test.cpp
build_menu_from_json.cpp
build_menu_from_json.h
)
target_link_libraries(rktwebview_prg PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
target_link_libraries(rktwebview_prg PRIVATE Qt${QT_VERSION_MAJOR}::WebEngineWidgets)
target_compile_definitions(rktwebview PRIVATE RKTWEBVIEW_LIBRARY)
target_compile_definitions(rktwebview_prg PRIVATE RKTWEBVIEW_PRG_EXE)
target_link_Libraries(rktwebview_test PRIVATE rktwebview)
add_executable(rktwebview_test
rktwebview_test.cpp
rkt_protocol.h
)
target_link_libraries(rktwebview_test PRIVATE Qt${QT_VERSION_MAJOR}::Core)
add_dependencies(rktwebview_test rktwebview_prg)
+63 -1
View File
@@ -1,3 +1,65 @@
# racket-webview-qt
The Qt backend of racket-webview
The Qt WebEngine backend of `racket-webview`.
## Version 0.2.3
This version replaces the shared-library/shared-memory transport with a
standalone process interface.
## Transport
`rktwebview_prg` receives line-delimited JSON commands on stdin, writes
line-delimited JSON results and events to stdout, and writes all diagnostics to
stderr. The existing command numbers and command payloads in `rkt_protocol.h`
are unchanged.
See `STDIO-PROTOCOL.md` for the transport envelope and shutdown rules.
The former shared library, shared-memory queue, and callback sources remain in
the repository for comparison, but they are no longer part of the CMake target.
## Build
The CMake target requires Qt Widgets and Qt WebEngineWidgets and builds only:
```text
rktwebview_prg
```
## Previous Qt update
QtWebEngine select popup rendering issues were observed with older Qt 6.x. The
native HTML `<select>` popup could keep growing or repainting while open while
the QtWebEngine process remained busy. Upgrading to Qt 6.11.1 resolved this.
## Integration test
`rktwebview_test` starts the built `rktwebview_prg` with `QProcess` and tests
the stdio handshake, request/result matching, basic webview commands, and clean
shutdown. Run it from the build directory:
```sh
./rktwebview_test
```
For a protocol-only test that does not create a window:
```sh
./rktwebview_test --protocol-only
```
Set `RKT_WEBVIEW_PRG` to test a backend executable outside the test program's
directory.
### Backend logging
Diagnostics written through `utils.h` use one-line structured stderr records:
```text
json:{"topic":"webview-backend","level":"info","elapsed":0.125,"message":"..."}
```
Stdout remains reserved for protocol messages. Unstructured stderr output from
Qt or third-party libraries may still occur and should be treated as diagnostic
text.
+86
View File
@@ -0,0 +1,86 @@
# racket-webview stdio protocol
The Qt backend is a child process of the Racket process. Communication uses
UTF-8 JSON records, one compact JSON object per line.
- stdin of `rktwebview_prg`: commands from Racket
- stdout of `rktwebview_prg`: handshake, results, and events
- stderr of `rktwebview_prg`: diagnostics only
No diagnostic text may be written to stdout.
## Compatibility
The existing numeric command identifiers in `rkt_protocol.h` and their JSON
payload objects are retained. The stdio transport adds only an envelope and a
request identifier. The request identifier allows replies to be matched even
when several Racket threads issue commands concurrently.
## Handshake
The backend writes this record after Qt has been initialized and the command
reader has started:
```json
{"type":"ready","protocol":1}
```
Racket must not send application commands until this record has been received.
## Command
```json
{"type":"command","id":17,"command":6,"data":{"wv":1,"url":"https://example.test"}}
```
Fields:
- `id`: positive request identifier chosen by Racket
- `command`: existing numeric command from `rkt_protocol.h`
- `data`: existing command payload object
## Result
```json
{"type":"result","id":17,"result":0,"data":null}
```
`result` has the same meaning as before. Depending on the command it can be a
`result_t`, a handle, a context identifier, a window-state value, or a metric.
For `CMD_CALL_JS`, `data` contains the JavaScript result string.
Every command now receives a result record, including commands that previously
returned `void`, such as close and setting the OU token. Their public Racket
return values remain unchanged.
## Event
```json
{"type":"event","wv":1,"data":"{\"event\":\"page-loaded\",...}"}
```
The `data` field is the original event JSON string. It is deliberately not
restructured by the transport, so existing event parsing remains unchanged.
Events are independent of command results.
## Shutdown
A normal shutdown sends `CMD_QUIT`, waits for its result, closes the child's
stdin, and waits for the child process. If the Racket process terminates
unexpectedly, the operating system closes the pipe; EOF on stdin makes the Qt
backend close all windows and quit.
## Structured stderr logging
The backend writes diagnostic logging to stderr, never to stdout. Log entries
created through `utils.h` are emitted as one line beginning with `json:` followed
by a compact JSON object:
```text
json:{"topic":"webview-backend","level":"debug","elapsed":1.234,"message":"..."}
```
Newlines and other control characters in the message are JSON escaped, so one
C++ log call always produces one physical stderr line. Consumers should also
accept unstructured stderr lines because Qt, Qt WebEngine, or another library
may write diagnostics directly.
+99
View File
@@ -0,0 +1,99 @@
#include <QJsonArray>
#include <QJsonParseError>
#include <QJsonObject>
#include <QMenu>
#include "rktwebview_qt.h"
#include "utils.h"
static QAction *addMenuItemFromJson(Rktwebview_qt *owner,
QMenu *menu,
const QJsonObject &item,
int source_handle);
static QMenu *buildMenuObjectFromJson(Rktwebview_qt *owner,
const QJsonArray &items,
int source_handle,
QWidget *parent = nullptr)
{
QMenu *menu = new QMenu(parent);
for (const QJsonValue &v : items) {
if (!v.isObject()) {
continue;
}
QJsonObject item = v.toObject();
if (item.value("separator").toBool(false)) {
menu->addSeparator();
continue;
}
addMenuItemFromJson(owner, menu, item, source_handle);
}
return menu;
}
static QAction *addMenuItemFromJson(Rktwebview_qt *owner,
QMenu *menu,
const QJsonObject &item,
int source_handle)
{
QString id = item.value("id").toString();
QString name = item.value("name").toString();
QString icon_file = item.value("icon").toString();
QAction *action = nullptr;
QJsonValue submenu_value = item.value("submenu");
if (submenu_value.isObject()) {
QJsonObject submenu_obj = submenu_value.toObject();
QJsonArray submenu_items = submenu_obj.value("menu").toArray();
QMenu *submenu = buildMenuObjectFromJson(owner,
submenu_items,
source_handle,
menu);
if (!icon_file.isEmpty()) {
submenu->setIcon(QIcon(icon_file));
}
submenu->setTitle(name);
action = menu->addMenu(submenu);
} else {
if (icon_file.isEmpty()) {
action = menu->addAction(name);
} else {
action = menu->addAction(QIcon(icon_file), name);
}
QObject::connect(action, &QAction::triggered,
owner, [owner, source_handle, id]() {
EventContainer e("tray-menu-item-chosen");
e["id"] = id;
e["menu_item"] = id;
owner->triggerEvent(source_handle, e);
});
}
action->setData(id);
return action;
}
QMenu *buildMenuFromJson(Rktwebview_qt *self, const QString &menu_json, int source_handle)
{
QJsonParseError err;
QJsonDocument doc = QJsonDocument::fromJson(menu_json.toUtf8(), &err);
if (err.error != QJsonParseError::NoError || !doc.isObject()) {
ERROR2("Json parse error for menu: %d, '%s'\n", err.error, err.errorString().toUtf8().constData());
return nullptr;
}
QJsonObject root = doc.object();
QJsonArray items = root.value("menu").toArray();
return buildMenuObjectFromJson(self, items, source_handle);
}
+10
View File
@@ -0,0 +1,10 @@
#ifndef BUILD_MENU_FROM_JSON_H
#define BUILD_MENU_FROM_JSON_H
class QMenu;
class Rktwebview_qt;
class QString;
QMenu *buildMenuFromJson(Rktwebview_qt *self, const QString &menu_json, int source_handle);
#endif // BUILD_MENU_FROM_JSON_H
+6
View File
@@ -28,6 +28,12 @@
#define COMMAND_SET_OU_TOKEN 22
#define COMMAND_NEW_CONTEXT 23
#define COMMAND_MESSAGE 24
#define COMMAND_SET_ICON 25
#define COMMAND_CREATE_TRAY 26
#define COMMAND_TRAY_SET_ICON 27
#define COMMAND_TRAY_SET_TOOLTIP 28
#define COMMAND_TRAY_SHOW_MESSAGE 29
#define COMMAND_TRAY_SET_MENU 30
class Command
{
+10 -5
View File
@@ -2,20 +2,21 @@
#
ARCH=`uname -m`
LIB="lib/linux/$ARCH"
LIB="../lib/linux-$ARCH"
BUILD="../build/Release"
mkdir -p $LIB
rm -f $LIB/*.so*
cp build/Release/*.so $LIB
cp build/Release/rktwebview_prg $LIB
cp $BUILD/*.so $LIB
cp $BUILD/rktwebview_prg $LIB
QT_PATH=`ldd build/Release/rktwebview_prg | grep Qt | awk '{print $3}' | head -1 | sed -e 's%[/]lib[/].*%%'`
QT_PATH=`ldd $BUILD/rktwebview_prg | grep Qt | awk '{print $3}' | head -1 | sed -e 's%[/]lib[/].*%%'`
echo "QT_PATH=$QT_PATH"
QT_PLUGINS="$QT_PATH/plugins"
PLUGINS="platforms position generic iconengines imageformats qmltooling tls xcbglintegrations"
EXTRA_LIBS_SO=`ldd build/Release/rktwebview_prg | grep Qt | awk '{ print $3 }'`
EXTRA_LIBS_SO=`ldd $BUILD/rktwebview_prg | grep Qt | awk '{ print $3 }'`
EXTRA_LIBS_PLATFORM_PLUGIN_XCB=`ldd $QT_PATH/plugins/platforms/libqxcb.so | grep Qt | awk '{print $3}'`
for pl in $PLUGINS
@@ -63,4 +64,8 @@ done
cp $QT_PATH/libexec/QtWebEngineProcess $LIB
(cd $LIB; cd ..; zip -9 -y -r linux-$ARCH.zip linux-$ARCH)
+3 -3
View File
@@ -92,7 +92,7 @@ const JSON &JSON::at(unsigned int index) const {
int JSON::length() const {
if( Type == Class::Array )
return Internal.List->size();
return static_cast<int>(Internal.List->size());
else
return -1;
}
@@ -105,9 +105,9 @@ bool JSON::hasKey(const std::string &key) const {
int JSON::size() const {
if( Type == Class::Object )
return Internal.Map->size();
return static_cast<int>(Internal.Map->size());
else if( Type == Class::Array )
return Internal.List->size();
return static_cast<int>(Internal.List->size());
else
return -1;
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More