This commit is contained in:
2026-03-02 23:10:29 +01:00
parent 07004097e9
commit 25ca1ec4a4
24 changed files with 1054 additions and 31 deletions

74
rktwebview_qt/.gitignore vendored Normal file
View File

@@ -0,0 +1,74 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.autosave
*.a
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*.so
*.so.*
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
.directory
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
*.res
*.rc
/.qmake.cache
/.qmake.stash
# qtcreator generated files
*.pro.user*
CMakeLists.txt.user*
# xemacs temporary files
*.flc
# Vim temporary files
.*.swp
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# MinGW generated files
*.Debug
*.Release
# Python byte code
*.pyc
# Binaries
# --------
*.dll
*.exe

View File

@@ -0,0 +1,47 @@
cmake_minimum_required(VERSION 3.14)
project(rktwebview_qt LANGUAGES CXX)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 17)
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_qt SHARED
rktwebview_qt_global.h
rktwebview_qt.cpp
rktwebview_qt.h
rktwebview.h
rktwebview.cpp
webviewqt.h webviewqt.cpp
rktwebview_internal.h
webviewwindow.h webviewwindow.cpp
webviewapp.h webviewapp.cpp
)
target_link_libraries(rktwebview_qt PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
target_link_libraries(rktwebview_qt PRIVATE Qt${QT_VERSION_MAJOR}::WebEngineWidgets)
target_compile_definitions(rktwebview_qt PRIVATE RKTWEBVIEW_QT_LIBRARY)
add_executable(rktwebview_qt_test
main.cpp
rktwebview_qt_global.h
rktwebview_qt.cpp
rktwebview_qt.h
rktwebview.h
rktwebview.cpp
webviewqt.h webviewqt.cpp
webviewwindow.h webviewwindow.cpp
rktwebview_internal.h
webviewapp.h webviewapp.cpp
)
target_compile_definitions(rktwebview_qt_test PRIVATE RKTWEBVIEW_QT_LIBRARY)
target_link_libraries(rktwebview_qt_test PRIVATE Qt${QT_VERSION_MAJOR}::Widgets)
target_link_libraries(rktwebview_qt_test PRIVATE Qt${QT_VERSION_MAJOR}::WebEngineWidgets)

37
rktwebview_qt/main.cpp Normal file
View File

@@ -0,0 +1,37 @@
#include "rktwebview.h"
#include <QThread>
static int _argc;
static char **_argv;
int main(int argc, char *argv[])
{
int wv1;
int wv2;
_argc = argc;
_argv = argv;
rkt_webview_init();
wv1 = rkt_webview_create(0);
rkt_webview_set_url(wv1, "https://wikipedia.org");
int i = 0;
while(i < 20) {
printf("Waiting...%d\n", i);
rkt_webview_process_events(1000);
if (i == 10) {
wv2 = rkt_webview_create(0);
}
if (i == 15) {
rkt_webview_close(wv1);
}
i += 1;
}
rkt_webview_close(wv2);
}

View File

@@ -0,0 +1,157 @@
#include "rktwebview_internal.h"
#include <malloc.h>
#include <string.h>
#include <chrono>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include "rktwebview_qt.h"
#include "webviewapp.h"
uint64_t current_ms() {
using namespace std::chrono;
return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
}
/////////////////////////////////////////////////////////////////////
// Main C Interface
/////////////////////////////////////////////////////////////////////
static int pipefd[2];
static bool started = false;
static pid_t webview_process;
static bool cannot_fork_or_pipe = false;
Rktwebview_qt *handler = nullptr;
void rkt_webview_init()
{
/*
if (!started) {
if (pipe(pipefd) != -1) {
webview_process = fork();
if (webview_process < 0)
cannot_fork_or_pipe = true;
} else if (webview_process == 0) {
WebViewApp app;
started = true;
} else {
cannot_fork_or_pipe = true;
}
}
*/
if (handler == nullptr) {
handler = new Rktwebview_qt(&handler);
}
}
int rkt_webview_create(int parent)
{
//rkt_webview_init();
return handler->rktWebViewCreate(parent);
}
void rkt_webview_close(int wv)
{
handler->rktWebViewClose(wv);
}
result_t rkt_webview_set_url(int wv, const char *url)
{
result_t r = handler->rktSetUrl(wv, url);
return r;
}
/////////////////////////////////////////////////////////////////////
// Supporting functions
/////////////////////////////////////////////////////////////////////
void queue_init(queue_t **q)
{
*q = static_cast<queue_t *>(malloc(sizeof(queue_t)));
queue_t *Q = *q;
Q->length = 0;
Q->first = nullptr;
Q->last = nullptr;
}
void enqueue(queue_t *q, item_t item)
{
queue_item_t *itm = (queue_item_t *) malloc(sizeof(queue_item_t));
itm->item.context = item.context;
itm->item.data = strdup(item.data);
if (q->first == nullptr) {
q->first = itm;
q->last = itm;
itm->prev = nullptr;
itm->next = nullptr;
q->length = 1;
} else {
itm->prev = q->last;
itm->next = nullptr;
q->last->next = itm;
q->last = itm;
q->length += 1;
}
}
bool dequeue(queue_t *q, item_t *item)
{
if (q->length == 0) {
item->context = CONTEXT_INVALID;
item->data = nullptr;
return false;
} else {
queue_item_t *itm = q->first;
q->first = q->first->next;
q->length -= 1;
if (q->length == 0) {
q->first = nullptr;
q->last = nullptr;
}
item->context = itm->item.context;
item->data = itm->item.data;
free(itm);
return true;
}
}
int queue_length(queue_t *q)
{
return q->length;
}
void queue_destroy(queue_t *q)
{
item_t i;
while(dequeue(q, &i)) {
free(i.data);
}
free(q);
}
void free_item(item_t item)
{
free(item.data);
}
void rkt_webview_process_events(int for_ms)
{
//rkt_webview_init();
int64_t start_ms = current_ms();
int64_t end_ms = start_ms + for_ms;
while (current_ms() < end_ms) {
usleep(500); // sleep 0.5 ms
handler->doEvents();
}
}

View File

@@ -0,0 +1,57 @@
#ifndef RKTWEBVIEW_H
#define RKTWEBVIEW_H
#include "rktwebview_qt_global.h"
#define CONTEXT_QUIT -2
#define CONTEXT_NIL -1
#define CONTEXT_INVALID 0
#define CONTEXT_BOUND_EVENT 1
#define CONTEXT_WINDOW_RESIZE 2
#define CONTEXT_WINDOW_MOVE 3
#define CONTEXT_WINDOW_CAN_CLOSE 4
#define CONTEXT_WINDOW_CLOSED 5
#define CONTEXT_SET_HTML 6
#define CONTEXT_NAVIGATE 7
#define CONTEXT_EVAL_JS 8
#define CONTEXT_OPEN_DEVTOOLS 9
#define CONTEXT_CALL_JS 10
#define CONTEXT_CMD_CLOSE 11
#define CONTEXT_CMD_CREATE_VIEW 12
extern "C" {
typedef int rktwebview_t;
typedef struct {
rktwebview_t wv;
int context;
char *data;
} item_t;
typedef enum {
no_result_yet = -1,
oke = 0,
set_html_failed = 1,
set_navigate_failed = 2,
eval_js_failed = 3,
no_devtools_on_platform = 4,
no_delegate_for_context = 5,
webview_missing_dependency = 6,
webview_canceled = 7,
webview_invalid_state = 8,
webview_invalid_argument = 9,
webview_unspecified = 10,
webview_dispatch_failed = 11
} result_t;
RKTWEBVIEW_QT_EXPORT void rkt_webview_init();
RKTWEBVIEW_QT_EXPORT void rkt_webview_process_events(int for_ms);
RKTWEBVIEW_QT_EXPORT int rkt_webview_create(int parent);
RKTWEBVIEW_QT_EXPORT void rkt_webview_close(int wv);
RKTWEBVIEW_QT_EXPORT result_t rkt_webview_set_url(int wv, const char *url);
RKTWEBVIEW_QT_EXPORT result_t rkt_webview_set_html(int wv, const char *html);
}
#endif // RKTWEBVIEW_H

View File

@@ -0,0 +1,25 @@
#ifndef RKTWEBVIEW_INTERNAL_H
#define RKTWEBVIEW_INTERNAL_H
#include "rktwebview.h"
typedef struct _item {
item_t item;
struct _item *next;
struct _item *prev;
} queue_item_t;
typedef struct {
queue_item_t *first;
queue_item_t *last;
int length;
} queue_t;
void queue_init(queue_t **q);
void enqueue(queue_t *q, item_t item);
bool dequeue(queue_t *q, item_t *item);
int queue_length(queue_t *q);
void queue_destroy(queue_t *q);
void free_item(item_t i);
#endif // RKTWEBVIEW_INTERNAL_H

View File

@@ -0,0 +1,196 @@
#include "rktwebview_qt.h"
#include "webviewqt.h"
#include "rktwebview.h"
#include "webviewwindow.h"
#include <QApplication>
#include <QTimer>
#include <QSemaphore>
#define COMMAND_QUIT 1
#define COMMAND_CLOSE 2
#define COMMAND_CREATE 3
#define COMMAND_SET_URL 4
class Command
{
public:
int cmd;
QVector<QVariant> args;
QVariant result;
bool done;
public:
Command(int _cmd) {
cmd = _cmd;
done = false;
}
};
void Rktwebview_qt::processCommands()
{
printf("COmmand processing\n");
while(!_command_queue.empty()) {
Command *cmd = _command_queue.dequeue();
switch(cmd->cmd) {
case COMMAND_QUIT: {
_app->quit();
cmd->done = true;
}
break;
case COMMAND_CREATE: {
int parent = cmd->args[0].toInt();
QWidget *p;
if (_views.contains(parent)) {
p = _views[parent];
} else {
p = nullptr;
}
WebviewWindow *w = new WebviewWindow(p);
WebViewQt *view = new WebViewQt(nextHandle(), w);
w->addView(view, this);
int id = view->id();
_views[id] = w;
w->show();
cmd->result = id;
cmd->done = true;
}
break;
case COMMAND_CLOSE: {
int wv = cmd->args[0].toInt();
if (_views.contains(wv)) {
WebviewWindow *w= _views[wv];
_views.remove(wv);
w->close();
w->deleteLater();
cmd->result = true;
} else {
cmd->result = false;
}
cmd->done = true;
}
break;
case COMMAND_SET_URL: {
int wv = cmd->args[0].toInt();
QString url = cmd->args[1].toString();
if (_views.contains(wv)) {
WebviewWindow *w = _views[wv];
WebViewQt *v = w->view();
QUrl u(url);
v->setUrl(u);
cmd->result = true;
} else {
cmd->result = false;
}
cmd->done = true;
}
break;
default: {
cmd->result = false;
cmd->done = true;
}
break;
}
}
}
void Rktwebview_qt::interruptEventLoop()
{
printf("Exeting application event loop\n");
//_app->quit();
}
void Rktwebview_qt::removeView(int id)
{
_views.remove(id);
}
int Rktwebview_qt::nextHandle()
{
int h = ++_current_handle;
return h;
}
int Rktwebview_qt::rktWebViewCreate(int parent)
{
Command c(COMMAND_CREATE);
c.args.push_back(parent);
_command_queue.enqueue(&c);
while(!c.done) { doEvents(); }
int id = c.result.toInt();
return id;
}
void Rktwebview_qt::rktWebViewClose(int wv)
{
Command c(COMMAND_CLOSE);
c.args.push_back(wv);
_command_queue.enqueue(&c);
while(!c.done) { doEvents(); }
}
result_t Rktwebview_qt::rktSetUrl(rktwebview_t wv, const char *url)
{
Command c(COMMAND_SET_URL);
QString _url(url);
c.args.push_back(wv);
c.args.push_back(_url);
_command_queue.enqueue(&c);
while(!c.done) { doEvents(); }
bool r = c.result.toBool();
return r ? result_t::oke : result_t::set_navigate_failed;
}
void Rktwebview_qt::rktQuit()
{
QList<int> keys = _views.keys();
int i;
for(i = 0; i < keys.size(); i++) {
int view_handle = keys[i];
rktWebViewClose(view_handle);
}
Command c(COMMAND_QUIT);
_command_queue.enqueue(&c);
while(!c.done) { doEvents(); }
}
void Rktwebview_qt::doEvents()
{
//_quit_event_loop.start(2000);
//_app->exec();
//processCommands();
_app->processEvents();
}
Rktwebview_qt::Rktwebview_qt(Rktwebview_qt **handler) :
QObject()
{
_argc = 1;
_argv[0] = const_cast<char *>("Rktwebview_qt");
_current_handle = 0;
_handler = handler;
_app = new QApplication(_argc, _argv);
connect(&_process_commands, &QTimer::timeout, this, &Rktwebview_qt::processCommands);
connect(&_quit_event_loop, &QTimer::timeout, this, &Rktwebview_qt::interruptEventLoop);
_process_commands.start(1000);
*_handler = nullptr;
}

View File

@@ -0,0 +1,68 @@
#ifndef RKTWEBVIEW_QT_H
#define RKTWEBVIEW_QT_H
#include "rktwebview_qt_global.h"
#include "rktwebview_internal.h"
#include <QApplication>
#include <QWebEngineView>
#include <QMutex>
#include <QThread>
#include <QHash>
#include <QQueue>
#include <QTimer>
class WebViewQt;
class WebviewWindow;
class Command;
class RKTWEBVIEW_QT_EXPORT Rktwebview_qt : public QObject
{
Q_OBJECT
private:
QApplication *_app;
rktwebview_t _current_handle;
QHash<int, WebviewWindow *> _views;
QQueue<Command *> _command_queue;
QTimer _process_commands;
QTimer _quit_event_loop;
Rktwebview_qt **_handler;
int _argc;
char *_argv[1];
public slots:
void processCommands();
void interruptEventLoop();
public:
void removeView(int id);
public:
int nextHandle();
public:
int rktWebViewCreate(int parent = 0); // threadsafe
void rktWebViewClose(int wv);
void rktQuit();
void runJs(rktwebview_t wv, const char *js); // threadsafe
item_t callJs(rktwebview_t wv, const char *js); // threadsafe
void setHtml(rktwebview_t wv, const char *html); // threadsafe
void navigate(rktwebview_t wv, const char *url); // threadsafe
result_t rktSetUrl(rktwebview_t wv, const char *url);
public:
void doEvents();
public:
void runCommandThread();
public:
Rktwebview_qt(Rktwebview_qt **handler);
};
#endif // RKTWEBVIEW_QT_H

View File

@@ -0,0 +1,12 @@
#ifndef RKTWEBVIEW_QT_GLOBAL_H
#define RKTWEBVIEW_QT_GLOBAL_H
#include <QtCore/qglobal.h>
#if defined(RKTWEBVIEW_QT_LIBRARY)
#define RKTWEBVIEW_QT_EXPORT Q_DECL_EXPORT
#else
#define RKTWEBVIEW_QT_EXPORT Q_DECL_IMPORT
#endif
#endif // RKTWEBVIEW_QT_GLOBAL_H

View File

@@ -0,0 +1,8 @@
#include "webviewapp.h"
WebViewApp::WebViewApp(int &argc, char **argv) :
QApplication(argc, argv)
{
}

View File

@@ -0,0 +1,13 @@
#ifndef WEBVIEWAPP_H
#define WEBVIEWAPP_H
#include <QApplication>
class WebViewApp : public QApplication
{
Q_OBJECT
public:
WebViewApp(int &argc, char **argv);
};
#endif // WEBVIEWAPP_H

View File

@@ -0,0 +1,19 @@
#include "webviewqt.h"
WebViewQt::WebViewQt(int id, QWidget *parent)
: QWebEngineView(parent)
{
_id = id;
}
int WebViewQt::id() const
{
return _id;
}
void WebViewQt::runJs(const char *js)
{
}

22
rktwebview_qt/webviewqt.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef WEBVIEWQT_H
#define WEBVIEWQT_H
#include <QWebEngineView>
class WebViewQt : public QWebEngineView
{
private:
int _id;
public:
int id() const;
public:
void runJs(const char *js);
public:
WebViewQt(int id, QWidget *parent = nullptr);
};
#endif // WEBVIEWQT_H

View File

@@ -0,0 +1,27 @@
#include "webviewwindow.h"
#include "webviewqt.h"
#include "rktwebview_qt.h"
void WebviewWindow::closeEvent(QCloseEvent *evt)
{
_container->removeView(_view->id());
}
void WebviewWindow::addView(WebViewQt *v, Rktwebview_qt *c)
{
_container = c;
_view = v;
this->setCentralWidget(v);
}
WebViewQt *WebviewWindow::view()
{
return _view;
}
WebviewWindow::WebviewWindow(QWidget *parent)
: QMainWindow{parent}
{
_view = nullptr;
}

View File

@@ -0,0 +1,30 @@
#ifndef WEBVIEWWINDOW_H
#define WEBVIEWWINDOW_H
#include <QMainWindow>
class WebViewQt;
class Rktwebview_qt;
class WebviewWindow : public QMainWindow
{
Q_OBJECT
private:
Rktwebview_qt *_container;
WebViewQt *_view;
protected:
void closeEvent(QCloseEvent *evt);
public:
void addView(WebViewQt *v, Rktwebview_qt *c);
WebViewQt *view();
public:
explicit WebviewWindow(QWidget *parent = nullptr);
signals:
};
#endif // WEBVIEWWINDOW_H