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