Conversion to using agl-compositor 45/24945/1 9.99.2 jellyfish/9.99.2 jellyfish_9.99.2
authorMarius Vlad <marius.vlad@collabora.com>
Wed, 17 Jun 2020 12:53:22 +0000 (15:53 +0300)
committerMarius Vlad <marius.vlad@collabora.com>
Mon, 6 Jul 2020 13:19:44 +0000 (16:19 +0300)
Bug-AGL: SPEC-3382

Signed-off-by: Marius Vlad <marius.vlad@collabora.com>
Change-Id: I1d9bed80473069782f5ffd8b60669aeefdbeca67

app/CMakeLists.txt
app/main.cpp
app/protocol/agl-shell.xml [new file with mode: 0644]
conf.d/wgt/config.xml.in

index 3911e6a..e99daa7 100644 (file)
@@ -24,22 +24,51 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(OE_QMAKE_PATH_EXTERNAL_HOST_BINS $ENV{OE_QMAKE_PATH_HOST_BINS})
 
 find_package(Qt5 COMPONENTS Core Gui QuickControls2 QuickWidgets REQUIRED)
+find_package(Qt5Gui ${QT_MIN_VERSION} CONFIG REQUIRED Private)
 find_package(PkgConfig REQUIRED)
+find_program(WAYLAND_SCANNER_EXECUTABLE NAMES wayland-scanner)
 
 qt5_add_resources(RESOURCES cluster-gauges.qrc images/images.qrc)
 
 PROJECT_TARGET_ADD(cluster-gauges)
 
+add_custom_command(
+       OUTPUT  agl-shell-client-protocol.h
+       COMMAND ${WAYLAND_SCANNER_EXECUTABLE} client-header
+       < ${CMAKE_SOURCE_DIR}/app/protocol/agl-shell.xml
+       > ${CMAKE_SOURCE_DIR}/app/agl-shell-client-protocol.h
+       DEPENDS ${CMAKE_SOURCE_DIR}/app/protocol/agl-shell.xml
+)
+
+add_custom_command(
+       OUTPUT  ${CMAKE_BINARY_DIR}/app/agl-shell-client-protocol.h
+       COMMAND ${WAYLAND_SCANNER_EXECUTABLE} client-header
+       < ${CMAKE_SOURCE_DIR}/app/protocol/agl-shell.xml
+       > ${CMAKE_SOURCE_DIR}/app/agl-shell-client-protocol.h
+       DEPENDS ${CMAKE_SOURCE_DIR}/app/protocol/agl-shell.xml
+)
+
+add_custom_command(
+       OUTPUT  agl-shell-protocol.c
+       COMMAND ${WAYLAND_SCANNER_EXECUTABLE} code
+       < ${CMAKE_SOURCE_DIR}/app/protocol/agl-shell.xml
+       > ${CMAKE_BINARY_DIR}/app/agl-shell-protocol.c
+       DEPENDS ${CMAKE_SOURCE_DIR}/app/protocol/agl-shell.xml
+)
+
 add_executable(${TARGET_NAME}
        main.cpp
+       agl-shell-protocol.c
+       agl-shell-client-protocol.h
        ${RESOURCES}
 )
 
-pkg_check_modules(QLIBWINMGR REQUIRED qlibwindowmanager)
 pkg_check_modules(QTAPPFW REQUIRED qtappfw-signal-composer)
 pkg_check_modules(GLIB REQUIRED glib-2.0)
+pkg_check_modules(WAYLAND_CLIENT REQUIRED wayland-client)
 
 include_directories(
+       include_directories(${Qt5Gui_PRIVATE_INCLUDE_DIRS})
        ${QTAPPFW_INCLUDE_DIRS}
        ${GLIB_INCLUDE_DIRS}
 )
