Files
racket-webview-qt/main.cpp

433 lines
15 KiB
C++

#include <QThread>
#include <QFile>
#include <QApplication>
#include "rkt_protocol.h"
#include "shm.h"
#include "shmqueue.h"
#include "command.h"
#include "utils.h"
#include <QJsonDocument>
#include <QJsonObject>
#include "rktwebview_qt.h"
static void free_data(rkt_data_t *d)
{
do_free_data(d);
}
class Handler : public QThread
{
public:
Shm *shm;
ShmQueue *command_queue;
ShmQueue *result_queue;
ShmQueue *event_queue;
Rktwebview_qt *webview_handler;
bool quit;
// QThread interface
protected:
void run();
};
class Alive : public QThread
{
public:
Handler *handler;
ShmQueue *alive_queue;
ShmQueue *alive_ack_queue;
bool alive_timeout;
protected:
void run();
};
static Handler *_handler;
static Alive *_alive;
static void event_cb(rkt_data_t *data)
{
if (data->kind != rkt_data_kind_t::event) {
return;
}
int wv = data->data.event.wv;
char *evt = data->data.event.event;
std::string evt_d(evt);
_handler->event_queue->enqueue(wv, evt_d);
free_data(data);
}
int main(int argc, char *argv[])
{
const char *me = argv[0];
{
time_t my_time = time(NULL);
INFO1("Starting at %s", ctime(&my_time));
}
if (argc < 8) {
ERROR1("%s: wrong number of arguments\n", me);
exit(1);
}
const char *shm_name = argv[1];
const char *shm_size_str = argv[2];
const char *cmd_slot_str = argv[3];
const char *res_slot_str = argv[4];
const char *evt_slot_str = argv[5];
const char *alive_slot_str = argv[6];
const char *alive_ack_slot_str = argv[7];
size_t shm_size = atoi(shm_size_str);
int cmd_slot = atoi(cmd_slot_str);
int res_slot = atoi(res_slot_str);
int evt_slot = atoi(evt_slot_str);
int alive_slot = atoi(alive_slot_str);
int alive_ack_slot = atoi(alive_ack_slot_str);
MKLOGSTMT(LOG_INFO, fprintf(stderr, "%s %s %s %s %s %s %s %s\n", me, shm_name, shm_size_str,
cmd_slot_str, res_slot_str, evt_slot_str, alive_slot_str, alive_ack_slot_str));
MKLOGSTMT(LOG_INFO, fprintf(stderr, "%s %s %d %d %d %d %d %d\n", me, shm_name, static_cast<int>(shm_size),
cmd_slot, res_slot, evt_slot, alive_slot, alive_ack_slot));
if (!(shm_size > 0 && cmd_slot > 0 && res_slot > 0 && evt_slot > 0 && alive_slot > 0 && alive_ack_slot > 0)) {
ERROR1("%s: Invalid shm size or slots\n", me);
exit(2);
}
Handler *handler = new Handler();
_handler = handler;
handler->shm = new Shm(shm_name, shm_size, false);
handler->command_queue = new ShmQueue(handler->shm, cmd_slot, false);
handler->result_queue = new ShmQueue(handler->shm, res_slot, false);
handler->event_queue = new ShmQueue(handler->shm, evt_slot, false);
handler->webview_handler = new Rktwebview_qt(argc, argv);
handler->start();
Alive *alive = new Alive();
_alive = alive;
alive->handler = handler;
alive->alive_timeout = false;
alive->alive_queue = new ShmQueue(handler->shm, alive_slot, false);
alive->alive_ack_queue = new ShmQueue(handler->shm, alive_ack_slot, false);
alive->start();
handler->webview_handler->initApp();
handler->webview_handler->execApp();
INFO0("waiting for thread to end\n");
handler->wait();
INFO0("Handler thread stopped\n");
alive->wait();
INFO0("Alive thread stopped\n");
if (alive->alive_timeout) {
// take ownership over all queues and SHM, because we didn't get an alive message
// and probably the racket side has crashed because of that.
ERROR0("Taking ownership of shared memory, shared queues and shared semaphores\n");
handler->command_queue->takeOwnership();
handler->result_queue->takeOwnership();
handler->event_queue->takeOwnership();
handler->shm->takeOwnership();
alive->alive_queue->takeOwnership();
alive->alive_ack_queue->takeOwnership();
}
INFO0("cleaning up shm\n");
delete alive->alive_queue;
delete alive;
delete handler->webview_handler;
delete handler->command_queue;
delete handler->result_queue;
delete handler->event_queue;
delete handler->shm;
delete handler;
{
time_t my_time = time(NULL);
INFO1("Exiting at %s", ctime(&my_time));
}
return 0;
}
void Handler::run()
{
int wait_ms = 10 * 1000; // 10 seconds.
while (!quit) {
int cmd;
std::string data;
bool something_came = command_queue->dequeue(cmd, data, wait_ms);
if (!something_came) {
DEBUG1("No command received last %d seconds\n", wait_ms / 1000);
cmd = CMD_NOOP;
}
QJsonObject data_obj = QJsonDocument::fromJson(data.c_str()).object();
switch(cmd) {
case CMD_NOOP: {
if (quit) {
DEBUG1("Alive timeout, quit = %d\n", quit);
webview_handler->closeAllWindows();
DEBUG0("Closed all windows\n");
webview_handler->rktQuit();
DEBUG0("Quit application\n");
}
}
break;
case CMD_QUIT: {
INFO0("Got quit message\n");
webview_handler->rktQuit();
INFO0("Enqueing RESULT_QUIT to result queue\n");
result_queue->enqueue(RESULT_QUIT);
quit = true;
}
break;
case CMD_INFO: {
int open_windows = webview_handler->openWindows();
result_queue->enqueue(open_windows);
}
break;
case CMD_HANDLE_IS_VALID: {
int wv = data_obj["wv"].toInt();
bool oke = webview_handler->rktValid(wv);
result_queue->enqueue(oke);
}
break;
case CMD_SET_LOGLEVEL: {
int ll = data_obj["wv"].toInt();
setLogLevel(ll);
bool oke = true;
result_queue->enqueue(oke);
}
break;
case CMD_CONTEXT_NEW: {
QString boilerplate_js = data_obj["boilerplate_js"].toString();
bool has_pem_cert = data_obj["has_pem_cert"].toBool();
QString pem_cert = data_obj["pem_cert"].toString();
int context = webview_handler->newContext(boilerplate_js, has_pem_cert, pem_cert);
result_queue->enqueue(context);
}
break;
case CMD_CREATE_WV: {
int context = data_obj["context"].toInt();
int parent = data_obj["parent"].toInt();
int wv = webview_handler->rktWebViewCreate(context, parent, event_cb);
result_queue->enqueue(wv);
}
break;
case CMD_CLOSE_WV: {
int wv = data_obj["wv"].toInt();
webview_handler->rktWebViewClose(wv);
}
break;
case CMD_SET_URL: {
int wv = data_obj["wv"].toInt();
QString url = data_obj["url"].toString();
result_t r = webview_handler->rktSetUrl(wv, url.toUtf8().constData());
result_queue->enqueue(r);
}
break;
case CMD_SET_HTML: {
int wv = data_obj["wv"].toInt();
QString html = data_obj["html"].toString();
result_t r = webview_handler->rktSetHtml(wv, html.toUtf8().constData());
result_queue->enqueue(r);
}
break;
case CMD_RUN_JS: {
int wv = data_obj["wv"].toInt();
QString js = data_obj["js"].toString();
result_t r = webview_handler->rktRunJs(wv, js);
result_queue->enqueue(r);
}
break;
case CMD_CALL_JS: {
int wv = data_obj["wv"].toInt();
QString js = data_obj["js"].toString();
rkt_data_t *res = webview_handler->rktCallJs(wv, js.toUtf8().constData());
result_queue->enqueue(res->data.js_result.result, res->data.js_result.value);
free_data(res);
}
break;
case CMD_OPEN_DEVTOOLS: {
int wv = data_obj["wv"].toInt();
result_t r = webview_handler->rktOpenDevtools(wv);
result_queue->enqueue(r);
}
break;
case CMD_MOVE: {
int wv = data_obj["wv"].toInt();
int x = data_obj["x"].toInt();
int y = data_obj["y"].toInt();
result_t r = webview_handler->rktMove(wv, x, y);
result_queue->enqueue(r);
}
break;
case CMD_RESIZE: {
int wv = data_obj["wv"].toInt();
int w = data_obj["w"].toInt();
int h = data_obj["h"].toInt();
result_t r = webview_handler->rktResize(wv, w, h);
result_queue->enqueue(r);
}
break;
case CMD_HIDE: {
int wv = data_obj["wv"].toInt();
result_t r = webview_handler->rktHideWindow(wv);
result_queue->enqueue(r);
}
break;
case CMD_SHOW: {
int wv = data_obj["wv"].toInt();
result_t r = webview_handler->rktShowWindow(wv);
result_queue->enqueue(r);
}
break;
case CMD_PRESENT: {
int wv = data_obj["wv"].toInt();
result_t r = webview_handler->rktPresentWindow(wv);
result_queue->enqueue(r);
}
break;
case CMD_MAXIMIZE: {
int wv = data_obj["wv"].toInt();
result_t r = webview_handler->rktMaximizeWindow(wv);
result_queue->enqueue(r);
}
break;
case CMD_MINIMIZE: {
int wv = data_obj["wv"].toInt();
result_t r = webview_handler->rktMinimizeWindow(wv);
result_queue->enqueue(r);
}
break;
case CMD_SHOW_NORMAL: {
int wv = data_obj["wv"].toInt();
result_t r = webview_handler->rktShowNormalWindow(wv);
result_queue->enqueue(r);
}
break;
case CMD_WINDOW_STATE: {
int wv = data_obj["wv"].toInt();
window_state_t r = webview_handler->rktWindowState(wv);
result_queue->enqueue(r);
}
break;
case CMD_SET_TITLE: {
int wv = data_obj["wv"].toInt();
QString title = data_obj["title"].toString();
result_t r = webview_handler->rktWindowSetTitle(wv, title.toUtf8().constData());
result_queue->enqueue(r);
}
break;
case CMD_SET_ICON: {
int wv = data_obj["wv"].toInt();
QString icon_file = data_obj["icon"].toString();
result_t r = webview_handler->rktWindowSetIcon(wv, icon_file.toUtf8().constData());
result_queue->enqueue(r);
}
break;
case CMD_CHOOSE_DIR: {
int wv = data_obj["wv"].toInt();
QString title = data_obj["title"].toString();
QString base_dir = data_obj["base_dir"].toString();
result_t r = webview_handler->rktChooseDir(wv,
title.toUtf8().constData(),
base_dir.toUtf8().constData()
);
result_queue->enqueue(r);
}
break;
case CMD_FILE_OPEN: {
int wv = data_obj["wv"].toInt();
QString title = data_obj["title"].toString();
QString base_dir = data_obj["base_dir"].toString();
QString permitted_exts = data_obj["permitted_exts"].toString();
result_t r = webview_handler->rktFileOpen(wv,
title.toUtf8().constData(),
base_dir.toUtf8().constData(),
permitted_exts.toUtf8().constData()
);
result_queue->enqueue(r);
}
break;
case CMD_FILE_SAVE: {
int wv = data_obj["wv"].toInt();
QString title = data_obj["title"].toString();
QString base_dir = data_obj["base_dir"].toString();
QString permitted_exts = data_obj["permitted_exts"].toString();
result_t r = webview_handler->rktFileSave(wv,
title.toUtf8().constData(),
base_dir.toUtf8().constData(),
permitted_exts.toUtf8().constData()
);
result_queue->enqueue(r);
}
break;
case CMD_SET_OU_TOKEN: {
int wv = data_obj["wv"].toInt();
QString token = data_obj["token"].toString();
webview_handler->rktSetOUToken(wv, token.toUtf8().constData());
}
break;
case CMD_MSG_BOX: {
int wv = data_obj["wv"].toInt();
QString title = data_obj["title"].toString();
QString message = data_obj["message"].toString();
QString submsg = data_obj["submessage"].toString();
int type = data_obj["type"].toInt();
result_t r = webview_handler->rktMessageBox(wv,
title.toUtf8().constData(),
message.toUtf8().constData(),
submsg.toUtf8().constData(),
static_cast<rkt_messagetype_t>(type)
);
result_queue->enqueue(r);
}
break;
default: {
ERROR1("Unknown command: %d\n", cmd);
}
}
}
DEBUG0("Exiting handler thread\n");
}
void Alive::run()
{
int wait_ms = 10 * 1000;
int ping_no;
std::string data;
bool go_on = true;
while (go_on) {
bool something_came = alive_queue->dequeue(ping_no, data, wait_ms);
if (!something_came) {
ERROR0("No alive message received, stopping alive loop and quitting rktwebview_prg\n");
handler->quit = true;
handler->command_queue->enqueue(CMD_NOOP);
alive_timeout = true;
go_on = false;
} else {
if (ping_no == CMD_ALIVE_QUIT) {
DEBUG0("Got Quit ping for alive thread\n");
go_on = false;
} else {
DEBUG1("Got alive ping: %d\n", ping_no);
alive_ack_queue->enqueue(ping_no);
}
}
}
DEBUG0("Exiting alive thread\n");
}