diff options
Diffstat (limited to 'sample')
-rw-r--r-- | sample/app/app.pro | 11 | ||||
-rw-r--r-- | sample/app/eventhandler.cpp | 212 | ||||
-rw-r--r-- | sample/app/eventhandler.h | 19 | ||||
-rw-r--r-- | sample/app/main.cpp | 12 | ||||
-rw-r--r-- | sample/app/main.qml | 104 | ||||
-rw-r--r-- | sample/app/protocol/agl-shell-desktop.xml | 113 | ||||
-rw-r--r-- | sample/package/config.xml | 4 | ||||
-rw-r--r-- | sample/sample.pro | 4 |
8 files changed, 380 insertions, 99 deletions
diff --git a/sample/app/app.pro b/sample/app/app.pro index c72fd28..f6f5b82 100644 --- a/sample/app/app.pro +++ b/sample/app/app.pro @@ -14,12 +14,15 @@ # limitations under the License. TARGET = onstestapp -QT = quick quickcontrols2 qml +QT = quick quickcontrols2 qml gui-private -CONFIG += c++11 link_pkgconfig -PKGCONFIG += qlibwindowmanager qlibhomescreen +CONFIG += c++11 link_pkgconfig wayland-scanner pkgdatadir +#PKGCONFIG += qlibwindowmanager qlibhomescreen +PKGCONFIG += wayland-client DESTDIR = $${OUT_PWD}/../package/root/bin +include(../../pws/pws.pri) + SOURCES = main.cpp \ eventhandler.cpp @@ -31,3 +34,5 @@ HEADERS += \ LIBS += -ljson-c +WAYLANDCLIENTSOURCES += \ + protocol/agl-shell-desktop.xml diff --git a/sample/app/eventhandler.cpp b/sample/app/eventhandler.cpp index 4e55619..113b424 100644 --- a/sample/app/eventhandler.cpp +++ b/sample/app/eventhandler.cpp @@ -16,102 +16,188 @@ #include <functional> #include <QUrl> +#include <QGuiApplication> #include <QJsonDocument> #include <QJsonObject> #include <QQuickWindow> -//#include <QtQml/QQmlContext> +#include <QtQml/QQmlContext> #include <QQmlContext> #include <QtQml/QQmlApplicationEngine> +#include <qpa/qplatformnativeinterface.h> + #include "eventhandler.h" +static struct wl_output * +getWlOutput(QScreen *screen) +{ + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + void *output = native->nativeResourceForScreen("output", screen); + return static_cast<struct ::wl_output*>(output); +} + +static void +global_add(void *data, struct wl_registry *reg, uint32_t name, + const char *interface, uint32_t version) +{ + struct agl_shell_desktop **shell = + static_cast<struct agl_shell_desktop **>(data); + + if (strcmp(interface, agl_shell_desktop_interface.name) == 0) { + *shell = static_cast<struct agl_shell_desktop *>( + wl_registry_bind(reg, name, &agl_shell_desktop_interface, version) + ); + } +} + +static void global_remove(void *data, struct wl_registry *reg, uint32_t id) +{ + (void) data; + (void) reg; + (void) id; +} + +static const struct wl_registry_listener registry_listener = { + global_add, + global_remove, +}; + +static void +application_id_event(void *data, struct agl_shell_desktop *agl_shell_desktop, + const char *app_id) +{ + EventHandler *ev_handler = static_cast<EventHandler *>(data); + (void) agl_shell_desktop; + + // should probably add here to a list the application or trigger emit + // for QML code, also note that we get here our own application + if (strcmp(app_id, APP_ID) == 0) + return; + + qInfo() << "app_id: " << app_id; +} + +static void +application_state_event(void *data, struct agl_shell_desktop *agl_shell_desktop, + const char *app_id, const char *app_data, uint32_t app_state, uint32_t app_role) +{ + /* unused */ + (void) data; + (void) app_id; + (void) app_data; + (void) app_role; + (void) app_state; + (void) agl_shell_desktop; +} + +static const struct agl_shell_desktop_listener agl_shell_desk_listener = { + application_id_event, + application_state_event, +}; + +static struct agl_shell_desktop * +register_agl_shell_desktop(void) +{ + struct wl_display *wl; + struct wl_registry *registry; + struct agl_shell_desktop *shell = nullptr; + + QPlatformNativeInterface *native = qApp->platformNativeInterface(); + + wl = static_cast<struct wl_display *>(native->nativeResourceForIntegration("display")); + registry = wl_display_get_registry(wl); + + wl_registry_add_listener(registry, ®istry_listener, &shell); + // Roundtrip to get all globals advertised by the compositor + wl_display_roundtrip(wl); + wl_registry_destroy(registry); + + return shell; +} + + void* EventHandler::myThis = 0; const char _drawing_name[] = "drawing_name"; EventHandler::EventHandler(QObject *parent) : - QObject(parent), - mp_hs(NULL), - mp_wm(NULL), - mp_qw(NULL) + QObject(parent), mp_qw(NULL) { } EventHandler::~EventHandler() { - if (mp_hs != NULL) { - delete mp_hs; - } - if (mp_wm != NULL) { - delete mp_wm; - } + if (shell_desktop) + agl_shell_desktop_destroy(shell_desktop); } void EventHandler::init(int port, const char *token) { - myThis = this; - mp_wm = new QLibWindowmanager(); - mp_wm->init(port, token); - - mp_hs = new QLibHomeScreen(); - mp_hs->init(port, token); - - mp_hs->set_event_handler(QLibHomeScreen::Event_ShowWindow, [this](json_object *object){ - this->mp_wm->activateWindow(ROLE_NAME, "normal"); - HMI_DEBUG(APP_ID, "received showWindow event, end!, line=%d", __LINE__); - }); - - mp_hs->set_event_handler(QLibHomeScreen::Event_ReplyShowWindow, [this](json_object *object){ - HMI_DEBUG(APP_ID, "got Event_ReplyShowWindow!\n"); - const char* msg = json_object_to_json_string(object); - emit this->signalOnReplyShowWindow(msg); - }); - - if (mp_wm->requestSurface(ROLE_NAME) != 0) { - HMI_DEBUG(APP_ID, "!!!!LayoutHandler requestSurface Failed!!!!!"); - exit(EXIT_FAILURE); - } - - // Create an event callback against an event type. Here a lambda is called when SyncDraw event occurs - mp_wm->set_event_handler(QLibWindowmanager::Event_SyncDraw, [this](json_object *object) { - HMI_DEBUG(APP_ID, "Surface got syncDraw!"); - this->mp_wm->endDraw(ROLE_NAME); - }); - - mp_wm->set_event_handler(QLibWindowmanager::Event_Visible, [this](json_object *object) { - struct json_object *value; - json_object_object_get_ex(object, _drawing_name, &value); - const char *name = json_object_get_string(value); - - HMI_DEBUG(APP_ID, "Event_Active kKeyDrawingName = %s", name); - }); - - mp_wm->set_event_handler(QLibWindowmanager::Event_Invisible, [this](json_object *object) { - struct json_object *value; - json_object_object_get_ex(object, _drawing_name, &value); - const char *name = json_object_get_string(value); - - HMI_DEBUG(APP_ID, "Event_Inactive kKeyDrawingName = %s", name); - }); - - HMI_DEBUG(APP_ID, "LayoutHander::init() finished."); + (void) port; + (void) token; + + shell_desktop = register_agl_shell_desktop(); + if (shell_desktop) + agl_shell_desktop_add_listener(shell_desktop, &agl_shell_desk_listener, this); + + + m_launcher = new Launcher(DEFAULT_AFM_UNIX_SOCK, nullptr); + if (m_launcher->setup_pws_connection() != 0) + HMI_DEBUG("onscreen", "EventHandler::init failed to set-up connection to afm-system-daemon"); } void EventHandler::setQuickWindow(QQuickWindow *qw) { mp_qw = qw; - QObject::connect(mp_qw, SIGNAL(frameSwapped()), mp_wm, SLOT(slotActivateSurface())); } void EventHandler::showWindow(QString id, QString json) { - if(json.isNull()) - mp_hs->tapShortcut(id); - else - mp_hs->showWindow(id.toStdString().c_str(), json_tokener_parse(json.toStdString().c_str())); + if (shell_desktop) { + struct wl_output *output = getWlOutput(qApp->screens().first()); + qInfo() << "sending activate_app"; + agl_shell_desktop_activate_app(shell_desktop, + id.toStdString().c_str(), + json.toStdString().c_str(), + output); + } + + qInfo() << "data from json: " << json.toStdString().c_str(); } void EventHandler::hideWindow(QString id) { - mp_hs->hideWindow(id.toStdString().c_str()); + if (shell_desktop) + agl_shell_desktop_deactivate_app(shell_desktop, id.toStdString().c_str()); +} + +int +EventHandler::start(const QString &app_id) +{ + int pid = -1; + + if (m_launcher && m_launcher->connection_is_set()) + pid = m_launcher->start(app_id); + + return pid; +} + +bool +EventHandler::is_running(const QString &app_id) +{ + if (m_launcher && m_launcher->connection_is_set()) + return m_launcher->is_running(app_id); + + return false; +} + +void +EventHandler::set_window_popup(const QString &app_id, int x, int y) +{ + struct wl_output *output = getWlOutput(qApp->screens().first()); + + if (shell_desktop) + agl_shell_desktop_set_app_property(shell_desktop, + app_id.toStdString().c_str(), + AGL_SHELL_DESKTOP_APP_ROLE_POPUP, x, y, output); } diff --git a/sample/app/eventhandler.h b/sample/app/eventhandler.h index af66644..3a26b42 100644 --- a/sample/app/eventhandler.h +++ b/sample/app/eventhandler.h @@ -21,8 +21,14 @@ #include <string> #include <QVariant> -#include <qlibhomescreen.h> -#include <qlibwindowmanager.h> +#include <wayland-client.h> +#include "wayland-agl-shell-desktop-client-protocol.h" + +#ifndef DEFAULT_AFM_UNIX_SOCK +#define DEFAULT_AFM_UNIX_SOCK "unix:/run/platform/apis/ws/afm-main" +#endif + +#include "launcher.h" #include "hmi-debug.h" #define ROLE_NAME "onstestapp" @@ -46,14 +52,17 @@ public: Q_INVOKABLE void showWindow(QString id, QString json); Q_INVOKABLE void hideWindow(QString id); + Q_INVOKABLE int start(const QString &app_id); + Q_INVOKABLE bool is_running(const QString &app_id); + Q_INVOKABLE void set_window_popup(const QString &app_id, int x, int y); signals: void signalOnReplyShowWindow(QVariant val); private: - QLibHomeScreen *mp_hs; - QLibWindowmanager* mp_wm; - QQuickWindow *mp_qw; + struct agl_shell_desktop *shell_desktop = nullptr; + Launcher *m_launcher = nullptr; + QQuickWindow *mp_qw; }; #endif // EVENTHANDLER_H diff --git a/sample/app/main.cpp b/sample/app/main.cpp index c47c869..f3bd99c 100644 --- a/sample/app/main.cpp +++ b/sample/app/main.cpp @@ -28,12 +28,14 @@ int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); - app.setApplicationName("onstestapp"); - app.setApplicationVersion(QStringLiteral("3.99.3")); - app.setOrganizationDomain(QStringLiteral("automotivelinux.org")); - app.setOrganizationName(QStringLiteral("AutomotiveGradeLinux")); - QQuickStyle::setStyle("AGL"); + //app.setApplicationName("onstestapp"); + //app.setApplicationVersion(QStringLiteral("3.99.3")); + //app.setOrganizationDomain(QStringLiteral("automotivelinux.org")); + //app.setOrganizationName(QStringLiteral("AutomotiveGradeLinux")); + + //QQuickStyle::setStyle("AGL"); + app.setDesktopFileName("onstestapp"); QCommandLineParser parser; parser.addPositionalArgument("port", app.translate("main", "port for binding")); diff --git a/sample/app/main.qml b/sample/app/main.qml index b9f415b..b545c53 100644 --- a/sample/app/main.qml +++ b/sample/app/main.qml @@ -7,8 +7,8 @@ import AGL.Demo.Controls 1.0 ApplicationWindow { id: root visible: true - width: 1080 - height: 1487 + width: Screen.width + height: Screen.height property string onsId: qsTr("onscreenapp") property string onsTitle: qsTr("One Button title") @@ -19,6 +19,15 @@ ApplicationWindow { property string onsButton3: qsTr("") property string postmsg: qsTr("") property string btndata: qsTr("") + property int pid: -1 + property bool onscreen_role_set: false + + property string mapp_id: '' + property string mapp_data_msg: '' + + // position of the pop-up + property int x: 200 + property int y: 200 Label { id: title @@ -37,24 +46,24 @@ ApplicationWindow { anchors.horizontalCenter: title.horizontalCenter // show received reply information area - Flickable { - id: flickable - width: 800 - height: 320 - Layout.alignment: Qt.AlignCenter - flickableDirection: Flickable.VerticalFlick - boundsBehavior: Flickable.StopAtBounds - - TextArea.flickable: TextArea { - id: output - text: "show received reply information area\n...\n...\n...\n...\n" - font.pixelSize: 24 - wrapMode: TextArea.Wrap - color: '#00ADDC' - } - - ScrollBar.vertical: ScrollBar { } - } + //Flickable { + // id: flickable + // width: 400 + // height: 220 + // Layout.alignment: Qt.AlignCenter + // flickableDirection: Flickable.VerticalFlick + // boundsBehavior: Flickable.StopAtBounds + + // TextArea.flickable: TextArea { + // id: output + // text: "show received reply information area\n...\n...\n...\n...\n" + // font.pixelSize: 12 + // wrapMode: TextArea.Wrap + // color: '#00ADDC' + // } + + // ScrollBar.vertical: ScrollBar { } + // } // select onscreen type area GroupBox { @@ -316,6 +325,26 @@ ApplicationWindow { onsButton3 = qsTr("") } + Timer { + id: activate_timer + interval: 500 + running: false + repeat: false + onTriggered: { + console.log("calling eventHandler.showWindow for " + mapp_id + " and data msg " + mapp_data_msg) + eventHandler.showWindow(mapp_id, mapp_data_msg) + mapp_id = '' + mapp_data_msg = '' + } + } + + + function armTimer(app_id, msg) { + mapp_id = app_id + mapp_data_msg = msg + activate_timer.running = true + } + function postMessage() { console.log("poster pressed") btndata = "" @@ -339,11 +368,42 @@ ApplicationWindow { else postmsg += "}" - eventHandler.showWindow(onsId, postmsg); + if (!onscreen_role_set) { + console.log("onscreen_role_set is not set") + eventHandler.set_window_popup(onsId, x, y) + console.log("setting for popup for " + onsId) + onscreen_role_set = true + } + + // if the application is not already started, start it + if (pid === -1) { + // if the application is not started, then the first time + // we start we also display it using the default policy engine + pid = eventHandler.start(onsId, postmsg) + console.log("calling eventHandler.start for " + onsId + " with pid " + pid) + + // this is necessary jus the first time as we don't queue the + // 'activate_app' event and pass it over once onscreenapp is + // started. It is implementation detail in case it is required + console.log("calling armTimer for " + onsId) + armTimer(onsId, postmsg) + } else { + // onscreenapp is already start we just need to activate + pass the data + if (!eventHandler.is_running(onsId) && pid > 0) { + // this is mostly for testing, in case onscreenapp died + // unexpectedly + eventHandler.set_window_popup(onsId, x, y) + pid = eventHandler.start(onsId, postmsg) + armTimer(onsId, postmsg) + } else { + eventHandler.showWindow(onsId, postmsg) + } + } + } function qmlOnReplyShowWindow(text) { console.log("onstestapp received:",text); - output.text = text; + //output.text = text; } } diff --git a/sample/app/protocol/agl-shell-desktop.xml b/sample/app/protocol/agl-shell-desktop.xml new file mode 100644 index 0000000..05a3725 --- /dev/null +++ b/sample/app/protocol/agl-shell-desktop.xml @@ -0,0 +1,113 @@ +<?xml version="1.0" encoding="UTF-8"?> +<protocol name="agl_shell_desktop"> + <copyright> + Copyright © 2020 Collabora, Ltd. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + </copyright> + <interface name="agl_shell_desktop" version="1"> + <description summary="Private extension to allow applications activate other apps"> + This extension can be used by regular application to instruct to compositor + to activate or switch to other running (regular) applications. The client + is responsbile for filtering their own app_id when receiving application id. + + Note that other (regular) applications can bind to this interface and there is + no mechanism to place to restrict or limit that. + </description> + + <enum name="app_role"> + <entry name="popup" value="0"/> + <entry name="fullscreen" value="1"/> + </enum> + + <enum name="app_state"> + <entry name="activated" value="0"/> + <entry name="deactivated" value="1"/> + </enum> + + <event name="application"> + <description summary="advertise application id"> + The compositor may choose to advertise one or more application ids which + can be used to activate/switch to. + + When this global is bound, the compositor will send all application ids + available for activation, but may send additional application id at any + time (when they've been mapped in the compositor). + </description> + <arg name="app_id" type="string"/> + </event> + + <request name="activate_app"> + <description summary="make client current window"> + Ask the compositor to make a toplevel to become the current/focused + window for window management purposes. + + See xdg_toplevel.set_app_id from the xdg-shell protocol for a + description of app_id. + </description> + <arg name="app_id" type="string"/> + <arg name="app_data" type="string" allow-null="true"/> + <arg name="output" type="object" interface="wl_output"/> + </request> + + <request name="set_app_property"> + <description summary="set properties for a client identified by app_id"> + Ask the compositor to make a toplevel obey the app_role and, depending + on the role, to use the the x and y values as initial positional values. + The x and y values would only make sense for certain roles. + + See xdg_toplevel.set_app_id from the xdg-shell protocol for a + description of app_id. + </description> + <arg name="app_id" type="string"/> + <arg name="role" type="uint" enum="app_role"/> + <arg name="x" type="int"/> + <arg name="y" type="int"/> + <arg name="output" type="object" interface="wl_output"/> + </request> + + <request name="deactivate_app"> + <description summary="de-activate/hide window identified by app_id"> + Ask the compositor to hide the toplevel window for window + management purposes. Depending on the window role, this request + will either display the previously active window (or the background + in case there's no previously activate surface) or temporarly (or + until a 'activate_app' is called upon) hide the surface. All + the surfaces are identifiable by using the app_id, and no actions are + taken in case the app_id is not/was not present. + + See xdg_toplevel.set_app_id from the xdg-shell protocol for a + description of app_id. + </description> + <arg name="app_id" type="string"/> + </request> + + <event name="state_app"> + <description summary="event sent when application has suffered state modification"> + Notifies application(s) when other application have suffered state modifications. + </description> + <arg name="app_id" type="string"/> + <arg name="app_data" type="string" allow-null="true"/> + <arg name="state" type="uint" enum="app_state"/> + <arg name="role" type="uint" enum="app_role"/> + </event> + + </interface> +</protocol> diff --git a/sample/package/config.xml b/sample/package/config.xml index 2d9a5a1..38ec308 100644 --- a/sample/package/config.xml +++ b/sample/package/config.xml @@ -12,6 +12,10 @@ </feature> <feature name="urn:AGL:widget:required-permission"> <param name="urn:AGL:permission::public:no-htdocs" value="required"/> + <param name="urn:AGL:permission::public:display" value="required" /> + <param name="urn:AGL:permission:afm:system:widget:start" value="required" /> + <param name="urn:AGL:permission:afm:system:widget" value="required" /> + <param name="urn:AGL:permission:afm:system:runner" value="required" /> </feature> </widget> diff --git a/sample/sample.pro b/sample/sample.pro index f49af69..e4e613b 100644 --- a/sample/sample.pro +++ b/sample/sample.pro @@ -14,5 +14,7 @@ # limitations under the License. TEMPLATE = subdirs -SUBDIRS = app package +SUBDIRS = ../pws app package + +onstestapp.depends = ../pws package.depends += app |