#include "gui_bridge.h" #include #include #include #include #include #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"