diff options
-rw-r--r-- | launcher/launcher.pro | 11 | ||||
-rw-r--r-- | launcher/protocol/agl-shell-desktop.xml | 113 | ||||
-rw-r--r-- | launcher/qml/Launcher.qml | 8 | ||||
-rw-r--r-- | launcher/src/homescreenhandler.cpp | 96 | ||||
-rw-r--r-- | launcher/src/homescreenhandler.h | 7 | ||||
-rw-r--r-- | launcher/src/main.cpp | 46 | ||||
-rw-r--r-- | launcher/src/shell-desktop.cpp | 82 | ||||
-rw-r--r-- | launcher/src/shell-desktop.h | 95 | ||||
-rw-r--r-- | package/config.xml | 1 |
9 files changed, 394 insertions, 65 deletions
diff --git a/launcher/launcher.pro b/launcher/launcher.pro index e39c64d..0d61233 100644 --- a/launcher/launcher.pro +++ b/launcher/launcher.pro @@ -15,10 +15,10 @@ TEMPLATE = app TARGET = launcher -QT = qml quick websockets -CONFIG += c++11 link_pkgconfig +QT = qml quick websockets gui-private +CONFIG += c++11 link_pkgconfig wayland-scanner DESTDIR = $${OUT_PWD}/../package/root/bin -PKGCONFIG += qlibwindowmanager libhomescreen +PKGCONFIG += libhomescreen wayland-client CONFIG(release, debug|release) { QMAKE_POST_LINK = $(STRIP) --strip-unneeded $(TARGET) @@ -28,11 +28,13 @@ SOURCES += \ src/main.cpp \ src/applicationmodel.cpp \ src/appinfo.cpp \ + src/shell-desktop.cpp \ src/homescreenhandler.cpp HEADERS += \ src/applicationmodel.h \ src/appinfo.h \ + src/shell-desktop.h \ src/homescreenhandler.h OTHER_FILES += \ @@ -41,3 +43,6 @@ OTHER_FILES += \ RESOURCES += \ qml/images/images.qrc \ qml/qml.qrc + +WAYLANDCLIENTSOURCES += \ + protocol/agl-shell-desktop.xml diff --git a/launcher/protocol/agl-shell-desktop.xml b/launcher/protocol/agl-shell-desktop.xml new file mode 100644 index 0000000..05a3725 --- /dev/null +++ b/launcher/protocol/agl-shell-desktop.xml @@ -0,0 +1,113 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="agl_shell_desktop"> + <copyright> + Copyright © 2020 Collabora, Ltd. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + </copyright> + <interface name="agl_shell_desktop" version="1"> + <description summary="Private extension to allow applications activate other apps"> + This extension can be used by regular application to instruct to compositor + to activate or switch to other running (regular) applications. The client + is responsbile for filtering their own app_id when receiving application id. + + Note that other (regular) applications can bind to this interface and there is + no mechanism to place to restrict or limit that. + </description> + + <enum name="app_role"> + <entry name="popup" value="0"/> + <entry name="fullscreen" value="1"/> + </enum> + + <enum name="app_state"> + <entry name="activated" value="0"/> + <entry name="deactivated" value="1"/> + </enum> + + <event name="application"> + <description summary="advertise application id"> + The compositor may choose to advertise one or more application ids which + can be used to activate/switch to. + + When this global is bound, the compositor will send all application ids + available for activation, but may send additional application id at any + time (when they've been mapped in the compositor). + </description> + <arg name="app_id" type="string"/> + </event> + + <request name="activate_app"> + <description summary="make client current window"> + Ask the compositor to make a toplevel to become the current/focused + window for window management purposes. + + See xdg_toplevel.set_app_id from the xdg-shell protocol for a + description of app_id. + </description> + <arg name="app_id" type="string"/> + <arg name="app_data" type="string" allow-null="true"/> + <arg name="output" type="object" interface="wl_output"/> + </request> + + <request name="set_app_property"> + <description summary="set properties for a client identified by app_id"> + Ask the compositor to make a toplevel obey the app_role and, depending + on the role, to use the the x and y values as initial positional values. + The x and y values would only make sense for certain roles. + + See xdg_toplevel.set_app_id from the xdg-shell protocol for a + description of app_id. + </description> + <arg name="app_id" type="string"/> + <arg name="role" type="uint" enum="app_role"/> + <arg name="x" type="int"/> + <arg name="y" type="int"/> + <arg name="output" type="object" interface="wl_output"/> + </request> + + <request name="deactivate_app"> + <description summary="de-activate/hide window identified by app_id"> + Ask the compositor to hide the toplevel window for window + management purposes. Depending on the window role, this request + will either display the previously active window (or the background + in case there's no previously activate surface) or temporarly (or + until a 'activate_app' is called upon) hide the surface. All + the surfaces are identifiable by using the app_id, and no actions are + taken in case the app_id is not/was not present. + + See xdg_toplevel.set_app_id from the xdg-shell protocol for a + description of app_id. + </description> + <arg name="app_id" type="string"/> + </request> + + <event name="state_app"> + <description summary="event sent when application has suffered state modification"> + Notifies application(s) when other application have suffered state modifications. + </description> + <arg name="app_id" type="string"/> + <arg name="app_data" type="string" allow-null="true"/> + <arg name="state" type="uint" enum="app_state"/> + <arg name="role" type="uint" enum="app_role"/> + </event> + + </interface> +</protocol> diff --git a/launcher/qml/Launcher.qml b/launcher/qml/Launcher.qml index 2247f51..f7f1c1b 100644 --- a/launcher/qml/Launcher.qml +++ b/launcher/qml/Launcher.qml @@ -18,6 +18,7 @@ import QtQuick 2.6 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.0 +import QtQuick.Window 2.13 import AppModel 1.0 ApplicationWindow { @@ -29,9 +30,10 @@ ApplicationWindow { Item { id: container anchors.centerIn: parent - width: 1080 - height: 1488 - scale: screenInfo.scale_factor() + width: Screen.width + height: Screen.height + //scale: screenInfo.scale_factor() + scale: 1 Image { anchors.centerIn: parent diff --git a/launcher/src/homescreenhandler.cpp b/launcher/src/homescreenhandler.cpp index 9f15b6a..3c16c23 100644 --- a/launcher/src/homescreenhandler.cpp +++ b/launcher/src/homescreenhandler.cpp @@ -20,36 +20,95 @@ #include <functional> #include "hmi-debug.h" +#include <QGuiApplication> +#include <wayland-client.h> +#include <qpa/qplatformnativeinterface.h> + +#include "shell-desktop.h" + void* HomescreenHandler::myThis = 0; -HomescreenHandler::HomescreenHandler(QObject *parent) : - QObject(parent), - mp_hs(NULL) +static struct wl_output * +getWlOutput(QPlatformNativeInterface *native, QScreen *screen) +{ + void *output = native->nativeResourceForScreen("output", screen); + return static_cast<struct ::wl_output*>(output); +} + +static void +global_add(void *data, struct wl_registry *reg, uint32_t name, + const char *interface, uint32_t version) +{ + struct agl_shell_desktop **shell = + static_cast<struct agl_shell_desktop **>(data); + + if (strcmp(interface, agl_shell_desktop_interface.name) == 0) { + *shell = static_cast<struct agl_shell_desktop *>( + wl_registry_bind(reg, name, &agl_shell_desktop_interface, version) + ); + } +} + +static void +global_remove(void *data, struct wl_registry *reg, uint32_t id) +{ + (void) data; + (void) reg; + (void) id; +} + +static const struct wl_registry_listener registry_listener = { + global_add, + global_remove, +}; + + +static struct agl_shell_desktop * +register_agl_shell_desktop(void) { + struct wl_display *wl; + struct wl_registry *registry; + struct agl_shell_desktop *shell = nullptr; + + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + + wl = static_cast<struct wl_display *>(native->nativeResourceForIntegration("display")); + registry = wl_display_get_registry(wl); + + wl_registry_add_listener(registry, ®istry_listener, &shell); + // Roundtrip to get all globals advertised by the compositor + wl_display_roundtrip(wl); + wl_registry_destroy(registry); + + return shell; +} +HomescreenHandler::HomescreenHandler(QObject *parent) : QObject(parent) +{ } HomescreenHandler::~HomescreenHandler() { - if (mp_hs != NULL) { - delete mp_hs; - } } -void HomescreenHandler::init(int port, const char *token, QLibWindowmanager *qwm, QString myname) +void HomescreenHandler::init(int port, const char *token, QString myname) { myThis = this; - mp_qwm = qwm; m_myname = myname; mp_hs = new LibHomeScreen(); mp_hs->init(port, token); mp_hs->registerCallback(nullptr, HomescreenHandler::onRep_static); - mp_hs->set_event_handler(LibHomeScreen::Event_ShowWindow,[this](json_object *object){ - HMI_DEBUG("Launcher","Surface launcher got Event_ShowWindow\n"); - mp_qwm->activateWindow(m_myname); - }); + struct agl_shell_desktop *agl_shell = register_agl_shell_desktop(); + if (!agl_shell) { + fprintf(stderr, "agl_shell extension is not advertised. " + "Are you sure that agl-compositor is running?\n"); + exit(EXIT_FAILURE); + } + + std::shared_ptr<struct agl_shell_desktop> shell{agl_shell, agl_shell_desktop_destroy}; + this->aglShell = new Shell(shell, nullptr); mp_hs->set_event_handler(LibHomeScreen::Event_AppListChanged,[this](json_object *object){ HMI_DEBUG("Launcher","laucher: appListChanged [%s]\n", json_object_to_json_string(object)); @@ -115,13 +174,14 @@ void HomescreenHandler::init(int port, const char *token, QLibWindowmanager *qwm void HomescreenHandler::tapShortcut(QString application_id) { - HMI_DEBUG("Launcher","tapShortcut %s", application_id.toStdString().c_str()); - struct json_object* j_json = json_object_new_object(); - struct json_object* value; - value = json_object_new_string("normal.full"); - json_object_object_add(j_json, "area", value); + HMI_DEBUG("Launcher","tapShortcut %s", application_id.toStdString().c_str()); + struct json_object* j_json = json_object_new_object(); + struct json_object* value; + value = json_object_new_string("normal.full"); + json_object_object_add(j_json, "area", value); - mp_hs->showWindow(application_id.toStdString().c_str(), j_json); + mp_hs->showWindow(application_id.toStdString().c_str(), j_json); + aglShell->activate_app(nullptr, application_id, nullptr); } void HomescreenHandler::onRep_static(struct json_object* reply_contents) diff --git a/launcher/src/homescreenhandler.h b/launcher/src/homescreenhandler.h index 09b6848..ac490ce 100644 --- a/launcher/src/homescreenhandler.h +++ b/launcher/src/homescreenhandler.h @@ -21,7 +21,8 @@ #include <QObject> #include <libhomescreen.hpp> #include <string> -#include <qlibwindowmanager.h> + +#include "shell-desktop.h" using namespace std; @@ -32,7 +33,7 @@ public: explicit HomescreenHandler(QObject *parent = 0); ~HomescreenHandler(); - void init(int port, const char* token, QLibWindowmanager *qwm, QString myname); + void init(int port, const char* token, QString myname); Q_INVOKABLE void tapShortcut(QString application_id); Q_INVOKABLE void getRunnables(void); @@ -48,7 +49,7 @@ signals: private: LibHomeScreen *mp_hs; - QLibWindowmanager *mp_qwm; + Shell *aglShell; QString m_myname; }; diff --git a/launcher/src/main.cpp b/launcher/src/main.cpp index 5a44cc7..22e600e 100644 --- a/launcher/src/main.cpp +++ b/launcher/src/main.cpp @@ -25,21 +25,24 @@ #include <QQuickWindow> #include <QThread> -#include <qlibwindowmanager.h> #include "applicationmodel.h" #include "appinfo.h" #include "homescreenhandler.h" #include "hmi-debug.h" +#include "shell-desktop.h" int main(int argc, char *argv[]) { QString myname = QString("launcher"); QGuiApplication a(argc, argv); - QCoreApplication::setOrganizationDomain("LinuxFoundation"); - QCoreApplication::setOrganizationName("AutomotiveGradeLinux"); - QCoreApplication::setApplicationName(myname); - QCoreApplication::setApplicationVersion("0.1.0"); + //QCoreApplication::setOrganizationDomain("LinuxFoundation"); + //QCoreApplication::setOrganizationName("AutomotiveGradeLinux"); + //QCoreApplication::setApplicationName(myname); + //QCoreApplication::setApplicationVersion("0.1.0"); + + // necessary to identify correctly by app_id + a.setDesktopFileName(myname); QCommandLineParser parser; parser.addPositionalArgument("port", a.translate("main", "port for binding")); @@ -62,23 +65,8 @@ int main(int argc, char *argv[]) // import C++ class to QML qmlRegisterType<ApplicationModel>("AppModel", 1, 0, "ApplicationModel"); - QLibWindowmanager* layoutHandler = new QLibWindowmanager(); - if(layoutHandler->init(port,token) != 0){ - exit(EXIT_FAILURE); - } - - AGLScreenInfo screenInfo(layoutHandler->get_scale_factor()); - - if (layoutHandler->requestSurface(myname) != 0) { - exit(EXIT_FAILURE); - } - - layoutHandler->set_event_handler(QLibWindowmanager::Event_SyncDraw, [layoutHandler, myname](json_object *object) { - layoutHandler->endDraw(myname); - }); - HomescreenHandler* homescreenHandler = new HomescreenHandler(); - homescreenHandler->init(port, token.toStdString().c_str(), layoutHandler, myname); + homescreenHandler->init(port, token.toStdString().c_str(), myname); QUrl bindingAddress; bindingAddress.setScheme(QStringLiteral("ws")); @@ -90,27 +78,11 @@ int main(int argc, char *argv[]) query.addQueryItem(QStringLiteral("token"), token); bindingAddress.setQuery(query); - const QByteArray hack_delay = qgetenv("HMI_LAUNCHER_STARTUP_DELAY"); - int delay_time = 1; - - if (!hack_delay.isEmpty()) { - delay_time = (QString::fromLocal8Bit(hack_delay)).toInt(); - } - - QThread::sleep(delay_time); - qDebug("Sleep %d sec to resolve race condtion between HomeScreen and Launcher", delay_time); - // mail.qml loading QQmlApplicationEngine engine; - engine.rootContext()->setContextProperty(QStringLiteral("layoutHandler"), layoutHandler); engine.rootContext()->setContextProperty(QStringLiteral("homescreenHandler"), homescreenHandler); - engine.rootContext()->setContextProperty(QStringLiteral("screenInfo"), &screenInfo); engine.load(QUrl(QStringLiteral("qrc:/Launcher.qml"))); homescreenHandler->getRunnables(); - QObject *root = engine.rootObjects().first(); - QQuickWindow *window = qobject_cast<QQuickWindow *>(root); - QObject::connect(window, SIGNAL(frameSwapped()), layoutHandler, SLOT(slotActivateSurface())); - return a.exec(); } diff --git a/launcher/src/shell-desktop.cpp b/launcher/src/shell-desktop.cpp new file mode 100644 index 0000000..42cc6d1 --- /dev/null +++ b/launcher/src/shell-desktop.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020 Collabora Ltd. + * + * 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 <QGuiApplication> +#include <QDebug> +#include "shell-desktop.h" +#include <qpa/qplatformnativeinterface.h> +#include <stdio.h> + +static struct wl_output * +getWlOutput(QScreen *screen) +{ + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + void *output = native->nativeResourceForScreen("output", screen); + return static_cast<struct ::wl_output*>(output); +} + +void +Shell::activate_app(QWindow *win, const QString &app_id, + const QString &app_data) +{ + QScreen *screen = nullptr; + struct wl_output *output; + + if (!win || !win->screen()) { + screen = qApp->screens().first(); + } else { + screen = win->screen(); + } + + if (!screen) + return; + + output = getWlOutput(screen); + qDebug() << "will activate app: " << app_id; + agl_shell_desktop_activate_app(this->shell.get(), + app_id.toStdString().c_str(), + app_data.toStdString().c_str(), output); +} + +void +Shell::deactivate_app(const QString &app_id) +{ + agl_shell_desktop_deactivate_app(this->shell.get(), + app_id.toStdString().c_str()); +} + +void +Shell::set_window_props(QWindow *win, const QString &app_id, + uint32_t props, int x, int y) +{ + QScreen *screen = nullptr; + struct wl_output *output; + + if (!win || !win->screen()) { + screen = qApp->screens().first(); + } else { + screen = win->screen(); + } + + if (!screen) { + return; + } + + output = getWlOutput(screen); + agl_shell_desktop_set_app_property(this->shell.get(), + app_id.toStdString().c_str(), + props, x, y, output); +} diff --git a/launcher/src/shell-desktop.h b/launcher/src/shell-desktop.h new file mode 100644 index 0000000..a8f3326 --- /dev/null +++ b/launcher/src/shell-desktop.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2020 Collabora Ltd. + * + * 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. + */ + +#ifndef SHELLDESKTOP_H +#define SHELLDESKTOP_H + +#include <QObject> +#include <QString> +#include <QScreen> +#include <QWindow> +#include <QDebug> +#include <memory> + + +#include "wayland-agl-shell-desktop-client-protocol.h" + +static void +application_id_event(void *data, struct agl_shell_desktop *agl_shell_desktop, + const char *app_id); +static void +application_state_event(void *data, struct agl_shell_desktop *agl_shell_desktop, + const char *app_id, const char *app_data, + uint32_t app_state, uint32_t app_role); + +static const struct agl_shell_desktop_listener agl_shell_desktop_listener = { + application_id_event, + application_state_event, +}; + +class Shell : public QObject +{ +Q_OBJECT + +public: + std::shared_ptr<struct agl_shell_desktop> shell; + Shell(std::shared_ptr<struct agl_shell_desktop> shell, QObject *parent = nullptr) : + QObject(parent), shell(shell) + { + struct agl_shell_desktop *agl_shell_desktop = shell.get(); + agl_shell_desktop_add_listener(agl_shell_desktop, + &agl_shell_desktop_listener, this); + } + +public slots: // calls out of qml into CPP + void activate_app(QWindow *win, const QString &app_id, const QString &app_data); + void deactivate_app(const QString &app_id); + void set_window_props(QWindow *win, const QString &app_id, + uint32_t props, int x, int y); +}; + +static void +application_id_event(void *data, struct agl_shell_desktop *agl_shell_desktop, + const char *app_id) +{ + Shell *aglShell = static_cast<Shell *>(data); + (void) agl_shell_desktop; + + qInfo() << "app_id: " << app_id; + + // this ain't necessary in case the default policy API will activate + // applications by default (when they are started) but if that is not + // the case we can use this event handler to activate the application + // as this event is sent when the application is created (when the + // app surface is created that is) + QString qstr_app_id = QString::fromUtf8(app_id, -1); + aglShell->activate_app(nullptr, qstr_app_id, nullptr); +} + +static void +application_state_event(void *data, struct agl_shell_desktop *agl_shell_desktop, + const char *app_id, const char *app_data, + uint32_t app_state, uint32_t app_role) +{ + (void) data; + (void) agl_shell_desktop; + (void) app_id; + (void) app_data; + (void) app_state; + (void) app_role; +} + +#endif // SHELLDESKTOP_H diff --git a/package/config.xml b/package/config.xml index 0bd4e51..39a0132 100644 --- a/package/config.xml +++ b/package/config.xml @@ -8,7 +8,6 @@ <license>APL 2.0</license> <feature name="urn:AGL:widget:required-api"> <param name="homescreen" value="ws" /> - <param name="windowmanager" value="ws" /> </feature> <feature name="urn:AGL:widget:required-permission"> <param name="urn:AGL:permission::public:no-htdocs" value="required" /> |