/* * Copyright (C) 2016 The Qt Company Ltd. * Copyright (C) 2020 Konsulko Group * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "afbclient.h" #include // Disable window activation at launch by default, but keep option // for potential debug use. #define HIDE_AT_LAUNCH bool check_template_supported(json_object *data) { json_object *jtype = NULL; json_object_object_get_ex(data, "type", &jtype); if(!jtype) { qWarning("render_template event missing type element"); return false; } const char *type_value = json_object_get_string(jtype); if(!type_value) { qWarning("render_template event type element not parsed"); return false; } // We only handle BodyTemplate[12] and WeatherTemplate, ignore // others if(!(strcmp(type_value, "BodyTemplate1") && strcmp(type_value, "BodyTemplate2") && strcmp(type_value, "WeatherTemplate"))) return true; return false; } void async_event_cb(const char *event, json_object *data, void *closure) { if(!data) return; if(!strcmp(event, "vshl-capabilities/setDestination")) { // Slight hack here, there's currently no convenient place to hook up raising // the navigation app when a route is set by Alexa, so do so here for now. if(closure != nullptr) { static_cast(closure)->showWindow("navigation", "normal"); } } else if(!strcmp(event, "vshl-capabilities/render_template")) { // Raise ourselves, the UI code will receive the event as well and render it if(closure != nullptr) { if(!check_template_supported(data)) { qDebug() << "Unsupported template type, ignoring!"; return; } static_cast(closure)->showWindow("alexa-viewer", "on_screen"); } } else if(!strcmp(event, "vshl-capabilities/clear_template")) { // Hide ourselves if(closure != nullptr) { static_cast(closure)->hideWindow("alexa-viewer"); } } } void subscribe_async_events(AfbClient &client) { const char *vshl_capabilities_nav_events[] = { "setDestination", NULL, }; const char **tmp = vshl_capabilities_nav_events; json_object *args = json_object_new_object(); json_object *actions = json_object_new_array(); while (*tmp) { json_object_array_add(actions, json_object_new_string(*tmp++)); } json_object_object_add(args, "actions", actions); if(json_object_array_length(actions)) { client.subscribe("vshl-capabilities", args, "navigation/subscribe"); } else { json_object_put(args); } // NOTE: Not subscribing to "clear_template", as it will be passed to // the app QML to handle by the libqtappfw wrapper. const char *vshl_capabilities_guimetadata_events[] = { "render_template", NULL, }; tmp = vshl_capabilities_guimetadata_events; args = json_object_new_object(); actions = json_object_new_array(); while (*tmp) { json_object_array_add(actions, json_object_new_string(*tmp++)); } json_object_object_add(args, "actions", actions); if(json_object_array_length(actions)) { client.subscribe("vshl-capabilities", args, "guimetadata/subscribe"); } else { json_object_put(args); } } int main(int argc, char *argv[]) { QString graphic_role = QString("on_screen"); QGuiApplication app(argc, argv); QCommandLineParser parser; parser.addPositionalArgument("port", app.translate("main", "port for binding")); parser.addPositionalArgument("secret", app.translate("main", "secret for binding")); parser.addHelpOption(); parser.addVersionOption(); parser.process(app); QStringList positionalArguments = parser.positionalArguments(); QUrl bindingAddress; int port = 0; QString token; if (positionalArguments.length() == 2) { port = positionalArguments.takeFirst().toInt(); token = positionalArguments.takeFirst(); bindingAddress.setScheme(QStringLiteral("ws")); bindingAddress.setHost(QStringLiteral("localhost")); bindingAddress.setPort(port); bindingAddress.setPath(QStringLiteral("/api")); QUrlQuery query; query.addQueryItem(QStringLiteral("token"), token); bindingAddress.setQuery(query); } // QLibWM QLibWindowmanager* qwmHandler = new QLibWindowmanager(); int res; if((res = qwmHandler->init(port, token)) != 0){ qCritical("init qlibwm err(%d)", res); return -1; } if((res = qwmHandler->requestSurface(graphic_role)) != 0) { qCritical("requestSurface error(%d)", res); return -1; } qwmHandler->set_event_handler(QLibWindowmanager::Event_SyncDraw, [qwmHandler, &graphic_role](json_object *object) { qwmHandler->endDraw(graphic_role); }); // QLibHS QLibHomeScreen* qhsHandler = new QLibHomeScreen(); qhsHandler->init(port, token.toStdString().c_str()); qhsHandler->set_event_handler(QLibHomeScreen::Event_ShowWindow, [qwmHandler, &graphic_role](json_object *object){ qDebug("Surface %s got showWindow\n", graphic_role.toStdString().c_str()); qwmHandler->activateWindow(graphic_role, "on_screen"); }); qhsHandler->set_event_handler(QLibHomeScreen::Event_HideWindow, [qwmHandler, &graphic_role](json_object *object){ qDebug("Surface %s got hideWindow\n", graphic_role.toStdString().c_str()); qwmHandler->deactivateWindow(graphic_role); }); // Load qml QQmlApplicationEngine engine; QQmlContext *context = engine.rootContext(); context->setContextProperty("homescreen", qhsHandler); context->setContextProperty("GuiMetadata", new GuiMetadata(bindingAddress, context)); engine.load(QUrl(QStringLiteral("qrc:/Main.qml"))); #ifndef HIDE_AT_LAUNCH QObject *root = engine.rootObjects().first(); QQuickWindow *window = qobject_cast(root); QObject::connect(window, SIGNAL(frameSwapped()), qwmHandler, SLOT(slotActivateWindow())); #endif // Create app framework client to handle events when window is not visible AfbClient client(port, token.toStdString()); client.set_event_callback(async_event_cb, (void*) qhsHandler); subscribe_async_events(client); return app.exec(); }