You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

252 lines
7.3 KiB

#include "gui_bridge.h"
#include <QObject>
#include <QMetaObject>
#include <QByteArray>
#include <QString>
#include <cstring>
#include "../db/db_manager.h"
extern "C" {
#include "../../../lib/u_async.h"
#include "../../../lib/mem.h"
}
/* ── Внутренний объект-приёмник в GUI-потоке ── */
class GuiBridgeReceiver : public QObject {
Q_OBJECT
public:
void* ua = nullptr;
gui_result_fn callback = nullptr;
DbManager* db = nullptr;
GuiBridgeReceiver(QObject* parent = nullptr) : QObject(parent) {}
Q_INVOKABLE void processCall(int op, QByteArray packedReq);
Q_INVOKABLE void processPost(int eventType, QByteArray data);
};
static GuiBridgeReceiver* g_receiver = nullptr;
/* ── Трамплин для uasync_post: распаковывает результат и вызывает оригинальный callback ── */
struct TrampolineCtx {
gui_result_fn cb;
void* userdata;
int op;
int result_len;
/* result data follows after this struct in memory */
};
static void bridge_trampoline(void* arg) {
uint8_t* packed = (uint8_t*)arg;
TrampolineCtx ctx;
memcpy(&ctx, packed, sizeof(ctx));
const uint8_t* result = packed + sizeof(ctx);
if (ctx.cb) ctx.cb(ctx.userdata, ctx.op, result, ctx.result_len);
u_free(packed);
}
static void post_result_to_uasync(int op, const QByteArray& result,
void* userdata, gui_result_fn cb) {
int res_len = result.size();
int total = (int)sizeof(TrampolineCtx) + res_len;
uint8_t* buf = (uint8_t*)u_malloc(total);
if (!buf) return;
TrampolineCtx ctx;
ctx.cb = cb;
ctx.userdata = userdata;
ctx.op = op;
ctx.result_len = res_len;
memcpy(buf, &ctx, sizeof(ctx));
if (res_len > 0) memcpy(buf + sizeof(ctx), result.constData(), res_len);
uasync_post((struct UASYNC*)g_receiver->ua, bridge_trampoline, buf);
}
/* ── GuiBridgeReceiver implementation ── */
static QString readChId(const uint8_t*& r, int& rem) {
if (rem < 1) return {};
uint8_t len = r[0]; r++; rem--;
if (rem < len) return {};
QString s = QString::fromUtf8((const char*)r, len);
r += len; rem -= len;
return s;
}
void GuiBridgeReceiver::processCall(int op, QByteArray packedReq) {
if (!db || packedReq.size() < (int)(sizeof(gui_result_fn) + sizeof(void*))) return;
gui_result_fn cb;
void* userdata;
memcpy(&cb, packedReq.constData(), sizeof(cb));
memcpy(&userdata, packedReq.constData() + sizeof(cb), sizeof(userdata));
const uint8_t* r = (const uint8_t*)packedReq.constData() + sizeof(cb) + sizeof(userdata);
int rem = packedReq.size() - (int)(sizeof(cb) + sizeof(userdata));
QByteArray result;
switch (op) {
case GUI_OP_LIST_CHANNELS:
result = db->syncListChannels();
break;
case GUI_OP_COUNT: {
QString chId = readChId(r, rem);
if (!chId.isEmpty()) result = db->syncCount(chId);
break;
}
case GUI_OP_CHAIN_HASH_AT: {
QString chId = readChId(r, rem);
if (!chId.isEmpty() && rem >= 4) {
uint32_t pos; memcpy(&pos, r, 4);
result = db->syncChainHashAt(chId, pos);
}
break;
}
case GUI_OP_INSERT_RECORD: {
QString chId = readChId(r, rem);
if (!chId.isEmpty() && rem > 0) {
QByteArray rec((const char*)r, rem);
result = db->syncInsert(chId, rec);
}
break;
}
case GUI_OP_CURSOR_OPEN: {
QString chId = readChId(r, rem);
if (!chId.isEmpty()) result = db->syncCursorOpen(chId);
break;
}
case GUI_OP_CURSOR_NEXT:
if (rem >= 4) {
uint32_t curId; memcpy(&curId, r, 4);
result = db->syncCursorNext(curId);
}
break;
case GUI_OP_CURSOR_CLOSE:
if (rem >= 4) {
uint32_t curId; memcpy(&curId, r, 4);
db->syncCursorClose(curId);
}
break;
case GUI_OP_MARK_SENT: {
QString chId = readChId(r, rem);
if (!chId.isEmpty() && rem >= 24) {
uint64_t ts, dh, myId;
memcpy(&ts, r, 8); memcpy(&dh, r + 8, 8); memcpy(&myId, r + 16, 8);
result = db->syncMarkSent(chId, ts, dh, myId);
}
break;
}
case GUI_OP_TTL_DELETE: {
QString chId = readChId(r, rem);
if (!chId.isEmpty() && rem >= 16) {
uint64_t myId, cutoff;
memcpy(&myId, r, 8); memcpy(&cutoff, r + 8, 8);
result = db->syncTtlDelete(chId, myId, cutoff);
}
break;
}
case GUI_OP_LIST_PEERS: {
QString chId = readChId(r, rem);
if (!chId.isEmpty()) result = db->syncListPeers(chId);
break;
}
case GUI_OP_LOAD_NODEINFO:
if (rem >= 8) {
uint64_t nodeId; memcpy(&nodeId, r, 8);
result = db->loadNodeInfo(nodeId);
}
break;
default:
break;
}
post_result_to_uasync(op, result, userdata, cb);
}
void GuiBridgeReceiver::processPost(int eventType, QByteArray data) {
if (!db) return;
const uint8_t* d = (const uint8_t*)data.constData();
int dlen = data.size();
switch (eventType) {
case GUI_EVT_MSG_RECEIVED:
if (dlen >= 2) {
uint8_t chLen = d[0];
if (dlen >= 1 + chLen) {
QString chId = QString::fromUtf8((const char*)d + 1, chLen);
db->onSyncMessageReceived(chId);
}
}
break;
case GUI_EVT_CONNECT_RESULT:
if (dlen >= 12) {
uint64_t nodeId; int result;
memcpy(&nodeId, d, 8); memcpy(&result, d + 8, 4);
db->setNodeOnline(nodeId, result == 0);
}
break;
case GUI_EVT_NEW_PEER:
case GUI_EVT_CHANNEL_UPDATED:
default:
break;
}
}
void* gui_bridge_get_db() { return g_receiver ? (void*)g_receiver->db : nullptr; }
/* ── C API ── */
extern "C" {
void gui_bridge_init(struct UASYNC* ua, gui_result_fn callback, void* gui_target) {
g_receiver = new GuiBridgeReceiver((QObject*)gui_target);
g_receiver->ua = ua;
g_receiver->callback = callback;
}
void gui_bridge_set_db(void* db_ptr) {
if (g_receiver) g_receiver->db = (DbManager*)db_ptr;
}
void gui_bridge_call(int op, const uint8_t* request, int req_len,
void* userdata, gui_result_fn callback) {
if (!g_receiver) return;
/* Пакуем cb и userdata в начало, потом request */
QByteArray packed;
packed.append((const char*)&callback, sizeof(callback));
packed.append((const char*)&userdata, sizeof(userdata));
if (req_len > 0) packed.append((const char*)request, req_len);
QMetaObject::invokeMethod(g_receiver, "processCall",
Qt::QueuedConnection,
Q_ARG(int, op),
Q_ARG(QByteArray, packed));
}
void gui_bridge_post(int event_type, const uint8_t* data, int data_len) {
if (!g_receiver) return;
QByteArray d;
if (data_len > 0) d = QByteArray((const char*)data, data_len);
QMetaObject::invokeMethod(g_receiver, "processPost",
Qt::QueuedConnection,
Q_ARG(int, event_type),
Q_ARG(QByteArray, d));
}
void gui_bridge_post_uasync(struct UASYNC* ua, void (*fn)(void*), void* arg) {
if (ua && fn) uasync_post(ua, fn, arg);
}
} /* extern "C" */
#include "gui_bridge_impl.moc"