summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--.gitmodules6
-rw-r--r--CMakeLists.txt23
m---------afb-helpers0
-rw-r--r--app/CMakeLists.txt57
-rw-r--r--app/Mixer.qml151
-rw-r--r--app/app.pri12
-rw-r--r--app/app.pro18
-rw-r--r--app/main.cpp27
-rw-r--r--app/mixer.cpp91
-rw-r--r--app/mixer.h54
-rw-r--r--app/paclient.cpp329
-rw-r--r--app/paclient.h97
-rw-r--r--app/pacontrolmodel.cpp208
-rw-r--r--app/pacontrolmodel.h92
m---------conf.d/app-templates0
-rwxr-xr-xconf.d/autobuild/agl/autobuild21
-rwxr-xr-xconf.d/autobuild/linux/autobuild67
-rw-r--r--conf.d/cmake/config.cmake201
-rw-r--r--conf.d/wgt/config.xml.in21
-rw-r--r--conf.d/wgt/icon.svg (renamed from package/icon.svg)0
-rw-r--r--mixer.pro3
-rw-r--r--package/config.xml18
-rw-r--r--package/package.pro19
24 files changed, 627 insertions, 890 deletions
diff --git a/.gitignore b/.gitignore
index 89f64c7..3e1d785 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
*.pro.*
+build/
+.vscode/
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..af9d4e3
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "conf.d/app-templates"]
+ path = conf.d/app-templates
+ url = https://gerrit.automotivelinux.org/gerrit/p/apps/app-templates.git
+[submodule "afb-helpers"]
+ path = afb-helpers
+ url = https://gerrit.automotivelinux.org/gerrit/apps/app-afb-helpers-submodule
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..8c1fc7c
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,23 @@
+###########################################################################
+# Copyright 2018 IoT.bzh
+#
+# author: Loïc Collognon <loic.collignon@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+CMAKE_MINIMUM_REQUIRED(VERSION 3.3)
+
+set(AFB_HELPERS_QTWSCLIENT ON CACHE BOOL "enable Qt's WebSocket client" FORCE)
+
+include(${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/config.cmake)
diff --git a/afb-helpers b/afb-helpers
new file mode 160000
+Subproject 43ec9716bf83d8a6e5ff15909705cb1adc3c189
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
new file mode 100644
index 0000000..8f8bd1e
--- /dev/null
+++ b/app/CMakeLists.txt
@@ -0,0 +1,57 @@
+###########################################################################
+# Copyright 2018 IoT.bzh
+#
+# author: Loïc Collignon <loic.collignon@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+set(CMAKE_AUTOMOC ON)
+set(CMAKE_CXX_STANDARD 14)
+
+find_package(Qt5 COMPONENTS Core Gui QuickControls2 WebSockets QuickWidgets REQUIRED)
+qt5_add_resources(RESOURCES Mixer.qrc)
+
+PROJECT_TARGET_ADD(mixer)
+
+add_executable(mixer
+ main.cpp
+ mixer.cpp
+ ${RESOURCES}
+)
+
+set_target_properties(mixer PROPERTIES
+ LABELS "EXECUTABLE"
+ PREFIX ""
+ COMPILE_FLAGS "${EXTRAS_CFLAGS} -DFOR_AFB_BINDING"
+ LINK_FLAGS "${BINDINGS_LINK_FLAG}"
+ LINK_LIBRARIES "${EXTRAS_LIBRARIES}"
+ OUTPUT_NAME "${TARGET_NAME}"
+)
+
+target_link_libraries(mixer
+ Qt5::QuickControls2
+ Qt5::WebSockets
+ homescreen
+ qtWindowmanagerWrapper
+ json-c
+ afb-helpers
+)
+
+#add_custom_command(TARGET ${TARGET_NAME}
+#PRE_BUILD
+#COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/../package/htdocs
+#COMMAND cp -rv ${CMAKE_CURRENT_SOURCE_DIR}/../htdocs ${CMAKE_CURRENT_BINARY_DIR}/../package/
+#COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/../package/etc
+#COMMAND cp -rv ${CMAKE_CURRENT_SOURCE_DIR}/../etc ${CMAKE_CURRENT_BINARY_DIR}/../package/)
diff --git a/app/Mixer.qml b/app/Mixer.qml
index 96875e0..8b1ba06 100644
--- a/app/Mixer.qml
+++ b/app/Mixer.qml
@@ -14,85 +14,94 @@
* limitations under the License.
*/
+// BUG: ValueChanged event is raised by sliders when you are moving the caret, should be raised only when you release it.
+// TODO: Call mixer.setVolume(sliderName, Value) on value change
+// TODO: Call mixer.getVolume(sliderName) on load
+
import QtQuick 2.6
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.0
import AGL.Demo.Controls 1.0
-import PaControlModel 1.0
+import Mixer 1.0
ApplicationWindow {
- id: root
+ id: root
+
+ Mixer {
+ id: mixer
+ Component.objectName: {
+ mixer.open(bindingAddress)
+ }
+ onRolesChanged: {
+ // Remove existing sliders
+ for(var i = sliders.children.length; i > 0 ; --i) {
+ console.log("destroying: " + i)
+ sliders.children[i-1].destroy()
+ }
+
+ // Add slider for each role
+ for(var j = 0; j < mixer.roles.length; ++j) {
+ addSlider(mixer.roles[j])
+ }
+ }
- Label {
- id: title
- font.pixelSize: 48
- text: "Mixer"
- anchors.horizontalCenter: parent.horizontalCenter
- }
+ function addSlider(name) {
+ Qt.createQmlObject("
+import QtQuick.Layouts 1.1
+import QtQuick.Controls 2.0
+RowLayout {
+ property int value
+ id: slider_" + name + "
+ Layout.minimumHeight: 75
+ Label {
+ font.pixelSize: 24
+ text: \"" + name+ "\"
+ Layout.minimumWidth: 150
+ }
+ Label {
+ id: slider_" + name + "_textvalue
+ font.pixelSize: 24
+ text: \"0 %\"
+ }
+ Slider {
+ id: slider_" + name + "_slider
+ Layout.fillWidth: true
+ from: 0
+ to: 100
+ stepSize: 1
+ snapMode: Slider.SnapOnRelease
+ onValueChanged: {
+ slider_" + name + "_textvalue.text = value + \" %\"
+ mixer.setVolume(\"" + name + "\", value)
+ }
+ Component.objectName: {
+ mixer.getVolume(\"" + name + "\")
+ }
+ }
+ }", sliders, "volumeslider")
+ }
- Component {
- id: ctldesc
- Label {
- font.pixelSize: 32
- width: listView.width
- wrapMode: Text.WordWrap
- property var typeString: {modelType ? "Output" : "Input"}
- text: "[" + typeString + " #" + modelCIndex + "]: " + modelDesc
- }
- }
+ function deleteChilds(item) {
+ for(var i = item.children.length; i > 0 ; i--) {
+ deleteChilds(item.children[i-1])
+ }
+ item.destroy()
+ }
+ }
- Component {
- id: empty
- Item {
- }
- }
+ Label {
+ id: title
+ font.pixelSize: 48
+ text: "Mixer"
+ anchors.horizontalCenter: parent.horizontalCenter
+ }
- ListView {
- id: listView
- anchors.left: parent.left
- anchors.top: title.bottom
- anchors.margins: 80
- anchors.fill: parent
- model: PaControlModel { objectName: "pacm" }
- delegate: ColumnLayout {
- width: parent.width
- spacing: 40
- Connections {
- target: listView.model
- onDataChanged: slider.value = volume
- }
- Loader {
- property int modelType: type
- property int modelCIndex: cindex
- property string modelDesc: name
- sourceComponent: (channel == 0) ? ctldesc : empty
- }
- RowLayout {
- Layout.minimumHeight: 75
- Label {
- font.pixelSize: 24
- text: cdesc
- Layout.minimumWidth: 150
- }
- Label {
- font.pixelSize: 24
- text: "0 %"
- }
- Slider {
- id: slider
- Layout.fillWidth: true
- from: 0
- to: 65536
- stepSize: 256
- snapMode: Slider.SnapOnRelease
- onValueChanged: volume = value
- Component.onCompleted: value = volume
- }
- Label {
- font.pixelSize: 24
- text: "100 %"
- }
- }
- }
- }
+ ColumnLayout {
+ id: sliders
+ anchors.margins: 80
+ anchors.top: title.bottom
+ anchors.left: parent.left
+ anchors.right: parent.right
+ }
}
+
diff --git a/app/app.pri b/app/app.pri
deleted file mode 100644
index 014646f..0000000
--- a/app/app.pri
+++ /dev/null
@@ -1,12 +0,0 @@
-TEMPLATE = app
-
-load(configure)
-qtCompileTest(libhomescreen)
-
-config_libhomescreen {
- CONFIG += link_pkgconfig
- PKGCONFIG += homescreen
- DEFINES += HAVE_LIBHOMESCREEN
-}
-
-DESTDIR = $${OUT_PWD}/../package/root/bin
diff --git a/app/app.pro b/app/app.pro
deleted file mode 100644
index a33fc0d..0000000
--- a/app/app.pro
+++ /dev/null
@@ -1,18 +0,0 @@
-TARGET = mixer
-QT = quickcontrols2
-
-HEADERS += \
- pacontrolmodel.h \
- paclient.h
-
-SOURCES = main.cpp \
- pacontrolmodel.cpp \
- paclient.cpp
-
-CONFIG += link_pkgconfig
-PKGCONFIG += libhomescreen qlibwindowmanager libpulse
-
-RESOURCES += \
- Mixer.qrc
-
-include(app.pri)
diff --git a/app/main.cpp b/app/main.cpp
index bfce498..9c6339f 100644
--- a/app/main.cpp
+++ b/app/main.cpp
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2016 The Qt Company Ltd.
* Copyright (C) 2016,2017 Konsulko Group
+ * Copyright (C) 2018 IoT.bzh
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,9 +16,6 @@
* limitations under the License.
*/
-#include "paclient.h"
-#include "pacontrolmodel.h"
-
#include <QtCore/QDebug>
#include <QtCore/QCommandLineParser>
#include <QtCore/QUrlQuery>
@@ -34,6 +32,7 @@
#include <QQuickWindow>
#include <libhomescreen.hpp>
#include <qlibwindowmanager.h>
+#include "mixer.h"
int main(int argc, char *argv[])
{
@@ -51,14 +50,7 @@ int main(int argc, char *argv[])
parser.process(app);
QStringList positionalArguments = parser.positionalArguments();
- // Fire up PA client QThread
- QThread* pat = new QThread;
- PaClient* client = new PaClient();
- client->moveToThread(pat);
- pat->start();
-
- // Register the PA Control Model
- qmlRegisterType<PaControlModel>("PaControlModel", 1, 0, "PaControlModel");
+ qmlRegisterType<Mixer>("Mixer", 1, 0, "Mixer");
QQmlApplicationEngine engine;
if (positionalArguments.length() == 2) {
@@ -88,7 +80,7 @@ int main(int argc, char *argv[])
exit(EXIT_FAILURE);
}
// Create an event callbnewack against an event type. Here a lambda is called when SyncDraw event occurs
- qwm->set_event_handler(QLibWindowmanager::Event_SyncDraw, [qwm, myname](json_object *object) {
+ qwm->set_event_handler(QLibWindowmanager::Event_SyncDraw, [qwm, myname](json_object*) {
fprintf(stderr, "Surface got syncDraw!\n");
qwm->endDraw(myname);
});
@@ -116,17 +108,6 @@ int main(int argc, char *argv[])
QQuickWindow *window = qobject_cast<QQuickWindow *>(mobjs.first());
QObject::connect(window, SIGNAL(frameSwapped()), qwm, SLOT(slotActivateSurface()));
-
- PaControlModel *pacm = mobjs.first()->findChild<PaControlModel *>("pacm");
- QObject::connect(client, SIGNAL(controlAdded(int, QString, QString, int, int, const char *, int)),
- pacm, SLOT(addOneControl(int, QString, QString, int, int, const char *, int)));
- QObject::connect(client, SIGNAL(volumeExternallyChanged(uint32_t, uint32_t, uint32_t, uint32_t)),
- pacm, SLOT(changeExternalVolume(uint32_t, uint32_t, uint32_t, uint32_t)));
- QObject::connect(pacm, SIGNAL(volumeChanged(uint32_t, uint32_t, uint32_t, uint32_t)),
- client, SLOT(setVolume(uint32_t, uint32_t, uint32_t, uint32_t)));
-
- // Initalize PA client
- client->init();
}
return app.exec();
}
diff --git a/app/mixer.cpp b/app/mixer.cpp
new file mode 100644
index 0000000..6614569
--- /dev/null
+++ b/app/mixer.cpp
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ * Copyright (C) 2016,2017 Konsulko Group
+ * Copyright (C) 2018 IoT.bzh
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <QJsonArray>
+#include <QJsonObject>
+#include <QtDebug>
+#include "mixer.h"
+
+Mixer::Mixer(QObject* parent)
+ : QObject(parent)
+{
+ connect(&m_client, SIGNAL(connected()), this, SLOT(onClientConnected()));
+}
+
+QStringList Mixer::roles() const
+{
+ return m_roles;
+}
+
+void Mixer::open(const QUrl &url)
+{
+ m_client.open(url);
+}
+
+void Mixer::onClientConnected()
+{
+ // Call HAL to populate list
+ m_client.call("ahl-4a", "get_roles", QJsonValue(), [this](bool r, const QJsonValue& val) {
+ if (r)
+ {
+ m_roles.clear();
+ //BUG: should be able to add this, but not handled right now: m_roles.append("playback");
+ QJsonArray cards = val.toObject()["response"].toArray();
+ foreach (const QJsonValue& card, cards)
+ {
+ m_roles.append(card.toString());
+ qDebug() << "Mixer::onClientConnected - added this HAL: " << card.toString();
+ }
+ emit rolesChanged();
+ }
+ });
+}
+
+void Mixer::setVolume(const QString& name, int value)
+{
+ QJsonObject arg;
+ arg.insert("action", "volume");
+ arg.insert("value", QJsonValue(value));
+ m_client.call("ahl-4a", name, arg, [this, name](bool r, const QJsonValue& v) {
+ if (r && v.isObject())
+ {
+ // TODO: Success, update the slider
+ }
+ else
+ {
+ // TODO: Failed, reset the slider to previous value
+ }
+ });
+}
+
+void Mixer::getVolume(const QString& name)
+{
+ QJsonObject arg;
+ arg.insert("action", "volume");
+ arg.insert("value", QJsonValue("+0")); // FIXME: Hack to get volume: ask for a relative change with a delta of zero
+ m_client.call("ahl-4a", name, arg, [this, name](bool r, const QJsonValue& v) {
+ if (r && v.isObject())
+ {
+ // TODO: Success, update the slider
+ }
+ else
+ {
+ // TODO: Failed, what to do ?
+ }
+ });
+}
diff --git a/app/mixer.h b/app/mixer.h
new file mode 100644
index 0000000..a46c8a1
--- /dev/null
+++ b/app/mixer.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Qt Company Ltd.
+ * Copyright (C) 2016,2017 Konsulko Group
+ * Copyright (C) 2018 IoT.bzh
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <QObject>
+#include <QString>
+#include <QSharedPointer>
+#include <QStringList>
+#include "qafbwebsocketclient.h"
+//#include "volumeslider.h"
+
+class Mixer
+ : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QStringList roles READ roles NOTIFY rolesChanged)
+
+private:
+
+public:
+ explicit Mixer(QObject* parent = nullptr);
+ Mixer(const Mixer&) = delete;
+
+ Q_INVOKABLE void open(const QUrl& url);
+ Q_INVOKABLE QStringList roles() const;
+ Q_INVOKABLE void setVolume(const QString& name, int value);
+ Q_INVOKABLE void getVolume(const QString& name);
+
+signals:
+ void rolesChanged();
+ void volumeChanged(const QString& name, int value);
+
+private slots:
+ void onClientConnected();
+
+private:
+ QStringList m_roles;
+ QAfbWebsocketClient m_client;
+};
diff --git a/app/paclient.cpp b/app/paclient.cpp
deleted file mode 100644
index bd53cde..0000000
--- a/app/paclient.cpp
+++ /dev/null
@@ -1,329 +0,0 @@
-/*
- * Copyright (C) 2016,2017 Konsulko Group
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "paclient.h"
-
-#include <QtCore/QDebug>
-
-PaClient::PaClient()
- : m_init(false), m_ml(nullptr), m_mlapi(nullptr), m_ctx(nullptr)
-{
-}
-
-PaClient::~PaClient()
-{
- if (m_init)
- close();
-}
-
-void PaClient::close()
-{
- if (!m_init) return;
- pa_threaded_mainloop_stop(m_ml);
- pa_threaded_mainloop_free(m_ml);
- m_init = false;
-}
-
-static void set_sink_volume_cb(pa_context *c, int success, void *data)
-{
- Q_UNUSED(data);
-
- if (!success)
- qWarning() << "PaClient: set sink volume: " <<
- pa_strerror(pa_context_errno(c));
-}
-
-static void set_source_volume_cb(pa_context *c, int success, void *data)
-{
- Q_UNUSED(data);
-
- if (!success)
- qWarning() << "PaClient: set source volume: " <<
- pa_strerror(pa_context_errno(c));
-}
-
-void PaClient::setVolume(uint32_t type, uint32_t index, uint32_t channel, uint32_t volume)
-{
- pa_operation *o;
- pa_context *c = context();
- pa_cvolume *cvolume = NULL;
-
- if (type == C_SINK) {
- cvolume = m_sink_states.value(index);
- cvolume->values[channel] = volume;
- if (!(o = pa_context_set_sink_volume_by_index(c, index, cvolume, set_sink_volume_cb, NULL))) {
- qWarning() << "PaClient: set sink #" << index <<
- " channel #" << channel <<
- " volume: " << pa_strerror(pa_context_errno(c));
- return;
- }
- pa_operation_unref(o);
- } else if (type == C_SOURCE) {
- cvolume = m_source_states.value(index);
- cvolume->values[channel] = volume;
- if (!(o = pa_context_set_source_volume_by_index(c, index, cvolume, set_source_volume_cb, NULL))) {
- qWarning() << "PaClient: set source #" << index <<
- " channel #" << channel <<
- " volume: " << pa_strerror(pa_context_errno(c));
- return;
- }
- pa_operation_unref(o);
- }
-}
-
-void get_source_list_cb(pa_context *c,
- const pa_source_info *i,
- int eol,
- void *data)
-{
- int chan;
-
- PaClient *self = reinterpret_cast<PaClient*>(data);
-
- if (eol < 0) {
- qWarning() << "PaClient: get source list: " <<
- pa_strerror(pa_context_errno(c));
-
- self->close();
- return;
- }
-
- if (!eol) {
- self->addOneControlState(C_SOURCE, i->index, &i->volume);
- for (chan = 0; chan < i->channel_map.channels; chan++) {
- // NOTE: hide input control
- if (QString(i->name).endsWith("monitor"))
- continue;
-
- emit self->controlAdded(i->index, QString(i->name), QString(i->description),
- C_SOURCE, chan, channel_position_string[i->channel_map.map[chan]],
- i->volume.values[chan]);
- }
- }
-}
-
-void get_sink_list_cb(pa_context *c,
- const pa_sink_info *i,
- int eol,
- void *data)
-{
- PaClient *self = reinterpret_cast<PaClient*>(data);
- int chan;
-
- if(eol < 0) {
- qWarning() << "PaClient: get sink list: " <<
- pa_strerror(pa_context_errno(c));
- self->close();
- return;
- }
-
- if(!eol) {
- self->addOneControlState(C_SINK, i->index, &i->volume);
- for (chan = 0; chan < i->channel_map.channels; chan++) {
- emit self->controlAdded(i->index, QString(i->name), QString(i->description),
- C_SINK, chan, channel_position_string[i->channel_map.map[chan]],
- i->volume.values[chan]);
- }
- }
-}
-
-void get_sink_info_change_cb(pa_context *c,
- const pa_sink_info *i,
- int eol,
- void *data)
-{
- Q_UNUSED(c);
- Q_ASSERT(i);
- Q_ASSERT(data);
-
- if (eol) return;
-
- for (int chan = 0; chan < i->channel_map.channels; chan++) {
- PaClient *self = reinterpret_cast<PaClient*>(data);
- QHash<int, pa_cvolume *> states = self->sink_states();
- pa_cvolume *cvolume = states.value(i->index);
- // Check each channel for volume change
- if (cvolume->values[chan] != i->volume.values[chan]) {
- // On change, update cache and signal
- cvolume->values[chan] = i->volume.values[chan];
- emit self->volumeExternallyChanged(C_SINK, i->index, chan, i->volume.values[chan]);
- }
- }
-}
-
-void get_source_info_change_cb(pa_context *c,
- const pa_source_info *i,
- int eol,
- void *data)
-{
- Q_UNUSED(c);
- Q_ASSERT(i);
- Q_ASSERT(data);
-
- if (eol) return;
-
- for (int chan = 0; chan < i->channel_map.channels; chan++) {
- PaClient *self = reinterpret_cast<PaClient*>(data);
- QHash<int, pa_cvolume *> states = self->source_states();
- pa_cvolume *cvolume = states.value(i->index);
- // Check each channel for volume change
- if (cvolume->values[chan] != i->volume.values[chan]) {
- // On change, update cache and signal
- cvolume->values[chan] = i->volume.values[chan];
- emit self->volumeExternallyChanged(C_SOURCE, i->index, chan, i->volume.values[chan]);
- }
- }
-}
-
-
-void subscribe_cb(pa_context *c,
- pa_subscription_event_type_t type,
- uint32_t index,
- void *data)
-{
- pa_operation *o;
-
- if ((type & PA_SUBSCRIPTION_EVENT_TYPE_MASK) != PA_SUBSCRIPTION_EVENT_CHANGE) {
- qWarning("PaClient: unhandled subscribe event operation");
- return;
- }
-
- switch (type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK) {
- case PA_SUBSCRIPTION_EVENT_SINK:
- if (!(o = pa_context_get_sink_info_by_index(c, index, get_sink_info_change_cb, data))) {
- qWarning() << "PaClient: get sink info by index: " <<
- pa_strerror(pa_context_errno(c));
- return;
- }
- break;
- case PA_SUBSCRIPTION_EVENT_SOURCE:
- if (!(o = pa_context_get_source_info_by_index(c, index, get_source_info_change_cb, data))) {
- qWarning() << "PaClient: get source info by index: " <<
- pa_strerror(pa_context_errno(c));
- return;
- }
- break;
- default:
- qWarning("PaClient: unhandled subscribe event facility");
- }
-}
-
-void context_state_cb(pa_context *c, void *data)
-{
- pa_operation *o;
- PaClient *self = reinterpret_cast<PaClient*>(data);
-
- switch (pa_context_get_state(c)) {
- case PA_CONTEXT_CONNECTING:
- case PA_CONTEXT_AUTHORIZING:
- case PA_CONTEXT_SETTING_NAME:
- break;
- case PA_CONTEXT_READY:
- // Fetch the controls of interest
- if (!(o = pa_context_get_source_info_list(c, get_source_list_cb, data))) {
- qWarning() << "PaClient: get source info list: " <<
- pa_strerror(pa_context_errno(c));
- return;
- }
- pa_operation_unref(o);
- if (!(o = pa_context_get_sink_info_list(c, &get_sink_list_cb, data))) {
- qWarning() << "PaClient: get sink info list: " <<
- pa_strerror(pa_context_errno(c));
- return;
- }
- pa_operation_unref(o);
- pa_context_set_subscribe_callback(c, subscribe_cb, data);
- if (!(o = pa_context_subscribe(c, (pa_subscription_mask_t)(PA_SUBSCRIPTION_MASK_SINK | PA_SUBSCRIPTION_MASK_SOURCE), NULL, NULL))) {
- qWarning() << "PaClient: subscribe: " <<
- pa_strerror(pa_context_errno(c));
- return;
- }
- break;
- case PA_CONTEXT_TERMINATED:
- self->close();
- break;
-
- case PA_CONTEXT_FAILED:
- default:
- qCritical() << "PaClient: connection failed: " <<
- pa_strerror(pa_context_errno(c));
- self->close();
- break;
- }
-}
-
-void PaClient::init()
-{
- m_ml = pa_threaded_mainloop_new();
- if (!m_ml) {
- qCritical("PaClient: failed to create mainloop");
- return;
- }
-
- pa_threaded_mainloop_set_name(m_ml, "PaClient mainloop");
-
- m_mlapi = pa_threaded_mainloop_get_api(m_ml);
-
- lock();
-
- m_ctx = pa_context_new(m_mlapi, "Mixer");
- if (!m_ctx) {
- qCritical("PaClient: failed to create context");
- pa_threaded_mainloop_free(m_ml);
- return;
- }
- pa_context_set_state_callback(m_ctx, context_state_cb, this);
-
- if (pa_context_connect(m_ctx, 0, (pa_context_flags_t)0, 0) < 0) {
- qCritical("PaClient: failed to connect");
- pa_context_unref(m_ctx);
- pa_threaded_mainloop_free(m_ml);
- return;
- }
-
- if (pa_threaded_mainloop_start(m_ml) != 0) {
- qCritical("PaClient: failed to start mainloop");
- pa_context_unref(m_ctx);
- pa_threaded_mainloop_free(m_ml);
- return;
- }
-
- unlock();
-
- m_init = true;
-}
-
-void PaClient::addOneControlState(int type, int index, const pa_cvolume *cvolume)
-{
- pa_cvolume *cvolume_new = new pa_cvolume;
- cvolume_new->channels = cvolume->channels;
- for (int i = 0; i < cvolume->channels; i++)
- cvolume_new->values[i] = cvolume->values[i];
- if (type == C_SINK)
- m_sink_states.insert(index, cvolume_new);
- else if (type == C_SOURCE)
- m_source_states.insert(index, cvolume_new);
-}
-
-QHash<int, pa_cvolume *> PaClient::sink_states(void)
-{
- return m_sink_states;
-}
-
-QHash<int, pa_cvolume *> PaClient::source_states(void)
-{
- return m_source_states;
-}
diff --git a/app/paclient.h b/app/paclient.h
deleted file mode 100644
index 73137f2..0000000
--- a/app/paclient.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2016,2017 Konsulko Group
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <pulse/pulseaudio.h>
-
-#include <QtCore/QHash>
-#include <QtCore/QObject>
-
-const char * const channel_position_string[] =
-{
- "Mono",
- "Front Left",
- "Front Right",
- "Center",
- "Rear Center",
- "Rear Left",
- "Rear Right",
- "LFE",
- "Left Center",
- "Right Center",
- "Side Left",
- "Side Right",
-};
-
-enum control_type
-{
- C_SOURCE,
- C_SINK
-};
-
-typedef struct
-{
- uint32_t type;
- uint32_t index;
- pa_cvolume cvolume;
-} CState;
-
-class PaClient : public QObject
-{
- Q_OBJECT
- public:
- PaClient();
- ~PaClient();
-
- void init();
- void close();
-
- inline pa_context *context() const
- {
- return m_ctx;
- }
-
- inline void lock()
- {
- pa_threaded_mainloop_lock(m_ml);
- }
-
- inline void unlock()
- {
- pa_threaded_mainloop_unlock(m_ml);
- }
-
- void addOneControlState(int type, int index, const pa_cvolume *cvolume);
-
- QHash<int, pa_cvolume *> sink_states();
- QHash<int, pa_cvolume *> source_states();
-
- public slots:
- void setVolume(uint32_t type, uint32_t index, uint32_t channel, uint32_t volume);
-
- signals:
- void controlAdded(int cindex, QString name, QString desc, int type, int channel, const char *cdesc, int volume);
- void volumeExternallyChanged(uint32_t type, uint32_t cindex, uint32_t channel, uint32_t volume);
-
- private:
- bool m_init;
- pa_threaded_mainloop *m_ml;
- pa_mainloop_api *m_mlapi;
- pa_context *m_ctx;
- QHash<int, pa_cvolume *> m_sink_states;
- QHash<int, pa_cvolume *> m_source_states;
-
- public slots:
-};
diff --git a/app/pacontrolmodel.cpp b/app/pacontrolmodel.cpp
deleted file mode 100644
index 9489052..0000000
--- a/app/pacontrolmodel.cpp
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright (C) 2016,2017 Konsulko Group
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "pacontrolmodel.h"
-
-PaControl::PaControl(const quint32 &cindex, const QString &name, const QString &desc, const quint32 &type, const quint32 &channel, const QString &cdesc, const quint32 &volume)
- : m_cindex(cindex), m_name(name), m_desc(desc), m_type(type), m_channel(channel), m_cdesc(cdesc), m_volume(volume)
-{
-}
-
-quint32 PaControl::cindex() const
-{
- return m_cindex;
-}
-
-QString PaControl::name() const
-{
- QStringList list = m_name.split(".");
-
- return list.at(1);
-}
-
-QString PaControl::desc() const
-{
- return m_desc;
-}
-
-quint32 PaControl::type() const
-{
- return m_type;
-}
-
-quint32 PaControl::channel() const
-{
- return m_channel;
-}
-
-QString PaControl::cdesc() const
-{
- return m_cdesc;
-}
-
-
-quint32 PaControl::volume() const
-{
- return m_volume;
-}
-
-// FIXME: Not all of these should be editable roles
-void PaControl::setCIndex(const QVariant &cindex)
-{
- m_cindex = cindex.toUInt();
-}
-
-void PaControl::setName(const QVariant &name)
-{
- m_name = name.toString();
-}
-
-void PaControl::setDesc(const QVariant &desc)
-{
- m_desc = desc.toString();
-}
-
-void PaControl::setType(const QVariant &type)
-{
- m_type = type.toUInt();
-}
-
-void PaControl::setChannel(const QVariant &channel)
-{
- m_channel = channel.toUInt();
-}
-
-void PaControl::setCDesc(const QVariant &cdesc)
-{
- m_cdesc = cdesc.toString();
-}
-
-void PaControl::setVolume(PaControlModel *pacm, const QVariant &volume)
-{
- if (volume != m_volume) {
- m_volume = volume.toUInt();
- if (pacm)
- emit pacm->volumeChanged(type(), cindex(), channel(), m_volume);
- }
-}
-
-PaControlModel::PaControlModel(QObject *parent)
- : QAbstractListModel(parent)
-{
-}
-
-void PaControlModel::addControl(const PaControl &control)
-{
- beginInsertRows(QModelIndex(), rowCount(), rowCount());
- m_controls << control;
- endInsertRows();
-}
-
-void PaControlModel::addOneControl(int cindex, QString name, QString desc, int type, int channel, const char *cdesc, int volume)
-{
- addControl(PaControl(cindex, name, desc, type, channel, cdesc, volume));
-}
-
-void PaControlModel::changeExternalVolume(uint32_t type, uint32_t cindex, uint32_t channel, uint32_t volume)
-{
- QList<PaControl>::iterator i;
- int row;
-
- for (i = m_controls.begin(), row = 0; i < m_controls.end(); ++i, ++row) {
- if ((i->type() == type) &&
- (i->cindex() == cindex) &&
- (i->channel() == channel)) {
- break;
- }
- }
-
- i->setVolume(NULL, QVariant(volume));
- QModelIndex qmindex = index(row);
- QVector<int> roles;
- roles.push_back(VolumeRole);
- emit dataChanged(qmindex, qmindex, roles);
-}
-
-int PaControlModel::rowCount(const QModelIndex & parent) const {
- Q_UNUSED(parent);
- return m_controls.count();
-}
-
-bool PaControlModel::setData(const QModelIndex &index, const QVariant &value, int role) {
- if (index.row() < 0 || index.row() >= m_controls.count())
- return false;
- PaControl &control = m_controls[index.row()];
- if (role == CIndexRole)
- control.setCIndex(value);
- else if (role == NameRole)
- control.setName(value);
- else if (role == DescRole)
- control.setDesc(value);
- else if (role == TypeRole)
- control.setType(value);
- else if (role == ChannelRole)
- control.setChannel(value);
- else if (role == CDescRole)
- control.setCDesc(value);
- else if (role == VolumeRole)
- control.setVolume(this, value);
- QVector<int> roles;
- roles.push_back(role);
- emit dataChanged(index, index, roles);
- return true;
-}
-
-QVariant PaControlModel::data(const QModelIndex & index, int role) const {
- if (index.row() < 0 || index.row() >= m_controls.count())
- return QVariant();
-
- const PaControl &control = m_controls[index.row()];
- if (role == CIndexRole)
- return control.cindex();
- else if (role == NameRole)
- return control.name();
- else if (role == DescRole)
- return control.desc();
- else if (role == TypeRole)
- return control.type();
- else if (role == ChannelRole)
- return control.channel();
- else if (role == CDescRole)
- return control.cdesc();
- else if (role == VolumeRole)
- return control.volume();
- return QVariant();
-}
-
-Qt::ItemFlags PaControlModel::flags(const QModelIndex &index) const
-{
- if (!index.isValid())
- return Qt::ItemIsEnabled;
-
- return QAbstractListModel::flags(index) | Qt::ItemIsEditable;
-}
-
-QHash<int, QByteArray> PaControlModel::roleNames() const {
- QHash<int, QByteArray> roles;
- roles[CIndexRole] = "cindex";
- roles[NameRole] = "name";
- roles[DescRole] = "desc";
- roles[TypeRole] = "type";
- roles[ChannelRole] = "channel";
- roles[CDescRole] = "cdesc";
- roles[VolumeRole] = "volume";
- return roles;
-}
diff --git a/app/pacontrolmodel.h b/app/pacontrolmodel.h
deleted file mode 100644
index 81eb70b..0000000
--- a/app/pacontrolmodel.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2016,2017 Konsulko Group
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <pulse/pulseaudio.h>
-
-#include <QtCore/QAbstractListModel>
-#include <QtCore/QList>
-
-class PaControlModel;
-
-class PaControl
-{
- public:
- PaControl(const quint32 &index, const QString &name, const QString &desc, const quint32 &type, const quint32 &channel, const QString &cdesc, const quint32 &volume);
-
- quint32 cindex() const;
- QString name() const;
- QString desc() const;
- quint32 type() const;
- quint32 channel() const;
- QString cdesc() const;
- quint32 volume() const;
- void setCIndex(const QVariant&);
- void setName(const QVariant&);
- void setDesc(const QVariant&);
- void setType(const QVariant&);
- void setChannel(const QVariant&);
- void setCDesc(const QVariant&);
- void setVolume(PaControlModel *, const QVariant&);
-
- private:
- quint32 m_cindex;
- QString m_name;
- QString m_desc;
- quint32 m_type;
- quint32 m_channel;
- QString m_cdesc;
- quint32 m_volume;
-};
-
-class PaControlModel : public QAbstractListModel
-{
- Q_OBJECT
- public:
- enum PaControlRoles {
- CIndexRole = Qt::UserRole + 1,
- NameRole,
- DescRole,
- TypeRole,
- ChannelRole,
- CDescRole,
- VolumeRole
- };
-
- PaControlModel(QObject *parent = 0);
-
- void addControl(const PaControl &control);
-
- int rowCount(const QModelIndex &parent = QModelIndex()) const;
-
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
-
- bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
-
- Qt::ItemFlags flags(const QModelIndex &index) const;
-
- public slots:
- void addOneControl(int cindex, QString name, QString desc, int type, int channel, const char *cdesc, int volume);
- void changeExternalVolume(uint32_t type, uint32_t cindex, uint32_t chan, uint32_t volume);
-
- signals:
- void volumeChanged(uint32_t type, uint32_t index, uint32_t channel, uint32_t volume);
-
- protected:
- QHash<int, QByteArray> roleNames() const;
- private:
- QList<PaControl> m_controls;
- pa_context *pa_ctx;
-};
diff --git a/conf.d/app-templates b/conf.d/app-templates
new file mode 160000
+Subproject 1f2944eea3a418ec02920673a390ed4b5d417a2
diff --git a/conf.d/autobuild/agl/autobuild b/conf.d/autobuild/agl/autobuild
new file mode 100755
index 0000000..ea352e7
--- /dev/null
+++ b/conf.d/autobuild/agl/autobuild
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+SOURCE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/../../../"
+BUILD_DIR=$( echo "$2" | cut -d'=' -f2 )
+
+# HACK: alias should be expanded in script for cmake to work properly
+shopt -s expand_aliases
+# HACK: source again the SDK because of the alias
+source $SDKTARGETSYSROOT/../../environment-setup-*
+
+pushd $BUILD_DIR
+
+ cmake $SOURCE_DIR
+ make
+
+ if [ "$1" == "package" ]; then
+ make widget
+ fi
+
+popd
+
diff --git a/conf.d/autobuild/linux/autobuild b/conf.d/autobuild/linux/autobuild
new file mode 100755
index 0000000..3a1ba5f
--- /dev/null
+++ b/conf.d/autobuild/linux/autobuild
@@ -0,0 +1,67 @@
+#!/usr/bin/make -f
+# Copyright (C) 2015, 2016 "IoT.bzh"
+# Author "Romain Forlot" <romain.forlot@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+THISFILE := $(lastword $(MAKEFILE_LIST))
+BUILD_DIR := $(abspath $(dir $(THISFILE)/../../../../..)/build)
+DEST := ${BUILD_DIR}/target
+
+.PHONY: all clean distclean configure build package help update
+
+all: help
+
+help:
+ @echo "List of targets available:"
+ @echo ""
+ @echo "- all"
+ @echo "- clean"
+ @echo "- distclean"
+ @echo "- configure"
+ @echo "- build: compilation, link and prepare files for package into a widget"
+ @echo "- package: output a widget file '*.wgt'"
+ @echo "- install: install in your ${CMAKE_INSTALL_DIR} directory"
+ @echo ""
+ @echo "Usage: ./conf.d/autobuild/agl/autobuild package DEST=${HOME}/opt"
+ @echo "Don't use your build dir as DEST as wgt file is generated at this location"
+
+update: configure
+ @cmake --build ${BUILD_DIR} --target autobuild
+
+clean:
+ @([ -d ${BUILD_DIR} ] && make -C ${BUILD_DIR} clean) || echo Nothing to clean
+
+distclean:
+ @rm -rf ${BUILD_DIR}
+
+configure: ${BUILD_DIR}/Makefile
+
+build: configure
+ @cmake --build ${BUILD_DIR} ${BUILD_ARGS} --target all
+
+package: build
+ @mkdir -p ${BUILD_DIR}/$@/bin
+ @mkdir -p ${BUILD_DIR}/$@/etc
+ @mkdir -p ${BUILD_DIR}/$@/lib
+ @mkdir -p ${BUILD_DIR}/$@/htdocs
+ @mkdir -p ${BUILD_DIR}/$@/data
+ @cmake --build ${BUILD_DIR} --target widget
+ @mkdir -p ${DEST} && cp ${BUILD_DIR}/*wgt ${DEST}
+
+install: build
+ @cmake --build ${BUILD_DIR} --target install
+
+${BUILD_DIR}/Makefile:
+ @[ -d ${BUILD_DIR} ] || mkdir -p ${BUILD_DIR}
+ @[ -f ${BUILD_DIR}/Makefile ] || (cd ${BUILD_DIR} && cmake ${CONFIGURE_ARGS} ..)
diff --git a/conf.d/cmake/config.cmake b/conf.d/cmake/config.cmake
new file mode 100644
index 0000000..03bf212
--- /dev/null
+++ b/conf.d/cmake/config.cmake
@@ -0,0 +1,201 @@
+###########################################################################
+# Copyright 2018 IoT.bzh
+#
+# author: Loïc Collignon <loic.collignon@iot.bzh>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+###########################################################################
+
+# Project Info
+# ------------------
+set(PROJECT_NAME mixer)
+set(PROJECT_PRETTY_NAME "Mixer")
+set(PROJECT_DESCRIPTION "AGL Default Mixer")
+set(PROJECT_URL "https://gerrit.automotivelinux.org/gerrit/apps/mixer")
+set(PROJECT_ICON "icon.svg")
+set(PROJECT_AUTHOR "Collignon, Loïc")
+set(PROJECT_AUTHOR_MAIL "loic.collignon@iot.bzh")
+set(PROJECT_LICENSE "APL2.0")
+set(PROJECT_LANGUAGES "C")
+
+# Where are stored default templates files from submodule or subtree app-templates in your project tree
+# relative to the root project directory
+set(PROJECT_APP_TEMPLATES_DIR "conf.d/app-templates")
+
+# Where are stored your external libraries for your project. This is 3rd party library that you don't maintain
+# but used and must be built and linked.
+# set(PROJECT_LIBDIR "libs")
+
+# Which directories inspect to find CMakeLists.txt target files
+# set(PROJECT_SRC_DIR_PATTERN "*")
+
+# Compilation Mode (DEBUG, RELEASE)
+# ----------------------------------
+#set(CMAKE_BUILD_TYPE "DEBUG")
+set(USE_EFENCE 1)
+
+# Kernel selection if needed. You can choose between a
+# mandatory version to impose a minimal version.
+# Or check Kernel minimal version and just print a Warning
+# about missing features and define a preprocessor variable
+# to be used as preprocessor condition in code to disable
+# incompatibles features. Preprocessor define is named
+# KERNEL_MINIMAL_VERSION_OK.
+#
+# NOTE*** FOR NOW IT CHECKS KERNEL Yocto environment and
+# Yocto SDK Kernel version.
+# -----------------------------------------------
+#set (kernel_mandatory_version 4.8)
+#set (kernel_minimal_version 4.8)
+
+# Compiler selection if needed. Impose a minimal version.
+# -----------------------------------------------
+set (gcc_minimal_version 4.9)
+
+# PKG_CONFIG required packages
+# -----------------------------
+set (PKG_REQUIRED_LIST
+ json-c
+ libsystemd>=222
+ afb-daemon
+ #libhomescreen
+ # qlibwindowmanager
+)
+
+# Prefix path where will be installed the files
+# Default: /usr/local (need root permission to write in)
+# ------------------------------------------------------
+#set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt)
+
+# Customize link option
+# -----------------------------
+#list(APPEND link_libraries -an-option)
+
+# Compilation options definition
+# Use CMake generator expressions to specify only for a specific language
+# Values are prefilled with default options that is currently used.
+# Either separate options with ";", or each options must be quoted separately
+# DO NOT PUT ALL OPTION QUOTED AT ONCE , COMPILATION COULD FAILED !
+# ----------------------------------------------------------------------------
+#set(COMPILE_OPTIONS
+# -Wall
+# -Wextra
+# -Wconversion
+# -Wno-unused-parameter
+# -Wno-sign-compare
+# -Wno-sign-conversion
+# -Werror=maybe-uninitialized
+# -Werror=implicit-function-declaration
+# -ffunction-sections
+# -fdata-sections
+# -fPIC
+# CACHE STRING "Compilation flags")
+#set(C_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C language.")
+#set(CXX_COMPILE_OPTIONS "" CACHE STRING "Compilation flags for C++ language.")
+#set(PROFILING_COMPILE_OPTIONS
+# -g
+# -O0
+# -pg
+# -Wp,-U_FORTIFY_SOURCE
+# CACHE STRING "Compilation flags for PROFILING build type.")
+#set(DEBUG_COMPILE_OPTIONS
+# -g
+# -ggdb
+# -Wp,-U_FORTIFY_SOURCE
+# CACHE STRING "Compilation flags for DEBUG build type.")
+#set(CCOV_COMPILE_OPTIONS
+# -g
+# -O2
+# --coverage
+# CACHE STRING "Compilation flags for CCOV build type.")
+#set(RELEASE_COMPILE_OPTIONS
+# -g
+# -O2
+# CACHE STRING "Compilation flags for RELEASE build type.")
+
+# (BUG!!!) as PKG_CONFIG_PATH does not work [should be an env variable]
+# ---------------------------------------------------------------------
+set(CMAKE_PREFIX_PATH ${CMAKE_INSTALL_PREFIX}/lib64/pkgconfig ${CMAKE_INSTALL_PREFIX}/lib/pkgconfig)
+set(LD_LIBRARY_PATH ${CMAKE_INSTALL_PREFIX}/lib64 ${CMAKE_INSTALL_PREFIX}/lib)
+
+# Optional location for config.xml.in
+# -----------------------------------
+#set(WIDGET_ICON "\"conf.d/wgt/${PROJECT_ICON}\"" CACHE PATH "Path to the widget icon")
+set(WIDGET_CONFIG_TEMPLATE "${CMAKE_CURRENT_SOURCE_DIR}/conf.d/wgt/config.xml.in" CACHE PATH "Path to widget config file template (config.xml.in)")
+
+# Mandatory widget Mimetype specification of the main unit
+# --------------------------------------------------------------------------
+# Choose between :
+#- text/html : HTML application,
+# content.src designates the home page of the application
+#
+#- application/vnd.agl.native : AGL compatible native,
+# content.src designates the relative path of the binary.
+#
+# - application/vnd.agl.service: AGL service, content.src is not used.
+#
+#- ***application/x-executable***: Native application,
+# content.src designates the relative path of the binary.
+# For such application, only security setup is made.
+#
+set(WIDGET_TYPE application/vnd.agl.native)
+
+# Mandatory Widget entry point file of the main unit
+# --------------------------------------------------------------
+# This is the file that will be executed, loaded,
+# at launch time by the application framework.
+#
+set(WIDGET_ENTRY_POINT mixer)
+
+# Optional dependencies order
+# ---------------------------
+#set(EXTRA_DEPENDENCIES_ORDER)
+
+# Optional Extra global include path
+# -----------------------------------
+#set(EXTRA_INCLUDE_DIRS)
+
+# Optional extra libraries
+# -------------------------
+#set(EXTRA_LINK_LIBRARIES)
+
+# Optional force binding Linking flag
+# ------------------------------------
+# set(BINDINGS_LINK_FLAG LinkOptions )
+
+# Optional force package prefix generation, like widget
+# -----------------------------------------------------
+# set(PKG_PREFIX DestinationPath)
+
+# Optional Application Framework security token
+# and port use for remote debugging.
+#------------------------------------------------------------
+set(AFB_TOKEN "" CACHE PATH "Default binder security token")
+set(AFB_REMPORT "1234" CACHE PATH "Default binder listening port")
+
+# Print a helper message when every thing is finished
+# ----------------------------------------------------
+set(CLOSING_MESSAGE "Typical binding launch: afb-daemon --port=${AFB_REMPORT} --workdir=${CMAKE_BINARY_DIR}/package --ldpaths=lib --roothttp=htdocs --token=\"${AFB_TOKEN}\" --tracereq=common --verbose")
+set(PACKAGE_MESSAGE "Install widget file using in the target : afm-util install ${PROJECT_NAME}.wgt")
+
+# Optional schema validator about now only XML, LUA and JSON
+# are supported
+#------------------------------------------------------------
+#set(LUA_CHECKER "luac" "-p" CACHE STRING "LUA compiler")
+#set(XML_CHECKER "xmllint" CACHE STRING "XML linter")
+#set(JSON_CHECKER "json_verify" CACHE STRING "JSON linter")
+
+# This include is mandatory and MUST happens at the end
+# of this file, else you expose you to unexpected behavior
+# -----------------------------------------------------------
+include(${PROJECT_APP_TEMPLATES_DIR}/cmake/common.cmake)
diff --git a/conf.d/wgt/config.xml.in b/conf.d/wgt/config.xml.in
new file mode 100644
index 0000000..597f19d
--- /dev/null
+++ b/conf.d/wgt/config.xml.in
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<widget xmlns="http://www.w3.org/ns/widgets" id="@PROJECT_NAME@" version="@PROJECT_VERSION@">
+ <name>@PROJECT_NAME@</name>
+ <icon src="@PROJECT_ICON@"/>
+ <content src="@WIDGET_ENTRY_POINT@" type="@WIDGET_TYPE@"/>
+ <description>@PROJECT_DESCRIPTION@</description>
+ <author>
+ <author href="https://www.automotivelinux.org/" email = "info@automotivelinux.org">
+ Loïc Collignon &lt;loic.collignon@iot.bzh&gt;
+ Matt Porter &lt;mporter@konsulko.com&gt;
+ </author>
+ <license>@PROJECT_LICENSE@</license>
+ <feature name="urn:AGL:widget:required-api">
+ <param name="windowmanager" value="ws" />
+ <param name="homescreen" value="ws" />
+ </feature>
+ <feature name="urn:AGL:widget:required-permission">
+ <param name="urn:AGL:permission::public:no-htdocs" value="required" />
+ <param name="urn:AGL:permission::public:4a-audio-mixer" value="required" />
+ </feature>
+</widget>
diff --git a/package/icon.svg b/conf.d/wgt/icon.svg
index 85f7384..85f7384 100644
--- a/package/icon.svg
+++ b/conf.d/wgt/icon.svg
diff --git a/mixer.pro b/mixer.pro
deleted file mode 100644
index 579a952..0000000
--- a/mixer.pro
+++ /dev/null
@@ -1,3 +0,0 @@
-TEMPLATE = subdirs
-SUBDIRS = app package
-package.depends += app
diff --git a/package/config.xml b/package/config.xml
deleted file mode 100644
index bee25a0..0000000
--- a/package/config.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<widget xmlns="http://www.w3.org/ns/widgets" id="mixer" version="0.1">
- <name>Mixer</name>
- <icon src="icon.svg"/>
- <content src="bin/mixer" type="application/vnd.agl.native"/>
- <description>Simple PulseAudio mixer app</description>
- <author>Matt Porter &lt;mporter@konsulko.com&gt;</author>
- <license>APL 2.0</license>
- <feature name="urn:AGL:widget:required-api">
- <param name="windowmanager" value="ws" />
- <param name="homescreen" value="ws" />
- </feature>
- <feature name="urn:AGL:widget:required-permission">
- <param name="urn:AGL:permission::public:no-htdocs" value="required" />
- </feature>
-</widget>
-
-
diff --git a/package/package.pro b/package/package.pro
deleted file mode 100644
index 3d37bd7..0000000
--- a/package/package.pro
+++ /dev/null
@@ -1,19 +0,0 @@
-
-DISTFILES = icon.svg config.xml
-
-copy_icon.target = $$OUT_PWD/root/icon.svg
-copy_icon.depends = $$_PRO_FILE_PWD_/icon.svg
-copy_icon.commands = $(COPY_FILE) \"$$replace(copy_icon.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_icon.target, /, $$QMAKE_DIR_SEP)\"
-QMAKE_EXTRA_TARGETS += copy_icon
-PRE_TARGETDEPS += $$copy_icon.target
-
-copy_config.target = $$OUT_PWD/root/config.xml
-copy_config.depends = $$_PRO_FILE_PWD_/config.xml
-copy_config.commands = $(COPY_FILE) \"$$replace(copy_config.depends, /, $$QMAKE_DIR_SEP)\" \"$$replace(copy_config.target, /, $$QMAKE_DIR_SEP)\"
-QMAKE_EXTRA_TARGETS += copy_config
-PRE_TARGETDEPS += $$copy_config.target
-
-wgt.target = package
-wgt.commands = wgtpkg-pack -f -o mixer.wgt root
-
-QMAKE_EXTRA_TARGETS += wgt