diff --git a/data/node-lib-qt-rss.js b/data/node-lib-qt-rss.js index 6dcee0f..11a5a07 100644 --- a/data/node-lib-qt-rss.js +++ b/data/node-lib-qt-rss.js @@ -3,43 +3,40 @@ var request = require('request'); // for fetching the feed var emitRequest = function () { console.log("Refreshing feeds...") - var feedparser = new FeedParser([]); + var feedparser = new FeedParser([]); var req = request('http://feeds.bbci.co.uk/news/world/rss.xml') req.on('error', function (error) { - // handle any request errors + // catch all request errors but don't handle them in this demo }); req.on('response', function (res) { var stream = this; // `this` is `req`, which is a stream - + if (res.statusCode !== 200) { this.emit('error', new Error('Bad status code')); } else { - cppDemoModule.clearFeed(); + cppQtGui.clearFeed(); stream.pipe(feedparser); } }); - + feedparser.on('error', function (error) { - // catch all parser errors but don't handle them + // catch all parser errors but don't handle them in this demo }); - + feedparser.on('readable', function () { - // This is where the action is! var stream = this; // `this` is `feedparser`, which is a stream - var meta = this.meta; // **NOTE** the "meta" is always available in the context of the feedparser instance - var item; - - var itemString = ''; - while (item = stream.read()) { - itemString = itemString + item['title'] + '\n' + item['description']; + var item = stream.read(); + + if (item && item['title']) { + var itemString = item['title'] + '\n' + item['description']; + cppQtGui.addFeedItem( { item: itemString } ); } - cppDemoModule.cppLog({itemTitle: itemString}); }); feedparser.on('end', function (){ - cppDemoModule.redrawGUI(); + cppQtGui.redraw(); }); } diff --git a/data/node-qt-rss.js b/data/node-qt-rss.js index 628b8cd..06c878a 100644 --- a/data/node-qt-rss.js +++ b/data/node-qt-rss.js @@ -1,54 +1,47 @@ -var cppDemoModule = process.binding('CppDemoModule'); +var cppQtGui = process.binding('cpp-qt-gui'); var FeedParser = require('feedparser'); var request = require('request'); // for fetching the feed -var killMe = function () { - process.exit(); -} - -cppDemoModule.registerExitFunc(killMe); +cppQtGui.registerExitFunc(process.exit); var emitRequest = function () { console.log("Refreshing feeds...") - var feedparser = new FeedParser([]); + var feedparser = new FeedParser([]); var req = request('http://feeds.bbci.co.uk/news/world/rss.xml') req.on('error', function (error) { - // handle any request errors + // catch all request errors but don't handle them in this demo }); req.on('response', function (res) { var stream = this; // `this` is `req`, which is a stream - + if (res.statusCode !== 200) { this.emit('error', new Error('Bad status code')); } else { - cppDemoModule.clearFeed(); + cppQtGui.clearFeed(); stream.pipe(feedparser); } }); - + feedparser.on('error', function (error) { - // catch all parser errors but don't handle them + // catch all parser errors but don't handle them in this demo }); - + feedparser.on('readable', function () { - // This is where the action is! var stream = this; // `this` is `feedparser`, which is a stream - var meta = this.meta; // **NOTE** the "meta" is always available in the context of the feedparser instance - var item; - - var itemString = ''; - while (item = stream.read()) { - itemString = itemString + item['title'] + '\n' + item['description']; + var item = stream.read(); + + if (item && item['title']) { + var itemString = item['title'] + '\n' + item['description']; + cppQtGui.addFeedItem( { item: itemString } ); } - cppDemoModule.cppLog({itemTitle: itemString}); }); feedparser.on('end', function (){ - cppDemoModule.redrawGUI(); + cppQtGui.redraw(); setTimeout(emitRequest, 3000); }); } @@ -56,7 +49,7 @@ var emitRequest = function () { emitRequest(); function processQtEvents() { - cppDemoModule.processQtEvents() + cppQtGui.processQtEvents() setTimeout(processQtEvents, 20); } diff --git a/source/node-lib-qt-rss/RssFeed.cpp b/source/node-lib-qt-rss/RssFeed.cpp index dedd772..fe022a1 100644 --- a/source/node-lib-qt-rss/RssFeed.cpp +++ b/source/node-lib-qt-rss/RssFeed.cpp @@ -7,54 +7,63 @@ RssFeed* RssFeed::instance = nullptr; RssFeed::RssFeed(QObject* parent) - : QObject(parent) + : QObject(parent) { - + } RssFeed& RssFeed::getInstance(){ - if (instance == nullptr){ - instance = new RssFeed(); - } - return *instance; + if (instance == nullptr){ + instance = new RssFeed(); + } + return *instance; } QStringList RssFeed::getEntries() const { - return entries; + return entries; } void RssFeed::clearFeed(const v8::FunctionCallbackInfo& args) { - getInstance().entries.clear(); + getInstance().entries.clear(); } void RssFeed::redrawGUI(const v8::FunctionCallbackInfo& args) { - emit getInstance().entriesChanged(); + emit getInstance().entriesChanged(); } void RssFeed::refreshFeed() { - node::Evaluate("emitRequest()"); - node::RunEventLoop([](){ QGuiApplication::processEvents(); }); + // invoke the embedded JavaScript in order to fetch new RSS feeds: + node::Evaluate("emitRequest()"); + + // wait for the embedded JavaScript to finish its execution + // meanwhile, process any QT events + node::RunEventLoop([](){ QGuiApplication::processEvents(); }); } -void RssFeed::cppLog(const v8::FunctionCallbackInfo& args){ - v8::Isolate* isolate = args.GetIsolate(); - if (args.Length() < 1 || !args[0]->IsObject()) { - isolate->ThrowException(v8::Exception::TypeError( - v8::String::NewFromUtf8(isolate, "Error: One object expected"))); - return; - } - - v8::Local context = isolate->GetCurrentContext(); - v8::Local obj = args[0]->ToObject(context).ToLocalChecked(); - v8::Local props = obj->GetOwnPropertyNames(context).ToLocalChecked(); - //getInstance().entries.clear(); - - - for (int i = 0, l = props->Length(); i < l; i++) { - v8::Local localKey = props->Get(i); - v8::Local localVal = obj->Get(context, localKey).ToLocalChecked(); - std::string key = *v8::String::Utf8Value(localKey); - std::string val = *v8::String::Utf8Value(localVal); - getInstance().entries << QString::fromStdString(val); - } +void RssFeed::addFeedItem(const v8::FunctionCallbackInfo& args) { + // check, whether this method was called as expected + // therefore, we need to make sure, that the first argument exists + // and that it is an object + v8::Isolate* isolate = args.GetIsolate(); + if (args.Length() < 1 || !args[0]->IsObject()) { + isolate->ThrowException(v8::Exception::TypeError( + v8::String::NewFromUtf8(isolate, "Error: One object expected"))); + return; + } + + v8::Local context = isolate->GetCurrentContext(); + v8::Local obj = args[0]->ToObject(context).ToLocalChecked(); + + // we want to get all properties of our input object: + v8::Local props = obj->GetOwnPropertyNames(context).ToLocalChecked(); + + for (int i = 0, l = props->Length(); i < l; i++) { + v8::Local localKey = props->Get(i); + v8::Local localVal = obj->Get(context, localKey).ToLocalChecked(); + std::string key = *v8::String::Utf8Value(localKey); + std::string val = *v8::String::Utf8Value(localVal); + + // append the RSS feed body to the list of RSS feeds: + getInstance().entries << QString::fromStdString(val); + } } diff --git a/source/node-lib-qt-rss/RssFeed.h b/source/node-lib-qt-rss/RssFeed.h index 4311d03..00a1be0 100644 --- a/source/node-lib-qt-rss/RssFeed.h +++ b/source/node-lib-qt-rss/RssFeed.h @@ -5,35 +5,79 @@ #include "node.h" /** - * @brief The RssFeed class retrieves an RSS feed from the Internet and provides its entries. + * @brief The RssFeed class retrieves an RSS feed from the Internet and + * provides its entries. */ class RssFeed : public QObject { - Q_OBJECT + Q_OBJECT - Q_PROPERTY(QStringList entries READ getEntries NOTIFY entriesChanged) + Q_PROPERTY(QStringList entries READ getEntries NOTIFY entriesChanged) + +private: + /** + * @brief Creates a new RssFeed with a given QObject as its parent. + * + * @param parent The parent object. + */ + explicit RssFeed(QObject* parent=nullptr); public: - explicit RssFeed(QObject* parent=nullptr); - static void clearFeed(const v8::FunctionCallbackInfo& args); - static void cppLog(const v8::FunctionCallbackInfo& args); - static RssFeed& getInstance(); - static void redrawGUI(const v8::FunctionCallbackInfo& args); - Q_INVOKABLE static void refreshFeed(); + /** + * @brief Returns the singleton instance for this class. + * + * @return The singlton instance for this class. + */ + static RssFeed& getInstance(); + + /** + * @brief This method is called from the embedded JavaScript. + * It is used for deleting all items from this RSS feed. + * + * @param args The arguments passed from the embedded JavaScript. + * Hint: This method does not expect any arguments. + */ + static void clearFeed(const v8::FunctionCallbackInfo& args); + + /** + * @brief This method is called from the embedded JavaScript. + * It is used to add an entry to this RSS feed. + * + * @param args The arguments passed from the embedded JavaScript. + * Hint: This method expects an object, which contains the RSS feed item. + */ + static void addFeedItem(const v8::FunctionCallbackInfo& args); + + /** + * @brief This method is called from the embedded JavaScript. + * It is used to refresh the GUI, after all retrieved feed items have been + * appended. + * + * @param args The arguments passed from the embedded JavaScript. + * Hint: This method does not expect any arguments. + */ + static void redrawGUI(const v8::FunctionCallbackInfo& args); + + /** + * @brief This method updates the feed by calling a JS function + * and running the Node.js main loop until the whole feed was received. + */ + Q_INVOKABLE static void refreshFeed(); private: - static RssFeed* instance; - QStringList entries; + static RssFeed* instance; /*!< The singleton instance for this class. */ + QStringList entries; /*!< The list of RSS feeds to display. */ signals: - void entriesChanged(); + void entriesChanged(); /*!< Emitted after entries were added or removed. */ public slots: - /** - * @brief getEntries returns the entries of the RSS feed - * @return a list of text entries - */ - QStringList getEntries() const; + /** + * @brief getEntries returns the entries of the RSS feed + * + * @return a list of text entries + */ + QStringList getEntries() const; }; #endif // RSSFEED_H diff --git a/source/node-lib-qt-rss/main.cpp b/source/node-lib-qt-rss/main.cpp index 1928af2..15b16b6 100644 --- a/source/node-lib-qt-rss/main.cpp +++ b/source/node-lib-qt-rss/main.cpp @@ -1,12 +1,11 @@ +#include #include #include -#include #include #include #include #include -#include #include @@ -16,37 +15,51 @@ #include "RssFeed.h" int main(int argc, char* argv[]) { - const std::string js_file = "data/node-lib-qt-rss.js"; - const std::string data_path = cpplocate::locatePath(js_file); - if (data_path.empty()) { - std::cerr << "Could not find data path." << std::endl; - return 1; - } - const std::string js_path = data_path + "/" + js_file; - - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QGuiApplication app(argc, argv); - - QQmlApplicationEngine engine; - - // to be able to access the public slots of the RssFeed instance - // we inject a pointer to it in the QML context: - engine.rootContext()->setContextProperty("rssFeed", &RssFeed::getInstance()); - - engine.load(QUrl(QLatin1String("qrc:/main.qml"))); - if (engine.rootObjects().isEmpty()) { - qWarning() << "Error while loading QML file"; - return -1; - } - - node::Initialize(); - node::RegisterModule("cpp-demo-module", { - {"cppLog", RssFeed::cppLog}, - {"clearFeed", RssFeed::clearFeed}, - {"redrawGUI", RssFeed::redrawGUI}, - }, "cppDemoModule"); - node::Run(js_path); - RssFeed::refreshFeed(); - app.exec(); - node::Deinitialize(); + // Locate the JavaScript file we want to embed: + const std::string js_file = "data/node-lib-qt-rss.js"; + const std::string data_path = cpplocate::locatePath(js_file); + if (data_path.empty()) { + qWarning() << "Could not find data path."; + return 1; + } + const std::string js_path = data_path + "/" + js_file; + + // Initialize Qt: + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication app(argc, argv); + QQmlApplicationEngine engine; + + // to be able to access the public slots of the RssFeed instance + // we inject a pointer to it in the QML context: + engine.rootContext()->setContextProperty("rssFeed", &RssFeed::getInstance()); + + engine.load(QUrl(QLatin1String("qrc:/main.qml"))); + if (engine.rootObjects().isEmpty()) { + qWarning() << "Error while loading QML file"; + return 1; + } + + // Initialize Node.js engine: + node::Initialize(); + + // Register C++ methods to be used within JavaScript + // The third parameter binds the C++ module to that name, + // allowing the functions to be called like this: "cppQtGui.clearFeed(...)" + node::RegisterModule("cpp-qt-gui", { + {"addFeedItem", RssFeed::addFeedItem}, + {"clearFeed", RssFeed::clearFeed}, + {"redraw", RssFeed::redrawGUI}, + }, "cppQtGui"); + + // Evaluate the JavaScript file once: + node::Run(js_path); + + // Load intial RSS feed to display it: + RssFeed::refreshFeed(); + + // Run the Qt application: + app.exec(); + + // After we are done, deinitialize the Node.js engine: + node::Deinitialize(); } diff --git a/source/node-qt-rss/RssFeed.cpp b/source/node-qt-rss/RssFeed.cpp index cca1c6b..e38922e 100644 --- a/source/node-qt-rss/RssFeed.cpp +++ b/source/node-qt-rss/RssFeed.cpp @@ -1,52 +1,60 @@ #include "RssFeed.h" + #include +#include +#include "node_lib.h" RssFeed* RssFeed::instance = nullptr; RssFeed::RssFeed(QObject* parent) - : QObject(parent) + : QObject(parent) { - + } RssFeed& RssFeed::getInstance(){ - if (instance == nullptr){ - instance = new RssFeed(); - } - return *instance; + if (instance == nullptr){ + instance = new RssFeed(); + } + return *instance; } QStringList RssFeed::getEntries() const { - return entries; + return entries; } void RssFeed::clearFeed(const v8::FunctionCallbackInfo& args) { - getInstance().entries.clear(); + getInstance().entries.clear(); } void RssFeed::redrawGUI(const v8::FunctionCallbackInfo& args) { - emit getInstance().entriesChanged(); + emit getInstance().entriesChanged(); } -void RssFeed::cppLog(const v8::FunctionCallbackInfo& args){ - v8::Isolate* isolate = args.GetIsolate(); - if(args.Length() < 1 || !args[0]->IsObject()) { - isolate->ThrowException(v8::Exception::TypeError( - v8::String::NewFromUtf8(isolate, "Error: One object expected"))); - return; - } - - v8::Local context = isolate->GetCurrentContext(); - v8::Local obj = args[0]->ToObject(context).ToLocalChecked(); - v8::Local props = obj->GetOwnPropertyNames(context).ToLocalChecked(); - //getInstance().entries.clear(); - - - for(int i = 0, l = props->Length(); i < l; i++) { - v8::Local localKey = props->Get(i); - v8::Local localVal = obj->Get(context, localKey).ToLocalChecked(); - std::string key = *v8::String::Utf8Value(localKey); - std::string val = *v8::String::Utf8Value(localVal); - getInstance().entries << QString::fromStdString(val); - } -} \ No newline at end of file +void RssFeed::addFeedItem(const v8::FunctionCallbackInfo& args) { + // check, whether this method was called as expected + // therefore, we need to make sure, that the first argument exists + // and that it is an object + v8::Isolate* isolate = args.GetIsolate(); + if (args.Length() < 1 || !args[0]->IsObject()) { + isolate->ThrowException(v8::Exception::TypeError( + v8::String::NewFromUtf8(isolate, "Error: One object expected"))); + return; + } + + v8::Local context = isolate->GetCurrentContext(); + v8::Local obj = args[0]->ToObject(context).ToLocalChecked(); + + // we want to get all properties of our input object: + v8::Local props = obj->GetOwnPropertyNames(context).ToLocalChecked(); + + for (int i = 0, l = props->Length(); i < l; i++) { + v8::Local localKey = props->Get(i); + v8::Local localVal = obj->Get(context, localKey).ToLocalChecked(); + std::string key = *v8::String::Utf8Value(localKey); + std::string val = *v8::String::Utf8Value(localVal); + + // append the RSS feed body to the list of RSS feeds: + getInstance().entries << QString::fromStdString(val); + } +} diff --git a/source/node-qt-rss/RssFeed.h b/source/node-qt-rss/RssFeed.h index 2ced1f5..7d9be49 100644 --- a/source/node-qt-rss/RssFeed.h +++ b/source/node-qt-rss/RssFeed.h @@ -5,34 +5,73 @@ #include "node.h" /** - * @brief The RssFeed class retrieves an RSS feed from the Internet and provides its entries. + * @brief The RssFeed class retrieves an RSS feed from the Internet and + * provides its entries. */ class RssFeed : public QObject { - Q_OBJECT + Q_OBJECT - Q_PROPERTY(QStringList entries READ getEntries NOTIFY entriesChanged) + Q_PROPERTY(QStringList entries READ getEntries NOTIFY entriesChanged) + +private: + /** + * @brief Creates a new RssFeed with a given QObject as its parent. + * + * @param parent The parent object. + */ + explicit RssFeed(QObject* parent=nullptr); public: - explicit RssFeed(QObject* parent=nullptr); - static void clearFeed(const v8::FunctionCallbackInfo& args); - static void cppLog(const v8::FunctionCallbackInfo& args); - static RssFeed& getInstance(); - static void redrawGUI(const v8::FunctionCallbackInfo& args); + /** + * @brief Returns the singleton instance for this class. + * + * @return The singlton instance for this class. + */ + static RssFeed& getInstance(); + + /** + * @brief This method is called from the embedded JavaScript. + * It is used for deleting all items from this RSS feed. + * + * @param args The arguments passed from the embedded JavaScript. + * Hint: This method does not expect any arguments. + */ + static void clearFeed(const v8::FunctionCallbackInfo& args); + + /** + * @brief This method is called from the embedded JavaScript. + * It is used to add an entry to this RSS feed. + * + * @param args The arguments passed from the embedded JavaScript. + * Hint: This method expects an object, which contains the RSS feed item. + */ + static void addFeedItem(const v8::FunctionCallbackInfo& args); + + /** + * @brief This method is called from the embedded JavaScript. + * It is used to refresh the GUI, after all retrieved feed items have been + * appended. + * + * @param args The arguments passed from the embedded JavaScript. + * Hint: This method does not expect any arguments. + */ + static void redrawGUI(const v8::FunctionCallbackInfo& args); private: - static RssFeed* instance; - QStringList entries; + static RssFeed* instance; /*!< The singleton instance for this class. */ + QStringList entries; /*!< The list of RSS feeds to display. */ signals: - void entriesChanged(); + void entriesChanged(); /*!< Emitted after entries were added or removed. */ public slots: - /** - * @brief getEntries returns the entries of the RSS feed - * @return a list of text entries - */ - QStringList getEntries() const; + /** + * @brief getEntries returns the entries of the RSS feed + * + * @return a list of text entries + */ + QStringList getEntries() const; }; #endif // RSSFEED_H diff --git a/source/node-qt-rss/main.cpp b/source/node-qt-rss/main.cpp index b5851f0..3f648db 100644 --- a/source/node-qt-rss/main.cpp +++ b/source/node-qt-rss/main.cpp @@ -1,14 +1,12 @@ #include -#include #include #include +#include #include #include #include #include -#include -#include #include @@ -25,98 +23,98 @@ struct AsyncData { AsyncData* jsExitData = nullptr; void execAsyncCall(uv_async_t* req) { - AsyncData* data = static_cast(req->data); - v8::Isolate* isolate = v8::Isolate::GetCurrent(); - v8::HandleScope scope(isolate); - v8::Handle argv[] = { v8::Array::New(isolate) }; - v8::Local::New(isolate, data->callback)-> - Call(isolate->GetCurrentContext()->Global(), 1, argv); - data->callback.Reset(); - delete data; + AsyncData* data = static_cast(req->data); + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::HandleScope scope(isolate); + v8::Handle argv[] = { v8::Array::New(isolate) }; + v8::Local::New(isolate, data->callback)-> + Call(isolate->GetCurrentContext()->Global(), 1, argv); + data->callback.Reset(); + delete data; } // callback for Node.js to register the exit-function. The exit-function is supposed to get called once the Qt-GUI // gets closed, this function should be called on startup of node.js void registerExitFunc(const v8::FunctionCallbackInfo& args) { - v8::Isolate* isolate = args.GetIsolate(); - v8::Local callback = v8::Local::Cast(args[0]); + v8::Isolate* isolate = args.GetIsolate(); + v8::Local callback = v8::Local::Cast(args[0]); - jsExitData = new AsyncData(); - jsExitData->request.data = jsExitData; - jsExitData->callback.Reset(isolate, callback); + jsExitData = new AsyncData(); + jsExitData->request.data = jsExitData; + jsExitData->callback.Reset(isolate, callback); - uv_async_init(uv_default_loop(), &jsExitData->request, execAsyncCall); + uv_async_init(uv_default_loop(), &jsExitData->request, execAsyncCall); } void processQtEvents(const v8::FunctionCallbackInfo&) { - QCoreApplication::processEvents(); + QCoreApplication::processEvents(); } // exposes C++-functions in node.js-Module _rssModule (called CppDemoModule in node.js) // format is NODE_SET_METHOD(exports, "functionNameInJS", functionPointerInC++). // From what we understand, object member functions can not be exposed to node.js like this. void onRegisterModule(v8::Local exports, v8::Local, v8::Local, void * /*data*/) { - NODE_SET_METHOD(exports, "cppLog", RssFeed::cppLog); - NODE_SET_METHOD(exports, "registerExitFunc", registerExitFunc); - NODE_SET_METHOD(exports, "clearFeed", RssFeed::clearFeed); - NODE_SET_METHOD(exports, "redrawGUI", RssFeed::redrawGUI); - NODE_SET_METHOD(exports, "processQtEvents", processQtEvents); + NODE_SET_METHOD(exports, "addFeedItem", RssFeed::addFeedItem); + NODE_SET_METHOD(exports, "registerExitFunc", registerExitFunc); + NODE_SET_METHOD(exports, "clearFeed", RssFeed::clearFeed); + NODE_SET_METHOD(exports, "redraw", RssFeed::redrawGUI); + NODE_SET_METHOD(exports, "processQtEvents", processQtEvents); } int main(int argc, char* argv[]) { - const std::string js_file = "data/node-qt-rss.js"; - const std::string data_path = cpplocate::locatePath(js_file); - if (data_path.empty()) { - std::cerr << "Could not find data path." << std::endl; - return 1; - } - std::string js_path = data_path + "/" + js_file; - - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); - QGuiApplication app(argc, argv); - - QQmlApplicationEngine engine; - - // to be able to access the public slots of the RssFeed instance - // we inject a pointer to it in the QML context: - engine.rootContext()->setContextProperty("rssFeed", &RssFeed::getInstance()); - - engine.load(QUrl(QLatin1String("qrc:/main.qml"))); - if (engine.rootObjects().isEmpty()) { - qWarning() << "Error while loading QML file"; - return -1; - } - - // Create node module - static node::node_module _rssModule = - { - NODE_MODULE_VERSION, - 0x01, // FIXME: before: #define NM_F_BUILTIN 0x01 - nullptr, - __FILE__, - nullptr, - onRegisterModule, - "CppDemoModule", - nullptr, // nm_priv, will be given to onRegisterModule as data? i.e. "this" pointer - nullptr - }; - - // Register module - node_module_register(&_rssModule); - - QObject::connect(&engine, &QQmlEngine::quit, [](){ uv_async_send(&jsExitData->request); }); - - const size_t argv0_len = std::strlen(argv[0]) + 1; - const size_t argv1_len = js_path.size() + 1; - const size_t total_size = argv0_len + argv1_len; - - char* node_arg_string = new char[total_size](); - std::strcpy(&node_arg_string[0], argv[0]); - std::copy(js_path.begin(), js_path.end(), &node_arg_string[argv0_len]); - - char* node_argv[] = {&node_arg_string[0], &node_arg_string[argv0_len]}; - - node::Start(2, node_argv); - - delete[] node_arg_string; + const std::string js_file = "data/node-qt-rss.js"; + const std::string data_path = cpplocate::locatePath(js_file); + if (data_path.empty()) { + qWarning() << "Could not find data path."; + return 1; + } + std::string js_path = data_path + "/" + js_file; + + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + QGuiApplication app(argc, argv); + + QQmlApplicationEngine engine; + + // to be able to access the public slots of the RssFeed instance + // we inject a pointer to it in the QML context: + engine.rootContext()->setContextProperty("rssFeed", &RssFeed::getInstance()); + + engine.load(QUrl(QLatin1String("qrc:/main.qml"))); + if (engine.rootObjects().isEmpty()) { + qWarning() << "Error while loading QML file"; + return 1; + } + + // Create node module + static node::node_module _rssModule = + { + NODE_MODULE_VERSION, + 0x01, // value of private node variable NM_F_BUILTIN + nullptr, + __FILE__, + nullptr, + onRegisterModule, + "cpp-qt-gui", + nullptr, // nm_priv, will be given to onRegisterModule as data? i.e. "this" pointer + nullptr + }; + + // Register module + node_module_register(&_rssModule); + + QObject::connect(&engine, &QQmlEngine::quit, [](){ uv_async_send(&jsExitData->request); }); + + const size_t argv0_len = std::strlen(argv[0]) + 1; + const size_t argv1_len = js_path.size() + 1; + const size_t total_size = argv0_len + argv1_len; + + char* node_arg_string = new char[total_size](); + std::strcpy(&node_arg_string[0], argv[0]); + std::copy(js_path.begin(), js_path.end(), &node_arg_string[argv0_len]); + + char* node_argv[] = {&node_arg_string[0], &node_arg_string[argv0_len]}; + + node::Start(2, node_argv); + + delete[] node_arg_string; }