@@ -47,7 +76,7 @@ include_directories(
 set_target_properties(${TARGET_NAME} PROPERTIES
        LABELS "EXECUTABLE"
        PREFIX ""
-       COMPILE_FLAGS "${QLIBWINMGR_FLAGS} ${QTAPPFW_FLAGS} ${GLIB_FLAGS} ${EXTRAS_CFLAGS} -DFOR_AFB_BINDING"
+       COMPILE_FLAGS "${QTAPPFW_FLAGS} ${GLIB_FLAGS} ${EXTRAS_CFLAGS} -DFOR_AFB_BINDING"
        LINK_FLAGS "${BINDINGS_LINK_FLAG}"
        LINK_LIBRARIES "${EXTRAS_LIBRARIES}"
        OUTPUT_NAME "${TARGET_NAME}"
@@ -56,7 +85,7 @@ set_target_properties(${TARGET_NAME} PROPERTIES
 target_link_libraries(${TARGET_NAME}
        Qt5::QuickControls2
        Qt5::QuickWidgets
-       ${QLIBWINMGR_LIBRARIES}
        ${QTAPPFW_LIBRARIES}
        ${GLIB_LDFLAGS}
+       ${WAYLAND_CLIENT_LIBRARIES}
 )
index a5b9ece..3c460d5 100644 (file)
  */
 
 #include <QtCore/QDebug>
+#include <QGuiApplication>
 #include <QtCore/QCommandLineParser>
 #include <QtCore/QUrlQuery>
 #include <QtGui/QGuiApplication>
 #include <QtQml/QQmlApplicationEngine>
 #include <QtQml/QQmlContext>
+#include <QtQml/QQmlComponent>
 #include <QtQml/qqml.h>
 #include <QQuickWindow>
 #include <QtQuickControls2/QQuickStyle>
+#include <qpa/qplatformnativeinterface.h>
+#include <QTimer>
 #include <glib.h>
+#include <QDebug>
+#include <QScreen>
 
-#include <qlibwindowmanager.h>
 #include <signalcomposer.h>
+#include <wayland-client.h>
+#include "agl-shell-client-protocol.h"
 
 // Global indicating whether canned animation should run
 bool runAnimation = true;
 
+static void
+global_add(void *data, struct wl_registry *reg, uint32_t name,
+          const char *interface, uint32_t version)
+{
+       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, 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 *
+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, &registry_listener, &shell);
+       wl_display_roundtrip(wl);
+       wl_registry_destroy(registry);
+
+       return shell;
+}
+
+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);
+}
+
 void read_config(void)
 {
        GKeyFile* conf_file;
@@ -64,69 +127,98 @@ void read_config(void)
 
 }
 
