diff options
-rw-r--r-- | homescreen/homescreen.pro | 12 | ||||
-rw-r--r-- | homescreen/protocol/agl-shell.xml | 117 | ||||
-rw-r--r-- | homescreen/qml/MediaArea.qml | 3 | ||||
-rw-r--r-- | homescreen/qml/MediaAreaBlank.qml | 3 | ||||
-rw-r--r-- | homescreen/qml/ShortcutArea.qml | 9 | ||||
-rw-r--r-- | homescreen/qml/ShortcutIcon.qml | 11 | ||||
-rw-r--r-- | homescreen/qml/StatusArea.qml | 4 | ||||
-rw-r--r-- | homescreen/qml/TopArea.qml | 10 | ||||
-rw-r--r-- | homescreen/qml/background.qml | 15 | ||||
-rw-r--r-- | homescreen/qml/bottompanel.qml | 50 | ||||
-rw-r--r-- | homescreen/qml/qml.qrc | 5 | ||||
-rw-r--r-- | homescreen/qml/toppanel.qml | 69 | ||||
-rw-r--r-- | homescreen/src/homescreenhandler.cpp | 45 | ||||
-rw-r--r-- | homescreen/src/homescreenhandler.h | 6 | ||||
-rw-r--r-- | homescreen/src/main.cpp | 205 | ||||
-rw-r--r-- | homescreen/src/shell.cpp | 42 | ||||
-rw-r--r-- | homescreen/src/shell.h | 46 | ||||
-rw-r--r-- | package/config.xml | 2 |
18 files changed, 575 insertions, 79 deletions
diff --git a/homescreen/homescreen.pro b/homescreen/homescreen.pro index 3a08ddc..d4feb5d 100644 --- a/homescreen/homescreen.pro +++ b/homescreen/homescreen.pro @@ -15,10 +15,10 @@ TEMPLATE = app TARGET = HomeScreen -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 qtappfw afb-helpers-qt +PKGCONFIG += qtappfw afb-helpers-qt wayland-client json-c LIBS += -lhomescreen @@ -29,6 +29,7 @@ SOURCES += \ src/applicationlauncher.cpp \ src/mastervolume.cpp \ src/homescreenhandler.cpp \ + src/shell.cpp \ src/aglsocketwrapper.cpp \ src/chromecontroller.cpp @@ -38,6 +39,7 @@ HEADERS += \ src/applicationlauncher.h \ src/mastervolume.h \ src/homescreenhandler.h \ + src/shell.h \ src/aglsocketwrapper.h \ src/chromecontroller.h \ src/constants.h @@ -54,3 +56,7 @@ RESOURCES += \ qml/images/images.qrc \ qml/qml.qrc \ qml/images/SpeechChrome/speechchrome.qrc + + +WAYLANDCLIENTSOURCES += \ + protocol/agl-shell.xml diff --git a/homescreen/protocol/agl-shell.xml b/homescreen/protocol/agl-shell.xml new file mode 100644 index 0000000..1096c64 --- /dev/null +++ b/homescreen/protocol/agl-shell.xml @@ -0,0 +1,117 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="agl_shell"> + <copyright> + Copyright © 2019 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" version="1"> + <description summary="user interface for weston-ivi"> + </description> + + <enum name="error"> + <entry name="invalid_argument" value="0"/> + <entry name="background_exists" value="1"/> + <entry name="panel_exists" value="2"/> + </enum> + + <enum name="edge"> + <entry name="top" value="0"/> + <entry name="bottom" value="1"/> + <entry name="left" value="2"/> + <entry name="right" value="3"/> + </enum> + + <request name="ready"> + <description summary="client is ready to be shown"> + Tell the server that this client is ready to be shown. The server + will delay presentation during start-up until all shell clients are + ready to be shown, and will display a black screen instead. + This gives the client an oppurtunity to set up and configure several + surfaces into a coherent interface. + + The client that binds to this interface must send this request, otherwise + they may stall the compositor unnecessarily. + + If this request is called after the compositor has already finished + start-up, no operation is performed. + </description> + </request> + + <request name="set_background"> + <description summary="set surface as output's background"> + Set the surface to act as the background of an output. After this + request, the server will immediately send a configure event with + the dimensions the client should use to cover the entire output. + + The surface must have a "desktop" surface role, as supported by + libweston-desktop. + + Only a single surface may be the background for any output. If a + background surface already exists, a protocol error is raised. + </description> + <arg name="surface" type="object" interface="wl_surface"/> + <arg name="output" type="object" interface="wl_output"/> + </request> + + <request name="set_panel"> + <description summary="set surface as panel"> + Set the surface to act as a panel of an output. The 'edge' argument + says what edge of the output the surface will be anchored to. + After this request, the server will send a configure event with the + correponding width/height that the client should use, and 0 for the + other dimension. E.g. if the edge is 'top', the width will be the + output's width, and the height will be 0. + + The surface must have a "desktop" surface role, as supported by + libweston-desktop. + + The compositor will take the panel's window geometry into account when + positioning other windows, so the panels are not covered. + + XXX: What happens if e.g. both top and left are used at the same time? + Who gets to have the corner? + + Only a single surface may be the panel for an output's edge. If a + surface already exists on an edge, a protocol error is raised. + </description> + <arg name="surface" type="object" interface="wl_surface"/> + <arg name="output" type="object" interface="wl_output"/> + <arg name="edge" type="uint" enum="edge"/> + </request> + + <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. + + If multiple toplevels have the same app_id, the result is unspecified. + + XXX: Do we need feedback to say it didn't work? (e.g. client does + not exist) + </description> + <arg name="app_id" type="string"/> + <arg name="output" type="object" interface="wl_output"/> + </request> + </interface> +</protocol> diff --git a/homescreen/qml/MediaArea.qml b/homescreen/qml/MediaArea.qml index 3b6d18a..88eaf1e 100644 --- a/homescreen/qml/MediaArea.qml +++ b/homescreen/qml/MediaArea.qml @@ -20,8 +20,7 @@ import QtQuick.Controls 2.0 StackView { id: root - width: parent.width - height: parent.height + anchors.fill: parent initialItem: blank diff --git a/homescreen/qml/MediaAreaBlank.qml b/homescreen/qml/MediaAreaBlank.qml index 60d0c92..c971f6e 100644 --- a/homescreen/qml/MediaAreaBlank.qml +++ b/homescreen/qml/MediaAreaBlank.qml @@ -22,8 +22,7 @@ import AGL.Demo.Controls 1.0 import MasterVolume 1.0 Image { - width: parent.width - height: parent.height + anchors.fill: parent source: './images/Utility_Logo_Background-01.svg' property bool displayVolume: false; diff --git a/homescreen/qml/ShortcutArea.qml b/homescreen/qml/ShortcutArea.qml index a8ce127..489b1e0 100644 --- a/homescreen/qml/ShortcutArea.qml +++ b/homescreen/qml/ShortcutArea.qml @@ -18,12 +18,10 @@ import QtQuick 2.2 import QtQuick.Layouts 1.1 +import QtQuick.Window 2.2 Item { id: root - width: 785 - height: 218 - ListModel { id: applicationModel @@ -53,7 +51,7 @@ Item { RowLayout { anchors.fill: parent - spacing: 2 + spacing: 0 Repeater { model: applicationModel delegate: ShortcutIcon { @@ -62,9 +60,10 @@ Item { name: model.name active: model.name === launcher.current onClicked: { + console.log("Activating: " + model.appid) homescreenHandler.tapShortcut(model.appid) } } - } + } } } diff --git a/homescreen/qml/ShortcutIcon.qml b/homescreen/qml/ShortcutIcon.qml index 1100a7c..d039d36 100644 --- a/homescreen/qml/ShortcutIcon.qml +++ b/homescreen/qml/ShortcutIcon.qml @@ -21,8 +21,6 @@ import QtGraphicalEffects 1.0 MouseArea { id: root - width: 195 - height: 216.8 property string name: 'Home' property bool active: false Item { @@ -32,12 +30,14 @@ MouseArea { Image { id: inactiveIcon anchors.fill: parent - source: './images/Shortcut/%1.svg'.arg(root.name.toLowerCase()) + source: './images/Shortcut/%1.svg'.arg(root.name.toLowerCase()) + fillMode: Image.PreserveAspectFit } Image { id: activeIcon anchors.fill: parent source: './images/Shortcut/%1_active.svg'.arg(root.name.toLowerCase()) + fillMode: Image.PreserveAspectFit opacity: 0.0 } layer.enabled: true @@ -49,12 +49,13 @@ MouseArea { } Label { id: name - y: 160 width: root.width - 10 font.pixelSize: 15 font.letterSpacing: 5 // wrapMode: Text.WordWrap - anchors.horizontalCenter: parent.horizontalCenter + anchors.centerIn: icon + anchors.verticalCenterOffset: icon.height * 0.2 + //anchors.horizontalCenter: parent.horizontalCenter horizontalAlignment: Text.AlignHCenter color: "white" text: qsTr(model.name.toUpperCase()) diff --git a/homescreen/qml/StatusArea.qml b/homescreen/qml/StatusArea.qml index 14ccb8f..507b6db 100644 --- a/homescreen/qml/StatusArea.qml +++ b/homescreen/qml/StatusArea.qml @@ -22,8 +22,8 @@ import HomeScreen 1.0 Item { id: root - width: 295 - height: 218 + //width: 295 + //height: 218 property date now: new Date Timer { diff --git a/homescreen/qml/TopArea.qml b/homescreen/qml/TopArea.qml index 2a75cf8..19bed91 100644 --- a/homescreen/qml/TopArea.qml +++ b/homescreen/qml/TopArea.qml @@ -20,10 +20,10 @@ import QtQuick.Layouts 1.1 import QtQuick.Controls 2.0 Image { - width: 1920 - height: 218 + anchors.fill: parent source: './images/TopSection_NoText_NoIcons-01.svg' - fillMode: Image.PreserveAspectCrop + //fillMode: Image.PreserveAspectCrop + fillMode: Image.Stretch RowLayout { anchors.fill: parent @@ -32,13 +32,13 @@ Image { id: shortcutArea Layout.fillWidth: true Layout.fillHeight: true - Layout.preferredWidth: 785 + Layout.preferredWidth: 775 } StatusArea { id: statusArea Layout.fillWidth: true Layout.fillHeight: true - Layout.preferredWidth: 295 + Layout.preferredWidth: 291 } } diff --git a/homescreen/qml/background.qml b/homescreen/qml/background.qml new file mode 100644 index 0000000..c2bb309 --- /dev/null +++ b/homescreen/qml/background.qml @@ -0,0 +1,15 @@ +import QtQuick 2.13 +import QtQuick.Window 2.13 + +Window { + id: background + width: Screen.width + height: Screen.height + flags: Qt.FramelessWindowHint + visible: true + + Image { + anchors.fill: parent + source: './images/AGL_HMI_Blue_Background_NoCar-01.png' + } +} diff --git a/homescreen/qml/bottompanel.qml b/homescreen/qml/bottompanel.qml new file mode 100644 index 0000000..1e0b105 --- /dev/null +++ b/homescreen/qml/bottompanel.qml @@ -0,0 +1,50 @@ +import QtQuick 2.13 +import QtQuick.Window 2.13 + +Window { + id: bottompanel + width: Screen.width + height: Screen.height * (215.0 / 1920.0) + flags: Qt.FramelessWindowHint + visible: true + //color: "#aaaa0000" + MediaArea { + } + + Timer { + id:informationTimer + interval: 3000 + running: false + repeat: true + onTriggered: { + bottomInformation.visible = false + } + } + + Item { + id: bottomInformation + width: parent.width + height: 215 + anchors.bottom: parent.bottom + visible: false + Text { + id: bottomText + anchors.centerIn: parent + font.pixelSize: 25 + font.letterSpacing: 5 + horizontalAlignment: Text.AlignHCenter + color: "white" + text: "" + z:1 + } + } + + Connections { + target: homescreenHandler + onShowInformation: { + bottomText.text = info + bottomInformation.visible = true + informationTimer.restart() + } + } +} diff --git a/homescreen/qml/qml.qrc b/homescreen/qml/qml.qrc index d901481..8381337 100644 --- a/homescreen/qml/qml.qrc +++ b/homescreen/qml/qml.qrc @@ -7,9 +7,12 @@ <file>MediaAreaRadio.qml</file> <file>ShortcutArea.qml</file> <file>ShortcutIcon.qml</file> + <file>SpeechChrome.qml</file> <file>StatusArea.qml</file> <file>TopArea.qml</file> <file>IconItem.qml</file> - <file>SpeechChrome.qml</file> + <file>background.qml</file> + <file>toppanel.qml</file> + <file>bottompanel.qml</file> </qresource> </RCC> diff --git a/homescreen/qml/toppanel.qml b/homescreen/qml/toppanel.qml new file mode 100644 index 0000000..4098c73 --- /dev/null +++ b/homescreen/qml/toppanel.qml @@ -0,0 +1,69 @@ +import QtQuick 2.13 +import QtQuick.Window 2.13 + +Window { + id: toppanel + width: Screen.width + height: Screen.height * (240.0 / 1920.0) + flags: Qt.FramelessWindowHint + visible: true + //color: "#aaaa0000" + + TopArea { + } + + Timer { + id:notificationTimer + interval: 3000 + running: false + repeat: true + onTriggered: notificationItem.visible = false + } + + Item { + id: notificationItem + x: 0 + y: 0 + z: 1 + width: 1280 + height: 100 + opacity: 0.8 + visible: false + + Rectangle { + width: parent.width + height: parent.height + anchors.fill: parent + color: "gray" + Image { + id: notificationIcon + width: 70 + height: 70 + anchors.left: parent.left + anchors.leftMargin: 20 + anchors.verticalCenter: parent.verticalCenter + source: "" + } + + Text { + id: notificationtext + font.pixelSize: 25 + anchors.left: notificationIcon.right + anchors.leftMargin: 5 + anchors.verticalCenter: parent.verticalCenter + color: "white" + text: qsTr("") + } + } + } + + Connections { + target: homescreenHandler + onShowNotification: { + notificationIcon.source = icon_path + notificationtext.text = text + notificationItem.visible = true + notificationTimer.restart() + } + } +} diff --git a/homescreen/src/homescreenhandler.cpp b/homescreen/src/homescreenhandler.cpp index 4db60fb..0afe2df 100644 --- a/homescreen/src/homescreenhandler.cpp +++ b/homescreen/src/homescreenhandler.cpp @@ -14,16 +14,19 @@ * limitations under the License. */ +#include <QGuiApplication> #include <QFileInfo> #include "homescreenhandler.h" #include <functional> #include "hmi-debug.h" +#include <qpa/qplatformnativeinterface.h> + void* HomescreenHandler::myThis = 0; -HomescreenHandler::HomescreenHandler(QObject *parent) : +HomescreenHandler::HomescreenHandler(Shell *_aglShell, QObject *parent) : QObject(parent), - mp_hs(NULL) + aglShell(_aglShell) { } @@ -42,14 +45,18 @@ void HomescreenHandler::init(int port, const char *token) myThis = this; + mp_hs->registerCallback(nullptr, HomescreenHandler::onRep_static); mp_hs->set_event_handler(LibHomeScreen::Event_OnScreenMessage, [this](json_object *object){ const char *display_message = json_object_get_string( json_object_object_get(object, "display_message")); HMI_DEBUG("HomeScreen","set_event_handler Event_OnScreenMessage display_message = %s", display_message); + + fprintf(stderr, "HomeScreen, set_event_handler Event_OnScreenMessage display_message = %s\n", display_message); }); + // should be handled in the top panel mp_hs->set_event_handler(LibHomeScreen::Event_ShowNotification,[this](json_object *object){ json_object *p_obj = json_object_object_get(object, "parameter"); const char *icon = json_object_get_string( @@ -67,27 +74,49 @@ void HomescreenHandler::init(int port, const char *token) icon_path = "./images/Utility_Logo_Grey-01.svg"; } + fprintf(stderr, "HomeScreen, LibHomeScreen::Event_ShowNotification()\n"); emit showNotification(QString(QLatin1String(app_id)), icon_path, QString(QLatin1String(text))); }); + // should be handled in the bottom panel mp_hs->set_event_handler(LibHomeScreen::Event_ShowInformation,[this](json_object *object){ json_object *p_obj = json_object_object_get(object, "parameter"); const char *info = json_object_get_string( json_object_object_get(p_obj, "info")); + fprintf(stderr, "HomeScreen, LibHomeScreen::Event_ShowInformation()\n"); emit showInformation(QString(QLatin1String(info))); }); } +static struct wl_output * +getWlOutput(QPlatformNativeInterface *native, QScreen *screen) +{ + void *output = native->nativeResourceForScreen("output", screen); + return static_cast<struct ::wl_output*>(output); +} + void HomescreenHandler::tapShortcut(QString application_id) { - HMI_DEBUG("HomeScreen","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("HomeScreen","tapShortcut %s", application_id.toStdString().c_str()); + + struct json_object* j_json = json_object_new_object(); + struct json_object* value; + + struct agl_shell *agl_shell = aglShell->shell.get(); + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + struct wl_output *output = getWlOutput(native, qApp->screens().first()); + + 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); + // this works (and it is redundant the first time), due to the default + // policy engine installed which actives the application, when starting + // the first time. Later calls to HomescreenHandler::tapShortcut will + // require calling 'agl_shell_activate_app' + agl_shell_activate_app(agl_shell, application_id.toStdString().c_str(), output); } void HomescreenHandler::onRep_static(struct json_object* reply_contents) diff --git a/homescreen/src/homescreenhandler.h b/homescreen/src/homescreenhandler.h index 5dfe041..3d939ce 100644 --- a/homescreen/src/homescreenhandler.h +++ b/homescreen/src/homescreenhandler.h @@ -18,7 +18,10 @@ #define HOMESCREENHANDLER_H #include <QObject> + #include <libhomescreen.hpp> + +#include "shell.h" #include <string> using namespace std; @@ -27,7 +30,7 @@ class HomescreenHandler : public QObject { Q_OBJECT public: - explicit HomescreenHandler(QObject *parent = 0); + explicit HomescreenHandler(Shell *aglShell, QObject *parent = 0); ~HomescreenHandler(); void init(int port, const char* token); @@ -46,6 +49,7 @@ signals: void showInformation(QString info); private: LibHomeScreen *mp_hs; + Shell *aglShell; }; #endif // HOMESCREENHANDLER_H diff --git a/homescreen/src/main.cpp b/homescreen/src/main.cpp index b92ff60..ff31ac5 100644 --- a/homescreen/src/main.cpp +++ b/homescreen/src/main.cpp @@ -21,8 +21,10 @@ #include <QtGui/QGuiApplication> #include <QtQml/QQmlApplicationEngine> #include <QtQml/QQmlContext> +#include <QtQml/QQmlComponent> #include <QtQml/qqml.h> #include <QQuickWindow> +#include <QTimer> #include <qlibwindowmanager.h> #include <weather.h> @@ -34,9 +36,142 @@ #include "hmi-debug.h" #include "chromecontroller.h" +#include <qpa/qplatformnativeinterface.h> +#include <wayland-client.h> + +#include "wayland-agl-shell-client-protocol.h" +#include "shell.h" + +static void +global_add(void *data, struct wl_registry *reg, uint32_t name, + const char *interface, uint32_t) +{ + struct agl_shell **shell = static_cast<struct agl_shell **>(data); + + if (strcmp(interface, agl_shell_interface.name) == 0) { + *shell = static_cast<struct agl_shell *>( + wl_registry_bind(reg, name, &agl_shell_interface, 1) + ); + } +} + +static void +global_remove(void *data, struct wl_registry *reg, uint32_t id) +{ + /* Don't care */ + (void) data; + (void) reg; + (void) id; +} + +static const struct wl_registry_listener registry_listener = { + global_add, + global_remove, +}; + +static struct wl_surface * +getWlSurface(QPlatformNativeInterface *native, QWindow *window) +{ + void *surf = native->nativeResourceForWindow("surface", window); + return static_cast<struct ::wl_surface *>(surf); +} + +static struct wl_output * +getWlOutput(QPlatformNativeInterface *native, QScreen *screen) +{ + void *output = native->nativeResourceForScreen("output", screen); + return static_cast<struct ::wl_output*>(output); +} + + +static struct agl_shell * +register_agl_shell(QPlatformNativeInterface *native) +{ + struct wl_display *wl; + struct wl_registry *registry; + struct agl_shell *shell = nullptr; + + 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; +} + +static struct wl_surface * +create_component(QPlatformNativeInterface *native, QQmlComponent *comp, + QScreen *screen, QObject **qobj) +{ + QObject *obj = comp->create(); + obj->setParent(screen); + + QWindow *win = qobject_cast<QWindow *>(obj); + *qobj = obj; + + return getWlSurface(native, win); +} + +static void +load_agl_shell_app(QPlatformNativeInterface *native, + QQmlApplicationEngine *engine, + struct agl_shell *agl_shell, QUrl &bindingAddress) +{ + struct wl_surface *bg, *top, *bottom; + struct wl_output *output; + + QObject *qobj_bg, *qobj_top, *qobj_bottom; + + QQmlComponent bg_comp(engine, QUrl("qrc:/background.qml")); + qInfo() << bg_comp.errors(); + + QQmlComponent top_comp(engine, QUrl("qrc:/toppanel.qml")); + qInfo() << top_comp.errors(); + + QQmlComponent bot_comp(engine, QUrl("qrc:/bottompanel.qml")); + qInfo() << bot_comp.errors(); + + QScreen *screen = qApp->screens().first(); + if (!screen) + return; + + output = getWlOutput(native, screen); + + bg = create_component(native, &bg_comp, screen, &qobj_bg); + top = create_component(native, &top_comp, screen, &qobj_top); + bottom = create_component(native, &bot_comp, screen, &qobj_bottom); + + /* engine.rootObjects() works only if we had a load() */ + StatusBarModel *statusBar = qobj_top->findChild<StatusBarModel *>("statusBar"); + if (statusBar) { + qDebug() << "got statusBar objectname, doing init()"; + statusBar->init(bindingAddress, engine->rootContext()); + } + + agl_shell_set_panel(agl_shell, top, output, AGL_SHELL_EDGE_TOP); + agl_shell_set_panel(agl_shell, bottom, output, AGL_SHELL_EDGE_BOTTOM); + + agl_shell_set_background(agl_shell, bg, output); + + /* Delay the ready signal until after Qt has done all of its own setup + * in a.exec() */ + QTimer::singleShot(500, [agl_shell](){ + agl_shell_ready(agl_shell); + }); +} + + int main(int argc, char *argv[]) { QGuiApplication a(argc, argv); + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + struct agl_shell *agl_shell = nullptr; QCoreApplication::setOrganizationDomain("LinuxFoundation"); QCoreApplication::setOrganizationName("AutomotiveGradeLinux"); @@ -62,6 +197,16 @@ int main(int argc, char *argv[]) HMI_DEBUG("HomeScreen","port = %d, token = %s", port, token.toStdString().c_str()); + agl_shell = register_agl_shell(native); + 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> shell{agl_shell, agl_shell_destroy}; + Shell *aglShell = new Shell(shell, &a); + // import C++ class to QML // qmlRegisterType<ApplicationLauncher>("HomeScreen", 1, 0, "ApplicationLauncher"); qmlRegisterType<StatusBarModel>("HomeScreen", 1, 0, "StatusBarModel"); @@ -70,33 +215,8 @@ int main(int argc, char *argv[]) QLatin1String("SpeechChromeController is uncreatable.")); ApplicationLauncher *launcher = new ApplicationLauncher(); - QLibWindowmanager* layoutHandler = new QLibWindowmanager(); - if(layoutHandler->init(port,token) != 0){ - exit(EXIT_FAILURE); - } - - AGLScreenInfo screenInfo(layoutHandler->get_scale_factor()); - - if (layoutHandler->requestSurface(graphic_role) != 0) { - exit(EXIT_FAILURE); - } - layoutHandler->set_event_handler(QLibWindowmanager::Event_SyncDraw, [layoutHandler, &graphic_role](json_object *object) { - layoutHandler->endDraw(graphic_role); - }); - - layoutHandler->set_event_handler(QLibWindowmanager::Event_ScreenUpdated, [layoutHandler, launcher](json_object *object) { - json_object *jarray = json_object_object_get(object, "ids"); - int arrLen = json_object_array_length(jarray); - for( int idx = 0; idx < arrLen; idx++) - { - QString label = QString(json_object_get_string( json_object_array_get_idx(jarray, idx) )); - HMI_DEBUG("HomeScreen","Event_ScreenUpdated application: %s.", label.toStdString().c_str()); - QMetaObject::invokeMethod(launcher, "setCurrent", Qt::QueuedConnection, Q_ARG(QString, label)); - } - }); - - HomescreenHandler* homescreenHandler = new HomescreenHandler(); + HomescreenHandler* homescreenHandler = new HomescreenHandler(aglShell); homescreenHandler->init(port, token.toStdString().c_str()); QUrl bindingAddress; @@ -109,25 +229,22 @@ int main(int argc, char *argv[]) query.addQueryItem(QStringLiteral("token"), token); bindingAddress.setQuery(query); - // mail.qml loading QQmlApplicationEngine engine; - engine.rootContext()->setContextProperty("bindingAddress", bindingAddress); - engine.rootContext()->setContextProperty("layoutHandler", layoutHandler); - engine.rootContext()->setContextProperty("homescreenHandler", homescreenHandler); - engine.rootContext()->setContextProperty("launcher", launcher); - engine.rootContext()->setContextProperty("weather", new Weather(bindingAddress)); - engine.rootContext()->setContextProperty("bluetooth", new Bluetooth(bindingAddress, engine.rootContext())); - engine.rootContext()->setContextProperty("speechChromeController", new ChromeController(bindingAddress, &engine)); - engine.rootContext()->setContextProperty("screenInfo", &screenInfo); - engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); - - QObject *root = engine.rootObjects().first(); - QQuickWindow *window = qobject_cast<QQuickWindow *>(root); - QObject::connect(window, SIGNAL(frameSwapped()), layoutHandler, SLOT(slotActivateSurface())); - - QList<QObject *> sobjs = engine.rootObjects(); - StatusBarModel *statusBar = sobjs.first()->findChild<StatusBarModel *>("statusBar"); - statusBar->init(bindingAddress, engine.rootContext()); + QQmlContext *context = engine.rootContext(); + context->setContextProperty("bindingAddress", bindingAddress); + + context->setContextProperty("homescreenHandler", homescreenHandler); + context->setContextProperty("launcher", launcher); + context->setContextProperty("weather", new Weather(bindingAddress)); + context->setContextProperty("bluetooth", new Bluetooth(bindingAddress, context)); + context->setContextProperty("speechChromeController", new ChromeController(bindingAddress, &engine)); + // we add it here even if we don't use it + context->setContextProperty("shell", aglShell); + + /* instead of loading main.qml we load one-by-one each of the QMLs, + * devided now between several surfaces: panels, background. + */ + load_agl_shell_app(native, &engine, agl_shell, bindingAddress); return a.exec(); } diff --git a/homescreen/src/shell.cpp b/homescreen/src/shell.cpp new file mode 100644 index 0000000..1549fcd --- /dev/null +++ b/homescreen/src/shell.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2019 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.h" +#include <qpa/qplatformnativeinterface.h> +#include <stdio.h> + +static struct wl_output * +getWlOutput(QPlatformNativeInterface *native, QScreen *screen) +{ + void *output = native->nativeResourceForScreen("output", screen); + return static_cast<struct ::wl_output*>(output); +} + +void Shell::activate_app(QWindow *win, const QString &app_id) +{ + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + QScreen *screen = win->screen(); + + struct wl_output *output = getWlOutput(native, screen); + + qDebug() << "++ activating app_id " << app_id.toStdString().c_str(); + + agl_shell_activate_app(this->shell.get(), + app_id.toStdString().c_str(), + output); +} diff --git a/homescreen/src/shell.h b/homescreen/src/shell.h new file mode 100644 index 0000000..664b855 --- /dev/null +++ b/homescreen/src/shell.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2019 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 SHELLHANDLER_H +#define SHELLHANDLER_H + +#include <QObject> +#include <QString> +#include <QScreen> +#include <QWindow> +#include <memory> +#include "wayland-agl-shell-client-protocol.h" + +/* + * Basic type to wrap the agl_shell wayland object into a QObject, so that it + * can be used in callbacks from QML. + */ + +class Shell : public QObject +{ + Q_OBJECT + +public: + std::shared_ptr<struct agl_shell> shell; + + Shell(std::shared_ptr<struct agl_shell> shell, QObject *parent = nullptr) : + QObject(parent), shell(shell) + {} +public slots: + void activate_app(QWindow *win, const QString &app_id); +}; + +#endif // SHELLHANDLER_H diff --git a/package/config.xml b/package/config.xml index d866513..8b2a749 100644 --- a/package/config.xml +++ b/package/config.xml @@ -11,7 +11,7 @@ <param name="network-manager" value="ws" /> <param name="weather" value="ws" /> <param name="Bluetooth-Manager" value="ws" /> - <param name="windowmanager" value="ws" /> + <!-- <param name="windowmanager" value="ws" /> --> <param name="audiomixer" value="ws" /> <param name="vshl-core" value="ws" /> </feature> |