diff options
author | Daniel d'Andrada <daniel.dandrada@luxoft.com> | 2018-07-10 14:40:25 +0200 |
---|---|---|
committer | Robert Griebl <robert.griebl@pelagicore.com> | 2018-07-11 11:57:12 +0000 |
commit | ba2a7ede9e1689c03099cd0ef619bc18cda29ef4 (patch) | |
tree | 043a0d1581988a56b5fd9956814defa774f3a031 /examples/applicationmanager | |
parent | c414cb25d0cc2f7ce33e21e15ff52bc14d6421ad (diff) |
Fix installation of the examples
- Only the project files and wrapper scripts were being installed
- They were being put directly into Qt's example dir. They should be grouped
into a applicationmanager subdirectory there, similarly to what other
modules do.
Task-number: AUTOSUITE-591
Change-Id: I1da6b28a8fe2e9210ad109309d30dfc1ad0d1e99
Reviewed-by: Robert Griebl <robert.griebl@pelagicore.com>
Diffstat (limited to 'examples/applicationmanager')
48 files changed, 3585 insertions, 0 deletions
diff --git a/examples/applicationmanager/applicationmanager.pro b/examples/applicationmanager/applicationmanager.pro new file mode 100644 index 00000000..bf10ac2c --- /dev/null +++ b/examples/applicationmanager/applicationmanager.pro @@ -0,0 +1,14 @@ +TEMPLATE = subdirs + +SUBDIRS = \ + minidesk \ + monitor \ + multi-views \ + startup-plugin \ + +# remove the !headless and handle this in the example when we switch to the new configure system +!headless:SUBDIRS += \ + custom-appman \ + +linux:!android:SUBDIRS += \ + softwarecontainer-plugin \ diff --git a/examples/applicationmanager/custom-appman/custom-appman.cpp b/examples/applicationmanager/custom-appman/custom-appman.cpp new file mode 100644 index 00000000..decccd08 --- /dev/null +++ b/examples/applicationmanager/custom-appman/custom-appman.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +#include <QtAppManCommon/global.h> +#include <QtAppManCommon/logging.h> +#include <QtAppManMain/main.h> +#include <QtAppManMain/defaultconfiguration.h> +#include <QtAppManPackage/package.h> +#include <QtAppManInstaller/sudo.h> + + +QT_USE_NAMESPACE_AM + +Q_DECL_EXPORT int main(int argc, char *argv[]) +{ + QCoreApplication::setApplicationName(qSL("Custom ApplicationManager")); + QCoreApplication::setApplicationVersion("0.1"); + + Logging::initialize(argc, argv); + + Package::ensureCorrectLocale(); + + try { + QStringList deploymentWarnings; + Sudo::forkServer(Sudo::DropPrivilegesPermanently, &deploymentWarnings); + + Main a(argc, argv); + + DefaultConfiguration cfg; + cfg.parse(); + + a.setup(&cfg, deploymentWarnings); + a.loadQml(cfg.loadDummyData()); + a.showWindow(cfg.fullscreen() && !cfg.noFullscreen()); + + return MainBase::exec(); + } catch (const std::exception &e) { + qCCritical(LogSystem) << "ERROR:" << e.what(); + return 2; + } +} diff --git a/examples/applicationmanager/custom-appman/custom-appman.pro b/examples/applicationmanager/custom-appman/custom-appman.pro new file mode 100644 index 00000000..e1ebab9e --- /dev/null +++ b/examples/applicationmanager/custom-appman/custom-appman.pro @@ -0,0 +1,22 @@ +TEMPLATE = app +TARGET = custom-appman + +CONFIG += c++11 link_pkgconfig exceptions console +CONFIG -= app_bundle qml_debug + +DEFINES += QT_MESSAGELOGCONTEXT + +QT = appman_main-private + +SOURCES = custom-appman.cpp + +OTHER_FILES += \ + doc/src/*.qdoc \ + doc/images/*.png \ + +target.path = $$[QT_INSTALL_EXAMPLES]/applicationmanager/custom-appman +INSTALLS += target + +example_sources.path = $$target.path +example_sources.files = $SOURCES doc +INSTALLS += example_sources diff --git a/examples/applicationmanager/custom-appman/doc/images/custom-appman.png b/examples/applicationmanager/custom-appman/doc/images/custom-appman.png Binary files differnew file mode 100644 index 00000000..64bae006 --- /dev/null +++ b/examples/applicationmanager/custom-appman/doc/images/custom-appman.png diff --git a/examples/applicationmanager/custom-appman/doc/src/custom-appman.qdoc b/examples/applicationmanager/custom-appman/doc/src/custom-appman.qdoc new file mode 100644 index 00000000..5ea6d44f --- /dev/null +++ b/examples/applicationmanager/custom-appman/doc/src/custom-appman.qdoc @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:FDL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + +\example custom-appman +\title Implementing a Custom Application-Manager Example +\image custom-appman.png Screenshot +\brief Basic structure and starting point for a custom application-manager executable. +\ingroup applicationmanager-examples + +\section1 Introduction + +The application-manager is compiled as a self-contained executable that can be configured +in large parts through the YAML based config file system and startup plugins. However it may still +be necessary to implement a custom application-manager executable to have more influence over the +startup behavior. + +\note Please note however, that all C++ classes in the application-manager modules are considered private +API at the moment, so there are no compatibility guarantees at all. + +If you still desire to go down that road however, this example will provide you with an starting +point to build your custom implementation upon. + +Keep in mind though, that this custom application-manager executable will need a System-UI to +display something on the screen, just as the standard \c appman executable. + +\section1 Walkthrough + +Following is a breakdown of the minimal code needed for such a custom implementation: + +\quotefromfile custom-appman/custom-appman.cpp +\skipto #include +\printuntil QT_USE_NAMESPACE_AM + +The application-manager is split into functional building blocks/libraries. These includes will +pull in the basic set of classes needed. +In order to avoid possible clashes with QML plugins, all of the application-manager's symbols are +namespaced - \c QT_USE_NAMESPACE_AM will expand to the matching \c using statement. + +\skipto QCoreApplication::setApplicationName +\printuntil QCoreApplication::setApplicationVersion + +Not application-manager specific, but having an application name and version set is generally +a good idea. + +\printline Logging::init + +We want the logging part of the application-manager initialized as early as possible, especially +when dealing with DLT logging. + +\printline Package::ensure + +If you are using the installer part of the application-manager, this function needs to be called +\e before the QApplication constructor to make sure your C locale is an UTF-8 variant (this is a +requirement in order to get deterministic results when using \c libarchive with non-ASCII filenames). + +\printto try + +Again, for the installer part only, an additional setup step is necessary before running the +QApplication constructor: if the executable is setuid-root, this call will \c fork of a child +process which keeps the root privileges while the main process permanently drop them. + +\printuntil return 2 +\printline } + +This \c try block is the heart of the custom application-manager. You need to create a \c Main +(which is a class derived from QGuiApplication) object plus a suitable configuration object: in +this simple case we just use the application-manager's default YAML parsing, so we instantiate +a \c DefaultConfiguration object. +The rest of the function consists of parsing the configuration and then calling the relevant +setup routines on the \c Main object. +Since \c Main can be derived differently depending on your application-manager configuration +(headless, with widgets or standard), you would need to know the correct base-class for the exec() +call - the \c MainBase typedef will circumvent that problem though. + +Keep in mind that most functions in the application-manager will throw exceptions that are +derived from \c std::exception, so a \c catch handler is a must. + +*/ diff --git a/examples/applicationmanager/minidesk/am-config.yaml b/examples/applicationmanager/minidesk/am-config.yaml new file mode 100644 index 00000000..269e20c7 --- /dev/null +++ b/examples/applicationmanager/minidesk/am-config.yaml @@ -0,0 +1,37 @@ +formatVersion: 1 +formatType: am-configuration +--- +installationLocations: +- id: "internal-0" + installationPath: "/tmp/minidesk/apps" + documentPath: "/tmp/minidesk/docs" + mountPoint: "/tmp" + isDefault: true + +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" + installedAppsManifestDir: "/tmp/minidesk/manifests" + appImageMountDir: "/tmp/minidesk/image-mounts" + database: "/tmp/minidesk/apps.db" + +logging: + rules: + - "*=false" + - "qt.*=false" + - "am.*=false" + - "qml*=true" + - "*.warning=true" + - "*.critical=true" + +ui: + fullscreen: no + mainQml: "${CONFIG_PWD}/system-ui/main.qml" + +systemProperties: + protected: + light: on + +# development setup: +flags: + noSecurity: yes + noUiWatchdog: yes diff --git a/examples/applicationmanager/minidesk/apps/tld.minidesk.app1/app1.qml b/examples/applicationmanager/minidesk/apps/tld.minidesk.app1/app1.qml new file mode 100644 index 00000000..76ab5dc0 --- /dev/null +++ b/examples/applicationmanager/minidesk/apps/tld.minidesk.app1/app1.qml @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager 1.0 + +ApplicationManagerWindow { + id: root + color: "peachpuff" + + Rectangle { + anchors.centerIn: parent + width: 180; height: 180; radius: width/4 + color: "peru" + + Image { + source: ApplicationInterface.icon + anchors.centerIn: parent + } + + RotationAnimation on rotation { + id: rotation + from: 0; to: 360; loops: Animation.Infinite; duration: 4000 + } + + MouseArea { + anchors.fill: parent + onClicked: { + if (rotation.paused) { + rotation.resume(); + } else { + rotation.pause(); + root.setWindowProperty("rotation", parent.rotation); + } + popUp.visible = rotation.paused; + } + } + } + + ApplicationManagerWindow { + id: popUp + visible: false + color: "lightcoral" + + Text { + anchors.centerIn: parent + text: "App1 paused!" + } + + Component.onCompleted: setWindowProperty("type", "pop-up"); + } +} diff --git a/examples/applicationmanager/minidesk/apps/tld.minidesk.app1/icon.png b/examples/applicationmanager/minidesk/apps/tld.minidesk.app1/icon.png Binary files differnew file mode 100644 index 00000000..adb840ce --- /dev/null +++ b/examples/applicationmanager/minidesk/apps/tld.minidesk.app1/icon.png diff --git a/examples/applicationmanager/minidesk/apps/tld.minidesk.app1/info.yaml b/examples/applicationmanager/minidesk/apps/tld.minidesk.app1/info.yaml new file mode 100644 index 00000000..953e852f --- /dev/null +++ b/examples/applicationmanager/minidesk/apps/tld.minidesk.app1/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'tld.minidesk.app1' +icon: 'icon.png' +code: 'app1.qml' +runtime: 'qml' +name: + en: 'App1' diff --git a/examples/applicationmanager/minidesk/apps/tld.minidesk.app2/app2.qml b/examples/applicationmanager/minidesk/apps/tld.minidesk.app2/app2.qml new file mode 100644 index 00000000..88b1f745 --- /dev/null +++ b/examples/applicationmanager/minidesk/apps/tld.minidesk.app2/app2.qml @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager 1.0 + +ApplicationManagerWindow { + color: ApplicationInterface.systemProperties.light ? "peachpuff" : "black" + + Image { + anchors.centerIn: parent + source: ApplicationInterface.icon + + MouseArea { + anchors.fill: parent + onClicked: { + var notification = ApplicationInterface.createNotification(); + notification.summary = "Let there be light!" + notification.show(); + } + } + } + + onWindowPropertyChanged: console.log("App2: onWindowPropertyChanged - " + name + ": " + value); + + Connections { + target: ApplicationInterface + onOpenDocument: console.log("App2: onOpenDocument - " + documentUrl); + onQuit: target.acknowledgeQuit(); + } + + ApplicationInterfaceExtension { + id: extension + name: "tld.minidesk.interface" + + onReadyChanged: console.log("App2: circumference function returned: " + + object.circumference(2.0, "plate") + ", it used pi = " + object.pi); + } + + Connections { + target: extension.object + onComputed: console.log("App2: " + what + " has been computed"); + onPiChanged: console.log("App2: pi changed: " + target.pi); + } +} diff --git a/examples/applicationmanager/minidesk/apps/tld.minidesk.app2/icon.png b/examples/applicationmanager/minidesk/apps/tld.minidesk.app2/icon.png Binary files differnew file mode 100644 index 00000000..3f8caa42 --- /dev/null +++ b/examples/applicationmanager/minidesk/apps/tld.minidesk.app2/icon.png diff --git a/examples/applicationmanager/minidesk/apps/tld.minidesk.app2/info.yaml b/examples/applicationmanager/minidesk/apps/tld.minidesk.app2/info.yaml new file mode 100644 index 00000000..65759aa8 --- /dev/null +++ b/examples/applicationmanager/minidesk/apps/tld.minidesk.app2/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'tld.minidesk.app2' +icon: 'icon.png' +code: 'app2.qml' +runtime: 'qml' +name: + en: 'App2' diff --git a/examples/applicationmanager/minidesk/doc/images/minidesk.png b/examples/applicationmanager/minidesk/doc/images/minidesk.png Binary files differnew file mode 100644 index 00000000..0a16982a --- /dev/null +++ b/examples/applicationmanager/minidesk/doc/images/minidesk.png diff --git a/examples/applicationmanager/minidesk/doc/src/minidesk.qdoc b/examples/applicationmanager/minidesk/doc/src/minidesk.qdoc new file mode 100644 index 00000000..ad85c36e --- /dev/null +++ b/examples/applicationmanager/minidesk/doc/src/minidesk.qdoc @@ -0,0 +1,256 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:FDL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + +\example minidesk +\title Desktop System-UI Example +\image minidesk.png Screenshot +\brief Minimal Desktop System-UI in pure QML +\ingroup applicationmanager-examples + +\section1 Introduction + +The \e {Desktop System-UI Example} showcases the application-manager API in a simple fashion. The +focus is on the concepts, less on elegance or completeness, for instance no error checking is done. +Some features will only print debug messages without further functionality. A classic desktop with +server-side window decorations is emulated. + +The following features are supported: +\list +\li Start applications by clicking an icon on the top left +\li Stop applications by clicking on the top/left window decoration rectangle +\li Raise applications by clicking on the decoration +\li Drag windows by pressing on the window decoration and moving +\li System-UI sends 'propA' change when an app is started +\li System-UI and App2 react on window property changes with a debug message +\li App1 animation can be stopped and restarted by a click +\li App1 sends 'rotation' property to System-UI on stop +\li App1 shows a pop-up on the System-UI while it is paused +\li App2 will make use of an IPC extension when it is started +\li App2 logs the document URL it has been started with +\li App2 triggers a notification in the System-UI when the bulb icon is clicked +\li A separate (\"wayland\") process started outside of appman will be shown as App1 +\endlist + +\note The example can be run in single- and multi-process mode. In the following, multi-process is +assumed and corresponding terminology is used. The terms \e client and \e application, respectively +\e server and \e {System-UI} are used interchangeably. The System-UI comprises compositing and +generic inter-process communication (IPC). + +\section2 Invocation +The example can be started from within the minidesk folder with: +\badcode +path/to/bin/appman -c am-config.yaml -r +\endcode + +\note The application-manager attempts to register a \c freedesktop.org compliant notification +server. DBus errors might occur if it conflicts with the server running on the host desktop +environment. In this case, a private session bus needs to be started by adding the +\c --start-session-dbus option: +\badcode +path/to/bin/appman -c am-config.yaml -r --start-session-dbus +\endcode + +\section1 Walkthrough + +\section2 System-UI Window + +\quotefromfile minidesk/system-ui/main.qml +\skipto import Qt +\printuntil text: +\printline } +\dots + +The \l{QtApplicationManager} module has to be imported to be able to access the application-manager +APIs. The System-UI window has a fixed size and linen background color. Instead of a \l{Rectangle}, +the root element could as well be a \l{Window}. On top of the background the +\l{minidesk/system-ui/Readme.qml}{Readme} element is shown that displays a usage how-to. At the +bottom left there is a textual indication for whether the application-manager runs in single- or +multi-process mode. + +\section2 Launcher + +\printto System-UI chrome for applications + +A \l{Repeater} provides the application icons arranged in a \l{Column} on the top-left corner of +the System-UI. This is achieved by using the \l{ApplicationManager} element as the model. Amongst +others, it provides the \c icon role which is used as the \l{Image} source URL. The \c icon URL is +defined in the \l{Manifest Definition}{info.yaml} file of the application. To indicate that an +application has been launched, the opacity of the corresponding application's icon is decreased +through a binding to the \c isRunning role. + +Clicking on an application icon will launch the corresponding application through a call to +\l {ApplicationManager::startApplication()}{ApplicationManager.startApplication()}. The first +argument, \c applicationId, is provided by the ApplicationManager model (e.g. "tld.minidesk.app1" +for the first application). Both applications will be started with the (optional) document URL +"documentUrl". Subsequent clicks on an already launched icon will still call +\l{ApplicationManager::}{startApplication()}, but will be silently ignored. The content of the +\c appContainter will be set below in the \c onWindowReady handler. The content is provided by the +two applications in an \l{ApplicationManagerWindow} root element. + +\section2 Application Windows in the System-UI + +\printto System-UI for a pop-up and notification + +This second \l{Repeater} provides the window chrome for the actual applications in its delegate. +The model is the same as for the first \l{Repeater}, essentially the \l{ApplicationManager} +element. The chrome consists of: +\list +\li A fixed size window \l{Rectangle} with a "tan" color. The default location depends on the + \c model.index, hence each application has a different location. +\li The name of the application, prefixed with "Decoration" on the top horizontal center. The name + is also provided by the \l{ApplicationManager} model and read from the application's + \l{Manifest Definition}{info.yaml} file. +\li A \l{MouseArea} for dragging the window and setting the z-order. The \l{MouseArea} fills the + entire window, though the application container is placed on top of it and hence it will not + handle dragging. Simply increasing the z-order on each click is only done to keep the code to + a minimum. +\li A small chocolate-colored \l Rectangle on the top left corner for closing the window chrome and + stopping the actual application (see + \l{ApplicationManager::stopApplication()}{stopApplication()}). +\li A container \l{Item} where the actual application will be placed in. The \c appContainer is + exposed, so it can later be referenced to place the actual application window in it. +\endlist + +\section2 Pop-ups + +Two approaches are implemented to display pop-ups in the System-UI: +\list +\li Through a window rendered by the client application +\li Through the notification API provided by the application-manager +\endlist +This is the corresponding System-UI code: +\printto Handler for WindowManager signals + +\section3 Client Application Rendering +The \c popUpContainer \l{Item} provides a System-UI recipient for the pop-up rendered +by App1. App1 instantiates another \l{ApplicationManagerWindow} for the pop-up within its +\l{ApplicationManagerWindow} root element, as shown here: +\quotefromfile minidesk/apps/tld.minidesk.app1/app1.qml +\skipto Rectangle +\skipto ApplicationManagerWindow +\printuntil Component.onCompleted +\printline } +The \l{ApplicationManagerWindow::setWindowProperty}{ApplicationManagerWindow.setWindowProperty()} +method is used to set a freely selectable shared property. Here we choose \c{type: "pop-up"} to +indicate that the window is supposed to be shown as a pop-up. In the \c onWindowReady handler below +the System-UI checks for this property and handles the window appropriately as a pop-up. + +\section3 Notification API Usage + +An alternative to the above approach is to use the application-manager's \l{Notification} API on +the application (client) side and the \l{NotificationManager} API on the System-UI (server) side. +The following code is invoked when the \e bulb icon of App2 is clicked: + +\quotefromfile minidesk/apps/tld.minidesk.app2/app2.qml +\skipto var notification +\printuntil notification.show(); + +App2 creates a new \l{Notification} element, sets its \l{Notification::summary}{summary} property +and calls \l{Notification::show()}{show()} on it. This call will increase the +\l{NotificationManager::count}{NotificationManager.count} on the System-UI side, and subsequently +the \l{Text} element's text property will be set to the \c summary string of the first +notification. Presenting the first notification only is a simplification to keep the code short. + +\section2 WindowManager Signal Handler + +\quotefromfile minidesk/system-ui/main.qml +\skipto Handler for WindowManager signals +\printto IPC extension +This is the vital part of the System-UI, where the window surfaces of the applications are mapped +to items in the System-UI. The \l {WindowManager::windowReady}{onWindowReady} handler is invoked +when a new application window is shown (visibility set to true). The \c index parameter references +into the \l{WindowManager} model, which holds the window surfaces. The first line translates this +index to the application index of the \l{ApplicationManager} model and assigns it to \c appIndex. + +Only the "pop-up" ApplicationManagerWindow of App1 has the user-defined \c type property set. All +other windows don't have the \c type property. In the latter case, the application's \c window +passed to \c onWindowReady is re-parented to the System-UI's \c winChrome. Also the size of the +window is set to fill the entire \c appContainer. + +There is a special treatment for an invalid \c appIndex for demonstration purposes: it is assumed +that the window surface is from an external application that follows the Wayland protocol. For +example a Qt application could be started on the command line as follows: + +\badcode +QT_WAYLAND_DISABLE_WINDOWDECORATION=1 ./qtapp -platform wayland +\endcode + +This application will then be shown inside the App1 container. +\c QT_WAYLAND_DISABLE_WINDOWDECORATION is set to disable client side window decorations, since they +are provided by the System-UI. Note, that an application started in this way, can just as well be +closed only from the command line. + +Pop-ups (windows with type "pop-up") are re-parented to their respective \c popUpContainer. + +The \c onWindowClosing handler determines if the window is a top-level application window. If so, +the applications's \c winChrome is set to invisible and the corresponding application icon is set +to fully opaque to indicate that the application is not running. The \c onWindowLost handler calls +\l{WindowManager::releaseWindow()}{WindowManager.releaseWindow()} to free all resources associated +with the \c window. + + +\section2 IPC Extension +The following snippet demonstrates how the \l{ApplicationIPCInterface} can be used to define an IPC +extension. The IPC interface has to be defined in the System-UI, for instance: +\printuntil Component.onCompleted +\printline } + +Here, a property \c pi is defined, as well as a signal \c computed and a function \c circumference. +After registering this interface with +\l{ApplicationIPCManager::registerInterface()}{ApplicationIPCManager.registerInterface()}, it can +be used from the application processes. + +On the application side, the \l{ApplicationInterfaceExtension} type has to be used. Here is how +App2 makes use of this interface extension: + +\quotefromfile minidesk/apps/tld.minidesk.app2/app2.qml +\skipto ApplicationInterfaceExtension +\printuntil onPiChanged +\printline } + +The interface is used here immediately when it becomes ready. It can, of course, be accessed from +elsewhere, too. The \l{ApplicationInterfaceExtension::name}{ApplicationInterfaceExtension.name} has +to match the name it was registered with in +\l{ApplicationIPCManager::registerInterface()}{ApplicationIPCManager.registerInterface()}. + +\section2 Application Termination +When an application is stopped from the System-UI through +\l{ApplicationManager::stopApplication()}{ApplicationManager.stopApplication()}, it will be sent +the \l{ApplicationInterface::quit()}{ApplicationInterface.quit()} signal. The application can do +some clean-up and must subsequently confirm with +\l{ApplicationInterface::acknowledgeQuit()}{ApplicationInterface.acknowledgeQuit()}, like App2 does: +\quotefromfile minidesk/apps/tld.minidesk.app2/app2.qml +\skipto Connections +\printuntil onQuit +\printline } + +Note that App1 is not well-behaved: it does not acknowledge the \c quit signal and will hence simply +be terminated by the application-manager. + +*/ diff --git a/examples/applicationmanager/minidesk/minidesk.pro b/examples/applicationmanager/minidesk/minidesk.pro new file mode 100644 index 00000000..d12a6917 --- /dev/null +++ b/examples/applicationmanager/minidesk/minidesk.pro @@ -0,0 +1,27 @@ +TEMPLATE = app +CONFIG += am-systemui + +OTHER_FILES += \ + am-config.yaml \ + minidesk.qmlproject \ + doc/src/*.qdoc \ + doc/images/*.png \ + system-ui/*.qml \ + apps/tld.minidesk.app1/*.yaml \ + apps/tld.minidesk.app1/*.qml \ + apps/tld.minidesk.app1/*.png \ + apps/tld.minidesk.app2/*.yaml \ + apps/tld.minidesk.app2/*.qml \ + apps/tld.minidesk.app2/*.png \ + +target.path = $$[QT_INSTALL_EXAMPLES]/applicationmanager/minidesk +INSTALLS += target + +AM_COPY_DIRECTORIES += apps system-ui +AM_COPY_FILES += am-config.yaml + +AM_DEFAULT_ARGS=-c am-config.yaml --start-session-dbus --verbose + +example_sources.path = $$target.path +example_sources.files = $$AM_COPY_FILES $$AM_COPY_DIRECTORIES doc +INSTALLS += example_sources diff --git a/examples/applicationmanager/minidesk/minidesk.qmlproject b/examples/applicationmanager/minidesk/minidesk.qmlproject new file mode 100644 index 00000000..5ce6345e --- /dev/null +++ b/examples/applicationmanager/minidesk/minidesk.qmlproject @@ -0,0 +1,17 @@ +import QmlProject 1.1 + +Project { + mainFile: "system-ui/main.qml" + + QmlFiles { + directory: "." + } + ImageFiles { + directory: "apps" + } + Files { + directory: "." + filter: "*.yaml" + } +} + diff --git a/examples/applicationmanager/minidesk/system-ui/Readme.qml b/examples/applicationmanager/minidesk/system-ui/Readme.qml new file mode 100644 index 00000000..01144436 --- /dev/null +++ b/examples/applicationmanager/minidesk/system-ui/Readme.qml @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.4 + +Item { + anchors.fill: parent + + Column { + width: paragraph.implicitWidth + height: heading.implicitHeight + paragraph.implicitHeight + 80 + anchors.centerIn: parent + spacing: 10 + + Text { + id: heading + color: "cornflowerblue" + font { pixelSize: 20; weight: Font.Bold } + text: "Minimal Desktop System-UI in pure QML" + } + + Text { + id: paragraph + color: "grey" + font.pixelSize: 16 + text: "The following features are supported:\n" + + "\u2022 Start applications by clicking an icon on the top left\n" + + "\u2022 Stop applications by clicking on the top/left window decoration rectangle\n" + + "\u2022 Raise applications by clicking on the decoration\n" + + "\u2022 Drag windows by pressing on the window decoration and moving\n" + + "\u2022 System-UI sends 'propA' change when an app is started\n" + + "\u2022 System-UI and App2 react on window property changes with a debug message\n" + + "\u2022 App1 animation can be stopped and restarted by a click\n" + + "\u2022 App1 sends 'rotation' property to System-UI on stop\n" + + "\u2022 App1 shows a pop-up on the System-UI while it is paused\n" + + "\u2022 App2 will make use of an IPC extension when it is started\n" + + "\u2022 App2 logs the document URL it has been started with\n" + + "\u2022 App2 triggers a notification in the System-UI when the bulb icon is clicked\n" + + "\u2022 A separate (\"wayland\") process started outside of appman will be shown as App1" + } + } +} diff --git a/examples/applicationmanager/minidesk/system-ui/main.qml b/examples/applicationmanager/minidesk/system-ui/main.qml new file mode 100644 index 00000000..1ba08ddc --- /dev/null +++ b/examples/applicationmanager/minidesk/system-ui/main.qml @@ -0,0 +1,238 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager 1.0 + +Rectangle { + width: 1024 + height: 640 + color: "linen" + + Readme {} + + Text { + anchors.bottom: parent.bottom + text: (ApplicationManager.singleProcess ? "Single" : "Multi") + "-Process Mode" + } + + // Application launcher panel + Column { + id: launcher + Repeater { + id: menuItems + model: ApplicationManager + + Image { + source: icon + opacity: isRunning ? 0.3 : 1.0 + + MouseArea { + anchors.fill: parent + onClicked: application.start(); + } + } + } + } + + // System-UI chrome for applications + Repeater { + model: ListModel { id: topLevelWindowsModel } + + delegate: Rectangle { + id: winChrome + + width: 400; height: 320 + z: model.index + color: "tan" + + property bool manuallyClosed: false + + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: "Decoration: " + model.window.application.name("en") + } + + MouseArea { + anchors.fill: parent + drag.target: parent + onPressed: topLevelWindowsModel.move(model.index, topLevelWindowsModel.count-1, 1); + } + + Rectangle { + width: 25; height: 25 + color: "chocolate" + + MouseArea { + anchors.fill: parent + onClicked: { + winChrome.manuallyClosed = true; + model.window.application.stop(); + } + } + } + + WindowItem { + anchors.fill: parent + anchors.margins: 3 + anchors.topMargin: 25 + window: model.window + } + + Component.onCompleted: { + winChrome.x = 300 + model.index * 50; + winChrome.y = 10 + model.index * 30; + } + + states: [ + State { + name: "open" + when: model.window && model.window.contentState === WindowObject.SurfaceWithContent && !manuallyClosed + PropertyChanges { + target: winChrome + opacity: 1 + scale: 1 + visible: true + } + } + ] + + opacity: 0.25 + scale: 0.50 + visible: false + + transitions: [ + Transition { + to: "open" + NumberAnimation { target: winChrome; properties: "opacity,scale"; duration: 500; easing.type: Easing.OutQuad} + }, + Transition { + from: "open" + SequentialAnimation { + PropertyAction { target: winChrome; property: "visible"; value: true } // we wanna see the window during the closing animation + NumberAnimation { target: winChrome; properties: "opacity,scale"; duration: 500; easing.type: Easing.InQuad} + ScriptAction { script: { + if (model.window.contentState === WindowObject.NoSurface) + topLevelWindowsModel.remove(model.index, 1); + } } + } + } + ] + } + } + + // System-UI for pop-ups + Repeater { + model: ListModel { id: popupsModel } + delegate: WindowItem { + z: 100 + model.index + width: 200; height: 60 + anchors.centerIn: parent + window: model.window + Connections { + target: model.window + onContentStateChanged: { + if (model.window.contentState === WindowObject.NoSurface) + popupsModel.remove(model.index, 1); + } + } + Rectangle { + width: 25 + height: 25 + anchors.horizontalCenter: parent.right + anchors.verticalCenter: parent.top + color: "chocolate" + Text { text: "X"; anchors.centerIn: parent } + MouseArea { + anchors.fill: parent + onClicked: model.window.close() + } + } + } + } + + // System-UI for a notification + Text { + z: 30001 + font.pixelSize: 46 + anchors.centerIn: parent + text: NotificationManager.count > 0 ? NotificationManager.get(0).summary : "" + } + + // Handler for WindowManager signals + Connections { + target: WindowManager + onWindowAdded: { + // separate windows by their type, adding them to their respective model + var model = window.windowProperty("type") === "pop-up" ? popupsModel : topLevelWindowsModel; + model.append({"window":window}); + } + } + + // IPC extension + ApplicationIPCInterface { + id: extension + + property double pi + signal computed(string what) + readonly property var _decltype_circumference: { "double": [ "double", "string" ] } + function circumference(radius, thing) { + console.log("SystemUI: circumference(" + radius + ", \"" + thing + "\") has been called"); + pi = 3.14; + var circ = 2 * pi * radius; + computed("circumference for " + thing); + return circ; + } + + Component.onCompleted: ApplicationIPCManager.registerInterface(extension, "tld.minidesk.interface", {}); + } +} diff --git a/examples/applicationmanager/monitor/am-config.yaml b/examples/applicationmanager/monitor/am-config.yaml new file mode 100644 index 00000000..5212d8fb --- /dev/null +++ b/examples/applicationmanager/monitor/am-config.yaml @@ -0,0 +1,33 @@ +formatVersion: 1 +formatType: am-configuration +--- + +installationLocations: +- id: "internal-0" + installationPath: "/tmp/am/apps" + documentPath: "/tmp/am/docs" + mountPoint: "/tmp" + isDefault: true + +applications: + builtinAppsManifestDir: "apps" + installedAppsManifestDir: "/tmp/am/manifests" + appImageMountDir: "/tmp/am/image-mounts" + database: "/tmp/am/apps.db" + +logging: + rules: + - "*=false" + - "qt.*=false" + - "am.*=false" + - "qml*=true" + - "*.warning=true" + - "*.critical=true" + +ui: + mainQml: "system-ui/main.qml" + +# development setup: +flags: + noSecurity: yes + noUiWatchdog: yes diff --git a/examples/applicationmanager/monitor/apps/tld.monitor.app/ApplicationWindow.qml b/examples/applicationmanager/monitor/apps/tld.monitor.app/ApplicationWindow.qml new file mode 100644 index 00000000..d326205a --- /dev/null +++ b/examples/applicationmanager/monitor/apps/tld.monitor.app/ApplicationWindow.qml @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager 1.0 + +ApplicationManagerWindow { + id: root + + property alias title: title.text + property alias idle: pause.duration + property string windowType + + color: "darkgreen" + + Text{ + id: title + color: "white" + anchors.centerIn: parent + } + + SequentialAnimation on color { + loops: Animation.Infinite + ColorAnimation { to: "darkolivegreen"; duration: 500 } + PauseAnimation { id: pause; duration: 0 } + ColorAnimation { to: "darkgreen"; duration: 500 } + PauseAnimation { duration: pause.duration } + } + + onWindowTypeChanged: root.setWindowProperty("windowType", windowType) +} diff --git a/examples/applicationmanager/monitor/apps/tld.monitor.app/app.qml b/examples/applicationmanager/monitor/apps/tld.monitor.app/app.qml new file mode 100644 index 00000000..e8c57e0a --- /dev/null +++ b/examples/applicationmanager/monitor/apps/tld.monitor.app/app.qml @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.6 +import QtApplicationManager 1.0 + +ApplicationWindow { + title: "Primary Window" + windowType: "primary" + + ApplicationWindow { + title: "Secondary Window" + windowType: "secondary" + visible: true + idle: 500 + } + + Timer { + property int count: 0 + running: true + repeat: true + onTriggered: { + if (count < 100) { + var arr = []; + var idx = 200000; + while (idx > 0) + arr[idx] = idx--; + count++; + } else { + repeat = false; + } + } + } +} diff --git a/examples/applicationmanager/monitor/apps/tld.monitor.app/dummyicon b/examples/applicationmanager/monitor/apps/tld.monitor.app/dummyicon new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/examples/applicationmanager/monitor/apps/tld.monitor.app/dummyicon diff --git a/examples/applicationmanager/monitor/apps/tld.monitor.app/info.yaml b/examples/applicationmanager/monitor/apps/tld.monitor.app/info.yaml new file mode 100644 index 00000000..cd0a58c8 --- /dev/null +++ b/examples/applicationmanager/monitor/apps/tld.monitor.app/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'tld.monitor.app' +icon: 'dummyicon' +code: 'app.qml' +runtime: 'qml' +name: + en: 'Application' diff --git a/examples/applicationmanager/monitor/doc/images/monitor.png b/examples/applicationmanager/monitor/doc/images/monitor.png Binary files differnew file mode 100644 index 00000000..d3b3cddd --- /dev/null +++ b/examples/applicationmanager/monitor/doc/images/monitor.png diff --git a/examples/applicationmanager/monitor/doc/src/monitor.qdoc b/examples/applicationmanager/monitor/doc/src/monitor.qdoc new file mode 100644 index 00000000..603f6b36 --- /dev/null +++ b/examples/applicationmanager/monitor/doc/src/monitor.qdoc @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:FDL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Free Documentation License Usage +** Alternatively, this file may be used under the terms of the GNU Free +** Documentation License version 1.3 as published by the Free Software +** Foundation and appearing in the file included in the packaging of +** this file. Please review the following information to ensure +** the GNU Free Documentation License version 1.3 requirements +** will be met: https://www.gnu.org/licenses/fdl-1.3.html. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + +\example monitor +\title Performance Monitoring Example +\image monitor.png Screenshot +\brief A resource and performance monitor +\ingroup applicationmanager-examples + +\section1 Introduction + +This example shows the basic usage of system and process monitoring APIs offered by the +application-manager: + \list + \li SystemMonitor + \li ProcessMonitor + \endlist + +The example monitors performance (frame rate), as well as rescource usage (memory and CPU). + +\section2 Invocation +The example can be started from within the "monitor" folder with: +\badcode +path/to/bin/appman -c am-config.yaml -r +\endcode + +\note The application-manager attempts to register a \c freedesktop.org compliant notification +server. DBus errors might occur if it conflicts with the server running on the host desktop +environment. In this case, a private session bus needs to be started by adding the +\c --start-session-dbus option. + +\section1 Walkthrough + +We will start from the bottom, because this is the essential part. + +\section2 SystemMonitor and ProcessMonitor Usage + +\quotefromfile monitor/system-ui/main.qml +\skipto ProcessMonitor +\printuntil ApplicationManager.startApplication +\printline } +The ProcessMonitor is an instantiatable type that will monitor the process that is associated with +its \l {ProcessMonitor::applicationId}{applicationId} property. An empty applicationId string means +that the System-UI process is monitored. By clicking the "Switch Process" button, the monitored +process can be changed to the \c tld.monitor.app application. Measurements will be performed each +second (1000 ms) and 12 reading points will be kept in the model (the ProcessMonitor is a model). +The model is used below to display the bar charts. + +Memory and frame rate reporting need to be enabled, since we are interested in both. For frame rate +measurements the list of \c monitoredWindows has to be filled with windows that we are interested +in. For the System-UI this is the single root window itself and for the tld.monitor.app process it +is the currently selected window. Hence the list will always include just one element. The memory +and frame rate changed signal handlers will update the current values of the corresponding views. + +The Connections type is used to implement signal handlers for the property changes of the +SystemMonitor. The current values of the corresponding views are updated, as well. + +The SystemMonitor is a singleton type. It is set-up in the Component.onCompleted handler. Like +above, measurements will be performed each second (1000 ms) and 12 reading points will be kept in +the model (the SystemMonitor is a model, as well). + +The very last line will start the only application that is provided by this example. Note that, if +the application-manager is running in single-process mode, the application will run within the +System-UI process and consequently the ProcessMonitor will not report anything and the associated +ProcessMonitor::processId will be set to 0. + +\section2 The User Interface + +\quotefromfile monitor/system-ui/main.qml +\skipto import Qt +\printto ProcessMonitor +\dots + +We won't go into much detail, because it's rather conventional QML code. The MonitorChart is a +ListView that uses either an instance of a ProcessMonitor or the SystemMonitor singleton as its +model. The reading values are represented as bars. Their height is determined by the +corresponding model role, e.g. for the PSS memory consumption it is \c memoryPss.total. + +When the tld.monitor.app process is monitored, its two windows will be shown (primary and +secondary). The window that is monitored in terms of frame rate can be selected and is highlighted +with a white border. + +*/ diff --git a/examples/applicationmanager/monitor/monitor.pro b/examples/applicationmanager/monitor/monitor.pro new file mode 100644 index 00000000..8a93c3bd --- /dev/null +++ b/examples/applicationmanager/monitor/monitor.pro @@ -0,0 +1,23 @@ +TEMPLATE = app +CONFIG += am-systemui + +OTHER_FILES += \ + am-config.yaml \ + monitor.qmlproject \ + doc/src/*.qdoc \ + doc/images/*.png \ + system-ui/*.qml \ + apps/tld.monitor.app/*.yaml \ + apps/tld.monitor.app/*.qml + +target.path = $$[QT_INSTALL_EXAMPLES]/applicationmanager/monitor +INSTALLS += target + +AM_COPY_DIRECTORIES += apps system-ui +AM_COPY_FILES += am-config.yaml + +AM_DEFAULT_ARGS=-c am-config.yaml --start-session-dbus --verbose + +example_sources.path = $$target.path +example_sources.files = $$AM_COPY_FILES $$AM_COPY_DIRECTORIES doc +INSTALLS += example_sources diff --git a/examples/applicationmanager/monitor/monitor.qmlproject b/examples/applicationmanager/monitor/monitor.qmlproject new file mode 100644 index 00000000..5ce6345e --- /dev/null +++ b/examples/applicationmanager/monitor/monitor.qmlproject @@ -0,0 +1,17 @@ +import QmlProject 1.1 + +Project { + mainFile: "system-ui/main.qml" + + QmlFiles { + directory: "." + } + ImageFiles { + directory: "apps" + } + Files { + directory: "." + filter: "*.yaml" + } +} + diff --git a/examples/applicationmanager/monitor/system-ui/MonitorChart.qml b/examples/applicationmanager/monitor/system-ui/MonitorChart.qml new file mode 100644 index 00000000..0d49e346 --- /dev/null +++ b/examples/applicationmanager/monitor/system-ui/MonitorChart.qml @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.6 + +Rectangle { + property alias title: title.text + property alias reading: reading.text + property alias model: listview.model + property alias delegate: listview.delegate + + width: 185 + height: 250 + color: "black" + border { width: 1; color: "white" } + + Rectangle { + width: parent.width + height: 30 + color: "black" + border { width: 1; color: "white" } + + MonitorText { + id: title + anchors.left: parent.left + anchors.leftMargin: 10 + anchors.verticalCenter: parent.verticalCenter + } + + MonitorText { + id: reading + anchors.right: parent.right + anchors.rightMargin: 10 + anchors.verticalCenter: parent.verticalCenter + } + } + + ListView { + id: listview + anchors.fill: parent + anchors.margins: 10 + anchors.topMargin: 40 + orientation: ListView.Horizontal + layoutDirection: Qt.RightToLeft + spacing: 3 + clip: true + } +} diff --git a/examples/applicationmanager/monitor/system-ui/MonitorText.qml b/examples/applicationmanager/monitor/system-ui/MonitorText.qml new file mode 100644 index 00000000..263a22ec --- /dev/null +++ b/examples/applicationmanager/monitor/system-ui/MonitorText.qml @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.6 + +Text { + font.pixelSize: 14 + color: "white" +} diff --git a/examples/applicationmanager/monitor/system-ui/Switch.qml b/examples/applicationmanager/monitor/system-ui/Switch.qml new file mode 100644 index 00000000..6ed638fb --- /dev/null +++ b/examples/applicationmanager/monitor/system-ui/Switch.qml @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.6 + +Rectangle { + signal toggle() + + width: 180; height: 30; radius: 4 + color: 'white' + + MonitorText { + anchors.centerIn: parent + text: 'Switch Process' + font.bold: true + color: 'black' + } + + MouseArea { + anchors.fill: parent + onClicked: toggle(); + } +} diff --git a/examples/applicationmanager/monitor/system-ui/Tile.qml b/examples/applicationmanager/monitor/system-ui/Tile.qml new file mode 100644 index 00000000..d3a06b18 --- /dev/null +++ b/examples/applicationmanager/monitor/system-ui/Tile.qml @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.6 + +Item { + width: 225 + height: 300 +} diff --git a/examples/applicationmanager/monitor/system-ui/WindowContainer.qml b/examples/applicationmanager/monitor/system-ui/WindowContainer.qml new file mode 100644 index 00000000..3b292cda --- /dev/null +++ b/examples/applicationmanager/monitor/system-ui/WindowContainer.qml @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.6 + +Rectangle { + property bool active: false + property alias container: container + signal activated() + + width: 180; height: 65 + + color: active ? "white" : "transparent" + + Item { + id: container + anchors.margins: 2 + anchors.fill: parent + } + + MouseArea { + anchors.fill: parent + onClicked: { + active = true; + activated(); + } + } +} diff --git a/examples/applicationmanager/monitor/system-ui/main.qml b/examples/applicationmanager/monitor/system-ui/main.qml new file mode 100644 index 00000000..5343a55d --- /dev/null +++ b/examples/applicationmanager/monitor/system-ui/main.qml @@ -0,0 +1,267 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.6 +import QtQuick.Window 2.0 +import QtApplicationManager 1.0 + + +Window { + id: root + + property var primaryWindow + property var secondaryWindow + + width: 720 + height: 600 + color: "black" + + Column { + x: 10 + padding: 20 + + Tile { + Column { + id: systemOverview + spacing: 10 + + MonitorText { text: "System"; font.pixelSize: 26 } + MonitorText { text: "CPU Cores: " + SystemMonitor.cpuCores } + MonitorText { text: "Total Memory: " + (SystemMonitor.totalMemory / 1e9).toFixed(1) + " GB" } + MonitorText { id: systemMem; text: "Used Memory:" } + MonitorText { text: "Idle Threshold: " + SystemMonitor.idleLoadThreshold * 100 + " %" } + MonitorText { text: "Idle: " + SystemMonitor.idle } + MonitorText { text: "GPU Load: " + SystemMonitor.gpuLoad * 100 + " %" } + } + } + + Tile { + MonitorChart { + id: systemLoad + title: "CPU Load" + model: SystemMonitor + delegate: Rectangle { + width: 11 + height: parent.height + gradient: Gradient { + GradientStop { position: 0.5; color: "#FF0000" } + GradientStop { position: 1.0; color: "#00FF00" } + } + Rectangle { + width: parent.width + height: parent.height - cpuLoad * parent.height + color: "black" + } + } + } + } + } + + Rectangle { + x: 246; y: 25 + width: 1; height: root.height - 50 + color: "white" + } + + Grid { + columns: 2 + padding: 20 + x: 260 + + Tile { + Column { + spacing: 20 + + MonitorText { + text: processMon.applicationId === "" ? "System-UI" : processMon.applicationId + font.pixelSize: 26 + } + MonitorText { text: "Process ID: " + processMon.processId } + + Switch { + onToggle: { + processMon.applicationId = processMon.applicationId === "" + ? ApplicationManager.application(0).id : "" + if (processMon.applicationId !== "") { + if (ApplicationManager.singleProcess) + console.warn("There is no dedicated application process in single-process mode"); + + if (primaryWindow && secondaryWindow) { + processMon.monitoredWindows = primary.active ? [primaryWindow] : [secondaryWindow] + } else { + processMon.monitoredWindows = [] + console.warn("No application window available. Please check your QtWayland configuration."); + } + } else { + processMon.monitoredWindows = [root] + } + } + } + } + } + + Tile { + Column { + visible: processMon.applicationId !== "" + spacing: 20 + Item { + width: 200; height: 65 + MonitorText { anchors.bottom: parent.bottom; text: "Application Windows:" } + } + + WindowContainer { + id: primary + active: true + onActivated: { + processMon.monitoredWindows = [primaryWindow]; + secondary.active = false; + } + } + + WindowContainer { + id: secondary + onActivated: { + processMon.monitoredWindows = [secondaryWindow]; + primary.active = false; + } + } + } + } + + Tile { + MonitorChart { + id: processPss + title: "Memory PSS" + model: processMon + delegate: Rectangle { + width: 11 + height: memoryPss.total / 5e6 + anchors.bottom: parent.bottom + color: "lightsteelblue" + } + } + } + + Tile { + MonitorChart { + id: frameChart + title: "Frame rate" + model: processMon + delegate: Rectangle { + width: 11 + height: frameRate[0] ? frameRate[0].average * 3 : 0 + anchors.bottom: parent.bottom + color.r: frameRate[0] ? (frameRate[0].average >= 60 ? 0 : 1 - frameRate[0].average / 60) : 0 + color.g: frameRate[0] ? (frameRate[0].average >= 60 ? 1 : frameRate[0].average / 60) : 0 + color.b: 0 + + } + } + } + } + + Connections { + target: WindowManager + onWindowReady: { + if (WindowManager.windowProperty(window, "windowType") === "primary") { + primaryWindow = window + window.parent = primary.container + window.anchors.fill = primary.container + } else { + secondaryWindow = window + window.parent = secondary.container + window.anchors.fill = secondary.container + } + } + + onWindowLost: { + processMon.monitoredWindows = [] + WindowManager.releaseWindow(window); + } + } + + ProcessMonitor { + id: processMon + applicationId: "" + reportingInterval: 1000 + count: 12 + + memoryReportingEnabled: true + frameRateReportingEnabled: true + monitoredWindows: [root] + + onMemoryReportingChanged: processPss.reading = (memoryPss.total / 1e6).toFixed(0) + " MB"; + onFrameRateReportingChanged: { + if (frameRate[0]) + frameChart.reading = frameRate[0].average.toFixed(0) + " fps"; + } + } + + Connections { + target: SystemMonitor + onCpuLoadReportingChanged: systemLoad.reading = (load * 100).toFixed(1) + " %"; + onMemoryReportingChanged: systemMem.text = "Used Memory: " + (used / 1e9).toFixed(1) + " GB" + } + + + Component.onCompleted: { + SystemMonitor.reportingInterval = 1000; + SystemMonitor.count = 12; + + SystemMonitor.idleLoadThreshold = 0.05; + SystemMonitor.cpuLoadReportingEnabled = true; + SystemMonitor.gpuLoadReportingEnabled = true; + SystemMonitor.memoryReportingEnabled = true; + + ApplicationManager.startApplication(ApplicationManager.application(0).id); + } +} diff --git a/examples/applicationmanager/multi-views/am-config.yaml b/examples/applicationmanager/multi-views/am-config.yaml new file mode 100644 index 00000000..1fa63b8a --- /dev/null +++ b/examples/applicationmanager/multi-views/am-config.yaml @@ -0,0 +1,37 @@ +formatVersion: 1 +formatType: am-configuration +--- +installationLocations: +- id: "internal-0" + installationPath: "/tmp/multi-views/apps" + documentPath: "/tmp/multi-views/docs" + mountPoint: "/tmp" + isDefault: true + +applications: + builtinAppsManifestDir: "${CONFIG_PWD}/apps" + installedAppsManifestDir: "/tmp/multi-views/manifests" + appImageMountDir: "/tmp/multi-views/image-mounts" + database: "/tmp/multi-views/apps.db" + +logging: + rules: + - "*=false" + - "qt.*=false" + - "am.*=false" + - "qml*=true" + - "*.warning=true" + - "*.critical=true" + +ui: + fullscreen: no + mainQml: "${CONFIG_PWD}/system-ui/main.qml" + +systemProperties: + protected: + light: on + +# development setup: +flags: + noSecurity: yes + noUiWatchdog: yes diff --git a/examples/applicationmanager/multi-views/apps/tld.multi-views.app/app1.qml b/examples/applicationmanager/multi-views/apps/tld.multi-views.app/app1.qml new file mode 100644 index 00000000..dff73e7a --- /dev/null +++ b/examples/applicationmanager/multi-views/apps/tld.multi-views.app/app1.qml @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager 1.0 + +ApplicationManagerWindow { + id: root + color: mouseArea.pressed ? "red" : "peachpuff" + + Rectangle { + anchors.centerIn: parent + width: 180; height: 180; radius: width/4 + color: "peru" + + Image { + source: ApplicationInterface.icon + anchors.centerIn: parent + } + + RotationAnimation on rotation { + from: 0; to: 360; loops: Animation.Infinite; duration: 4000 + } + } + + MouseArea { + id: mouseArea + anchors.fill: parent + } +} diff --git a/examples/applicationmanager/multi-views/apps/tld.multi-views.app/icon.png b/examples/applicationmanager/multi-views/apps/tld.multi-views.app/icon.png Binary files differnew file mode 100644 index 00000000..adb840ce --- /dev/null +++ b/examples/applicationmanager/multi-views/apps/tld.multi-views.app/icon.png diff --git a/examples/applicationmanager/multi-views/apps/tld.multi-views.app/info.yaml b/examples/applicationmanager/multi-views/apps/tld.multi-views.app/info.yaml new file mode 100644 index 00000000..36ace0e1 --- /dev/null +++ b/examples/applicationmanager/multi-views/apps/tld.multi-views.app/info.yaml @@ -0,0 +1,9 @@ +formatVersion: 1 +formatType: am-application +--- +id: 'tld.multi-views.app' +icon: 'icon.png' +code: 'app1.qml' +runtime: 'qml' +name: + en: 'App1' diff --git a/examples/applicationmanager/multi-views/multi-views.pro b/examples/applicationmanager/multi-views/multi-views.pro new file mode 100644 index 00000000..82c6fece --- /dev/null +++ b/examples/applicationmanager/multi-views/multi-views.pro @@ -0,0 +1,21 @@ +TEMPLATE = app +CONFIG += am-systemui + +OTHER_FILES += \ + am-config.yaml \ + system-ui/*.qml \ + apps/tld.multi-views.app/*.yaml \ + apps/tld.multi-views.app/*.qml \ + apps/tld.multi-views.app/*.png \ + +target.path = $$[QT_INSTALL_EXAMPLES]/applicationmanager/multi-views +INSTALLS += target + +AM_COPY_DIRECTORIES += apps system-ui +AM_COPY_FILES += am-config.yaml + +AM_DEFAULT_ARGS=-c am-config.yaml --start-session-dbus --verbose -r + +example_sources.path = $$target.path +example_sources.files = $$AM_COPY_FILES $$AM_COPY_DIRECTORIES +INSTALLS += example_sources diff --git a/examples/applicationmanager/multi-views/system-ui/Readme.qml b/examples/applicationmanager/multi-views/system-ui/Readme.qml new file mode 100644 index 00000000..000e5fdf --- /dev/null +++ b/examples/applicationmanager/multi-views/system-ui/Readme.qml @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.4 + +Item { + anchors.fill: parent + + Column { + width: heading.implicitWidth + height: heading.implicitHeight + 80 + anchors.centerIn: parent + spacing: 10 + + Text { + id: heading + color: "cornflowerblue" + font { pixelSize: 20; weight: Font.Bold } + text: "Multiple WindowItems displaying the same WindowObject" + } + } +} diff --git a/examples/applicationmanager/multi-views/system-ui/main.qml b/examples/applicationmanager/multi-views/system-ui/main.qml new file mode 100644 index 00000000..64d5ce4f --- /dev/null +++ b/examples/applicationmanager/multi-views/system-ui/main.qml @@ -0,0 +1,227 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +import QtQuick 2.4 +import QtApplicationManager 1.0 + +Rectangle { + width: 1024 + height: 640 + color: "linen" + + Readme {} + + Text { + anchors.bottom: parent.bottom + text: (ApplicationManager.singleProcess ? "Single" : "Multi") + "-Process Mode" + } + + // Application launcher panel + Column { + id: launcher + Repeater { + id: menuItems + model: ApplicationManager + + Image { + source: icon + + Text { + anchors.fill: parent + fontSizeMode: Text.Fit; minimumPixelSize: 10; font.pixelSize: height + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + text: model.isRunning ? "Stop" : "Start" + } + + MouseArea { + anchors.fill: parent + onClicked: { + if (model.isRunning) + application.stop(); + else + application.start(); + } + } + } + } + } + + // System-UI chrome for applications + Repeater { + model: ListModel { id: topLevelWindowsModel } + + delegate: Rectangle { + id: winChrome + + width: 400; height: 320 + z: model.index + color: "tan" + + property bool manuallyClosed: false + + Text { + anchors.horizontalCenter: parent.horizontalCenter + text: (windowItem.primary ? "Primary: " : "Secondary: ") + model.window.application.name("en") + } + + MouseArea { + anchors.fill: parent + drag.target: parent + onPressed: topLevelWindowsModel.move(model.index, topLevelWindowsModel.count-1, 1); + } + + Rectangle { + width: 25; height: 25 + color: "chocolate" + Text { + anchors.fill: parent + fontSizeMode: Text.Fit; minimumPixelSize: 10; font.pixelSize: 25 + horizontalAlignment: Text.AlignHCenter + text: "-" + } + + MouseArea { + anchors.fill: parent + onClicked: { + winChrome.manuallyClosed = true; + } + } + } + + Text { + width: 25; height: 25 + anchors.right: addWindowItemButton.left + fontSizeMode: Text.Fit; minimumPixelSize: 10; font.pixelSize: 25 + + text: "P" + visible: !windowItem.primary + + MouseArea { + anchors.fill: parent + onClicked: windowItem.makePrimary() + } + } + Text { + id: addWindowItemButton + width: 25; height: 25 + anchors.right: parent.right + fontSizeMode: Text.Fit; minimumPixelSize: 10; font.pixelSize: 25 + + text: "+" + + MouseArea { + anchors.fill: parent + onClicked: { + topLevelWindowsModel.append({"window":model.window}); + } + } + } + + WindowItem { + id: windowItem + anchors.fill: parent + anchors.margins: 3 + anchors.topMargin: 25 + window: model.window + } + + Component.onCompleted: { + winChrome.x = 300 + model.index * 50; + winChrome.y = 10 + model.index * 30; + } + + states: [ + State { + name: "open" + when: model.window && model.window.contentState === WindowObject.SurfaceWithContent && !manuallyClosed + PropertyChanges { + target: winChrome + opacity: 1 + scale: 1 + visible: true + } + } + ] + + opacity: 0.25 + scale: 0.50 + visible: false + + transitions: [ + Transition { + to: "open" + NumberAnimation { target: winChrome; properties: "opacity,scale"; duration: 500; easing.type: Easing.OutQuad} + }, + Transition { + from: "open" + SequentialAnimation { + PropertyAction { target: winChrome; property: "visible"; value: true } // we wanna see the window during the closing animation + NumberAnimation { target: winChrome; properties: "opacity,scale"; duration: 500; easing.type: Easing.InQuad} + ScriptAction { script: { + if (model.window.contentState === WindowObject.NoSurface || winChrome.manuallyClosed) + topLevelWindowsModel.remove(model.index, 1); + } } + } + } + ] + } + } + + // Handler for WindowManager signals + Connections { + target: WindowManager + onWindowAdded: { + topLevelWindowsModel.append({"window":window}); + } + } +} diff --git a/examples/applicationmanager/softwarecontainer-plugin/README.md b/examples/applicationmanager/softwarecontainer-plugin/README.md new file mode 100644 index 00000000..57d7eb3b --- /dev/null +++ b/examples/applicationmanager/softwarecontainer-plugin/README.md @@ -0,0 +1,71 @@ +# SoftwareContainer Plugin + +This is a very basic PoC integration of Pelagicore's Software-Containers + +[https://github.com/Pelagicore/softwarecontainer][] + +Please also read the "Containers" page in the official Qt Application Manager +documentation: +[https://doc-snapshots.qt.io/qtapplicationmanager/containers.html] + +Parts of the container configuration are hardcoded in `softwarecontainer.cpp`, +while all of the capability definition is in the JSON manifest at +`service-manifest.d/io.qt.ApplicationManager.Application/`. + +The Wayland/OpenGL pass-through is tested against Intel GPUs and VMware's +virtual GPU. + +The softwarecontainer-agent needs to be started as root. By default it will +register itself on the system D-Bus, so a policy file must be in place, +allowing it to register itself on the system-bus. If you want to run the +agent on the session bus instead (via the `--session-bus` parameter), you +have to add the following to one of your config.yaml files: +``` +containers: + softwarecontainer: + dbus: 'session' +``` + +Passing the service-manifest directory that comes with the plugin via +`-m` is mandatory - otherwise the container setup will fail due to the +missing `io.qt.ApplicationManager.Application` capability. + +You have to make sure that the agent has access to the same session-bus +that the application-manager is using, if you intend on forwarding this +bus. This is a bit tricky if the agent is run as root and the application- +manager as non-root user, since the default session-bus policy in most +distros disallows root to access user session-busses: the workaround is to +add a `<allow user="root"/>` policy in `/etc/dbus-1/session.conf`. + +Please do also not forget to tell the agent about your environment, when +running it via sudo: +``` +sudo XDG_RUNTIME_DIR=$XDG_RUNTIME_DIR +DBUS_SESSION_BUS_ADDRESS=$DBUS_SESSION_BUS_ADDRESS softwarecontainer-agent -m +/path/to/application-manager/examples/softwarecontainer-plugin/service-manifest.d/ +``` + +On the AM side, you need to activate the plugin by adding something like +this to one of your config.yaml files: +``` +plugins: + container: [ "/path/to/libsoftwarecontainer-plugin.so" ] +``` +In order to actually run apps within softwarecontainers, you have to add a +container selection configuration: +[https://doc-snapshots.qt.io/qtapplicationmanager/containers.html#container-selection-configuration][] + + +Please be aware that for easier development on the desktop, you normally want +your $HOME directory mounted into the container in read-only mode, so you do +not have to install your application-manager into /usr/ after every build +(given that your build directory is somewhere in $HOME, the container would +not see the appman-launcher-qml binary). +This is *not* done by default, but you can activate this behavior by adding +this to one of your config.yaml files: + +``` +containers: + softwarecontainer: + bindMountHome: yes +``` diff --git a/examples/applicationmanager/softwarecontainer-plugin/service-manifest.d/io.qt.ApplicationManager.Application.json b/examples/applicationmanager/softwarecontainer-plugin/service-manifest.d/io.qt.ApplicationManager.Application.json new file mode 100644 index 00000000..68592c08 --- /dev/null +++ b/examples/applicationmanager/softwarecontainer-plugin/service-manifest.d/io.qt.ApplicationManager.Application.json @@ -0,0 +1,58 @@ +{ + "version": "1", + "capabilities": [ + { + "name": "io.qt.ApplicationManager.Application", + "gateways": [ + { + "id": "dbus", + "config": [ + { + "dbus-gateway-config-session": [ + { + "direction": "*", + "interface": "*", + "object-path": "*", + "method": "*" + } + ], + "dbus-gateway-config-system": [ + { + "direction": "*", + "interface": "*", + "object-path": "*", + "method": "*" + } + ] + } + ] + }, + { + "id": "wayland", + "config": [ + { + "enabled": true + } + ] + }, + { + "id": "devicenode", + "config": [ + { + "name": "/dev/dri/card0" + }, + { + "name": "/dev/dri/renderD128" + }, + { + "name": "/dev/tty0" + }, + { + "name": "/dev/tty1" + } + ] + } + ] + } + ] +} diff --git a/examples/applicationmanager/softwarecontainer-plugin/softwarecontainer-plugin.pro b/examples/applicationmanager/softwarecontainer-plugin/softwarecontainer-plugin.pro new file mode 100644 index 00000000..2e8febc8 --- /dev/null +++ b/examples/applicationmanager/softwarecontainer-plugin/softwarecontainer-plugin.pro @@ -0,0 +1,21 @@ +requires(linux:!android) + +TEMPLATE = lib +CONFIG += plugin c++11 +TARGET = softwarecontainer-plugin + +QT = core dbus appman_common-private appman_plugininterfaces-private + +HEADERS += \ + softwarecontainer.h + +SOURCES += \ + softwarecontainer.cpp + +target.path = $$[QT_INSTALL_EXAMPLES]/applicationmanager/softwarecontainer-plugin +INSTALLS += target + +DISTFILES += \ + service-manifest.d/io.qt.ApplicationManager.Application.json + +OTHER_FILES += README.md diff --git a/examples/applicationmanager/softwarecontainer-plugin/softwarecontainer.cpp b/examples/applicationmanager/softwarecontainer-plugin/softwarecontainer.cpp new file mode 100644 index 00000000..7e34bd86 --- /dev/null +++ b/examples/applicationmanager/softwarecontainer-plugin/softwarecontainer.cpp @@ -0,0 +1,585 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#include <tuple> + +#include <QtDBus/QtDBus> +#include <QtAppManCommon/global.h> +#include <QJsonDocument> +#include <QSocketNotifier> +#include <qplatformdefs.h> +#include <unistd.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/fcntl.h> +#include "softwarecontainer.h" + + +QT_BEGIN_NAMESPACE + +QDBusArgument &operator<<(QDBusArgument &argument, const QMap<QString,QString> &map) +{ + argument.beginMap(QMetaType::QString, QMetaType::QString); + for (auto it = map.cbegin(); it != map.cend(); ++it) { + argument.beginMapEntry(); + argument << it.key() << it.value(); + argument.endMapEntry(); + } + argument.endMap(); + + return argument; +} + +const QDBusArgument &operator>>(const QDBusArgument &argument, QMap<QString,QString> &map) +{ + argument.beginMap(); + while (!argument.atEnd()) { + argument.beginMapEntry(); + QString key, value; + argument >> key >> value; + map.insert(key, value); + argument.endMapEntry(); + } + argument.endMap(); + + return argument; +} + +QT_END_NAMESPACE + + +QT_USE_NAMESPACE_AM + +// unfortunately, this is a copy of the code from debugwrapper.cpp +static QStringList substituteCommand(const QStringList &debugWrapperCommand, const QString &program, + const QStringList &arguments) +{ + QString stringifiedArguments = arguments.join(qL1C(' ')); + QStringList result; + + for (const QString &s : debugWrapperCommand) { + if (s == qSL("%arguments%")) { + result << arguments; + } else { + QString str(s); + str.replace(qL1S("%program%"), program); + str.replace(qL1S("%arguments%"), stringifiedArguments); + result << str; + } + } + return result; +} + +SoftwareContainerManager::SoftwareContainerManager() +{ + static bool once = false; + if (!once) { + once = true; + qDBusRegisterMetaType<QMap<QString, QString>>(); + } +} + +QString SoftwareContainerManager::identifier() const +{ + return QStringLiteral("softwarecontainer"); +} + +bool SoftwareContainerManager::supportsQuickLaunch() const +{ + return false; +} + +void SoftwareContainerManager::setConfiguration(const QVariantMap &configuration) +{ + m_configuration = configuration; +} + +ContainerInterface *SoftwareContainerManager::create(bool isQuickLaunch, const QVector<int> &stdioRedirections, + const QMap<QString, QString> &debugWrapperEnvironment, + const QStringList &debugWrapperCommand) +{ + if (!m_interface) { + QString dbus = configuration().value(QStringLiteral("dbus")).toString(); + QDBusConnection conn(QStringLiteral("sc-bus")); + + if (dbus.isEmpty()) + dbus = QStringLiteral("system"); + + if (dbus == QLatin1String("system")) + conn = QDBusConnection::systemBus(); + else if (dbus == QLatin1String("session")) + conn = QDBusConnection::sessionBus(); + else + conn = QDBusConnection::connectToBus(dbus, QStringLiteral("sc-bus")); + + if (!conn.isConnected()) { + qWarning() << "The" << dbus << "D-Bus is not available to connect to the SoftwareContainer agent."; + return nullptr; + } + m_interface = new QDBusInterface(QStringLiteral("com.pelagicore.SoftwareContainerAgent"), + QStringLiteral("/com/pelagicore/SoftwareContainerAgent"), + QStringLiteral("com.pelagicore.SoftwareContainerAgent"), + conn, this); + if (m_interface->lastError().isValid()) { + qWarning() << "Could not connect to com.pelagicore.SoftwareContainerAgent, " + "/com/pelagicore/SoftwareContainerAgent on the" << dbus << "D-Bus"; + delete m_interface; + m_interface = nullptr; + return nullptr; + } + + if (!connect(m_interface, SIGNAL(ProcessStateChanged(int,uint,bool,uint)), this, SLOT(processStateChanged(int,uint,bool,uint)))) { + qWarning() << "Could not connect to the com.pelagicore.SoftwareContainerAgent.ProcessStateChanged " + "signal on the" << dbus << "D-Bus"; + delete m_interface; + m_interface = nullptr; + return nullptr; + } + } + + QString config = qSL("[]"); + QVariant v = configuration().value("createConfig"); + if (v.isValid()) + config = QString::fromUtf8(QJsonDocument::fromVariant(v).toJson(QJsonDocument::Compact)); + + QDBusMessage reply = m_interface->call(QDBus::Block, "Create", config); + if (reply.type() == QDBusMessage::ErrorMessage) { + qWarning() << "SoftwareContainer failed to create a new container:" << reply.errorMessage() + << "(config was:" << config << ")"; + return nullptr; + } + + int containerId = reply.arguments().at(0).toInt(); + + if (containerId < 0) { + qCritical() << "SoftwareContainer failed to create a new container. (config was:" << config << ")"; + return nullptr; + } + + // calculate where to dump stdout/stderr + int outputFd = stdioRedirections.value(STDERR_FILENO, -1); + if (outputFd < 0) + outputFd = stdioRedirections.value(STDOUT_FILENO, -1); + if ((::fcntl(outputFd, F_GETFD) < 0) && (errno == EBADF)) + outputFd = STDOUT_FILENO; + + SoftwareContainer *container = new SoftwareContainer(this, isQuickLaunch, containerId, + outputFd, debugWrapperEnvironment, + debugWrapperCommand); + m_containers.insert(containerId, container); + connect(container, &QObject::destroyed, this, [this, containerId]() { m_containers.remove(containerId); }); + return container; +} + +QDBusInterface *SoftwareContainerManager::interface() const +{ + return m_interface; +} + +QVariantMap SoftwareContainerManager::configuration() const +{ + return m_configuration; +} + +void SoftwareContainerManager::processStateChanged(int containerId, uint processId, bool isRunning, uint exitCode) +{ + Q_UNUSED(processId) + + SoftwareContainer *container = m_containers.value(containerId); + if (!container) { + qWarning() << "Received a processStateChanged signal for unknown container" << containerId; + return; + } + + if (!isRunning) + container->containerExited(exitCode); +} + + + +SoftwareContainer::SoftwareContainer(SoftwareContainerManager *manager, bool isQuickLaunch, int containerId, + int outputFd, const QMap<QString, QString> &debugWrapperEnvironment, + const QStringList &debugWrapperCommand) + : m_manager(manager) + , m_isQuickLaunch(isQuickLaunch) + , m_id(containerId) + , m_outputFd(outputFd) + , m_debugWrapperEnvironment(debugWrapperEnvironment) + , m_debugWrapperCommand(debugWrapperCommand) +{ } + +SoftwareContainer::~SoftwareContainer() +{ + if (m_fifoFd >= 0) + QT_CLOSE(m_fifoFd); + if (!m_fifoPath.isEmpty()) + ::unlink(m_fifoPath); + if (m_outputFd > STDERR_FILENO) + QT_CLOSE(m_outputFd); +} + +SoftwareContainerManager *SoftwareContainer::manager() const +{ + return m_manager; +} + +bool SoftwareContainer::attachApplication(const QVariantMap &application) +{ + + // In normal launch attachApplication is called first, then the start() + // method is called. During quicklaunch start() is called first and then + // attachApplication. In this case we need to configure the container + // with any extra capabilities etc. + + m_state = QProcess::Starting; + m_application = application; + + m_hostPath = application.value(qSL("codeDir")).toString(); + if (m_hostPath.isEmpty()) + m_hostPath = QDir::currentPath(); + + m_appRelativeCodePath = application.value(qSL("codeFilePath")).toString(); + m_containerPath = qSL("/app"); + + // If this is a quick launch instance, we need to renew the capabilities + // and send the bindmounts. + if (m_isQuickLaunch) { + if (!sendCapabilities()) + return false; + + if (!sendBindMounts()) + return false; + } + + m_ready = true; + emit ready(); + return true; +} + +QString SoftwareContainer::controlGroup() const +{ + return QString(); +} + +bool SoftwareContainer::setControlGroup(const QString &groupName) +{ + Q_UNUSED(groupName) + return false; +} + +bool SoftwareContainer::setProgram(const QString &program) +{ + m_program = program; + return true; +} + +void SoftwareContainer::setBaseDirectory(const QString &baseDirectory) +{ + m_baseDir = baseDirectory; +} + +bool SoftwareContainer::isReady() const +{ + return m_ready; +} + +QString SoftwareContainer::mapContainerPathToHost(const QString &containerPath) const +{ + return containerPath; +} + +QString SoftwareContainer::mapHostPathToContainer(const QString &hostPath) const +{ + return hostPath; +} + +bool SoftwareContainer::sendCapabilities() +{ + auto iface = manager()->interface(); + if (!iface) + return false; + + // this is the one and only capability in io.qt.ApplicationManager.Application.json + static const QStringList capabilities { qSL("io.qt.ApplicationManager.Application") }; + + QDBusMessage reply = iface->call(QDBus::Block, "SetCapabilities", m_id, QVariant::fromValue(capabilities)); + if (reply.type() == QDBusMessage::ErrorMessage) { + qWarning() << "SoftwareContainer failed to set capabilities to" << capabilities << ":" << reply.errorMessage(); + return false; + } + return true; +} + +bool SoftwareContainer::sendBindMounts() +{ + auto iface = manager()->interface(); + if (!iface) + return false; + + QFileInfo fontCacheInfo(qSL("/var/cache/fontconfig")); + + QVector<std::tuple<QString, QString, bool>> bindMounts; // bool == isReadOnly + // the private P2P D-Bus + bindMounts.append(std::make_tuple(m_dbusP2PInfo.absoluteFilePath(), m_dbusP2PInfo.absoluteFilePath(), false)); + + // we need to share the fontconfig cache - otherwise the container startup might take a long time + bindMounts.append(std::make_tuple(fontCacheInfo.absoluteFilePath(), fontCacheInfo.absoluteFilePath(), false)); + + // the actual path to the application + bindMounts.append(std::make_tuple(m_hostPath, m_containerPath, true)); + + // for development only - mount the user's $HOME dir into the container as read-only. Otherwise + // you would have to `make install` the AM into /usr on every rebuild + if (manager()->configuration().value(QStringLiteral("bindMountHome")).toBool()) + bindMounts.append(std::make_tuple(QDir::homePath(), QDir::homePath(), true)); + + // do all the bind-mounts in parallel to waste as little time as possible + QList<QDBusPendingReply<>> bindMountResults; + + for (auto it = bindMounts.cbegin(); it != bindMounts.cend(); ++it) + bindMountResults << iface->asyncCall("BindMount", m_id, std::get<0>(*it), std::get<1>(*it), std::get<2>(*it)); + + for (const auto &pending : qAsConst(bindMountResults)) + QDBusPendingCallWatcher(pending).waitForFinished(); + + for (int i = 0; i < bindMounts.size(); ++i) { + if (bindMountResults.at(i).isError()) { + qWarning() << "SoftwareContainer failed to bind-mount the directory" << std::get<0>(bindMounts.at(i)) + << "into the container at" << std::get<1>(bindMounts.at(i)) << ":" << bindMountResults.at(i).error().message(); + return false; + } + } + + return true; + +} + +bool SoftwareContainer::start(const QStringList &arguments, const QMap<QString, QString> &runtimeEnvironment) +{ + auto iface = manager()->interface(); + if (!iface) + return false; + + if (!QFile::exists(m_program)) + return false; + + // Send initial capabilities even if this is a quick-launch instance + if (!sendCapabilities()) + return false; + + // parse out the actual socket file name from the DBus specification + QString dbusP2PSocket = runtimeEnvironment.value(QStringLiteral("AM_DBUS_PEER_ADDRESS")); + dbusP2PSocket = dbusP2PSocket.mid(dbusP2PSocket.indexOf(QLatin1Char('=')) + 1); + dbusP2PSocket = dbusP2PSocket.left(dbusP2PSocket.indexOf(QLatin1Char(','))); + QFileInfo dbusP2PInfo(dbusP2PSocket); + m_dbusP2PInfo = dbusP2PInfo; + + // Only send the bindmounts if this is not a quick-launch instance. + if (!m_isQuickLaunch && !sendBindMounts()) + return false; + + // create an unique fifo name in /tmp + m_fifoPath = QDir::tempPath().toLocal8Bit() + "/sc-" + QUuid::createUuid().toByteArray().mid(1,36) + ".fifo"; + if (mkfifo(m_fifoPath, 0600) != 0) { + qWarning() << "Failed to create FIFO at" << m_fifoPath << "to redirect stdout/stderr out of the container:" << strerror(errno); + m_fifoPath.clear(); + return false; + } + + // open fifo for reading + // due to QTBUG-15261 (bytesAvailable() on a fifo always returns 0) we use a raw fd + m_fifoFd = QT_OPEN(m_fifoPath, O_RDONLY | O_NONBLOCK); + if (m_fifoFd < 0) { + qWarning() << "Failed to open FIFO at" << m_fifoPath << "for reading:" << strerror(errno); + return false; + } + + // read from fifo and dump to message handler + QSocketNotifier *sn = new QSocketNotifier(m_fifoFd, QSocketNotifier::Read, this); + int outputFd = m_outputFd; + connect(sn, &QSocketNotifier::activated, this, [sn, outputFd](int fifoFd) { + int bytesAvailable = 0; + if (ioctl(fifoFd, FIONREAD, &bytesAvailable) == 0) { + static const int bufferSize = 4096; + static QByteArray buffer(bufferSize, 0); + + while (bytesAvailable > 0) { + auto bytesRead = QT_READ(fifoFd, buffer.data(), std::min(bytesAvailable, bufferSize)); + if (bytesRead < 0) { + if (errno == EINTR || errno == EAGAIN) + continue; + sn->setEnabled(false); + return; + } else if (bytesRead > 0) { + (void) QT_WRITE(outputFd, buffer.constData(), bytesRead); + bytesAvailable -= bytesRead; + } + } + } + }); + + // Calculate the exact command to run + QStringList command; + if (!m_debugWrapperCommand.isEmpty()) { + command = substituteCommand(m_debugWrapperCommand, m_program, arguments); + } else { + command = arguments; + command.prepend(m_program); + } + + // SC expects a plain string instead of individual args + QString cmdLine; + for (const auto &part : qAsConst(command)) { + if (!cmdLine.isEmpty()) + cmdLine.append(QLatin1Char(' ')); + cmdLine.append(QLatin1Char('\"')); + cmdLine.append(part); + cmdLine.append(QLatin1Char('\"')); + } + //cmdLine.prepend(QStringLiteral("/usr/bin/strace ")); // useful if things go wrong... + + // we start with a copy of the AM's environment, but some variables would overwrite important + // redirections set by SC gateways. + static const QStringList forbiddenVars = { + qSL("XDG_RUNTIME_DIR"), + qSL("DBUS_SESSION_BUS_ADDRESS"), + qSL("DBUS_SYSTEM_BUS_ADDRESS"), + qSL("PULSE_SERVER") + }; + + // since we have to translate between a QProcessEnvironment and a QMap<>, we cache the result + static QMap<QString, QString> baseEnvVars; + if (baseEnvVars.isEmpty()) { + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + const auto keys = env.keys(); + for (const auto key : keys) { + if (!key.isEmpty() && !forbiddenVars.contains(key)) + baseEnvVars.insert(key, env.value(key)); + } + } + + QMap<QString, QString> envVars = baseEnvVars; + + // set the env. variables coming from the runtime + for (auto it = runtimeEnvironment.cbegin(); it != runtimeEnvironment.cend(); ++it) { + if (it.value().isEmpty()) + envVars.remove(it.key()); + else + envVars.insert(it.key(), it.value()); + } + // set the env. variables coming from a debug wrapper + for (auto it = m_debugWrapperEnvironment.cbegin(); it != m_debugWrapperEnvironment.cend(); ++it) { + if (it.value().isEmpty()) + envVars.remove(it.key()); + else + envVars.insert(it.key(), it.value()); + } + + QVariant venvVars = QVariant::fromValue(envVars); + + qDebug () << "SoftwareContainer is trying to launch application" << m_id + << "\n * command ..." << cmdLine + << "\n * directory ." << m_containerPath + << "\n * output ...." << m_fifoPath + << "\n * environment" << envVars; + +#if 0 + qWarning() << "Attach to container now!"; + qWarning().nospace() << " sudo lxc-attach -n SC-" << m_id; + sleep(10000000); +#endif + + auto reply = iface->call(QDBus::Block, "Execute", m_id, cmdLine, m_containerPath, QString::fromLocal8Bit(m_fifoPath), venvVars); + if (reply.type() == QDBusMessage::ErrorMessage) { + qWarning() << "SoftwareContainer failed to execute application" << m_id << "in directory" << m_containerPath << "in the container:" << reply.errorMessage(); + return false; + } + + m_pid = reply.arguments().at(0).value<int>(); + + m_state = QProcess::Running; + QTimer::singleShot(0, this, [this]() { + emit stateChanged(m_state); + emit started(); + }); + return true; +} + +qint64 SoftwareContainer::processId() const +{ + return m_pid; +} + +QProcess::ProcessState SoftwareContainer::state() const +{ + return m_state; +} + +void SoftwareContainer::kill() +{ + auto iface = manager()->interface(); + + if (iface) { + QDBusMessage reply = iface->call(QDBus::Block, "Destroy", m_id); + if (reply.type() == QDBusMessage::ErrorMessage) { + qWarning() << "SoftwareContainer failed to destroy container" << reply.errorMessage(); + } + + if (!reply.arguments().at(0).toBool()) { + qWarning() << "SoftwareContainer failed to destroy container."; + } + } +} + +void SoftwareContainer::terminate() +{ + //TODO: handle graceful shutdown + kill(); +} + +void SoftwareContainer::containerExited(uint exitCode) +{ + m_state = QProcess::NotRunning; + emit stateChanged(m_state); + emit finished(WEXITSTATUS(exitCode), WIFEXITED(exitCode) ? QProcess::NormalExit : QProcess::CrashExit); + deleteLater(); +} diff --git a/examples/applicationmanager/softwarecontainer-plugin/softwarecontainer.h b/examples/applicationmanager/softwarecontainer-plugin/softwarecontainer.h new file mode 100644 index 00000000..50d47312 --- /dev/null +++ b/examples/applicationmanager/softwarecontainer-plugin/softwarecontainer.h @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:LGPL-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: LGPL-3.0 +** +****************************************************************************/ + +#pragma once + +#include <QVariantMap> +#include <QFileInfo> + +#include <QtAppManPluginInterfaces/containerinterface.h> + +QT_FORWARD_DECLARE_CLASS(QDBusInterface) + +class SoftwareContainerManager; + +class SoftwareContainer : public ContainerInterface +{ + Q_OBJECT + +public: + SoftwareContainer(SoftwareContainerManager *manager, bool isQuickLaunch, int containerId, + int outputFd, const QMap<QString, QString> &debugWrapperEnvironment, + const QStringList &debugWrapperCommand); + ~SoftwareContainer(); + + SoftwareContainerManager *manager() const; + + bool attachApplication(const QVariantMap &application) override; + + QString controlGroup() const override; + bool setControlGroup(const QString &groupName) override; + + bool setProgram(const QString &program) override; + void setBaseDirectory(const QString &baseDirectory) override; + + bool isReady() const override; + + QString mapContainerPathToHost(const QString &containerPath) const override; + QString mapHostPathToContainer(const QString &hostPath) const override; + + bool start(const QStringList &arguments, const QMap<QString, QString> &runtimeEnvironment) override; + + qint64 processId() const override; + QProcess::ProcessState state() const override; + + void kill() override; + void terminate() override; + + void containerExited(uint exitCode); + +private: + bool sendCapabilities(); + bool sendBindMounts(); + + SoftwareContainerManager *m_manager; + bool m_isQuickLaunch; + int m_id; + QString m_program; + QString m_baseDir; + bool m_ready = false; + qint64 m_pid = 0; + QProcess::ProcessState m_state = QProcess::NotRunning; + QVariantMap m_application; + QString m_appRelativeCodePath; + QString m_hostPath; + QString m_containerPath; + QByteArray m_fifoPath; + int m_fifoFd = -1; + int m_outputFd; + QMap<QString, QString> m_debugWrapperEnvironment; + QStringList m_debugWrapperCommand; + QFileInfo m_dbusP2PInfo; +}; + +class SoftwareContainerManager : public QObject, public ContainerManagerInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID AM_ContainerManagerInterface_iid) + Q_INTERFACES(ContainerManagerInterface) + +public: + SoftwareContainerManager(); + + QString identifier() const override; + bool supportsQuickLaunch() const override; + void setConfiguration(const QVariantMap &configuration) override; + + ContainerInterface *create(bool isQuickLaunch, + const QVector<int> &stdioRedirections, + const QMap<QString, QString> &debugWrapperEnvironment, + const QStringList &debugWrapperCommand) override; +public: + QDBusInterface *interface() const; + QVariantMap configuration() const; + +private slots: + void processStateChanged(int containerId, uint processId, bool isRunning, uint exitCode); + +private: + QVariantMap m_configuration; + QDBusInterface *m_interface = nullptr; + QMap<int, SoftwareContainer *> m_containers; +}; diff --git a/examples/applicationmanager/startup-plugin/startup-plugin.cpp b/examples/applicationmanager/startup-plugin/startup-plugin.cpp new file mode 100644 index 00000000..15bdd175 --- /dev/null +++ b/examples/applicationmanager/startup-plugin/startup-plugin.cpp @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +#include "startup-plugin.h" + +Q_LOGGING_CATEGORY(LogMe, "am.start") + +void TestStartupInterface::initialize(const QVariantMap &systemProperties) Q_DECL_NOEXCEPT_EXPR(false) +{ + qCWarning(LogMe) << "Startup initialize - systemProperties:" << systemProperties; +} + +void TestStartupInterface::afterRuntimeRegistration() Q_DECL_NOEXCEPT_EXPR(false) +{ + qCWarning(LogMe) << "Startup afterRuntimeRegistration"; +} + +void TestStartupInterface::beforeQmlEngineLoad(QQmlEngine *engine) Q_DECL_NOEXCEPT_EXPR(false) +{ + qCWarning(LogMe) << "Startup beforeQmlEngineLoad - engine:" << engine; +} + +void TestStartupInterface::afterQmlEngineLoad(QQmlEngine *engine) Q_DECL_NOEXCEPT_EXPR(false) +{ + qCWarning(LogMe) << "Startup afterQmlEngineLoad - engine:" << engine; +} + +void TestStartupInterface::beforeWindowShow(QWindow *window) Q_DECL_NOEXCEPT_EXPR(false) +{ + qCWarning(LogMe) << "Startup beforeWindowShow - window:" << window; +} + +void TestStartupInterface::afterWindowShow(QWindow *window) Q_DECL_NOEXCEPT_EXPR(false) +{ + qCWarning(LogMe) << "Startup afterWindowShow - window:" << window; +} diff --git a/examples/applicationmanager/startup-plugin/startup-plugin.h b/examples/applicationmanager/startup-plugin/startup-plugin.h new file mode 100644 index 00000000..c1fe0b05 --- /dev/null +++ b/examples/applicationmanager/startup-plugin/startup-plugin.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Pelagicore AG +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the Pelagicore Application Manager. +** +** $QT_BEGIN_LICENSE:BSD-QTAS$ +** Commercial License Usage +** Licensees holding valid commercial Qt Automotive Suite licenses may use +** this file in accordance with the commercial license agreement provided +** with the Software or, alternatively, in accordance with the terms +** contained in a written agreement between you and The Qt Company. For +** licensing terms and conditions see https://www.qt.io/terms-conditions. +** For further information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +** SPDX-License-Identifier: BSD-3-Clause +** +****************************************************************************/ + +#include <QLoggingCategory> +#include <QtAppManPluginInterfaces> +#include <QtAppManCommon/global.h> + +Q_DECLARE_LOGGING_CATEGORY(LogMe) + +class TestStartupInterface : public QObject, public StartupInterface +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID AM_StartupInterface_iid) + Q_INTERFACES(StartupInterface) + +public: + // StartupInterface + void initialize(const QVariantMap &systemProperties) Q_DECL_NOEXCEPT_EXPR(false) override; + void afterRuntimeRegistration() Q_DECL_NOEXCEPT_EXPR(false) override; + + void beforeQmlEngineLoad(QQmlEngine *engine) Q_DECL_NOEXCEPT_EXPR(false) override; + void afterQmlEngineLoad(QQmlEngine *engine) Q_DECL_NOEXCEPT_EXPR(false) override; + + void beforeWindowShow(QWindow *window) Q_DECL_NOEXCEPT_EXPR(false) override; + void afterWindowShow(QWindow *window) Q_DECL_NOEXCEPT_EXPR(false) override; +}; diff --git a/examples/applicationmanager/startup-plugin/startup-plugin.pro b/examples/applicationmanager/startup-plugin/startup-plugin.pro new file mode 100644 index 00000000..3cfdd7e3 --- /dev/null +++ b/examples/applicationmanager/startup-plugin/startup-plugin.pro @@ -0,0 +1,14 @@ +TEMPLATE = lib +CONFIG += plugin c++11 +TARGET = startup-plugin + +QT = core appman_plugininterfaces-private + +HEADERS += \ + startup-plugin.h + +SOURCES += \ + startup-plugin.cpp + +target.path = $$[QT_INSTALL_EXAMPLES]/applicationmanager/startup-plugin +INSTALLS += target |