+static struct wl_surface *
+create_component(QPlatformNativeInterface *native, QQmlComponent *comp,
+               QScreen *screen, QObject **qobj)
+{
+       QObject *obj = comp->create();
+       //QObject *screen_obj = new QScreen(screen);
+       obj->setParent(screen);
+
+       QWindow *win = qobject_cast<QWindow *>(obj);
+       *qobj = obj;
+
+       return getWlSurface(native, win);
+}
+
+
 int main(int argc, char *argv[])
 {
-    // Slight hack, using the homescreen role greatly simplifies things wrt
-    // the windowmanager
-    QString myname = QString("homescreen");
-
-    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();
-
-    QQmlApplicationEngine engine;
-
-    if (positionalArguments.length() == 2) {
-        int port = positionalArguments.takeFirst().toInt();
-        QString secret = positionalArguments.takeFirst();
-        QUrl bindingAddress;
-        bindingAddress.setScheme(QStringLiteral("ws"));
-        bindingAddress.setHost(QStringLiteral("localhost"));
-        bindingAddress.setPort(port);
-        bindingAddress.setPath(QStringLiteral("/api"));
-        QUrlQuery query;
-        query.addQueryItem(QStringLiteral("token"), secret);
-        bindingAddress.setQuery(query);
-        QQmlContext *context = engine.rootContext();
-        context->setContextProperty(QStringLiteral("bindingAddress"), bindingAddress);
-
-        std::string token = secret.toStdString();
-        QLibWindowmanager* qwm = new QLibWindowmanager();
-
-        // WindowManager
-        if(qwm->init(port, secret) != 0){
-            exit(EXIT_FAILURE);
-        }
-
-        // Request a surface as described in layers.json windowmanager’s file
-        if (qwm->requestSurface(myname) != 0) {
-            exit(EXIT_FAILURE);
-        }
-
-        // Create an event callback against an event type. Here a lambda is called when SyncDraw event occurs
-        qwm->set_event_handler(QLibWindowmanager::Event_SyncDraw, [qwm, myname](json_object*) {
-            fprintf(stderr, "Surface got syncDraw!\n");
-            qwm->endDraw(myname);
-        });
-
-        context->setContextProperty("SignalComposer", new SignalComposer(bindingAddress, context));
-        read_config();
-        context->setContextProperty("runAnimation", runAnimation);
-
-        engine.load(QUrl(QStringLiteral("qrc:/cluster-gauges.qml")));
-
-        // Find the instantiated model QObject and connect the signals/slots
-        QList<QObject *> mobjs = engine.rootObjects();
-
-        QQuickWindow *window = qobject_cast<QQuickWindow *>(mobjs.first());
-        QObject::connect(window, SIGNAL(frameSwapped()), qwm, SLOT(slotActivateSurface()));
-    }
-
-    return app.exec();
+       QString myname = QString("cluster-gauges");
+       struct agl_shell *agl_shell;
+       struct wl_output *output;
+
+       QObject *qobj_bg;
+       QScreen *screen;
+
+       QGuiApplication app(argc, argv);
+       app.setDesktopFileName(myname);
+       QPlatformNativeInterface *native = qApp->platformNativeInterface();
+
+       agl_shell = register_agl_shell(native);
+       if (!agl_shell) {
+               exit(EXIT_FAILURE);
+       }
+
+       std::shared_ptr<struct agl_shell> shell{agl_shell, agl_shell_destroy};
+
+       screen = qApp->primaryScreen();
+       output = getWlOutput(native, screen);
+
+       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();
+
+       QQmlApplicationEngine engine;
+       QQmlContext *context = engine.rootContext();
+
+       if (positionalArguments.length() == 2) {
+               int port = positionalArguments.takeFirst().toInt();
+               QString secret = positionalArguments.takeFirst();
+
+               QUrl bindingAddress;
+               QUrlQuery query;
+
+               struct wl_surface *bg;
+
+               bindingAddress.setScheme(QStringLiteral("ws"));
+               bindingAddress.setHost(QStringLiteral("localhost"));
+               bindingAddress.setPort(port);
+               bindingAddress.setPath(QStringLiteral("/api"));
+
+               query.addQueryItem(QStringLiteral("token"), secret);
+               bindingAddress.setQuery(query);
+
+               read_config();
+
+               context->setContextProperty(QStringLiteral("bindingAddress"),
+                                           bindingAddress);
+
+               context->setContextProperty("SignalComposer",
+                                           new SignalComposer(bindingAddress,
+                                                              context));
+               context->setContextProperty("runAnimation", runAnimation);
+
+               QQmlComponent bg_comp(&engine, QUrl("qrc:/cluster-gauges.qml"));
+               qDebug() << bg_comp.errors();
+
+               bg = create_component(native, &bg_comp, screen, &qobj_bg);
+
+               // set the surface as the background
+               agl_shell_set_background(agl_shell, bg, output);
+
+               // instruct the compositor it can display after Qt has a chance
+               // to load everything
+               QTimer::singleShot(500, [agl_shell](){
+                       qDebug() << "agl_shell ready!";
+                       agl_shell_ready(agl_shell);
+               });
+       }
+
+       return app.exec();
 }
diff --git a/app/protocol/agl-shell.xml b/app/protocol/agl-shell.xml
new file mode 100644 (file)
index 0000000..1096c64
--- /dev/null
@@ -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>
index 40796be..f8e02c8 100644 (file)
@@ -7,7 +7,6 @@
   <author>@PROJECT_AUTHOR@ &lt;@PROJECT_AUTHOR_MAIL@&gt;</author>
   <license>@PROJECT_LICENSE@</license>
   <feature name="urn:AGL:widget:required-api">
-    <param name="windowmanager" value="ws" />
     <param name="signal-composer" value="ws" />
   </feature>
   <feature name="urn:AGL:widget:required-permission">