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.
258 lines
7.6 KiB
258 lines
7.6 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; |
|
static gui_connect_result_fn g_connect_result_cb = 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); |
|
if (g_connect_result_cb) g_connect_result_cb(nodeId, result); |
|
} |
|
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); |
|
} |
|
|
|
void gui_bridge_set_connect_result_cb(gui_connect_result_fn cb) { |
|
g_connect_result_cb = cb; |
|
} |
|
|
|
} /* extern "C" */ |
|
|
|
#include "gui_bridge_impl.moc"
|
|
|