From 3e89d8118e510edb39b53264112a3c2c402435a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=88=9A=28noham=29=C2=B2?= <100566912+NohamR@users.noreply.github.com> Date: Sat, 6 Dec 2025 17:39:06 +0100 Subject: [PATCH] Add MessageBroker for QML and native communication Introduces MessageBroker to enable communication between the dylib and QML via signals. Updates CMakeLists.txt to include Qml components and conditionally add MessageBroker sources in qmlrebuild mode. reMarkable.m is updated to register the QML type, set up native callbacks, and demonstrate broadcasting signals. --- CMakeLists.txt | 18 ++++++++--- src/reMarkable/reMarkable.m | 17 ++++++++++ src/utils/MessageBroker.h | 63 +++++++++++++++++++++++++++++++++++++ src/utils/MessageBroker.mm | 58 ++++++++++++++++++++++++++++++++++ 4 files changed, 151 insertions(+), 5 deletions(-) create mode 100644 src/utils/MessageBroker.h create mode 100644 src/utils/MessageBroker.mm diff --git a/CMakeLists.txt b/CMakeLists.txt index d930f45..862a1ac 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,13 +57,13 @@ foreach(_qt_root ${_qt_candidate_roots}) endif() endforeach() -find_package(Qt6 COMPONENTS Core Network WebSockets QUIET) +find_package(Qt6 COMPONENTS Core Network WebSockets Qml QUIET) if(Qt6_FOUND) - set(QT_LIB_TARGETS Qt6::Core Qt6::Network Qt6::WebSockets) + set(QT_LIB_TARGETS Qt6::Core Qt6::Network Qt6::WebSockets Qt6::Qml) else() - find_package(Qt5 COMPONENTS Core Network WebSockets QUIET) + find_package(Qt5 COMPONENTS Core Network WebSockets Qml QUIET) if(Qt5_FOUND) - set(QT_LIB_TARGETS Qt5::Core Qt5::Network Qt5::WebSockets) + set(QT_LIB_TARGETS Qt5::Core Qt5::Network Qt5::WebSockets Qt5::Qml) endif() endif() @@ -105,7 +105,7 @@ set_target_properties(reMarkable PROPERTIES add_definitions(-DQT_NO_VERSION_TAGGING) -# Add build mode compile definitions +# Add build mode compile definitions and conditionally add sources if(BUILD_MODE_RMFAKECLOUD) target_compile_definitions(reMarkable PRIVATE BUILD_MODE_RMFAKECLOUD=1) message(STATUS "Build mode: rmfakecloud (cloud redirection)") @@ -113,6 +113,14 @@ endif() if(BUILD_MODE_QMLREBUILD) target_compile_definitions(reMarkable PRIVATE BUILD_MODE_QMLREBUILD=1) + + # Enable Qt MOC for MessageBroker + set_target_properties(reMarkable PROPERTIES AUTOMOC ON) + + # Add MessageBroker source (needs MOC processing) + target_sources(reMarkable PRIVATE + ${PROJECT_ROOT_DIR}/src/utils/MessageBroker.mm + ) message(STATUS "Build mode: qmlrebuild (resource hooking)") endif() diff --git a/src/reMarkable/reMarkable.m b/src/reMarkable/reMarkable.m index 24c8dae..b730f7d 100644 --- a/src/reMarkable/reMarkable.m +++ b/src/reMarkable/reMarkable.m @@ -7,6 +7,9 @@ #ifdef BUILD_MODE_DEV #import "DevHooks.h" #endif +#ifdef BUILD_MODE_QMLREBUILD +#import "MessageBroker.h" +#endif #import #import #include @@ -302,11 +305,25 @@ static inline bool shouldPatchURL(const QString &host) { #ifdef BUILD_MODE_QMLREBUILD NSLogger(@"[reMarkable] Build mode: qmlrebuild"); + + // Register MessageBroker QML type for dylib <-> QML communication + messagebroker::registerQmlType(); + + // Register native callback to receive signals from QML + messagebroker::setNativeCallback([](const char *signal, const char *value) { + NSLogger(@"[reMarkable] Native callback received signal '%s' with value '%s'", signal, value); + }); + [MemoryUtils hookSymbol:@"QtCore" symbolName:@"__Z21qRegisterResourceDataiPKhS0_S0_" hookFunction:(void *)hooked_qRegisterResourceData originalFunction:(void **)&original_qRegisterResourceData logPrefix:@"[reMarkable]"]; + + // Send a delayed broadcast to QML (after UI has loaded) + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + messagebroker::broadcast("signalName", "Hello from dylib!"); + }); #endif #ifdef BUILD_MODE_DEV diff --git a/src/utils/MessageBroker.h b/src/utils/MessageBroker.h new file mode 100644 index 0000000..de7fbe2 --- /dev/null +++ b/src/utils/MessageBroker.h @@ -0,0 +1,63 @@ +// Credits: asivery/rm-xovi-extensions +// (https://github.com/asivery/rm-xovi-extensions/blob/master/xovi-message-broker/src/XoviMessageBroker.h) +// Simplified for RMHook dylib <-> QML communication + +#pragma once + +#include +#include +#include +#include +#include + +// Forward declaration +class MessageBroker; + +// Native callback type for C++ listeners +typedef void (*NativeSignalCallback)(const char *signal, const char *value); + +namespace messagebroker { + void addBroadcastListener(MessageBroker *ref); + void removeBroadcastListener(MessageBroker *ref); + void broadcast(const char *signal, const char *value); + void registerQmlType(); + + // Register a native C++ callback to receive all signals + void setNativeCallback(NativeSignalCallback callback); +} + +class MessageBroker : public QObject +{ + Q_OBJECT + Q_PROPERTY(QStringList listeningFor READ getListeningFor WRITE setListeningFor) + +public: + explicit MessageBroker(QObject *parent = nullptr) : QObject(parent) { + messagebroker::addBroadcastListener(this); + } + + ~MessageBroker() { + messagebroker::removeBroadcastListener(this); + } + + // Send a signal from QML to all listeners (including C++ side) + Q_INVOKABLE void sendSignal(const QString &signal, const QString &message) { + QByteArray signalUtf8 = signal.toUtf8(); + QByteArray messageUtf8 = message.toUtf8(); + messagebroker::broadcast(signalUtf8.constData(), messageUtf8.constData()); + } + + void setListeningFor(const QStringList &l) { + _listeningFor = l; + } + + const QStringList& getListeningFor() const { + return _listeningFor; + } + +signals: + void signalReceived(const QString &signal, const QString &message); + +private: + QStringList _listeningFor; +}; \ No newline at end of file diff --git a/src/utils/MessageBroker.mm b/src/utils/MessageBroker.mm new file mode 100644 index 0000000..7b9e377 --- /dev/null +++ b/src/utils/MessageBroker.mm @@ -0,0 +1,58 @@ +// Credits: asivery/rm-xovi-extensions +// (https://github.com/asivery/rm-xovi-extensions/blob/master/xovi-message-broker/src/XoviMessageBroker.h) + +#import +#include "MessageBroker.h" +#include "Logger.h" +#include +#include +#include + +static std::vector brokers; +static NativeSignalCallback nativeCallback = nullptr; + +void messagebroker::setNativeCallback(NativeSignalCallback callback) { + nativeCallback = callback; + NSLogger(@"[MessageBroker] Native callback registered"); +} + +void messagebroker::addBroadcastListener(MessageBroker *ref) { + // Cannot have more than one. + if(std::find(brokers.begin(), brokers.end(), ref) == brokers.end()) { + brokers.push_back(ref); + NSLogger(@"[MessageBroker] Added broadcast listener, total: %zu", brokers.size()); + } +} + +void messagebroker::removeBroadcastListener(MessageBroker *ref) { + std::vector::iterator iter; + if((iter = std::find(brokers.begin(), brokers.end(), ref)) != brokers.end()) { + brokers.erase(iter); + NSLogger(@"[MessageBroker] Removed broadcast listener, remaining: %zu", brokers.size()); + } +} + +void messagebroker::broadcast(const char *signal, const char *value) { + QString qSignal(signal), qValue(value); + NSLogger(@"[MessageBroker] Broadcasting signal '%s' with value '%s'", signal, value); + + // Call native C++ callback if registered + if (nativeCallback) { + nativeCallback(signal, value); + } + + // Notify QML listeners + for(auto &ref : brokers) { + if(ref->getListeningFor().contains(qSignal)) { + emit ref->signalReceived(qSignal, qValue); + } + } +} + +void messagebroker::registerQmlType() { + qmlRegisterType("net.noham.MessageBroker", 1, 0, "MessageBroker"); + NSLogger(@"[MessageBroker] Registered QML type net.noham.MessageBroker"); +} + +// Include MOC output for MessageBroker class (generated by Qt's Meta-Object Compiler) +#include "moc_MessageBroker.cpp" \ No newline at end of file