diff options
71 files changed, 5562 insertions, 584 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b092854 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +build/ +CMakeCache.txt +Makefile +*.so +nbproject/private diff --git a/afb-utilities b/afb-utilities new file mode 160000 +Subproject 1860c197a402f76f960eb2e373daf1259592cb6 diff --git a/conf.d/app-templates/LICENSE b/conf.d/app-templates/LICENSE new file mode 100644 index 0000000..93b07d3 --- /dev/null +++ b/conf.d/app-templates/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2017 claneys + + 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. diff --git a/conf.d/app-templates/README.md b/conf.d/app-templates/README.md new file mode 100644 index 0000000..96dfb3c --- /dev/null +++ b/conf.d/app-templates/README.md @@ -0,0 +1,382 @@ +# AGL CMake template + +Files used to build an application, or binding, project with the +AGL Application Framework. + +To build your AGL project using these templates, you have to install +them within your project and adjust compilation option in `config.cmake`. +For technical reasons, you also have to specify **cmake** target in +sub CMakeLists.txt installed. Make a globbing search to find source files +isn't recommended now to handle project build especially in a multiuser +project because CMake will not be aware of new or removed source files. + +You'll find usage samples here: + +- [helloworld-service](https://github.com/iotbzh/helloworld-service) +- [low-level-can-service](https://gerrit.automotivelinux.org/gerrit/apps/low-level-can-service) +- [high-level-viwi-service](https://github.com/iotbzh/high-level-viwi-service) +- [audio-binding](https://github.com/iotbzh/audio-binding) +- [unicens2-binding](https://github.com/iotbzh/unicens2-binding) + +## Quickstart + +### Initialization + +To use these templates files on your project just install the reference files using +**git submodule** then use `config.cmake` file to configure your project specificities : + +```bash +git submodule add https://gerrit.automotivelinux.org/gerrit/p/apps/app-templates.git conf.d/app-templates +mkdir conf.d/cmake +cp conf.d/app-templates/cmake/config.cmake.sample conf.d/cmake/config.cmake +``` + +Edit the copied config.cmake file to fit your needs. + +Now, create your top CMakeLists.txt file which include `config.cmake` file. + +An example is available in **app-templates** submodule that you can copy and +use: + +```bash +cp conf.d/app-templates/cmake/CMakeLists.txt.sample CMakeLists.txt +``` + +### Create your CMake targets + +For each target part of your project, you need to use ***PROJECT_TARGET_ADD*** +to include this target to your project. + +Using it, make available the cmake variable ***TARGET_NAME*** until the next +***PROJECT_TARGET_ADD*** is invoked with a new target name. + +So, typical usage defining a target is: + +```cmake +PROJECT_TARGET_ADD(SuperExampleName) --> Adding target to your project + +add_executable/add_library(${TARGET_NAME}.... --> defining your target sources + +SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES.... --> fit target properties +for macros usage + +INSTALL(TARGETS ${TARGET_NAME}.... +``` + +### Targets PROPERTIES + +You should set properties on your targets that will be used to package your +apps in a widget file that could be installed on an AGL system. + +Specify what is the type of your targets that you want to be included in the +widget package with the property **LABELS**: + +Choose between: + +- **BINDING**: Shared library that be loaded by the AGL Application Framework +- **BINDINGV2**: Shared library that be loaded by the AGL Application Framework. + This has to be accompagnied with a JSON file named like the *${OUTPUT_NAME}-apidef* of + the target that describe the API with OpenAPI syntax (e.g: *mybinding-apidef*). + Or you can choose the name by setting the *CACHE* cmake variable *OPENAPI_DEF* + (***CAUTION***: setting a CACHE variable is needed, or set a normal variable + with the *PARENT_SCOPE* option to make it visible for the parent scope + where the target is defined) JSON file will be used to generate header file + using `afb-genskel` tool. +- **HTDOCS**: Root directory of a web app. This target has to build its + directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} +- **DATA**: Resources used by your application. This target has to build its + directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} +- **EXECUTABLE**: Entry point of your application executed by the AGL + Application Framework + +```cmake +SET_TARGET_PROPERTIES(${TARGET_NAME} + PREFIX "afb-" + LABELS "BINDING" + OUTPUT_NAME "file_output_name") +``` + +> **TIP** you should use the prefix _afb-_ with your **BINDING* targets which +> stand for **Application Framework Binding**. + +## More details: Typical project architecture + +A typical project architecture would be : + +```tree +<project-root-path> +│ +├── conf.d/ +│ ├── autobuild/ +│ │ ├── agl +│ │ │ └── autobuild +│ │ ├── linux +│ │ │ └── autobuild +│ │ └── windows +│ │ └── autobuild +│ ├── app-templates/ +│ │ ├── README.md +│ │ ├── autobuild/ +│ │ │ ├── agl +│ │ │ │ └── autobuild.in +│ │ │ ├── linux +│ │ │ │ └── autobuild.in +│ │ │ └── windows +│ │ │ └── autobuild.in +│ │ ├── cmake/ +│ │ │ ├── config.cmake.sample +│ │ │ ├── export.map +│ │ │ └── macros.cmake +│ │ ├── deb/ +│ │ │ └── config.deb.in +│ │ ├── rpm/ +│ │ │ └── config.spec.in +│ │ └── wgt/ +│ │ ├── config.xml.in +│ │ ├── config.xml.in.sample +│ │ ├── icon-default.png +│ │ ├── icon-html5.png +│ │ ├── icon-native.png +│ │ ├── icon-qml.png +│ │ └── icon-service.png +│ ├── packaging/ +│ │ ├── config.spec +│ │ └── config.deb +│ ├── cmake +│ │ └── config.cmake +│ └── wgt +│ └── config.xml.in +├── <libs> +├── <target> +│ └── <files> +├── <target> +│ └── <file> +└── <target> + └── <files> +``` + +| # | Parent | Description | +| - | -------| ----------- | +| \<root-path\> | - | Path to your project. Hold master CMakeLists.txt and general files of your projects. | +| conf.d | \<root-path\> | Holds needed files to build, install, debug, package an AGL app project | +| app-templates | conf.d | Git submodule to app-templates AGL repository which provides CMake helpers macros library, and build scripts. config.cmake is a copy of config.cmake.sample configured for the projects. SHOULD NOT BE MODIFIED MANUALLY !| +| autobuild | conf.d | Scripts generated from app-templates to build packages the same way for differents platforms.| +| cmake | conf.d | Contains at least config.cmake file modified from the sample provided in app-templates submodule. | +| wgt | conf.d | Contains at least config.xml.in template file modified from the sample provided in app-templates submodule for the needs of project (See config.xml.in.sample file for more details). | +| packaging | conf.d | Contains output files used to build packages. | +| \<libs\> | \<root-path\> | External dependencies libraries. This isn't to be used to include header file but build and link statically specifics libraries. | Library sources files. Can be a decompressed library archive file or project fork. | +| \<target\> | \<root-path\> | A target to build, typically library, executable, etc. | + +### Update app-templates submodule + +You may have some news bug fixes or features available from app-templates +repository that you want. To update your submodule proceed like the following: + +```bash +git submodule update --remote +git commit -s conf.d/app-templates +``` + +This will update the submodule to the HEAD of master branch repository. + +You could just want to update at a specified repository tag or branch or commit +, here are the method to do so: + +```bash +cd conf.d/app-templates +# Choose one of the following depending what you want +git checkout <tag_name> +git checkout --detach <branch_name> +git checkout --detach <commit_id> +# Then commit +cd ../.. +git commit -s conf.d/app-templates +``` + +### Build a widget + +#### config.xml.in file + +To build a widget you need a _config.xml_ file describing what is your apps and +how Application Framework would launch it. This repo provide a simple default +file _config.xml.in_ that should work for simple application without +interactions with others bindings. + +It is recommanded that you use the sample one which is more complete. You can +find it at the same location under the name _config.xml.in.sample_ (stunning +isn't it). Just copy the sample file to your _conf.d/wgt_ directory and name it +_config.xml.in_, then edit it to fit your needs. + +> ***CAUTION*** : The default file is only meant to be use for a +> simple widget app, more complicated ones which needed to export +> their api, or ship several app in one widget need to use the provided +> _config.xml.in.sample_ which had all new Application Framework +> features explained and examples. + +#### Using cmake template macros + +To leverage all cmake templates features, you have to specify ***properties*** +on your targets. Some macros will not works without specifying which is the +target type. + +As the type is not always specified for some custom targets, like an ***HTML5*** +application, macros make the difference using ***LABELS*** property. + +Choose between: + +- **BINDING**: Shared library that be loaded by the AGL Application Framework +- **BINDINGV2**: Shared library that be loaded by the AGL Application Framework. + This has to be accompagnied with a JSON file named like the *${OUTPUT_NAME}-apidef* of + the target that describe the API with OpenAPI syntax (e.g: *mybinding-apidef*). + Or you can choose the name by setting the *CACHE* cmake variable *OPENAPI_DEF* + (***CAUTION***: setting a CACHE variable is needed, or set a normal variable + with the *PARENT_SCOPE* option to make it visible for the parent scope + where the target is defined) JSON file will be used to generate header file + using `afb-genskel` tool. +- **HTDOCS**: Root directory of a web app. This target has to build its + directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} +- **DATA**: Resources used by your application. This target has to build its + directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} +- **EXECUTABLE**: Entry point of your application executed by the AGL + Application Framework + +Example: + +```cmake +SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + LABELS "HTDOCS" + OUTPUT_NAME dist.prod + ) +``` + +If your target output is not named as the ***TARGET_NAME***, you need to specify +***OUTPUT_NAME*** property that will be used by the ***populate_widget*** macro. + +Use the ***populate_widget*** macro as latest statement of your target +definition. Then at the end of your project definition you should use the macro +***build_widget*** that make an archive from the populated widget tree using the +`wgtpkg-pack` Application Framework tools. + +## Macro reference + +### PROJECT_TARGET_ADD + +Typical usage would be to add the target to your project using macro +`PROJECT_TARGET_ADD` with the name of your target as parameter. + +Example: + +```cmake +PROJECT_TARGET_ADD(low-can-demo) +``` + +> ***NOTE***: This will make available the variable `${TARGET_NAME}` +> set with the specificied name. This variable will change at the next call +> to this macros. + +### project_subdirs_add + +This macro will search in all subfolder any `CMakeLists.txt` file. If found then +it will be added to your project. This could be use in an hybrid application by +example where the binding lay in a sub directory. + +Usage : + +```cmake +project_subdirs_add() +``` + +You also can specify a globbing pattern as argument to filter which folders +will be looked for. + +To filter all directories that begin with a number followed by a dash the +anything: + +```cmake +project_subdirs_add("[0-9]-*") +``` + +## Advanced customization + +### Including additionnals cmake files + +Advanced tuning is possible using addionnals cmake files that are included +automatically from some specifics locations. They are included in that order: + +- Project CMake files normaly located in _<project-root-path>/conf.d/app-templates/cmake/cmake.d_ +- Home CMake files located in _$HOME/.config/app-templates/cmake.d_ +- System CMake files located in _/etc/app-templates/cmake.d_ + +CMake files has to be named using the following convention: `XX-common-*.cmake` +or `XX-${PROJECT_NAME}-*.cmake`, where `XX` are numbers, `*` file name +(ie. `99-common-my_customs.cmake`). + +> **NOTE** You need to specify after numbers that indicate include order, to +which project that file applies, if it applies to all project then use keyword +`common`. + +So, saying that you should be aware that every normal cmake variables used at +project level could be overwrited by home or system located cmake files if +variables got the same name. Exceptions are cached variables set using +**CACHE** keyword: + +Example: + +```cmake +set(VARIABLE_NAME 'value string random' CACHE STRING 'docstring') +``` + +### Include customs templated scripts + +As well as for additionnals cmake files you can include your own templated +scripts that will be passed to cmake command `configure_file`. + +Just create your own script to the following directories: + +- Home location in _$HOME/.config/app-templates/scripts_ +- System location in _/etc/app-templates/scripts_ + +Scripts only needs to use the extension `.in` to be parsed and configured by +CMake command. + +## Autobuild script usage + +### Generation + +To be integrated in the Yocto build workflow you have to generate `autobuild` +scripts using _autobuild_ target. + +To generate those scripts proceeds: + +```bash +mkdir -p build +cd build +cmake .. && make autobuild +``` + +You should see _conf.d/autobuild/agl/autobuild_ file now. + +### Available targets + +Here are the available targets available from _autobuild_ scripts: + +- **clean** : clean build directory from object file and targets results. +- **distclean** : delete build directory +- **configure** : generate project Makefile from CMakeLists.txt files. +- **build** : compile all project targets. +- **package** : build and output a wgt package. + +You can specify variables that modify the behavior of compilation using +the following variables: + +- **CONFIGURE_ARGS** : Variable used at **configure** time. +- **BUILD_ARGS** : Variable used at **build** time. +- **DEST** : Directory where to output ***wgt*** file. + +Variable as to be in CMake format. (ie: BUILD_ARGS="-DC_FLAGS='-g -O2'") + +Usage example: + +```bash +./conf.d/autobuild/wgt/autobuild package DEST=/tmp +``` diff --git a/conf.d/app-templates/book.json b/conf.d/app-templates/book.json new file mode 100644 index 0000000..dafc6ae --- /dev/null +++ b/conf.d/app-templates/book.json @@ -0,0 +1,94 @@ +{ + "title": "CMake Application templates", + "subtitle": "Developer Documentation", + "description": "CMake templates used to build an AGL apps and widget", + "keywords": "AGL, Development, Iotbzh", + "author": "IoT.Bzh Team", + "website": "http://iot.bzh", + "published": "July 2017", + "version": "1.0", + + "gitbook": "3.2.2", + "root": "docs", + "pdf": { + "fontFamily": "Verdana", + "fontSize": 12, + "paperSize": "a4", + "pageBreaksBefore": "//h:div[@class=\"page-break\"]" + }, + "styles": { + "website": "resources/ebook.css", + "ebook": "resources/ebook.css", + "pdf": "resources/ebook.css" + }, + + "hidepageheaders": [2, 3], + "hidepagefooters": [2, 3], + + "plugins": [ + "regexplace" + ], + "pluginsConfig": { + "regexplace": { + "removeFirstPartsInSectionNumber": true, + "substitutes": [{ + "pattern": "<!-- pagebreak -->", + "flags": "g", + "substitute": "<div class=\"page-break\"></div>" + }, + { + "pattern": "<!-- clear -->", + "flags": "g", + "substitute": "<div class=\"clear\"></div>" + }, + { + "pattern": "<!-- nopagebreak -->", + "flags": "g", + "substitute": "<div class=\"nopb\">" + }, + { + "pattern": "<!-- endnopagebreak -->", + "flags": "g", + "substitute": "</div>" + }, + { + "pattern": "<!-- note -->", + "flags": "g", + "substitute": "<div class=\"note\">" + }, + { + "pattern": "<!-- endnote -->", + "flags": "g", + "substitute": "</div>" + }, + { + "pattern": "!\\[(.*?)\\]\\((.*?)(?:\\s+\"(.*)\")?\\){0,}{caption=1([^\\}]*)}", + "flags": "gmi", + "substitute": "<figure id=\"fig_PAGE_LEVEL_._INDEX_\"><img $3 alt=\"$1\" title=\"$1\" href=\"$2\"><figcaption></figcaption></figure>", + "decode": true + }, + { + "pattern": "<img ([^>]*) {0,}\/{0,}> {0,}{caption=1([^\\}]*)}", + "flags": "g", + "substitute": "<figure id=\"fig_PAGE_LEVEL_._INDEX_\"><img $2 $1><figcaption></figcaption></figure>", + "decode": true + }, + { + "pattern": "<img (.*)alt=\"([^\"]*)\"(.*) {0,1}\/{0,1}><figcaption></figcaption>", + "flags": "g", + "substitute": "<img$1alt=\"$2\"$3><figcaption><span>Picture _PAGE_LEVEL_._INDEX_</span>: $2</figcaption>", + "store": { + "substitute": "<a href=\"_PAGE_PATH_#fig_PAGE_LEVEL_._INDEX_\">Pic. _PAGE_LEVEL_._INDEX_</a> <span>$2</span>", + "variable_name": "pictures" + } + }, + { + "pattern": "<img ([^>]*)> {0,}{style {1,}([^}]*)}", + "flags": "g", + "substitute": "<img $1 style=\"$2\">", + "decode": true + } + ] + } + } +} diff --git a/conf.d/app-templates/cmake/cmake.d/01-build_options.cmake b/conf.d/app-templates/cmake/cmake.d/01-build_options.cmake new file mode 100644 index 0000000..4fb10ef --- /dev/null +++ b/conf.d/app-templates/cmake/cmake.d/01-build_options.cmake @@ -0,0 +1,113 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll <fulup@iot.bzh> +# contrib: 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. +########################################################################### + + +#-------------------------------------------------------------------------- +# WARNING: +# Do not change this cmake template +# Customise your preferences in "./conf.d/cmake/config.cmake" +#-------------------------------------------------------------------------- + +# (BUG!!!) as PKG_CONFIG_PATH does not work [should be en env variable] +set(PKG_CONFIG_USE_CMAKE_PREFIX_PATH ON CACHE BOOLEAN "Flag for using prefix path") + +INCLUDE(FindPkgConfig) +INCLUDE(CheckIncludeFiles) +INCLUDE(CheckLibraryExists) +INCLUDE(GNUInstallDirs) + +set(CMAKE_BUILD_TYPE Debug CACHE STRING "the type of build") +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMP0048 1) + +# Default compilation options +############################################################################ +link_libraries(-Wl,--as-needed -Wl,--gc-sections) +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") +foreach(option ${COMPILE_OPTIONS}) + add_compile_options($<$<CONFIG:PROFILING>:${option}>) +endforeach() + +# Compilation OPTIONS depending on language +######################################### +foreach(option ${COMPILE_OPTIONS}) + add_compile_options(${option}) +endforeach() +foreach(option ${C_COMPILE_OPTIONS}) + add_compile_options($<$<COMPILE_LANGUAGE:C>:${option}>) +endforeach() +foreach(option ${CXX_COMPILE_OPTIONS}) + add_compile_options($<$<COMPILE_LANGUAGE:CXX>:${option}>) +endforeach() + +# Compilation option depending on CMAKE_BUILD_TYPE +################################################## +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.") +foreach(option ${PROFILING_COMPILE_OPTIONS}) + add_compile_options($<$<CONFIG:PROFILING>:${option}>) +endforeach() +foreach(option ${DEBUG_COMPILE_OPTIONS}) + add_compile_options($<$<CONFIG:PROFILING>:${option}>) +endforeach() +foreach(option ${CCOV_COMPILE_OPTIONS}) + add_compile_options($<$<CONFIG:PROFILING>:${option}>) +endforeach() +foreach(option ${RELEASE_COMPILE_OPTIONS}) + add_compile_options($<$<CONFIG:PROFILING>:${option}>) +endforeach() + +# Env variable overload default +if(DEFINED ENV{INSTALL_PREFIX}) + set(INSTALL_PREFIX $ENV{INSTALL_PREFIX} CACHE PATH "The path where to install") +else() + set(INSTALL_PREFIX "${CMAKE_SOURCE_DIR}/Install" CACHE PATH "The path where to install") +endif() +set(CMAKE_INSTALL_PREFIX ${INSTALL_PREFIX} CACHE STRING "Installation Prefix") + +# Loop on required package and add options +foreach (PKG_CONFIG ${PKG_REQUIRED_LIST}) + string(REGEX REPLACE "[<>]?=.*$" "" XPREFIX ${PKG_CONFIG}) + PKG_CHECK_MODULES(${XPREFIX} REQUIRED ${PKG_CONFIG}) + + INCLUDE_DIRECTORIES(${${XPREFIX}_INCLUDE_DIRS}) + list(APPEND link_libraries ${${XPREFIX}_LDFLAGS}) + add_compile_options (${${XPREFIX}_CFLAGS}) +endforeach(PKG_CONFIG) + +# Optional LibEfence Malloc debug library +IF(CMAKE_BUILD_TYPE MATCHES DEBUG) +CHECK_LIBRARY_EXISTS(efence malloc "" HAVE_LIBEFENCE) +IF(HAVE_LIBEFENCE) + MESSAGE(STATUS "Linking with ElectricFence for debugging purposes...") + SET(libefence_LIBRARIES "-lefence") + list (APPEND link_libraries ${libefence_LIBRARIES}) +ENDIF(HAVE_LIBEFENCE) +ENDIF(CMAKE_BUILD_TYPE MATCHES DEBUG) + +# set default include directories +INCLUDE_DIRECTORIES(${EXTRA_INCLUDE_DIRS}) + +# Default Linkflag +set (PKG_TEMPLATE_PREFIX ${CMAKE_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR} CACHE PATH "Default Package Templates Directory") +if(NOT BINDINGS_LINK_FLAG) + set(BINDINGS_LINK_FLAG "-Wl,--version-script=${PKG_TEMPLATE_PREFIX}/cmake/export.map") +endif() diff --git a/conf.d/app-templates/cmake/cmake.d/02-variables.cmake b/conf.d/app-templates/cmake/cmake.d/02-variables.cmake new file mode 100644 index 0000000..2491473 --- /dev/null +++ b/conf.d/app-templates/cmake/cmake.d/02-variables.cmake @@ -0,0 +1,174 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll <fulup@iot.bzh> +# contrib: 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. +########################################################################### + + +#-------------------------------------------------------------------------- +# WARNING: +# Do not change this cmake template +# Customise your preferences in "./etc/config.cmake" +#-------------------------------------------------------------------------- + +# Get colorized message output non Windows OS. You know bash ? :) +if(NOT WIN32) + string(ASCII 27 Esc) + set(ColourReset "${Esc}[m") + set(ColourBold "${Esc}[1m") + set(Red "${Esc}[31m") + set(Green "${Esc}[32m") + set(Yellow "${Esc}[33m") + set(Blue "${Esc}[34m") + set(Magenta "${Esc}[35m") + set(Cyan "${Esc}[36m") + set(White "${Esc}[37m") + set(BoldRed "${Esc}[1;31m") + set(BoldGreen "${Esc}[1;32m") + set(BoldYellow "${Esc}[1;33m") + set(BoldBlue "${Esc}[1;34m") + set(BoldMagenta "${Esc}[1;35m") + set(BoldCyan "${Esc}[1;36m") + set(BoldWhite "${Esc}[1;37m") +endif() + +# Native packaging name +set(NPKG_PROJECT_NAME agl-${PROJECT_NAME}) + +if(DEFINED ENV{SDKTARGETSYSROOT}) +file(STRINGS $ENV{SDKTARGETSYSROOT}/usr/include/linux/version.h LINUX_VERSION_CODE_LINE REGEX "LINUX_VERSION_CODE") +set(BUILD_ENV_SYSROOT $ENV{SDKTARGETSYSROOT}) +elseif(DEFINED ENV{PKG_CONFIG_SYSROOT_DIR}) +file(STRINGS $ENV{PKG_CONFIG_SYSROOT_DIR}/usr/include/linux/version.h LINUX_VERSION_CODE_LINE REGEX "LINUX_VERSION_CODE") +set(BUILD_ENV_SYSROOT $ENV{PKG_CONFIG_SYSROOT_DIR}) +else() +file(STRINGS /usr/include/linux/version.h LINUX_VERSION_CODE_LINE REGEX "LINUX_VERSION_CODE") +set(BUILD_ENV_SYSROOT "") +endif() + +string(REGEX MATCH "[0-9]+" LINUX_VERSION_CODE ${LINUX_VERSION_CODE_LINE}) +math(EXPR a "${LINUX_VERSION_CODE} >> 16") +math(EXPR b "(${LINUX_VERSION_CODE} >> 8) & 255") +math(EXPR c "(${LINUX_VERSION_CODE} & 255)") + +set(KERNEL_VERSION "${a}.${b}.${c}") + +# Get the os type +# Used to package .deb +set(OS_RELEASE_PATH "${BUILD_ENV_SYSROOT}/etc/os-release") +if(EXISTS ${OS_RELEASE_PATH}) + execute_process(COMMAND bash "-c" "grep -E '^ID(_LIKE)?=' ${OS_RELEASE_PATH} | tail -n 1" + OUTPUT_VARIABLE TMP_OSRELEASE + ) + + if (NOT TMP_OSRELEASE STREQUAL "") + string(REGEX REPLACE ".*=\"?([0-9a-z\._-]*)\"?\n" "\\1" OSRELEASE ${TMP_OSRELEASE}) + else() + set(OSRELEASE "NOT COMPATIBLE !") + endif() + +else() + set(OSRELEASE "NOT COMPATIBLE ! Missing ${OS_RELEASE_PATH} file.") +endif() +message(STATUS "Distribution used ${OSRELEASE}") + +# Include project configuration +# ------------------------------ +project(${PROJECT_NAME} VERSION ${PROJECT_VERSION} LANGUAGES ${PROJECT_LANGUAGES}) +set(PROJECT_LIBDIR "${CMAKE_SOURCE_DIR}/libs" CACHE PATH "Subpath to libraries") +set(PROJECT_RESOURCES "${CMAKE_SOURCE_DIR}/data" CACHE PATH "Subpath to data") + +set(AFB_TOKEN "" CACHE PATH "Default AFB_TOKEN") +set(AFB_REMPORT "1234" CACHE PATH "Default AFB_TOKEN") + +# Check GCC minimal version +if (gcc_minimal_version) +message (STATUS "${Cyan}-- Check gcc_minimal_version (found gcc version ${CMAKE_C_COMPILER_VERSION}) \ +(found g++ version ${CMAKE_CXX_COMPILER_VERSION})${ColourReset}") +if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS ${gcc_minimal_version} OR CMAKE_C_COMPILER_VERSION VERSION_LESS ${gcc_minimal_version}) +message(FATAL_ERROR "${Red}**** FATAL: Require at least gcc-${gcc_minimal_version} please set CMAKE_C[XX]_COMPILER") +endif() +endif(gcc_minimal_version) + +# Check Kernel mandatory version, will fail the configuration if required version not matched. +if (kernel_mandatory_version) +message (STATUS "${Cyan}-- Check kernel_mandatory_version (found kernel version ${KERNEL_VERSION})${ColourReset}") +if (KERNEL_VERSION VERSION_LESS ${kernel_mandatory_version}) +message(FATAL_ERROR "${Red}**** FATAL: Require at least ${kernel_mandatory_version} please use a recent kernel or source your SDK environment then clean and reconfigure your CMake project.") +endif (KERNEL_VERSION VERSION_LESS ${kernel_mandatory_version}) +endif(kernel_mandatory_version) + +# Check Kernel minimal version just print a Warning about missing features +# and set a definition to be used as preprocessor condition in code to disable +# incompatibles features. +if (kernel_minimal_version) +message (STATUS "${Cyan}-- Check kernel_minimal_version (found kernel version ${KERNEL_VERSION})${ColourReset}") +if (KERNEL_VERSION VERSION_LESS ${kernel_minimal_version}) +message(WARNING "${Yellow}**** Warning: Some feature(s) require at least ${kernel_minimal_version}. Please use a recent kernel or source your SDK environment then clean and reconfigure your CMake project.${ColourReset}") +else (KERNEL_VERSION VERSION_LESS ${kernel_minimal_version}) +add_definitions(-DKERNEL_MINIMAL_VERSION_OK) +endif (KERNEL_VERSION VERSION_LESS ${kernel_minimal_version}) +endif(kernel_minimal_version) + +# Project path variables +# ---------------------- +set(PKGOUT_DIR package CACHE PATH "Output directory for packages") + +# Define a default package directory +if(PKG_PREFIX) + set(PROJECT_PKG_BUILD_DIR ${PKG_PREFIX}/${PKGOUT_DIR} CACHE PATH "Application contents to be packaged") +else() + set(PROJECT_PKG_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/${PKGOUT_DIR} CACHE PATH "Application contents to be packaged") +endif() + +# Paths to templates files +set(TEMPLATE_DIR "${PKG_TEMPLATE_PREFIX}/template.d" CACHE PATH "Subpath to a directory where are stored needed files to launch on remote target to debuging purposes") + +string(REGEX REPLACE "^(.*)/.*$" "\\1" ENTRY_POINT "${PKG_TEMPLATE_PREFIX}") +set(PROJECT_PKG_ENTRY_POINT ${ENTRY_POINT}/packaging CACHE PATH "Where package build files, like rpm.spec file or config.xml, are write.") + +set(WIDGET_ICON "${ENTRY_POINT}/wgt/${PROJECT_ICON}" CACHE PATH "Path to the widget icon") +set(WIDGET_CONFIG_TEMPLATE ${TEMPLATE_DIR}/config.xml.in CACHE PATH "Path to widget config file template (config.xml.in)") + +# Path to autobuild template +set(PROJECT_TEMPLATE_AGL_AUTOBUILD_DIR ${CMAKE_SOURCE_DIR}/conf.d/autobuild/agl CACHE PATH "Subpath to a directory where are stored autobuild script") +set(PROJECT_TEMPLATE_LINUX_AUTOBUILD_DIR ${CMAKE_SOURCE_DIR}/conf.d/autobuild/linux CACHE PATH "Subpath to a directory where are stored autobuild script") + +# Archive target variables +set(ARCHIVE_OUTPUT_ARCHIVE ${PROJECT_PKG_ENTRY_POINT}/${NPKG_PROJECT_NAME}_${PROJECT_VERSION}.orig.tar) +set(ARCHIVE_OUTPUT ${ARCHIVE_OUTPUT_ARCHIVE}.gz) +set(TMP_ARCHIVE_SUBMODULE ${PROJECT_PKG_ENTRY_POINT}/${NPKG_PROJECT_NAME}-sub) +set(CMD_ARCHIVE_SUBMODULE \'git archive --verbose --prefix=${NPKG_PROJECT_NAME}-${PROJECT_VERSION}/$$path/ --format tar HEAD --output ${TMP_ARCHIVE_SUBMODULE}-$$sha1.tar\' ) + +if(OSRELEASE MATCHES "debian" AND NOT DEFINED ENV{SDKTARGETSYSROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) + # build deb spec file from template + set(PACKAGING_DEB_OUTPUT_DSC ${PROJECT_PKG_ENTRY_POINT}/${NPKG_PROJECT_NAME}.dsc) + set(PACKAGING_DEB_OUTPUT_INSTALL ${PROJECT_PKG_ENTRY_POINT}/debian.${NPKG_PROJECT_NAME}.install) + set(PACKAGING_DEB_OUTPUT_CHANGELOG ${PROJECT_PKG_ENTRY_POINT}/debian.changelog) + set(PACKAGING_DEB_OUTPUT_COMPAT ${PROJECT_PKG_ENTRY_POINT}/debian.compat) + set(PACKAGING_DEB_OUTPUT_CONTROL ${PROJECT_PKG_ENTRY_POINT}/debian.control) + set(PACKAGING_DEB_OUTPUT_RULES ${PROJECT_PKG_ENTRY_POINT}/debian.rules) +endif() + +# Break After Binding are loaded but before they get initialised +set(GDB_INITIAL_BREAK "personality" CACHE STRING "Initial Break Point for GDB remote") + +# Define some checker binaries to verify input DATA files +# to be included in package. Schema aren't checked for now. +# Dummy checker about JSON. +set(LUA_CHECKER "luac" "-p" CACHE STRING "LUA compiler") +set(XML_CHECKER "xmllint" CACHE STRING "XML linter") +set(JSON_CHECKER "echo" CACHE STRING "JSON linter") diff --git a/conf.d/app-templates/cmake/cmake.d/03-macros.cmake b/conf.d/app-templates/cmake/cmake.d/03-macros.cmake new file mode 100644 index 0000000..45d1ef5 --- /dev/null +++ b/conf.d/app-templates/cmake/cmake.d/03-macros.cmake @@ -0,0 +1,501 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll <fulup@iot.bzh> +# contrib: 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. +########################################################################### + +#-------------------------------------------------------------------------- +# WARNING: +# Do not change this cmake template +# Customise your preferences in "./conf.d/cmake/config.cmake" +#-------------------------------------------------------------------------- +# CMake 3.6 imported macros to simulate list(FILTER ...) subcommand +# ----------------------- +MACRO(PARSE_ARGUMENTS prefix arg_names option_names) +SET(DEFAULT_ARGS) +FOREACH(arg_name ${arg_names}) + SET(${prefix}_${arg_name}) +ENDFOREACH(arg_name) +FOREACH(option ${option_names}) + SET(${prefix}_${option} FALSE) +ENDFOREACH(option) + +SET(current_arg_name DEFAULT_ARGS) +SET(current_arg_list) +FOREACH(arg ${ARGN}) + LIST_CONTAINS(is_arg_name ${arg} ${arg_names}) + IF (is_arg_name) + SET(${prefix}_${current_arg_name} ${current_arg_list}) + SET(current_arg_name ${arg}) + SET(current_arg_list) + ELSE (is_arg_name) + LIST_CONTAINS(is_option ${arg} ${option_names}) + IF (is_option) + SET(${prefix}_${arg} TRUE) + ELSE (is_option) + SET(current_arg_list ${current_arg_list} ${arg}) + ENDIF (is_option) + ENDIF (is_arg_name) +ENDFOREACH(arg) +SET(${prefix}_${current_arg_name} ${current_arg_list}) +ENDMACRO(PARSE_ARGUMENTS) + +MACRO(LIST_CONTAINS var value) +SET(${var}) +FOREACH (value2 ${ARGN}) + IF (${value} STREQUAL ${value2}) + SET(${var} TRUE) + ENDIF (${value} STREQUAL ${value2}) +ENDFOREACH (value2) +ENDMACRO(LIST_CONTAINS) + +MACRO(LIST_FILTER) +PARSE_ARGUMENTS(LIST_FILTER "OUTPUT_VARIABLE" "" ${ARGV}) +# Check arguments. +LIST(LENGTH LIST_FILTER_DEFAULT_ARGS LIST_FILTER_default_length) +IF(${LIST_FILTER_default_length} EQUAL 0) + MESSAGE(FATAL_ERROR "LIST_FILTER: missing list variable.") +ENDIF(${LIST_FILTER_default_length} EQUAL 0) +IF(${LIST_FILTER_default_length} EQUAL 1) + MESSAGE(FATAL_ERROR "LIST_FILTER: missing regular expression variable.") +ENDIF(${LIST_FILTER_default_length} EQUAL 1) +# Reset output variable +IF(NOT LIST_FILTER_OUTPUT_VARIABLE) + SET(LIST_FILTER_OUTPUT_VARIABLE "LIST_FILTER_internal_output") +ENDIF(NOT LIST_FILTER_OUTPUT_VARIABLE) +SET(${LIST_FILTER_OUTPUT_VARIABLE}) +# Extract input list from arguments +LIST(GET LIST_FILTER_DEFAULT_ARGS 0 LIST_FILTER_input_list) +LIST(REMOVE_AT LIST_FILTER_DEFAULT_ARGS 0) +FOREACH(LIST_FILTER_item ${${LIST_FILTER_input_list}}) + FOREACH(LIST_FILTER_regexp_var ${LIST_FILTER_DEFAULT_ARGS}) + FOREACH(LIST_FILTER_regexp ${${LIST_FILTER_regexp_var}}) + IF(${LIST_FILTER_item} MATCHES ${LIST_FILTER_regexp}) + LIST(APPEND ${LIST_FILTER_OUTPUT_VARIABLE} ${LIST_FILTER_item}) + ENDIF(${LIST_FILTER_item} MATCHES ${LIST_FILTER_regexp}) + ENDFOREACH(LIST_FILTER_regexp ${${LIST_FILTER_regexp_var}}) + ENDFOREACH(LIST_FILTER_regexp_var) +ENDFOREACH(LIST_FILTER_item) +# If OUTPUT_VARIABLE is not specified, overwrite the input list. +IF(${LIST_FILTER_OUTPUT_VARIABLE} STREQUAL "LIST_FILTER_internal_output") + SET(${LIST_FILTER_input_list} ${${LIST_FILTER_OUTPUT_VARIABLE}}) +ENDIF(${LIST_FILTER_OUTPUT_VARIABLE} STREQUAL "LIST_FILTER_internal_output") +ENDMACRO(LIST_FILTER) + +# Generic useful macro +# ----------------------- +macro(PROJECT_TARGET_ADD TARGET_NAME) + set_property(GLOBAL APPEND PROPERTY PROJECT_TARGETS ${TARGET_NAME}) + set(TARGET_NAME ${TARGET_NAME}) +endmacro(PROJECT_TARGET_ADD) + +macro(PROJECT_PKGDEP_ADD PKG_NAME) + set_property(GLOBAL APPEND PROPERTY PROJECT_PKG_DEPS ${PKG_NAME}) +endmacro(PROJECT_PKGDEP_ADD) + +macro(defstr name value) + add_definitions(-D${name}=${value}) +endmacro(defstr) + +macro(configure_files_in_dir dir) + file(GLOB filelist "${dir}/*in") + foreach(file ${filelist}) + get_filename_component(filename ${file} NAME) + string(REGEX REPLACE "target" "${RSYNC_TARGET}" destinationfile ${filename}) + string(REGEX REPLACE ".in$" "" destinationfile ${destinationfile}) + configure_file(${file} ${CMAKE_CURRENT_BINARY_DIR}/target/${destinationfile}) + set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_CURRENT_BINARY_DIR}/target/${destinationfile}") + endforeach() +endmacro(configure_files_in_dir) + +macro(add_required_module PKG_CONFIG) + string(REGEX REPLACE "[<>]?=.*$" "" XPREFIX ${PKG_CONFIG}) + PKG_CHECK_MODULES(${XPREFIX} REQUIRED ${PKG_CONFIG}) + + INCLUDE_DIRECTORIES(${${XPREFIX}_INCLUDE_DIRS}) + list(APPEND link_libraries ${${XPREFIX}_LDFLAGS}) + add_compile_options (${${XPREFIX}_CFLAGS}) +endmacro(add_required_module) + +# Create custom target dedicated for HTML5 and DATA AGL target type +macro(add_input_files INPUT_FILES) + if(NOT DEFINED XML_FILES) + set(ext_reg "xml$") + set(XML_LIST ${INPUT_FILES}) + list_filter(XML_LIST ext_reg) + endif() + if(NOT DEFINED LUA_LIST) + set(ext_reg "lua$") + set(LUA_LIST ${INPUT_FILES}) + list_filter(LUA_LIST ext_reg) + endif() + if(NOT DEFINED JSON_FILES) + set(ext_reg "json$") + set(JSON_LIST ${INPUT_FILES}) + list_filter(JSON_LIST ext_reg) + endif() + + # These are v3.6 subcommand. Not used as default for now as + # many dev use Ubuntu 16.04 which have only 3.5 version + #list(FILTER XML_LIST INCLUDE REGEX "xml$") + #list(FILTER LUA_LIST INCLUDE REGEX "lua$") + #list(FILTER JSON_LIST INCLUDE REGEX "json$") + + add_custom_target(${TARGET_NAME} + DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} + ) + + foreach(file ${XML_LIST}) + add_custom_command(TARGET ${TARGET_NAME} + PRE_BUILD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${XML_CHECKER} ${file} + ) + endforeach() + foreach(file ${LUA_LIST}) + add_custom_command(TARGET ${TARGET_NAME} + PRE_BUILD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND ${LUA_CHECKER} ${file} + ) + endforeach() + foreach(file ${JSON_LIST}) + add_custom_command(TARGET ${TARGET_NAME} + PRE_BUILD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND cat ${file} | ${JSON_CHECKER} + ) + endforeach() + + add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} + DEPENDS ${INPUT_FILES} + COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} + COMMAND touch ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} + COMMAND cp -r ${INPUT_FILES} ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} + ) +endmacro() + +# Set the name of the OPENAPI definition JSON file for binding v2 +macro(set_openapi_filename openapi_filename) + set(OPENAPI_DEF ${openapi_filename} CACHE STRING "OpenAPI JSON file name used to generate binding header file before building a binding v2 target.") +endmacro() + +# Pre-packaging +macro(project_targets_populate) + # Default Widget default directory + set(BINDIR bin) + set(ETCDIR etc) + set(LIBDIR lib) + set(HTTPDIR htdocs) + set(DATADIR data) + set(PACKAGE_BINDIR ${PROJECT_PKG_BUILD_DIR}/${BINDIR}) + set(PACKAGE_ETCDIR ${PROJECT_PKG_BUILD_DIR}/${ETCDIR}) + set(PACKAGE_LIBDIR ${PROJECT_PKG_BUILD_DIR}/${LIBDIR}) + set(PACKAGE_HTTPDIR ${PROJECT_PKG_BUILD_DIR}/${HTTPDIR}) + set(PACKAGE_DATADIR ${PROJECT_PKG_BUILD_DIR}/${DATADIR}) + + add_custom_command(OUTPUT ${PACKAGE_BINDIR} ${PACKAGE_ETCDIR} ${PACKAGE_LIBDIR} ${PACKAGE_HTTPDIR} ${PACKAGE_DATADIR} + COMMAND mkdir -p ${PACKAGE_BINDIR} ${PACKAGE_ETCDIR} ${PACKAGE_LIBDIR} ${PACKAGE_HTTPDIR} ${PACKAGE_DATADIR}) + add_custom_target(populate DEPENDS ${PACKAGE_BINDIR} ${PACKAGE_ETCDIR} ${PACKAGE_LIBDIR} ${PACKAGE_HTTPDIR} ${PACKAGE_DATADIR}) + + INSTALL(CODE "execute_process(COMMAND make populate)") + INSTALL(DIRECTORY ${PROJECT_PKG_BUILD_DIR}/ + DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME} + ) + + get_property(PROJECT_TARGETS GLOBAL PROPERTY PROJECT_TARGETS) + foreach(TARGET ${PROJECT_TARGETS}) + get_target_property(T ${TARGET} LABELS) + if(T) + # Declaration of a custom command that will populate widget tree with the target + set(POPULE_PACKAGE_TARGET "project_populate_${TARGET}") + + get_target_property(P ${TARGET} PREFIX) + get_target_property(BD ${TARGET} BINARY_DIR) + get_target_property(SD ${TARGET} SOURCE_DIR) + get_target_property(OUT ${TARGET} OUTPUT_NAME) + + if(P MATCHES "NOTFOUND$") + if (${T} STREQUAL "BINDING") + set(P "lib") + else() + set(P "") + endif() + endif() + + if(${T} STREQUAL "BINDING") + list(APPEND BINDINGS_LIST "${P}${OUT}") + add_custom_command(OUTPUT ${PACKAGE_LIBDIR}/${P}${OUT}.so + DEPENDS ${BD}/${P}${OUT}.so + COMMAND mkdir -p ${PACKAGE_LIBDIR} + COMMAND cp ${BD}/${P}${OUT}.so ${PACKAGE_LIBDIR} + ) + add_custom_target(${POPULE_PACKAGE_TARGET} DEPENDS ${PACKAGE_LIBDIR}/${P}${OUT}.so) + add_dependencies(populate ${POPULE_PACKAGE_TARGET}) + add_dependencies(${POPULE_PACKAGE_TARGET} ${TARGET}) + elseif(${T} STREQUAL "BINDINGV2") + if (OPENAPI_DEF) + add_custom_command(OUTPUT ${SD}/${OPENAPI_DEF}.h + DEPENDS ${SD}/${OPENAPI_DEF}.json + COMMAND afb-genskel ${SD}/${OPENAPI_DEF}.json > ${SD}/${OPENAPI_DEF}.h + ) + add_custom_target("${TARGET}_GENSKEL" DEPENDS ${SD}/${OPENAPI_DEF}.h + COMMENT "Generating OpenAPI header file ${OPENAPI_DEF}.h") + add_dependencies(${TARGET} "${TARGET}_GENSKEL") + else() + add_custom_command(OUTPUT ${SD}/${OUT}-apidef.h + DEPENDS ${SD}/${OUT}-apidef.json + COMMAND afb-genskel ${SD}/${OUT}-apidef.json > ${SD}/${OUT}-apidef.h + ) + add_custom_target("${TARGET}_GENSKEL" DEPENDS ${SD}/${OUT}-apidef.h + COMMENT "Generating OpenAPI header file ${OUT}-apidef.h") + add_dependencies(${TARGET} "${TARGET}_GENSKEL") + endif() + + add_custom_command(OUTPUT ${PACKAGE_LIBDIR}/${P}${OUT}.so + DEPENDS ${BD}/${P}${OUT}.so + COMMAND mkdir -p ${PACKAGE_LIBDIR} + COMMAND cp ${BD}/${P}${OUT}.so ${PACKAGE_LIBDIR} + ) + add_custom_target(${POPULE_PACKAGE_TARGET} DEPENDS ${PACKAGE_LIBDIR}/${P}${OUT}.so) + add_dependencies(populate ${POPULE_PACKAGE_TARGET}) + add_dependencies(${POPULE_PACKAGE_TARGET} ${TARGET}) + elseif(${T} STREQUAL "EXECUTABLE") + add_custom_command(OUTPUT ${PACKAGE_BINDIR}/${P}${OUT} + DEPENDS ${BD}/${P}${OUT} + COMMAND mkdir -p ${PACKAGE_BINDIR} + COMMAND cp ${BD}/${P}${OUT} ${PACKAGE_BINDIR} + ) + add_custom_target(${POPULE_PACKAGE_TARGET} DEPENDS ${PACKAGE_BINDIR}/${P}${OUT}) + add_dependencies(populate ${POPULE_PACKAGE_TARGET}) + add_dependencies(${POPULE_PACKAGE_TARGET} ${TARGET}) + elseif(${T} STREQUAL "HTDOCS") + add_custom_command(OUTPUT ${PACKAGE_HTTPDIR}-xx + DEPENDS ${BD}/${P}${OUT} + COMMAND mkdir -p ${PACKAGE_HTTPDIR} + COMMAND touch ${PACKAGE_HTTPDIR} + COMMAND cp -r ${BD}/${P}${OUT}/* ${PACKAGE_HTTPDIR} + ) + add_custom_target(${POPULE_PACKAGE_TARGET} DEPENDS ${PACKAGE_HTTPDIR}-xx) + add_dependencies(populate ${POPULE_PACKAGE_TARGET}) + add_dependencies(${POPULE_PACKAGE_TARGET} ${TARGET}) + elseif(${T} STREQUAL "DATA") + # Generate list of output files instead of just one output directory + get_target_property(SF ${TARGET} SOURCES) + foreach(file ${SF}) + get_filename_component(JUST_FILENAME ${file} NAME) + list(APPEND OUTPUT_FILES ${PACKAGE_DATADIR}/${JUST_FILENAME}) + endforeach() + add_custom_target(${POPULE_PACKAGE_TARGET}) + add_custom_command(TARGET ${POPULE_PACKAGE_TARGET} + POST_BUILD + COMMAND mkdir -p ${PACKAGE_DATADIR} + COMMAND touch ${PACKAGE_DATADIR} + COMMAND cp -r ${BD}/${TARGET} ${PACKAGE_DATADIR} + ) + add_dependencies(populate ${POPULE_PACKAGE_TARGET}) + add_dependencies(${POPULE_PACKAGE_TARGET} ${TARGET}) + endif(${T} STREQUAL "BINDING") + elseif(${CMAKE_BUILD_TYPE} MATCHES "[Dd][Ee][Bb][Uu][Gg]") + MESSAGE("${Yellow}.. Warning: ${TARGET} ignored when packaging.${ColourReset}") + endif() + endforeach() +endmacro(project_targets_populate) + +macro(remote_targets_populate) + if (DEFINED ENV{RSYNC_TARGET}) + set (RSYNC_TARGET $ENV{RSYNC_TARGET}) + endif() + if (DEFINED ENV{RSYNC_PREFIX}) + set (RSYNC_PREFIX $ENV{RSYNC_PREFIX}) + endif() + + set( + REMOTE_LAUNCH "Test on target with: ${CMAKE_CURRENT_BINARY_DIR}/target/start-on-${RSYNC_TARGET}.sh" + CACHE STRING "Command to start ${PROJECT_NAME} on remote target ${RSYNC_TARGET}" + ) + + if(NOT RSYNC_TARGET OR NOT RSYNC_PREFIX) + message ("${Yellow}.. Warning: RSYNC_TARGET RSYNC_PREFIX not defined 'make remote-target-populate' not instanciated${ColourReset}") + add_custom_target(remote-target-populate + COMMENT "${Red}*** Fatal: RSYNC_TARGET RSYNC_PREFIX environment variables required with 'make remote-target-populate'${ColourReset}" + COMMAND exit -1 + ) + else() + set(BINDINGS_REGEX "not_set") + if(DEFINED BINDINGS_LIST) + list(LENGTH BINDINGS_LIST BINDINGS_LIST_LENGTH) + if(BINDINGS_LIST_LENGTH EQUAL 1) + list(GET BINDINGS_LIST 0 BINDINGS_REGEX) + string(APPEND BINDINGS_REGEX ".so") + elseif(BINDINGS_LIST_LENGTH GREATER 1) + foreach(B IN LISTS BINDINGS_LIST) + STRING(APPEND BINDINGS_STR "${B}|") + endforeach() + STRING(REGEX REPLACE "^(.*)\\|$" "(\\1).so" BINDINGS_REGEX ${BINDINGS_STR}) + endif() + endif() + + configure_files_in_dir(${TEMPLATE_DIR}) + configure_files_in_dir(${TEMPLATE_DIR}) + + add_custom_target(remote-target-populate + COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/target/*.sh + COMMAND rsync -e "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" --archive --delete ${PROJECT_PKG_BUILD_DIR}/ ${RSYNC_TARGET}:${RSYNC_PREFIX}/${PROJECT_NAME} + COMMENT "${REMOTE_LAUNCH}" + ) + add_dependencies(remote-target-populate populate) + endif() +endmacro(remote_targets_populate) + +macro(wgt_package_build) + if(NOT EXISTS ${WIDGET_CONFIG_TEMPLATE}) + MESSAGE(FATAL_ERROR "${Red}WARNING ! Missing mandatory files to build widget file.\nYou need a config.xml template: please specify WIDGET_CONFIG_TEMPLATE correctly.${ColourReset}") + endif() + if(NOT WIDGET_TYPE) + MESSAGE(FATAL_ERROR "WIDGET_TYPE must be set in your config.cmake.\neg.: set(WIDGET_TYPE application/vnd.agl.service)") + endif() + if(NOT DEFINED PROJECT_ICON) + if( ${WIDGET_TYPE} MATCHES "agl.native") + set(ICON_PATH ${PKG_APP_TEMPLATE_DIR}/wgt/icon-native.png) + elseif( ${WIDGET_TYPE} MATCHES "agl.service") + set(ICON_PATH ${PKG_APP_TEMPLATE_DIR}/wgt/icon-service.png) + elseif( ${WIDGET_TYPE} MATCHES "x-executable") + set(ICON_PATH ${PKG_APP_TEMPLATE_DIR}/wgt/icon-qml.png) + elseif( ${WIDGET_TYPE} MATCHES "text/html") + set(ICON_PATH ${PKG_APP_TEMPLATE_DIR}/wgt/icon-html5.png) + endif() + elseif(EXISTS "${CMAKE_SOURCE_DIR}/${WIDGET_ICON}") + set(ICON_PATH "${CMAKE_SOURCE_DIR}/${WIDGET_ICON}") + elseif(EXISTS "${WIDGET_ICON}") + set(ICON_PATH "${WIDGET_ICON}") + else() + set(ICON_PATH ${CMAKE_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/wgt/icon-default.png) + endif() + + if(NOT WIDGET_ENTRY_POINT) + set(WIDGET_ENTRY_POINT lib) + endif() + + add_custom_command(OUTPUT ${PROJECT_PKG_BUILD_DIR}/config.xml + COMMAND ${CMAKE_COMMAND} -DINFILE=${WIDGET_CONFIG_TEMPLATE} -DOUTFILE=${PROJECT_PKG_BUILD_DIR}/config.xml -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake + COMMAND cp ${ICON_PATH} ${PROJECT_PKG_BUILD_DIR}/${PROJECT_ICON} + + ) + add_custom_target(packaging_wgt DEPENDS ${PROJECT_PKG_BUILD_DIR}/config.xml) + + # Fulup ??? copy any extra file in wgt/etc into populate package before building the widget + file(GLOB PROJECT_CONF_FILES "${TEMPLATE_DIR}/etc/*") + if(${PROJECT_CONF_FILES}) + file(COPY "${TEMPLATE_DIR}/etc/*" DESTINATION ${PROJECT_PKG_BUILD_DIR}/etc/) + endif(${PROJECT_CONF_FILES}) + + add_custom_command(OUTPUT ${PROJECT_NAME}.wgt + DEPENDS ${PROJECT_TARGETS} + COMMAND wgtpkg-pack -f -o ${PROJECT_NAME}.wgt ${PROJECT_PKG_BUILD_DIR} + ) + + add_custom_target(widget DEPENDS ${PROJECT_NAME}.wgt) + add_dependencies(widget populate packaging_wgt) + set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.wgt") + + if(NOT RSYNC_TARGET) + message ("${Yellow}.. Warning: RSYNC_TARGET not defined 'make widget-target-install' not instanciated${ColourReset}") + add_custom_target(widget-target-install + COMMENT "${Red}*** Fatal: RSYNC_TARGET RSYNC_PREFIX environment variables required with 'make widget-target-install'${ColourReset}" + COMMAND exit -1 + ) + else() + configure_files_in_dir(${TEMPLATE_DIR}) + add_custom_target(widget-target-install + DEPENDS widget + COMMAND chmod +x ${CMAKE_CURRENT_BINARY_DIR}/target/install-wgt-on-${RSYNC_TARGET}.sh + COMMAND ${CMAKE_CURRENT_BINARY_DIR}/target/install-wgt-on-${RSYNC_TARGET}.sh + ) +endif() + + if(PACKAGE_MESSAGE) + add_custom_command(TARGET widget + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --cyan "++ ${PACKAGE_MESSAGE}") + endif() +endmacro(wgt_package_build) + +macro(rpm_package_build) + add_custom_command(OUTPUT ${NPKG_PROJECT_NAME}.spec + DEPENDS ${PROJECT_TARGETS} + archive + packaging + COMMAND rpmbuild --define=\"%_sourcedir ${PROJECT_PKG_ENTRY_POINT}\" -ba ${PROJECT_PKG_ENTRY_POINT}/${NPKG_PROJECT_NAME}.spec + ) + + add_custom_target(rpm DEPENDS ${NPKG_PROJECT_NAME}.spec) + add_dependencies(rpm populate packaging) + + if(PACKAGE_MESSAGE) + add_custom_command(TARGET rpm + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --cyan "++ ${PACKAGE_MESSAGE}") + endif() +endmacro(rpm_package_build) + +macro(deb_package_build) +#TODO +endmacro(deb_package_build) + +macro(project_package_build) + if(EXISTS ${TEMPLATE_DIR}) + rpm_package_build() + endif() + + if(EXISTS ${TEMPLATE_DIR}) + wgt_package_build() + endif() + + if(EXISTS ${TEMPLATE_DIR}) + deb_package_build() + endif() +endmacro(project_package_build) + +macro(project_subdirs_add) + set (ARGSLIST ${ARGN}) + list(LENGTH ARGSLIST ARGSNUM) + if(${ARGSNUM} GREATER 0) + file(GLOB filelist "${ARGV0}") + else() + file(GLOB filelist "*") + endif() + + foreach(filename ${filelist}) + if(EXISTS "${filename}/CMakeLists.txt") + add_subdirectory(${filename}) + endif(EXISTS "${filename}/CMakeLists.txt") + endforeach() +endmacro(project_subdirs_add) + +# Print developer helper message when build is done +# ------------------------------------------------------- +macro(project_closing_msg) + get_property(PROJECT_TARGETS_SET GLOBAL PROPERTY PROJECT_TARGETS SET) + get_property(PROJECT_TARGETS GLOBAL PROPERTY PROJECT_TARGETS) + if(CLOSING_MESSAGE AND ${PROJECT_TARGETS_SET}) + add_custom_target(${PROJECT_NAME}_build_done ALL + COMMAND ${CMAKE_COMMAND} -E cmake_echo_color --cyan "++ ${CLOSING_MESSAGE}" + ) + add_dependencies(${PROJECT_NAME}_build_done + ${DEPENDENCIES_TARGET} ${PROJECT_TARGETS}) + endif() +endmacro() diff --git a/conf.d/app-templates/cmake/cmake.d/04-extra_targets.cmake b/conf.d/app-templates/cmake/cmake.d/04-extra_targets.cmake new file mode 100644 index 0000000..d54e128 --- /dev/null +++ b/conf.d/app-templates/cmake/cmake.d/04-extra_targets.cmake @@ -0,0 +1,162 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll <fulup@iot.bzh> +# contrib: 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. +########################################################################### + + +#-------------------------------------------------------------------------- +# WARNING: +# Do not change this cmake template +# Customise your preferences in "./conf.d/cmake/config.cmake" +#-------------------------------------------------------------------------- + +# Add a dummy target to enable global dependency order +# ----------------------------------------------------- +if(EXTRA_DEPENDENCIES_ORDER) + set(DEPENDENCIES_TARGET ${PROJECT_NAME}_extra_dependencies) + add_custom_target(${DEPENDENCIES_TARGET} ALL + DEPENDS ${EXTRA_DEPENDENCY_ORDER} + ) +endif() + +# ---------------------------------------------------------------------------- +# Archive target +# ---------------------------------------------------------------------------- +add_custom_command(OUTPUT ${ARCHIVE_OUTPUT} + DEPENDS ${PROJECT_TARGETS} + #Create git archive of the main project + COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR}\; git --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git archive --format=tar --prefix=${NPKG_PROJECT_NAME}-${PROJECT_VERSION}/ HEAD -o ${ARCHIVE_OUTPUT_ARCHIVE} + #Create tmp git archive for each submodule + COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR}\; git --git-dir=${CMAKE_CURRENT_SOURCE_DIR}/.git submodule foreach --recursive ${CMD_ARCHIVE_SUBMODULE} + #Concatenate main archive and tmp submodule archive + COMMAND for SUBTAR in ${TMP_ARCHIVE_SUBMODULE}-*.tar\; do tar --concatenate --file=${ARCHIVE_OUTPUT_ARCHIVE} $$SUBTAR\;done + #Remove tmp submodule archive + COMMAND rm -rf ${TMP_ARCHIVE_SUBMODULE}-*.tar + #Compress main archive + COMMAND gzip --force --verbose ${ARCHIVE_OUTPUT_ARCHIVE} +) +add_custom_target(archive DEPENDS ${ARCHIVE_OUTPUT}) + +# ---------------------------------------------------------------------------- +# Packaging target +# ---------------------------------------------------------------------------- +#Format Build require package +foreach (PKG_CONFIG ${PKG_REQUIRED_LIST}) + #Unset TMP variable + unset(XPREFIX) + unset(XRULE) + unset(RPM_EXTRA_DEP) + unset(DEB_EXTRA_DEP) + #For deb package,add EOL format only for a new line + if(DEB_PKG_DEPS) + set(DEB_PKG_DEPS "${DEB_PKG_DEPS},\n") + endif() + #Get pkg-config rule on version + string(REGEX REPLACE "[<>]?=.*$" "" XPREFIX ${PKG_CONFIG}) + string(REGEX MATCH "[<>]?=" XRULE ${PKG_CONFIG}) + #Only if pkg-config has rule on version + if(XRULE) + string(REGEX REPLACE ".*[<>]?=" "" XVERS ${PKG_CONFIG}) + set(RPM_EXTRA_DEP " ${XRULE} ${XVERS}") + set(DEB_EXTRA_DEP " (${XRULE} ${XVERS})") + endif() + # Format for rpm package + set(RPM_PKG_DEPS "${RPM_PKG_DEPS}BuildRequires: pkgconfig(${XPREFIX})${RPM_EXTRA_DEP}\n") + + # Format for deb package + # Because the tool "dpkg" is used on the packages db to find the + # package providing the pkg-cong file ${XPREFIX}.pc, we need + # to test the OS release package type + # Only doable within a native environment not under SDK + if( OSRELEASE MATCHES "debian" AND NOT DEFINED ENV{SDKTARGETSYSROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) + execute_process( + COMMAND pkg-config --print-provides ${XPREFIX} + OUTPUT_VARIABLE TMP_PKG_BIN + ) + if(TMP_PKG_BIN) + string(REGEX REPLACE " *=.*$" "" PKG_BIN ${TMP_PKG_BIN}) + set(DEB_PKG_DEPS "${DEB_PKG_DEPS} ${PKG_BIN} ${DEB_EXTRA_DEP}") + else() + message(FATAL_ERROR "-- ${Red}${XPREFIX} development files not installed. Abort.${ColourReset}") + endif() + endif() +endforeach() + +if(NOT EXISTS ${TEMPLATE_DIR}/rpm-config.spec.in) + MESSAGE(FATAL_ERROR "${Red}Missing mandatory files: you need rpm-config.spec.in in ${TEMPLATE_DIR} folder.${ColourReset}") +endif() + +# Because the tool "dpkg" is used on the packages db to find the +# package providing the pkg-cong file ${XPREFIX}.pc, we need +# to test the OS release package type +# Only doable within a native environment not under SDK +if(OSRELEASE MATCHES "debian" AND NOT DEFINED ENV{SDKTARGETSYSROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) + add_custom_target(packaging_deb DEPENDS ${TEMPLATE_DIR}/debian.compat.in + ${TEMPLATE_DIR}/debian.changelog.in + ${TEMPLATE_DIR}/deb-config.dsc.in + ${TEMPLATE_DIR}/deb-config.install.in + ${TEMPLATE_DIR}/debian.control.in + ${TEMPLATE_DIR}/debian.rules.in + COMMAND ${CMAKE_COMMAND} -DINFILE=${TEMPLATE_DIR}/debian.compat.in -DOUTFILE=${PACKAGING_DEB_OUTPUT_COMPAT} -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake + COMMAND ${CMAKE_COMMAND} -DINFILE=${TEMPLATE_DIR}/debian.changelog.in -DOUTFILE=${PACKAGING_DEB_OUTPUT_CHANGELOG} -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake + COMMAND ${CMAKE_COMMAND} -DINFILE=${TEMPLATE_DIR}/deb-config.dsc.in -DOUTFILE=${PACKAGING_DEB_OUTPUT_DSC} -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake + COMMAND ${CMAKE_COMMAND} -DINFILE=${TEMPLATE_DIR}/deb-config.install.in -DOUTFILE=${PACKAGING_DEB_OUTPUT_INSTALL} -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake + COMMAND ${CMAKE_COMMAND} -DINFILE=${TEMPLATE_DIR}/debian.control.in -DOUTFILE=${PACKAGING_DEB_OUTPUT_CONTROL} -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake + COMMAND ${CMAKE_COMMAND} -DINFILE=${TEMPLATE_DIR}/debian.rules.in -DOUTFILE=${PACKAGING_DEB_OUTPUT_RULES} -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake + ) +endif() + +add_custom_target(packaging) +set(PACKAGING_SPEC_OUTPUT ${PROJECT_PKG_ENTRY_POINT}/${NPKG_PROJECT_NAME}.spec) +add_custom_target(packaging_rpm DEPENDS ${TEMPLATE_DIR}/rpm-config.spec.in + COMMAND ${CMAKE_COMMAND} -DINFILE=${TEMPLATE_DIR}/rpm-config.spec.in -DOUTFILE=${PACKAGING_SPEC_OUTPUT} -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake +) +add_dependencies(packaging packaging_rpm) +if(TARGET packaging_wgt) + add_dependencies(packaging packaging_wgt) +endif() +if(OSRELEASE MATCHES "debian" AND NOT DEFINED ENV{SDKTARGETSYSROOT} AND NOT DEFINED CMAKE_TOOLCHAIN_FILE) + # Target to add dependencies indirectly to "packaging" target. + add_dependencies(packaging packaging_deb) +endif() + +#Generate a cmake cache file usable by cmake script. +set(CacheForScript ${CMAKE_BINARY_DIR}/CMakeCacheForScript.cmake) +#Create a tmp cmake file. +file(WRITE ${CacheForScript} "") + +get_cmake_property(Vars VARIABLES) +foreach(Var ${Vars}) + if(${Var}) + #Replace unwanted char. + string(REPLACE "\\" "\\\\" VALUE ${${Var}}) + string(REPLACE "\n" "\\n" VALUE ${VALUE}) + string(REPLACE "\r" "\\n" VALUE ${VALUE}) + string(REPLACE "\"" "\\\"" VALUE ${VALUE}) + endif() + file(APPEND ${CacheForScript} "set(${Var} \"${VALUE}\")\n") +endforeach() + +# ---------------------------------------------------------------------------- +# Autobuild target +# ---------------------------------------------------------------------------- + +add_custom_target(autobuild ALL DEPENDS ${CMAKE_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/template.d/autobuild/agl/autobuild.in + ${CMAKE_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/template.d/autobuild/linux/autobuild.in + COMMAND ${CMAKE_COMMAND} -DINFILE=${CMAKE_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/template.d/autobuild/agl/autobuild.in -DOUTFILE=${PROJECT_TEMPLATE_AGL_AUTOBUILD_DIR}/autobuild -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake + COMMAND ${CMAKE_COMMAND} -DINFILE=${CMAKE_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/template.d/autobuild/agl/autobuild.in -DOUTFILE=${PROJECT_TEMPLATE_LINUX_AUTOBUILD_DIR}/autobuild -DPROJECT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR} -P ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT_APP_TEMPLATES_DIR}/cmake/configure_file.cmake +) diff --git a/conf.d/app-templates/cmake/common.cmake b/conf.d/app-templates/cmake/common.cmake new file mode 100644 index 0000000..d0ec12e --- /dev/null +++ b/conf.d/app-templates/cmake/common.cmake @@ -0,0 +1,63 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll <fulup@iot.bzh> +# contrib: 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. +########################################################################### + + +#-------------------------------------------------------------------------- +# WARNING: +# Do not change this cmake template +# Customise your preferences in "./conf.d/cmake/config.cmake" +#-------------------------------------------------------------------------- + +file(GLOB project_cmakefiles ${PROJECT_APP_TEMPLATES_DIR}/cmake/cmake.d/[0-9][0-9]-*.cmake) +list(SORT project_cmakefiles) +file(GLOB home_cmakefiles $ENV{HOME}/.config/app-templates/cmake.d/[0-9][0-9]-common*.cmake $ENV{HOME}/.config/app-templates/cmake.d/[0-9][0-9]-${PROJECT_NAME}*.cmake) +list(SORT home_cmakefiles) +file(GLOB system_cmakefiles /etc/app-templates/cmake.d/[0-9][0-9]-common*.cmake /etc/app-templates/cmake.d/[0-9][0-9]-${PROJECT_NAME}*.cmake) +list(SORT system_cmakefiles) + +foreach(file ${system_cmakefiles} ${home_cmakefiles} ${project_cmakefiles}) + message(STATUS "Include: ${file}") + include(${file}) +endforeach() + +file(GLOB project_cmakefiles ${ENTRY_POINT}/cmake/[0-9][0-9]-${OSRELEASE}*.cmake ) +list(SORT project_cmakefiles) +if(NOT project_cmakefiles) + file(GLOB project_cmakefiles ${ENTRY_POINT}/cmake/[0-9][0-9]-default*.cmake) +endif() + +foreach(file ${project_cmakefiles}) + message(STATUS "Include: ${file}") + include(${file}) +endforeach() + +if(DEFINED PROJECT_SRC_DIR_PATTERN) + project_subdirs_add(${PROJECT_SRC_DIR_PATTERN}) +else() + project_subdirs_add() +endif(DEFINED PROJECT_SRC_DIR_PATTERN) + +configure_files_in_dir(${PROJECT_APP_TEMPLATES_DIR}/${ENTRY_POINT}/template.d) +configure_files_in_dir($ENV{HOME}/.config/app-templates/scripts) +configure_files_in_dir(/etc/app-templates/scripts) + +project_targets_populate() +remote_targets_populate() +project_package_build() +project_closing_msg() diff --git a/conf.d/app-templates/cmake/configure_file.cmake b/conf.d/app-templates/cmake/configure_file.cmake new file mode 100644 index 0000000..2028388 --- /dev/null +++ b/conf.d/app-templates/cmake/configure_file.cmake @@ -0,0 +1,2 @@ +include(${CMAKE_BINARY_DIR}/CMakeCacheForScript.cmake) +configure_file(${INFILE} ${OUTFILE} @ONLY) diff --git a/conf.d/app-templates/cmake/export.map b/conf.d/app-templates/cmake/export.map new file mode 100644 index 0000000..ee2f413 --- /dev/null +++ b/conf.d/app-templates/cmake/export.map @@ -0,0 +1 @@ +{ global: afbBindingV*; local: *; }; diff --git a/conf.d/app-templates/docs/0-Doc-Revisions.md b/conf.d/app-templates/docs/0-Doc-Revisions.md new file mode 100644 index 0000000..38b6e74 --- /dev/null +++ b/conf.d/app-templates/docs/0-Doc-Revisions.md @@ -0,0 +1,6 @@ +Document revisions +================== + +| Date | Version | Designation | Author | +|-------------|---------|--------------------------------------|-------------------------| +| 4 Jul 2017 | 1.0 | Initial release | R. Forlot [ Iot.bzh ] | diff --git a/conf.d/app-templates/docs/README.md b/conf.d/app-templates/docs/README.md new file mode 100644 index 0000000..39f9209 --- /dev/null +++ b/conf.d/app-templates/docs/README.md @@ -0,0 +1,22 @@ +# Introduction + +This document explain how to use the CMake templates files and associated +files to ease developement of AGL application. + +<br> +<br> +<br> +<br> +<br> + +| *Meta* | *Data* | +| -- | -- | +| **Title** | {{ config.title }} | +| **Author** | {{ config.author }} | +| **Description** | {{ config.description }} | +| **Keywords** | {{ config.keywords }} | +| **Language** | English | +| **Published** | Published {{ config.published }} as an electronic book | +| **Updated** | {{ gitbook.time }} | +| **Collection** | Open-source | +| **Website** | [{{ config.website }}]({{ config.website }}) | diff --git a/conf.d/app-templates/docs/SUMMARY.md b/conf.d/app-templates/docs/SUMMARY.md new file mode 100644 index 0000000..f475678 --- /dev/null +++ b/conf.d/app-templates/docs/SUMMARY.md @@ -0,0 +1,10 @@ +# Summary + +* [Document revisions](0-Doc-Revisions.md) + +* [Developper guide](dev_guide/0_Abstract.md) + * [Quickstart](dev_guide/1_Quickstart.md) + * [Project architecture](dev_guide/2_project_architecture.md) + * [Advanced usage](dev_guide/3_advanced_usage.md) + * [Customization](dev_guide/4_advanced_customization.md) + * [Autobuild](dev_guide/5_autobuild.md) diff --git a/conf.d/app-templates/docs/_layouts/ebook/page.html b/conf.d/app-templates/docs/_layouts/ebook/page.html new file mode 100644 index 0000000..bf325e9 --- /dev/null +++ b/conf.d/app-templates/docs/_layouts/ebook/page.html @@ -0,0 +1,36 @@ +{% extends "layout.html" %} + +{% block title %}{{ page.title }}{% endblock %} +{% block description %}{{ page.description }}{% endblock %} + +{% block style %} + {### Include theme css before plugins css ###} + {% if not fileExists(config.styles.print) %} + {% if options.format %} + <link rel="stylesheet" href="{{ (options.format + ".css")|resolveAsset }}"> + {% else %} + <link rel="stylesheet" href="{{ "ebook.css"|resolveAsset }}"> + {% endif %} + {% endif %} + + {{ super() }} + + {### Custom stylesheets for the book ###} + + {% for type, style in config.styles %} + {% if fileExists(style) and (type == "ebook" or type == "print" or type == options.format) %} + <link rel="stylesheet" href="{{ style|resolveFile }}"> + {% endif %} + {% endfor %} +{% endblock %} + +{% block body %} +<div class="page"> + {% block page %} + <h1 class="book-chapter book-chapter-{{ page.depth }}">{{ page.title }}</h1> + <div class="section"> + {{ page.content|safe }} + </div> + {% endblock %} +</div> +{% endblock %} diff --git a/conf.d/app-templates/docs/_layouts/ebook/pdf_footer.html b/conf.d/app-templates/docs/_layouts/ebook/pdf_footer.html new file mode 100644 index 0000000..679e562 --- /dev/null +++ b/conf.d/app-templates/docs/_layouts/ebook/pdf_footer.html @@ -0,0 +1,13 @@ +{% extends "./page.html" %} +{% block body %} +<div id="pdf-footer" class="pdf-footer"> + <span class="footer-left">Version {{ config.version }}</span> + <span class="footer-right">{{ page.num }}</span> + <span class="footer-center">{{ config.published }}</span> +</div> + +<!-- Allow to hide footer for some pages using hidepagefooters config in book.json --> +<script> + if (({% for num in config.hidepagefooters %}{{ page.num }} == {{ num }} || {% endfor %}false)) document.getElementById('pdf-footer').style.display = 'none' +</script> +{% endblock %} diff --git a/conf.d/app-templates/docs/_layouts/ebook/pdf_header.html b/conf.d/app-templates/docs/_layouts/ebook/pdf_header.html new file mode 100644 index 0000000..ef49641 --- /dev/null +++ b/conf.d/app-templates/docs/_layouts/ebook/pdf_header.html @@ -0,0 +1,13 @@ +{% extends "./page.html" %} +{% block body %} +<div id="pdf-header" class="pdf-header"> + <span class="header-left">IoT.Bzh</span> + <span class="header-right">{{ config.title }}</span> +</div> + +<!-- Allow to hide header for some pages using hidepageheaders config in book.json --> +<script> + if (({% for num in config.hidepageheaders %}{{ page.num }} == {{ num }} || {% endfor %}false)) document.getElementById('pdf-header').style.display = 'none' +</script> + +{% endblock %}
\ No newline at end of file diff --git a/conf.d/app-templates/docs/_layouts/ebook/summary.html b/conf.d/app-templates/docs/_layouts/ebook/summary.html new file mode 100644 index 0000000..be328a4 --- /dev/null +++ b/conf.d/app-templates/docs/_layouts/ebook/summary.html @@ -0,0 +1,58 @@ +{% extends "./page.html" %} + +{% block title %}{{ "SUMMARY"|t }}{% endblock %} + +{% macro articles(_articles) %} + {% for article in _articles %} + <li> + <span class="inner"> + {% if article.path or article.url %} + {% if article.path %} + <a href="{{ article.path|contentURL }}{{ article.anchor }}">{{ article.title }}</a> + {% else %} + <a target="_blank" href="{{ article.url }}">{{ article.title }}</a> + {% endif %} + {% else %} + <span>{{ article.title }}</span> + {% endif %} + {% if 1 %} + <span class="page">{{ article.level }}</span> + {% endif %} + </span> + {% if article.articles.length > 0 %} + <ol> + {{ articles(article.articles) }} + </ol> + {% endif %} + </li> + {% endfor %} +{% endmacro %} + +{% block page %} +<div class="section toc"> + <h1>{{ "SUMMARY"|t }}</h1> + <ol> + {% for part in summary.parts %} + {% if part.title %} + <li class="part-title"> + <h2>{{ part.title }}</h2> + </li> + {% endif %} + {{ articles(part.articles) }} + + {% if not loop.last %} + <li class="divider"></li> + {% endif %} + {% endfor %} + + {% if glossary.path %} + <li> + <span class="inner"> + <a href="{{ ('/' + glossary.path)|contentURL }}">{{ "GLOSSARY"|t }}</a> + </span> + </li> + {% endif %} + </ol> +</div> +{% endblock %} + diff --git a/conf.d/app-templates/docs/_layouts/layout.html b/conf.d/app-templates/docs/_layouts/layout.html new file mode 100644 index 0000000..3d5aca6 --- /dev/null +++ b/conf.d/app-templates/docs/_layouts/layout.html @@ -0,0 +1,28 @@ +<!DOCTYPE HTML> +<html lang="{{ config.language }}" {% if page.dir == "rtl" %}dir="rtl"{% endif %}> + <head> + <meta charset="UTF-8"> + <meta content="text/html; charset=utf-8" http-equiv="Content-Type"> + <title>{% block title %}{{ config.title|d("GitBook", true) }}{% endblock %}</title> + <meta http-equiv="X-UA-Compatible" content="IE=edge" /> + <meta name="description" content="{% block description %}{% endblock %}"> + <meta name="generator" content="GitBook {{ gitbook.version }}"> + {% if config.author %}<meta name="author" content="{{ config.author }}">{% endif %} + {% if config.isbn %}<meta name="identifier" content="{{ config.isbn }}" scheme="ISBN">{% endif %} + {% block style %} + {% for resource in plugins.resources.css %} + {% if resource.url %} + <link rel="stylesheet" href="{{ resource.url }}"> + {% else %} + <link rel="stylesheet" href="{{ resource.path|resolveAsset }}"> + {% endif %} + {% endfor %} + {% endblock %} + + {% block head %}{% endblock %} + </head> + <body> + {% block body %}{% endblock %} + {% block javascript %}{% endblock %} + </body> +</html> diff --git a/conf.d/app-templates/docs/cover.jpg b/conf.d/app-templates/docs/cover.jpg Binary files differnew file mode 100644 index 0000000..4e04b0c --- /dev/null +++ b/conf.d/app-templates/docs/cover.jpg diff --git a/conf.d/app-templates/docs/cover_small.jpg b/conf.d/app-templates/docs/cover_small.jpg Binary files differnew file mode 100644 index 0000000..315816b --- /dev/null +++ b/conf.d/app-templates/docs/cover_small.jpg diff --git a/conf.d/app-templates/docs/dev_guide/0_Abstract.md b/conf.d/app-templates/docs/dev_guide/0_Abstract.md new file mode 100644 index 0000000..a04cc87 --- /dev/null +++ b/conf.d/app-templates/docs/dev_guide/0_Abstract.md @@ -0,0 +1,21 @@ +# Developper Guide: use AGL CMake Templates + +## Abstract + +Files used to build an application, or binding, project with the +AGL Application Framework. + +To build your AGL project using these templates, you have to install +them within your project and adjust compilation option in `config.cmake`. +For technical reasons, you also have to specify **cmake** target in +sub CMakeLists.txt installed. Make a globbing search to find source files +isn't recommended now to handle project build especially in a multiuser +project because CMake will not be aware of new or removed source files. + +You'll find usage samples here: + +- [helloworld-service](https://github.com/iotbzh/helloworld-service) +- [low-level-can-service](https://gerrit.automotivelinux.org/gerrit/apps/low-level-can-service) +- [high-level-viwi-service](https://github.com/iotbzh/high-level-viwi-service) +- [audio-binding](https://github.com/iotbzh/audio-binding) +- [unicens2-binding](https://github.com/iotbzh/unicens2-binding) diff --git a/conf.d/app-templates/docs/dev_guide/1_Quickstart.md b/conf.d/app-templates/docs/dev_guide/1_Quickstart.md new file mode 100644 index 0000000..9aad1ef --- /dev/null +++ b/conf.d/app-templates/docs/dev_guide/1_Quickstart.md @@ -0,0 +1,81 @@ + +# Quickstart + +## Initialization + +To use these templates files on your project just install the reference files using +**git submodule** then use `config.cmake` file to configure your project specificities : + +```bash +git submodule add https://gerrit.automotivelinux.org/gerrit/apps/app-templatesconf.d/app-templates conf.d/app-templates +mkdir conf.d/cmake +cp conf.d/app-templates/cmake/config.cmake.sample conf.d/cmake/config.cmake +``` + +Edit the copied config.cmake file to fit your needs. + +Now, create your top CMakeLists.txt file which include `config.cmake` file. + +An example is available in **app-templates** submodule that you can copy and +use: + +```bash +cp conf.d/app-templates/cmake/CMakeLists.txt CMakeLists.txt +``` + +## Create your CMake targets + +For each target part of your project, you need to use ***PROJECT_TARGET_ADD*** +to include this target to your project. + +Using it, make available the cmake variable ***TARGET_NAME*** until the next +***PROJECT_TARGET_ADD*** is invoked with a new target name. + +So, typical usage defining a target is: + +```cmake +PROJECT_TARGET_ADD(SuperExampleName) --> Adding target to your project + +add_executable/add_library(${TARGET_NAME}.... --> defining your target sources + +SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES.... --> fit target properties +for macros usage + +INSTALL(TARGETS ${TARGET_NAME}.... +``` + +## Targets PROPERTIES + +You should set properties on your targets that will be used to package your +apps in a widget file that could be installed on an AGL system. + +Specify what is the type of your targets that you want to be included in the +widget package with the property **LABELS**: + +Choose between: + +- **BINDING**: Shared library that be loaded by the AGL Application Framework +- **BINDINGV2**: Shared library that be loaded by the AGL Application Framework. + This has to be accompagnied with a JSON file named like the *${OUTPUT_NAME}-apidef* of + the target that describe the API with OpenAPI syntax (e.g: *mybinding-apidef*). + Or you can choose the name by setting the *CACHE* cmake variable *OPENAPI_DEF* + (***CAUTION***: setting a CACHE variable is needed, or set a normal variable + with the *PARENT_SCOPE* option to make it visible for the parent scope + where the target is defined) JSON file will be used to generate header file + using `afb-genskel` tool. +- **HTDOCS**: Root directory of a web app. This target has to build its + directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} +- **DATA**: Resources used by your application. This target has to build its + directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} +- **EXECUTABLE**: Entry point of your application executed by the AGL + Application Framework + +```cmake +SET_TARGET_PROPERTIES(${TARGET_NAME} + PREFIX "afb-" + LABELS "BINDING" + OUTPUT_NAME "file_output_name") +``` + +> **TIP** you should use the prefix _afb-_ with your **BINDING* targets which +> stand for **Application Framework Binding**. diff --git a/conf.d/app-templates/docs/dev_guide/2_project_architecture.md b/conf.d/app-templates/docs/dev_guide/2_project_architecture.md new file mode 100644 index 0000000..0cae607 --- /dev/null +++ b/conf.d/app-templates/docs/dev_guide/2_project_architecture.md @@ -0,0 +1,98 @@ +# Project architecture + +A typical project architecture would be : + +```tree +<project-root-path> +│ +├── conf.d/ +│ ├── autobuild/ +│ │ ├── agl +│ │ │ └── autobuild +│ │ ├── linux +│ │ │ └── autobuild +│ │ └── windows +│ │ └── autobuild +│ ├── app-templates/ +│ │ ├── README.md +│ │ ├── autobuild/ +│ │ │ ├── agl +│ │ │ │ └── autobuild.in +│ │ │ ├── linux +│ │ │ │ └── autobuild.in +│ │ │ └── windows +│ │ │ └── autobuild.in +│ │ ├── cmake/ +│ │ │ ├── config.cmake.sample +│ │ │ ├── export.map +│ │ │ └── macros.cmake +│ │ ├── deb/ +│ │ │ └── config.deb.in +│ │ ├── rpm/ +│ │ │ └── config.spec.in +│ │ └── wgt/ +│ │ ├── config.xml.in +│ │ ├── config.xml.in.sample +│ │ ├── icon-default.png +│ │ ├── icon-html5.png +│ │ ├── icon-native.png +│ │ ├── icon-qml.png +│ │ └── icon-service.png +│ ├── packaging/ +│ │ ├── config.spec +│ │ └── config.deb +│ ├── cmake +│ │ └── config.cmake +│ └── wgt +│ └── config.xml.in +├── <libs> +├── <target> +│ └── <files> +├── <target> +│ └── <file> +└── <target> + └── <files> +``` + +| # | Parent | Description | +| - | -------| ----------- | +| \<root-path\> | - | Path to your project. Hold master CMakeLists.txt and general files of your projects. | +| conf.d | \<root-path\> | Holds needed files to build, install, debug, package an AGL app project | +| app-templates | conf.d | Git submodule to app-templates AGL repository which provides CMake helpers macros library, and build scripts. config.cmake is a copy of config.cmake.sample configured for the projects. SHOULD NOT BE MODIFIED MANUALLY !| +| autobuild | conf.d | Scripts generated from app-templates to build packages the same way for differents platforms.| +| cmake | conf.d | Contains at least config.cmake file modified from the sample provided in app-templates submodule. | +| wgt | conf.d | Contains at least config.xml.in template file modified from the sample provided in app-templates submodule for the needs of project (See config.xml.in.sample file for more details). | +| packaging | conf.d | Contains output files used to build packages. | +| \<libs\> | \<root-path\> | External dependencies libraries. This isn't to be used to include header file but build and link statically specifics libraries. | Library sources files. Can be a decompressed library archive file or project fork. | +| \<target\> | \<root-path\> | A target to build, typically library, executable, etc. | + +## Manage app-templates submodule + +### Update + +You may have some news bug fixes or features available from app-templates +repository that you want. To update your submodule proceed like the following: + +```bash +git submodule update --remote +git commit -s conf.d/app-templates +``` + +This will update the submodule to the HEAD of master branch repository. Save +the modification by commiting it in your master git project. + +### Checkout submodule to a git tag + +You could just want to update at a specified repository tag or branch or commit +, here are the method to do so: + +```bash +cd conf.d/app-templates +# Choose one of the following depending what you want +git checkout <tag_name> +git checkout --detach <branch_name> +git checkout --detach <commit_id> +# Then commit +cd ../.. +git commit -s conf.d/app-templates +``` diff --git a/conf.d/app-templates/docs/dev_guide/3_advanced_usage.md b/conf.d/app-templates/docs/dev_guide/3_advanced_usage.md new file mode 100644 index 0000000..cf85986 --- /dev/null +++ b/conf.d/app-templates/docs/dev_guide/3_advanced_usage.md @@ -0,0 +1,102 @@ +# Build a widget + +## config.xml.in file + +To build a widget you need a _config.xml_ file describing what is your apps and +how Application Framework would launch it. This repo provide a simple default +file _config.xml.in_ that should work for simple application without +interactions with others bindings. + +It is recommanded that you use the sample one which is more complete. You can +find it at the same location under the name _config.xml.in.sample_ (stunning +isn't it). Just copy the sample file to your _conf.d/wgt_ directory and name it +_config.xml.in_, then edit it to fit your needs. + +> ***CAUTION*** : The default file is only meant to be use for a +> simple widget app, more complicated ones which needed to export +> their api, or ship several app in one widget need to use the provided +> _config.xml.in.sample_ which had all new Application Framework +> features explained and examples. + +## Using cmake template macros + +To leverage all cmake templates features, you have to specify ***properties*** +on your targets. Some macros will not works without specifying which is the +target type. + +As the type is not always specified for some custom targets, like an ***HTML5*** +application, macros make the difference using ***LABELS*** property. + +Choose between: + +- **BINDING**: Shared library that be loaded by the AGL Application Framework +- **BINDINGV2**: Shared library that be loaded by the AGL Application Framework. + This has to be accompagnied with a JSON file named like the *${OUTPUT_NAME}-apidef* of + the target that describe the API with OpenAPI syntax (e.g: *mybinding-apidef*). + Or you can choose the name by setting the *CACHE* cmake variable *OPENAPI_DEF* + (***CAUTION***: setting a CACHE variable is needed, or set a normal variable + with the *PARENT_SCOPE* option to make it visible for the parent scope + where the target is defined) JSON file will be used to generate header file + using `afb-genskel` tool. +- **HTDOCS**: Root directory of a web app. This target has to build its + directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} +- **DATA**: Resources used by your application. This target has to build its + directory and puts its files in the ${CMAKE_CURRENT_BINARY_DIR}/${TARGET_NAME} +- **EXECUTABLE**: Entry point of your application executed by the AGL + Application Framework + +Example: + +```cmake +SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES + LABELS "HTDOCS" + OUTPUT_NAME dist.prod + ) +``` + +If your target output is not named as the ***TARGET_NAME***, you need to specify +***OUTPUT_NAME*** property that will be used by the ***populate_widget*** macro. + +Use the ***populate_widget*** macro as latest statement of your target +definition. Then at the end of your project definition you should use the macro +***build_widget*** that make an archive from the populated widget tree using the +`wgtpkg-pack` Application Framework tools. + +## Macro reference + +### PROJECT_TARGET_ADD + +Typical usage would be to add the target to your project using macro +`PROJECT_TARGET_ADD` with the name of your target as parameter. + +Example: + +```cmake +PROJECT_TARGET_ADD(low-can-demo) +``` + +> ***NOTE***: This will make available the variable `${TARGET_NAME}` +> set with the specificied name. This variable will change at the next call +> to this macros. + +### project_subdirs_add + +This macro will search in all subfolder any `CMakeLists.txt` file. If found then +it will be added to your project. This could be use in an hybrid application by +example where the binding lay in a sub directory. + +Usage : + +```cmake +project_subdirs_add() +``` + +You also can specify a globbing pattern as argument to filter which folders +will be looked for. + +To filter all directories that begin with a number followed by a dash the +anything: + +```cmake +project_subdirs_add("[0-9]-*") +``` diff --git a/conf.d/app-templates/docs/dev_guide/4_advanced_customization.md b/conf.d/app-templates/docs/dev_guide/4_advanced_customization.md new file mode 100644 index 0000000..0d8957e --- /dev/null +++ b/conf.d/app-templates/docs/dev_guide/4_advanced_customization.md @@ -0,0 +1,42 @@ +# Advanced customization + +## Including additionnals cmake files + +Advanced tuning is possible using addionnals cmake files that are included +automatically from some specifics locations. They are included in that order: + +- Project CMake files normaly located in _<project-root-path>/conf.d/app-templates/cmake/cmake.d_ +- Home CMake files located in _$HOME/.config/app-templates/cmake.d_ +- System CMake files located in _/etc/app-templates/cmake.d_ + +CMake files has to be named using the following convention: `XX-common-*.cmake` +or `XX-${PROJECT_NAME}-*.cmake`, where `XX` are numbers, `*` file name +(ie. `99-common-my_customs.cmake`). + +> **NOTE** You need to specify after numbers that indicate include order, to +which project that file applies, if it applies to all project then use keyword +`common`. + +So, saying that you should be aware that every normal cmake variables used at +project level could be overwrited by home or system located cmake files if +variables got the same name. Exceptions are cached variables set using +**CACHE** keyword: + +Example: + +```cmake +set(VARIABLE_NAME 'value string random' CACHE STRING 'docstring') +``` + +## Include customs templated scripts + +As well as for additionnals cmake files you can include your own templated +scripts that will be passed to cmake command `configure_file`. + +Just create your own script to the following directories: + +- Home location in _$HOME/.config/app-templates/scripts_ +- System location in _/etc/app-templates/scripts_ + +Scripts only needs to use the extension `.in` to be parsed and configured by +CMake command. diff --git a/conf.d/app-templates/docs/dev_guide/5_autobuild.md b/conf.d/app-templates/docs/dev_guide/5_autobuild.md new file mode 100644 index 0000000..fe1c63d --- /dev/null +++ b/conf.d/app-templates/docs/dev_guide/5_autobuild.md @@ -0,0 +1,41 @@ +# Autobuild script usage + +## Generation + +To be integrated in the Yocto build workflow you have to generate `autobuild` +scripts using _autobuild_ target. + +To generate those scripts proceeds: + +```bash +mkdir -p build +cd build +cmake .. && make autobuild +``` + +You should see _conf.d/autobuild/agl/autobuild_ file now. + +## Available targets + +Here are the available targets available from _autobuild_ scripts: + +- **clean** : clean build directory from object file and targets results. +- **distclean** : delete build directory +- **configure** : generate project Makefile from CMakeLists.txt files. +- **build** : compile all project targets. +- **package** : build and output a wgt package. + +You can specify variables that modify the behavior of compilation using +the following variables: + +- **CONFIGURE_ARGS** : Variable used at **configure** time. +- **BUILD_ARGS** : Variable used at **build** time. +- **DEST** : Directory where to output ***wgt*** file. + +Variable as to be in CMake format. (ie: BUILD_ARGS="-DC_FLAGS='-g -O2'") + +Usage example: + +```bash +./conf.d/autobuild/wgt/autobuild package DEST=/tmp +``` diff --git a/conf.d/app-templates/docs/dev_guide/pictures/iotbzh_logo_small.png b/conf.d/app-templates/docs/dev_guide/pictures/iotbzh_logo_small.png Binary files differnew file mode 100644 index 0000000..6a98c60 --- /dev/null +++ b/conf.d/app-templates/docs/dev_guide/pictures/iotbzh_logo_small.png diff --git a/conf.d/app-templates/docs/resources/cover.svg b/conf.d/app-templates/docs/resources/cover.svg new file mode 100644 index 0000000..6726de7 --- /dev/null +++ b/conf.d/app-templates/docs/resources/cover.svg @@ -0,0 +1,210 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="1800" + height="2360" + viewBox="0 0 1800 2360" + id="svg2" + version="1.1" + inkscape:version="0.91 r13725" + sodipodi:docname="cover.svg"> + <defs + id="defs4175"> + <filter + style="color-interpolation-filters:sRGB" + inkscape:label="Drop Shadow" + id="filter4000"> + <feFlood + result="flood" + flood-color="rgb(0,0,0)" + flood-opacity="0.475" + id="feFlood4002" /> + <feComposite + result="composite1" + operator="in" + in="flood" + in2="SourceGraphic" + id="feComposite4004" /> + <feGaussianBlur + result="blur" + stdDeviation="5" + id="feGaussianBlur4006" /> + <feOffset + result="offset" + dy="8" + dx="8" + id="feOffset4008" /> + <feComposite + result="composite2" + operator="over" + in="SourceGraphic" + in2="offset" + id="feComposite4010" /> + </filter> + <filter + style="color-interpolation-filters:sRGB" + id="filter4000-6" + inkscape:label="Drop Shadow"> + <feFlood + id="feFlood4002-4" + flood-opacity="0.475" + flood-color="rgb(0,0,0)" + result="flood" /> + <feComposite + id="feComposite4004-6" + in2="SourceGraphic" + in="flood" + operator="in" + result="composite1" /> + <feGaussianBlur + id="feGaussianBlur4006-2" + stdDeviation="5" + result="blur" /> + <feOffset + id="feOffset4008-8" + dx="8" + dy="8" + result="offset" /> + <feComposite + id="feComposite4010-9" + in2="offset" + in="SourceGraphic" + operator="over" + result="composite2" /> + </filter> + </defs> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="0.24748737" + inkscape:cx="928.3577" + inkscape:cy="404.58117" + inkscape:document-units="px" + inkscape:current-layer="layer1" + showgrid="false" + inkscape:snap-text-baseline="false" + units="px" + inkscape:window-width="1920" + inkscape:window-height="1171" + inkscape:window-x="1920" + inkscape:window-y="0" + inkscape:window-maximized="1" + showguides="false" /> + <metadata + id="metadata7"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title /> + </cc:Work> + </rdf:RDF> + </metadata> + <g + inkscape:label="Layer 1" + inkscape:groupmode="layer" + id="layer1" + transform="translate(0,1307.6379)"> + <g + id="iotbzh-logo" + transform="matrix(2.3917866,0,0,2.3917866,216.6324,-1946.4393)" + inkscape:export-filename="/home/sdx/Pictures/Logo/logo_iot_bzh_100dpi.png" + inkscape:export-xdpi="100.22011" + inkscape:export-ydpi="100.22011" + style="display:inline;filter:url(#filter4000-6)"> + <text + sodipodi:linespacing="125%" + id="text3557-5-3-7-0-7-3" + y="519.50671" + x="27.886671" + style="font-style:normal;font-weight:normal;font-size:97.09867096px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none" + xml:space="preserve"><tspan + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:97.09867096px;line-height:125%;font-family:FreeEuro;-inkscape-font-specification:'FreeEuro Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" + y="519.50671" + x="27.886671" + id="tspan3559-5-4-1-5-0-6" + sodipodi:role="line">IOT</tspan></text> + <path + inkscape:connector-curvature="0" + d="m 286.73007,473.23356 c 28.21686,16.29102 28.75566,58.73779 0.99693,78.53831 -7.67688,5.47598 -8.77935,4.91028 -1.99529,-1.0238 17.47377,-15.28453 17.98492,-42.17775 1.08522,-57.09786 l -3.91266,-3.45435 0.72312,-3.71053 c 0.39771,-2.04076 0.5997,-5.73115 0.44885,-8.20083 -0.33876,-5.54623 0.15803,-6.49185 2.65383,-5.05094 z m -64.76568,11.40332 c 7.06047,-7.74198 18.64659,-14.16089 29.04027,-16.08874 l 6.87489,-1.27521 0.87404,2.89709 c 0.4807,1.59343 0.67439,5.2245 0.43037,8.06906 l -0.44364,5.17195 -6.13887,1.6918 c -10.91241,3.00731 -20.4022,10.85909 -25.4533,21.05979 l -2.41633,4.87984 -2.74281,-0.41238 c -5.14252,-0.77316 -12.72985,-3.97645 -12.79123,-5.40033 -0.092,-2.13451 8.34659,-15.74625 12.76661,-20.59287 z m 33.20546,36.39493 c -28.21687,16.29101 -65.24624,-4.46574 -68.51461,-38.40577 -0.9039,-9.38637 0.13723,-10.0583 1.88428,-1.21608 4.49989,22.77499 27.53453,36.66428 48.90556,29.48876 l 4.94788,-1.66128 2.85184,2.48149 c 1.56852,1.36481 4.66349,3.38493 6.87772,4.48914 4.97257,2.47973 5.54308,3.38282 3.04733,4.82374 z m 22.50729,-61.79039 c 3.17451,9.98553 2.94038,23.22889 -0.58688,33.19399 l -2.33309,6.59143 -2.94597,-0.69161 c -1.6203,-0.38041 -4.86173,-2.02821 -7.2032,-3.6618 l -4.25721,-2.97018 1.60429,-6.16234 c 2.85178,-10.95404 0.79685,-23.09833 -5.51167,-32.57307 l -3.01788,-4.53253 1.72854,-2.16916 c 3.24083,-4.06698 9.80863,-9.03614 11.07242,-8.37738 1.89457,0.98756 9.46336,15.1015 11.45065,21.35265 z m -48.80223,10.31437 c 0,-32.58201 36.49058,-54.27201 67.51771,-40.1325 8.58077,3.9104 8.6421,5.148 0.11108,2.23988 -21.97368,-7.49048 -45.51946,5.51348 -49.99082,27.6091 l -1.03521,5.11561 -3.57498,1.22902 c -1.96621,0.67596 -5.26316,2.34622 -7.32655,3.71171 -4.63379,3.06649 -5.70115,3.10904 -5.70115,0.22718 z m 42.25842,50.3871 c -10.23499,-2.24356 -21.58699,-9.06801 -28.45341,-17.10525 l -4.5418,-5.31622 2.07194,-2.20549 c 1.13957,-1.21302 4.18733,-3.19628 6.77282,-4.40726 l 4.70085,-2.20176 4.53458,4.47053 c 8.06061,7.94674 19.60535,12.23927 30.96496,11.51329 l 5.43422,-0.34731 1.01427,2.58154 c 1.90169,4.84014 2.92124,13.01261 1.71883,13.77769 -1.80254,1.14695 -17.80995,0.64475 -24.21726,-0.75976 z" + style="display:inline;fill:#5a2ca0" + id="path3415-4-2-2-5-0-3-7-4-4-1-5" /> + <text + sodipodi:linespacing="125%" + id="text3557-5-3-7-46-7-3-7" + y="519.50671" + x="317.95816" + style="font-style:normal;font-weight:normal;font-size:97.09867096px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none" + xml:space="preserve"><tspan + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:97.09867096px;line-height:125%;font-family:FreeEuro;-inkscape-font-specification:'FreeEuro Bold';text-align:start;writing-mode:lr-tb;text-anchor:start" + y="519.50671" + x="317.95816" + id="tspan3559-5-4-1-90-0-2-9" + sodipodi:role="line">BZH</tspan></text> + </g> + <flowRoot + xml:space="preserve" + id="root-title" + transform="matrix(2.3469382,0,0,2.3469382,464.13874,-1200)"><flowRegion + id="flowRegion4303"><rect + id="rect4305" + width="679.99994" + height="141.42853" + x="-154.28572" + y="400"/> + </flowRegion> + <flowPara + id="title" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:87.5px;line-height:125%;font-family:Verdana;-inkscape-font-specification:'Verdana, Bold';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1">{title}</flowPara></flowRoot> <flowRoot + xml:space="preserve" + id="root-subtitle" + transform="matrix(1.8523279,0,0,1.8523279,553.97647,-380)"><flowRegion + id="flowRegion4303-6"><rect + id="rect4305-0" + width="852.32806" + height="154.51677" + x="-239.3591" + y="290"/> + </flowRegion> + <flowPara + id="subtitle" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:62.5px;line-height:125%;font-family:Verdana;-inkscape-font-specification:'Verdana, Bold';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1">{subtitle}</flowPara> + </flowRoot> + <flowRoot + id="root-version" + xml:space="preserve" + transform="translate(-2.0185547,164)"><flowRegion + id="flowRegion4169"><rect + y="501.68909" + x="343.32947" + height="99.591171" + width="1117.3768" + id="rect4171" /></flowRegion><flowPara + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:50px;line-height:125%;font-family:Verdana;-inkscape-font-specification:'Verdana, Bold';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1" + id="version">{version}</flowPara></flowRoot> <flowRoot + transform="translate(-2.0185547,238)" + xml:space="preserve" + id="root-date"><flowRegion + id="flowRegion4170"><rect + id="rect4172" + width="1117.3768" + height="99.591171" + x="343.32947" + y="501.68909" /></flowRegion><flowPara + id="date" + style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:50px;line-height:125%;font-family:Verdana;-inkscape-font-specification:'Verdana, Bold';text-align:center;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1">{date}</flowPara></flowRoot> </g> +</svg> diff --git a/conf.d/app-templates/docs/resources/ebook.css b/conf.d/app-templates/docs/resources/ebook.css new file mode 100644 index 0000000..39f126c --- /dev/null +++ b/conf.d/app-templates/docs/resources/ebook.css @@ -0,0 +1,386 @@ +/* IoT.Bzh theaming */ + +h1 { + color: #330066; + border-bottom: 2px solid #330066; +} + +h2 { + color: #330066; +} + +h3 { + color: #330066; +} + +h4 { + color: #330066; +} + + +/* GENERAL ELEMENTS */ + +/* clear both */ + +.clear { + clear: both; +} + +.section> :last-child { + margin-bottom: 0 !important; +} + +.section> :first-child { + margin-top: 0 !important; +} + + +/* SPECIAL ELEMENTS */ + + +/* page break always after element on pdf/print definition */ + +div.pagebreak { + page-break-after: always; +} + + +/* no page break inside element on pdf/print definition */ + +div.nopb { + page-break-inside: avoid !important; + margin: 4px 0 4px 0; +} + + +/* note blocks */ + +div.note { + background: #FCF8E3 none repeat scroll 0% 0%; + color: #8A6D3B; + padding: 15px; + margin-bottom: 10px; + border-bottom: 5px solid #DDD; + border-color: #FAEBCC; + page-break-inside: avoid; +} + +div.note p { + padding-bottom: 0; + margin-bottom: 0; +} + + +/* images, figures and captions */ + +p img { + /* center all images */ + display: block; + margin: 0 auto; + padding: 10px 0; +} + +figure { + margin: 1.0em 0px; + padding: 10px 0; + text-align: center; + page-break-inside: avoid; + display: block; +} + +figure img { + display: block; + margin: 0 auto; +} + +figcaption { + clear: left; + margin: 1.0em 0 0 0; + text-align: center; + font-style: italic; + line-height: 1.5em; + font-size: 80%; + color: #666; + display: block; +} + +.page .section p img { + margin-top: 10px; +} + + +/* ul, ol list margin fix */ + +.page .section ol, +.page .section ul { + margin-bottom: 10px; +} + + +/* blockquotes */ + +.page .section blockquote { + margin: 0 0 0 5%; + font-style: italic; +} + + +/* PAGE SPECIFIC */ + + +/* set summary page to right side of the paper */ + +.page .toc h1 { + page-break-before: right; +} + +.page .section.toc { + page-break-inside: always; +} + +/* table headers */ + +div#README\.md table { + margin-top: 30px; + font-size: 95%; +} + +div#README\.md table thead { + display: none; +} + + + +/* CITATION AND IMAGES */ + + +/* math image styles */ + +.page .section p img.svg, +.page .section p img.png { + margin-top: 0px; + margin-bottom: -2px; +} + +.page .section p img.math { + vertical-align: middle; + height: auto; + width: auto; + margin-top: -4px; + max-height: 15px; +} + +.page .section p img.math.line1 { + margin-top: -7px; + max-height: 19px; +} + +.page .section p img.math.line2 { + margin-top: -1px; + max-height: 30px; +} + + +/* credits page */ + +.page .section ul.pictures { + margin-left: -30px; +} + +.page .section ul.pictures li { + list-style: outside none none; +} + +.page .section ul.pictures li a { + float: left; +} + +.page .section ul.pictures li span { + display: block; + margin-left: 100px; +} + + + +/* sub and super script */ + +.page .section sub { + font-size: 80%; + margin-left: 1px; +} + + +/* citations and references */ + +.page .section sup { + margin-left: -1px; + margin-right: 2px; + font-size: 80%; +} + +.page .section sup:before { + content: " "; +} + +.page .section ul.citations, +.page .section ul.references { + margin-left: -30px; +} + + +.page .section ul.citations li:nth-child(1) { + margin-top: 20px; + padding-top: 20px; + border-top: 1px solid #BBB; +} + +.page .section ul.citations li, +.page .section ul.references li { + list-style: outside none none; +} + +.page .section ul.citations li { + font-size: 80%; +} + +.page .section ul.citations li>span:nth-child(1), +.page .section ul.references li>span:nth-child(1) { + display: block; + float: left; + text-align: left; + width: 70px +} + +.page .section ul.citations li>span:nth-child(1) { + width: 50px +} + +.page .section ul.references li div { + margin-left: 70px; +} + +.page .section ul.citations li div { + margin-left: 50px; +} + +.page .section a[href="#"], +.page .section a[href="#"]:link, +.page .section a[href="#"]:visited, +.page .section a[href="#"]:hover, +.page .section a[href="#"]:focus { + text-decoration: none; + color: inherit; + cursor: text; + font-style: italic; +} + + +/* self referential footnotes */ + +.page .section div[type="selfref"] a[href="#"], +.page .section div[type="selfref"] a[href="#"]:link, +.page .section div[type="selfref"] a[href="#"]:visited, +.page .section div[type="selfref"] a[href="#"]:hover, +.page .section div[type="selfref"] a[href="#"]:focus { + font-style: normal; +} + +.page .section div[type="selfref"] span:nth-child(1) { + display: none; +} + + +/* page break always after element on pdf/print definition */ + +div.page-break { + page-break-inside: always; +} + +div.page-break:before { + content: ' '; +} + + +/* no page break inside element on pdf/print definition */ + +div.nopb { + page-break-inside: avoid; +} + +/* justify text */ +p { + text-align: justify; +} + +/* page header and footer */ + +.pdf-footer, +.pdf-header { + margin-top: 20px; + color: #aaa; +} + +.pdf-header .header-left { + float: left; + margin-left: 2em; + margin-right: auto; +} + +.pdf-header .header-right { + display: table; + margin-left: auto; + margin-right: 2em; +} + +.pdf-footer .sub { + padding-top: 8px; + font-size: 70%; +} + +.pdf-header .sub { + padding-top: 2px; + font-size: 70%; +} + +.pdf-footer { + padding-top: 10px; + border-top: 1px solid #eee; +} + +.pdf-footer .footer-left { + float: left; + margin-left: 2em; + margin-right: auto; +} + +.pdf-footer .footer-center { + display: table; + margin-left: auto; + margin-right: auto; +} + +.pdf-footer .footer-right { + float: right; + margin-left: auto; + margin-right: 2em; +} + +.pdf-header { + padding-bottom: 10px; + border-bottom: 1px solid #eee; +} + +.pdf-header .header-pages-count { + float: right; + text-align: right; +} + +.pdf-header .header-pages-count a, +.pdf-header .header-pages-count a:visited, +.pdf-header .header-pages-count a:active, +.pdf-header .header-pages-count a:focus, +.pdf-header .header-pages-count a:link { + text-decoration: none; + color: #aaa; + cursor: text; +} diff --git a/conf.d/app-templates/docs/resources/make_cover.sh b/conf.d/app-templates/docs/resources/make_cover.sh new file mode 100755 index 0000000..1026ecb --- /dev/null +++ b/conf.d/app-templates/docs/resources/make_cover.sh @@ -0,0 +1,27 @@ +#!/bin/bash +DOCS_DIR=$(cd $(dirname $0)/.. && pwd) +BOOKFILE=$DOCS_DIR/../book.json + +TITLE=$(grep '"title":' $BOOKFILE | cut -d'"' -f 4) +SUBTITLE=$(grep '"subtitle":' $BOOKFILE | cut -d'"' -f 4) +VERSION="Version $(grep '"version":' $BOOKFILE | cut -d'"' -f 4)" +DATE=$(grep '"published":' $BOOKFILE | cut -d'"' -f 4) + +[ -z "$TITLE" ] && { echo "Error TITLE not set!" ; exit 1; } +[ -z "$VERSION" ] && { echo "Error VERSION not set!" ; exit 1; } +[ -z "$DATE" ] && { echo "Error DATE not set!" ; exit 1; } + + +cat $(dirname $0)/cover.svg | sed -e "s/{title}/$TITLE/g" \ + -e "s/font-size:87.5px/font-size:54px/g" \ + -e "s/{subtitle}/$SUBTITLE/g" \ + -e "s/font-size:62.5px/font-size:40px/g" \ + -e "s/{version}/$VERSION/g" \ + -e "s/{date}/$DATE/g" \ + > /tmp/cover.svg + +# use imagemagick convert tool (cover size must be 1800x2360) +convert -resize "1600x2160!" -border 100 -bordercolor white -background white \ + -flatten -quality 100 /tmp/cover.svg $DOCS_DIR/cover.jpg + +convert -resize "200x262!" $DOCS_DIR/cover.jpg $DOCS_DIR/cover_small.jpg diff --git a/conf.d/app-templates/gendocs.sh b/conf.d/app-templates/gendocs.sh new file mode 100755 index 0000000..ea9400a --- /dev/null +++ b/conf.d/app-templates/gendocs.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +OUTFILENAME="Gitbook-Iotbzh-template" + +SCRIPT=$(basename $BASH_SOURCE) + +function usage() { + cat <<EOF >&2 +Usage: $SCRIPT [options] [pdf|serve|doxygen] + +Options: + --debug + enable debug when generating pdf or html documentation + -d|--dry + dry run + -h|--help + get this help + +Example: + $SCRIPT pdf + +EOF + exit 1 +} + +function info() { + echo "$@" >&2 +} + +#default values +DEBUG_FLAG="" +DRY="" +DO_ACTION="" +OUT_DIR=./build + +[[ $? != 0 ]] && usage +while [ $# -gt 0 ]; do + case "$1" in + --debug) DEBUG_FLAG="--log=debug --debug";; + -d|--dry) DRY=echo;; + -h|--help) usage;; + pdf | serve | doxygen) DO_ACTION=$1;; + --) break;; + esac + shift +done + +cd $(dirname $0) +ROOTDIR=`pwd -P` + +# Create out dir if needed +[ -d $OUT_DIR ] || mkdir -p $OUT_DIR + +if [ "$DO_ACTION" = "pdf" -o "$DO_ACTION" = "serve" ]; then + GITBOOK=`which gitbook` + [ "$?" = "1" ] && { echo "You must install gitbook first, using: sudo npm install -g gitbook-cli"; exit 1; } + + EBCONV=`which ebook-convert` + [ "$?" = "1" ] && { echo "You must install calibre first, using: 'sudo apt install calibre' or refer to https://calibre-ebook.com/download"; exit 1; } + + if [ "$DO_ACTION" = "pdf" ]; then + + # Update cover when book.json has been changed + [[ $ROOTDIR/book.json -nt $ROOTDIR/docs/cover.jpg ]] && { echo "Update cover files"; $ROOTDIR/docs/resources/make_cover.sh || exit 1; } + + OUTFILE=$OUT_DIR/$OUTFILENAME.pdf + $DRY $GITBOOK pdf $ROOTDIR $OUTFILE $DEBUG_FLAG + [ "$?" = "0" ] && echo "PDF has been successfully generated in $OUTFILE" + else + $DRY $GITBOOK serve $DEBUG_FLAG + fi + +elif [ "$DO_ACTION" = "doxygen" ]; then + $DRY cd $OUT_DIR && cmake .. && make doxygen $ROOTDIR/Doxyfile + +else + echo "Unknown action !" + usage +fi diff --git a/conf.d/app-templates/samples.d/CMakeLists.txt.sample b/conf.d/app-templates/samples.d/CMakeLists.txt.sample new file mode 100644 index 0000000..b485097 --- /dev/null +++ b/conf.d/app-templates/samples.d/CMakeLists.txt.sample @@ -0,0 +1,21 @@ +########################################################################### +# Copyright 2015, 2016, 2017 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. +########################################################################### + +CMAKE_MINIMUM_REQUIRED(VERSION 3.3) + +include(${CMAKE_CURRENT_SOURCE_DIR}/conf.d/cmake/config.cmake) diff --git a/conf.d/app-templates/samples.d/config.cmake.sample b/conf.d/app-templates/samples.d/config.cmake.sample new file mode 100644 index 0000000..393c893 --- /dev/null +++ b/conf.d/app-templates/samples.d/config.cmake.sample @@ -0,0 +1,203 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: Fulup Ar Foll <fulup@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 example) +set(PROJECT_VERSION "0.0") +set(PROJECT_PRETTY_NAME "Example") +set(PROJECT_DESCRIPTION "AGL application example") +set(PROJECT_URL "https://gerrit.automotivelinux.org/gerrit/apps/app-templates") +set(PROJECT_ICON "icon.png") +set(PROJECT_AUTHOR "Last Name, First Name") +set(PROJECT_AUTHOR_MAIL "example.man@bigouden.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") + +# Where are stored data for your application. Pictures, static resources must be placed in that folder. +# set(PROJECT_RESOURCES "data") + +# Which directories inspect to find CMakeLists.txt target files +# set(PROJECT_SRC_DIR_PATTERN "*") + +# Compilation Mode (DEBUG, RELEASE) +# ---------------------------------- +set(CMAKE_BUILD_TYPE "DEBUG") + +# 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 + libmicrohttpd>=0.9.55 +) + +# 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.") + +# Print a helper message when every thing is finished +# ---------------------------------------------------- +#set(CLOSING_MESSAGE "") +#set(PACKAGE_MESSAGE "Install widget file using in the target : afm-util install ${PROJECT_NAME}.wgt") + +# Prefix path where will be installed the files +# Default: /usr/local (need root permission to write in) +# ------------------------------------------------------ +#set(CMAKE_INSTALL_PREFIX $ENV{HOME}/opt) + +# (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 MimeType_Not_Set) + +# 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 EntryPoint_Path_Not_Set) + +# 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 AFB_TOKEN") +#set(AFB_REMPORT "1234" CACHE PATH "Default AFB_TOKEN") + +# Optional schema validator about now only XML, LUA and JSON +# are supported +#------------------------------------------------------------ +#set(LUA_CHECKER "luac -o /dev/null" 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/app-templates/samples.d/config.xml.in.sample b/conf.d/app-templates/samples.d/config.xml.in.sample new file mode 100644 index 0000000..b93f3d9 --- /dev/null +++ b/conf.d/app-templates/samples.d/config.xml.in.sample @@ -0,0 +1,142 @@ +<?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>@PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@></author> + <license>@PROJECT_LICENSE@</license> + +<!-- Feature : required-api ------------------------------------------------------ +#### param name="#target" OPTIONAL + +Declares the name of the unit requiring the listed apis. +Only one instance of the param "#target" is allowed. +When there is not instance of this param, it behave as if +the target main was specified. + +#### param name=[required api name] + +The name is the name of the required API. +The value describes how to connect to the required api. +It is either: + + - local: The binding is a local shared object. + In that case, the name is the relative path of the + shared object to be loaded. + - auto: The framework set automatically the kind of + the connection to the API + - ws: The framework connect using internal websockets + - dbus: The framework connect using internal dbus + - link: The framework connect in memory by dinamically linking + +Example: + <feature name="urn:AGL:widget:required-api"> + <param name="#target" value="low-can" /> + <param name="<a-required-api>" value="auto" /> + <param name="<another-required-api>" value="auto" /> + </feature> +---------------------------------------------------------------------------------> + +<!-- Feature : required-permission ------------------------------------------ + +List of the permissions required by the unit. +Each required permission must be explicited using a <param> entry. + +#### param name="#target" OPTIONAL + +Declares the name of the unit requiring the listed permissions. +Only one instance of the param "#target" is allowed. +When there is not instance of this param, it behave as if +the target main was specified. + +#### param name=[required permission name] + +The value is either: +- required: the permission is mandatorily needed except if the feature +isn't required (required="false") and in that case it is optional. +- optional: the permission is optional + +Example: +<feature name="urn:AGL:widget:required-permission"> + <param name="#target" value="geoloc" /> + <param name="urn:AGL:permission:real-time" value="required" /> + <param name="urn:AGL:permission:syscall:*" value="required" /> +</feature> +---------------------------------------------------------------------------------> + +<!-- Feature : provided-unit ------------------------------------------------- +This feature is made for declaring new units +for the widget. Using this feature, a software publisher +can provide more than one application in the same widget. + +#### param name="#target" REQUIRED + +Declares the name of the unit. The default unit, the unit +of the main of the widget, has the name "main". The value +given here must be unique within the widget file. It will +be used in other places of the widget config.xml file to +designate the unit. + +Only one instance of the param "#target" is allowed. +The value can't be "main". + +#### param name="content.type" REQUIRED + +The mimetype of the provided unit. + +#### param name="content.src" + +A path to the file (subject to localisation), this is the entry point +to that unit. + +#### other parameters + +The items that can be set for the main unit +can also be set using the params if needed. + + - description + - name.content + - name.short + - ... + +Example: +<feature name="urn:AGL:widget:provided-unit"> + <param name="#target" value="geoloc" /> + <param name="description" value="binding of name geoloc" /> + <param name="content.src" value="index.html" /> + <param name="content.type" value="application/vnd.agl.service" /> +</feature> +---------------------------------------------------------------------------------> + +<!-- Feature: provided-api --------------------------------------------------- +Use this feature for exporting one or more API of a unit +to other widgets of the platform. + +This feature is an important feature of the framework. + +#### param name="#target" OPTIONAL + +Declares the name of the unit exporting the listed apis. +Only one instance of the param "#target" is allowed. +When there is not instance of this param, it behave as if +the target main was specified. + +#### param name=[name of exported api] + +The name give the name of the api that is exported. + +The value is one of the following values: + +- ws: export the api using UNIX websocket +- dbus: export the API using dbus +- auto: export the api using the default method(s). + +Example: +<feature name="urn:AGL:widget:provided-api"> + <param name="#target" value="geoloc" /> + <param name="geoloc" value="auto" /> + <param name="moonloc" value="auto" /> +</feature> +---------------------------------------------------------------------------------> +</widget> diff --git a/conf.d/app-templates/samples.d/xds-config.env.sample b/conf.d/app-templates/samples.d/xds-config.env.sample new file mode 100644 index 0000000..a4c51b1 --- /dev/null +++ b/conf.d/app-templates/samples.d/xds-config.env.sample @@ -0,0 +1,10 @@ +XDS_PROJECT_ID=W2EAQBA-HQI75XA_unicens2-binding +XDS_SDK_ID=poky-agl_aarch64_3.99.1+snapshot +XDS_SERVER_URL=localhost:8000 + +DOCKER_TARGET=docker-sdk + +RSYNC_TARGET=root@192.168.168.11 +RSYNC_PREFIX=./opt +PROJECT_DIR=/home/seb/tmp/unicens2-binding + diff --git a/conf.d/app-templates/template.d/autobuild/agl/autobuild.in b/conf.d/app-templates/template.d/autobuild/agl/autobuild.in new file mode 100755 index 0000000..4811441 --- /dev/null +++ b/conf.d/app-templates/template.d/autobuild/agl/autobuild.in @@ -0,0 +1,63 @@ +#!/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" + @echo "- package" + @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} + +${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/app-templates/template.d/autobuild/linux/autobuild.in b/conf.d/app-templates/template.d/autobuild/linux/autobuild.in new file mode 100755 index 0000000..0b6f06c --- /dev/null +++ b/conf.d/app-templates/template.d/autobuild/linux/autobuild.in @@ -0,0 +1,65 @@ +#!/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" + @echo "- package" + @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 packaging + @cmake --build ${BUILD_DIR} --target widget + @mkdir -p ${DEST} && cp ${BUILD_DIR}/*wgt ${DEST} + + +${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/app-templates/template.d/config.xml.in b/conf.d/app-templates/template.d/config.xml.in new file mode 100644 index 0000000..35d20b8 --- /dev/null +++ b/conf.d/app-templates/template.d/config.xml.in @@ -0,0 +1,9 @@ +<?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>@PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@></author> + <license>@PROJECT_LICENSE@</license> +</widget> diff --git a/conf.d/app-templates/template.d/deb-config.dsc.in b/conf.d/app-templates/template.d/deb-config.dsc.in new file mode 100644 index 0000000..28a4835 --- /dev/null +++ b/conf.d/app-templates/template.d/deb-config.dsc.in @@ -0,0 +1,15 @@ +Format: @PROJECT_VERSION@ +Source: @NPKG_PROJECT_NAME@ +Binary: @NPKG_PROJECT_NAME@-bin +Architecture: any +Version: 2.0-0 +Maintainer: @PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@> +Standards-Version: 3.8.2 +Homepage: @PROJECT_URL@ +Build-Depends: debhelper (>= 5), + pkg-config, + cmake, + gcc, + g++, +@DEB_PKG_DEPS@ +Debtransform-Tar: @NPKG_PROJECT_NAME@_@PROJECT_VERSION@.orig.tar.gz diff --git a/conf.d/app-templates/template.d/deb-config.install.in b/conf.d/app-templates/template.d/deb-config.install.in new file mode 100644 index 0000000..5858efd --- /dev/null +++ b/conf.d/app-templates/template.d/deb-config.install.in @@ -0,0 +1,2 @@ +/opt/AGL/* +/etc/profile.d/* diff --git a/conf.d/app-templates/template.d/debian.changelog.in b/conf.d/app-templates/template.d/debian.changelog.in new file mode 100644 index 0000000..f72c717 --- /dev/null +++ b/conf.d/app-templates/template.d/debian.changelog.in @@ -0,0 +1,5 @@ +@NPKG_PROJECT_NAME@ (@PROJECT_VERSION@-0) UNRELEASED; urgency=low + + * init build + + -- @PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@> Mon, 25 Dec 2007 10:50:38 +0100 diff --git a/conf.d/app-templates/template.d/debian.compat.in b/conf.d/app-templates/template.d/debian.compat.in new file mode 100644 index 0000000..45a4fb7 --- /dev/null +++ b/conf.d/app-templates/template.d/debian.compat.in @@ -0,0 +1 @@ +8 diff --git a/conf.d/app-templates/template.d/debian.control.in b/conf.d/app-templates/template.d/debian.control.in new file mode 100644 index 0000000..ea4ad8a --- /dev/null +++ b/conf.d/app-templates/template.d/debian.control.in @@ -0,0 +1,16 @@ +Priority: optional +Maintainer: @PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@> +Source: @NPKG_PROJECT_NAME@ +Build-Depends: debhelper (>= 5), + pkg-config, + cmake, + gcc, + g++, +@DEB_PKG_DEPS@ +Standards-Version: 3.8.2 +Homepage: @PROJECT_URL@ + +Package: @NPKG_PROJECT_NAME@ +Section: libs +Architecture: any +Description: @PROJECT_DESCRIPTION@ diff --git a/conf.d/app-templates/template.d/debian.rules.in b/conf.d/app-templates/template.d/debian.rules.in new file mode 100644 index 0000000..6bb2825 --- /dev/null +++ b/conf.d/app-templates/template.d/debian.rules.in @@ -0,0 +1,87 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Uncomment this to turn on verbose mode. +export DH_VERBOSE=1 + +# These are used for cross-compiling and for saving the configure script +# from having to guess our platform (since we know it already) +DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE) +DEB_BUILD_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_BUILD_GNU_TYPE) + + +CFLAGS = -Wall -g + +ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) + CFLAGS += -O0 +else + CFLAGS += -O2 +endif +ifeq (,$(findstring nostrip,$(DEB_BUILD_OPTIONS))) + INSTALL_PROGRAM += -s +endif + +configure: configure-stamp +configure-stamp: + dh_testdir + # + touch configure-stamp + +build: build-stamp +build-stamp: configure-stamp + dh_testdir + mkdir -p build + cd build;cmake ../ -DCMAKE_INSTALL_PREFIX:PATH=/opt/AGL/@PROJECT_NAME@ -DCMAKE_INSTALL_LIBDIR:PATH=lib/$(DEB_HOST_MULTIARCH);$(MAKE) + # + touch build-stamp + +clean: + #dh_testdir + dh_testroot + rm -f configure-stamp build-stamp + [ ! -f Makefile ] || $(MAKE) distclean + #dh_clean + +install: build + dh_testdir + dh_testroot + dh_prep + dh_installdirs + # Add here commands to install the package into debian/tmp + mkdir -p debian/tmp/opt/AGL/@PROJECT_NAME@;cd build;make populate;cp -r package/* ../debian/tmp/opt/AGL/@PROJECT_NAME@/ + mkdir -p debian/tmp/etc/profile.d + echo '#---------- AGL @PROJECT_NAME@ options Start ---------" ' > debian/tmp/etc/profile.d/AGL_@PROJECT_NAME@.sh + echo '# Object: AGL cmake option for binder/bindings' >> debian/tmp/etc/profile.d/AGL_@PROJECT_NAME@.sh + echo 'export LD_LIBRARY_PATH=/opt/AGL/@PROJECT_NAME@/lib/$(DEB_HOST_MULTIARCH):$$LD_LIBRARY_PATH' >> debian/tmp/etc/profile.d/AGL_@PROJECT_NAME@.sh + echo 'export LIBRARY_PATH=/opt/AGL/@PROJECT_NAME@/lib/$(DEB_HOST_MULTIARCH):$$LIBRARY_PATH' >> debian/tmp/etc/profile.d/AGL_@PROJECT_NAME@.sh + echo 'export PATH=/opt/AGL/@PROJECT_NAME@/bin:$$PATH' >> debian/tmp/etc/profile.d/AGL_@PROJECT_NAME@.sh + echo '#---------- AGL options End ---------' >> debian/tmp/etc/profile.d/AGL_@PROJECT_NAME@.sh + # Move all files in their corresponding package + dh_install --list-missing -s --sourcedir=debian/tmp + # empty dependency_libs in .la files + #sed -i "/dependency_libs/ s/'.*'/''/" `find debian/ -name '*.la'` + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_installexamples + dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms + dh_makeshlibs -V + dh_installdeb + dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install diff --git a/conf.d/app-templates/template.d/gdb-native-target.ini.in b/conf.d/app-templates/template.d/gdb-native-target.ini.in new file mode 100644 index 0000000..1ba6bdb --- /dev/null +++ b/conf.d/app-templates/template.d/gdb-native-target.ini.in @@ -0,0 +1,35 @@ +# gdb-remote.init file for IDE +# Object: allow to use standard gdb to remote debug a target +# Usage: remote-target-populate update script under ./build directory +# Author: Fulup Ar Foll (IoT.bzh) +# Reference: https://blog.flameeyes.eu/2010/02/remote-debugging-with-gdb-part-2-gdb/ +# +# Warning: In remote native mode netbeans impose debug-command to point on a local instance of afb-daemon binary + +# Fulup Hoops: --ldpath should be absolute as solib-search-path refuse to work as documented + +# Start gdbserver on target and connect through SSH link WARNING: +target remote | ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null @RSYNC_TARGET@ gdbserver - afb-daemon --port=@AFB_REMPORT@ --workdir=$HOME/@RSYNC_PREFIX@/@PROJECT_NAME@ --roothttp=htdocs --ldpaths=$HOME/@RSYNC_PREFIX@/@PROJECT_NAME@/lib --verbose --token=@AFB_TOKEN@ + +# Disable auto answer no on questions and to set breakpoint +set confirm off + +# Disable auto load of libraries to improved perf +set auto-solib-add off + +# Define path for project libraries +set solib-search-path ${PROJECT_PKG_BUILD_DIR} + +# Replace run by continue (gdb use 'run' when gdbserver wants 'continue') +define run +continue +end + +# Manually load project libraries when loaded by afb-daemon +tbreak @GDB_INITIAL_BREAK@ +commands +sharedlibrary @RSYNC_PREFIX@/@PROJECT_NAME@ +continue +end + + diff --git a/conf.d/app-templates/template.d/install-wgt-on-target.sh.in b/conf.d/app-templates/template.d/install-wgt-on-target.sh.in new file mode 100755 index 0000000..39a69d5 --- /dev/null +++ b/conf.d/app-templates/template.d/install-wgt-on-target.sh.in @@ -0,0 +1,19 @@ +#!/bin/sh +# +# File: install-wgt-on-target.sh +# Author: Sebastien Douheret @ IoT.bzh +# Object: install widget on target +# Created on 24-May-2017, 09:23:37 +# Usage: + +# Do not change manually use 'make remote-target-populate' +export RSYNC_TARGET=@RSYNC_TARGET@ +export WGT_FILE_L=@CMAKE_CURRENT_BINARY_DIR@/@PROJECT_NAME@.wgt +export WGT_FILE_T=/tmp/@PROJECT_NAME@.wgt + +scp $WGT_FILE_L $RSYNC_TARGET:$WGT_FILE_T \ + && ssh -o "StrictHostKeyChecking no" -tt $RSYNC_TARGET -- \ + afm-util install $WGT_FILE_T + +# && rm -f $WGT_FILE_T + diff --git a/conf.d/app-templates/template.d/rpm-config.spec.in b/conf.d/app-templates/template.d/rpm-config.spec.in new file mode 100644 index 0000000..34a605d --- /dev/null +++ b/conf.d/app-templates/template.d/rpm-config.spec.in @@ -0,0 +1,62 @@ +########################################################################### +# Copyright 2015, 2016, 2017 IoT.bzh +# +# author: @PROJECT_AUTHOR@ <@PROJECT_AUTHOR_MAIL@> +# +# 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. +########################################################################### + + +Name: @NPKG_PROJECT_NAME@ +Version: @PROJECT_VERSION@ +Release: 1 +Group: AGL +License: @PROJECT_LICENSE@ +Summary: @PROJECT_DESCRIPTION@ +Url: @PROJECT_URL@ +Source0: %{name}_%{version}.orig.tar.gz + +BuildRequires: cmake +BuildRequires: gcc gcc-c++ +@RPM_PKG_DEPS@ + +BuildRoot: %{_tmppath}/%{name}-%{version}-build + +%define _prefix /opt/AGL/@PROJECT_NAME@ +%define __cmake cmake + +%description +@PROJECT_DESCRIPTION@ + +%prep +%setup -q + +%build +%cmake -DCMAKE_INSTALL_PREFIX:PATH=%{_libdir} +make %{?_smp_mflags} + +%install +CURDIR=$(pwd) +[ -d build ] && cd build +make populate +mkdir -p %{?buildroot}%{_prefix} +cp -r package/* %{?buildroot}%{_prefix} + +cd $CURDIR +find %{?buildroot}%{_prefix} -type d -exec echo "%dir {}" \;>> pkg_file +find %{?buildroot}%{_prefix} -type f -exec echo "{}" \;>> pkg_file +sed -i 's@%{?buildroot}@@g' pkg_file + + +%files -f pkg_file +%defattr(-,root,root) diff --git a/conf.d/app-templates/template.d/start-on-target.sh.in b/conf.d/app-templates/template.d/start-on-target.sh.in new file mode 100755 index 0000000..d5426ed --- /dev/null +++ b/conf.d/app-templates/template.d/start-on-target.sh.in @@ -0,0 +1,25 @@ +#!/bin/sh +# +# File: start-on-target.sh +# Author: Fulup Ar Foll @ IoT.bzh +# Object: Forward signal (SIGTERM) to remote process +# Created on 24-May-2017, 09:23:37 +# Usage: remote-target-populate update script under ./build directory + +# Do not change manually use 'make remote-target-populate' +export RSYNC_TARGET=@RSYNC_TARGET@ +export PROJECT_NAME=@PROJECT_NAME@ +export RSYNC_PREFIX=@RSYNC_PREFIX@/@PROJECT_NAME@ +export AFB_REMPORT=@AFB_REMPORT@ +export AFB_TOKEN=@AFB_TOKEN@ + +exec ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -tt $RSYNC_TARGET << EOF + afb-daemon --workdir=$RSYNC_PREFIX --port=$AFB_REMPORT --roothttp=./htdocs --ldpath=./lib --verbose --token=$AFB_TOKEN & + PID_DAEMON=\$! + + trap "echo REMOTE-SIGNAL TRAP; kill -15 \$PID_DAEMON" INT QUIT TERM EXIT + + # wait for daemon to finish + wait \$PID_DAEMON + exit +EOF diff --git a/conf.d/app-templates/wgt/icon-default.png b/conf.d/app-templates/wgt/icon-default.png Binary files differnew file mode 100644 index 0000000..def888b --- /dev/null +++ b/conf.d/app-templates/wgt/icon-default.png diff --git a/conf.d/app-templates/wgt/icon-html5.png b/conf.d/app-templates/wgt/icon-html5.png Binary files differnew file mode 100644 index 0000000..a3573ef --- /dev/null +++ b/conf.d/app-templates/wgt/icon-html5.png diff --git a/conf.d/app-templates/wgt/icon-native.png b/conf.d/app-templates/wgt/icon-native.png Binary files differnew file mode 100644 index 0000000..fcb5d35 --- /dev/null +++ b/conf.d/app-templates/wgt/icon-native.png diff --git a/conf.d/app-templates/wgt/icon-qml.png b/conf.d/app-templates/wgt/icon-qml.png Binary files differnew file mode 100644 index 0000000..ba248ea --- /dev/null +++ b/conf.d/app-templates/wgt/icon-qml.png diff --git a/conf.d/app-templates/wgt/icon-service.png b/conf.d/app-templates/wgt/icon-service.png Binary files differnew file mode 100644 index 0000000..3aeb6f0 --- /dev/null +++ b/conf.d/app-templates/wgt/icon-service.png diff --git a/conf.d/project/json.d/ahl-binding.json b/conf.d/project/json.d/ahl-binding.json new file mode 100644 index 0000000..d53a9aa --- /dev/null +++ b/conf.d/project/json.d/ahl-binding.json @@ -0,0 +1,127 @@ +{ + "description" : "Audio Plugin configuration files", + "Audioroles" : { + "Description" : "Specify configuration and default value for Audio", + "Warning" :{ + "Attributes" :{ + "Interruptible" : "No", + "Routing" : "None" + }, + "devices" :{ + "output" : ["Speaker", "Lineout", "HDMI", "Default"] + } + }, + "Guidance" : { + "Attributes" :{ + "Interruptible" : "Yes", + "Routing" : "None" + }, + "devices" :{ + "output" : ["Speaker", "Lineout", "Default"] + } + }, + "Notification" : { + "Attributes" :{ + "Interruptible" : "Yes", + "Routing" : "None" + }, + "devices" :{ + "output" : ["Speaker", "Lineout", "HDMI", "Default"] + } + }, + "Communications" :{ + "Attributes" :{ + "Interruptible" : "Yes", + "Routing" : "Custom" + }, + "devices" :{ + "output" : [ "Speaker", "Lineout", "Default"], + "input" : [ "BT", "USB", "Headset", "Linein", "Default"] + }, + "Routing" :{ + "phone":{ + "Input" : "BT", + "Output" : "Speaker" + }, + "usb":{ + "Input" : "USB", + "Output" : "Speaker" + }, + "headset":{ + "Input" : "Headset", + "Output" : "Speaker" + }, + "phone_aux":{ + "Input" : "BT", + "Output" : "Lineout" + }, + "usb_aux":{ + "Input" : "USB", + "Output" : "Lineout" + } + + } + + + }, + "Entertainment" : { + "Attributes" :{ + "Interruptible" : "Yes", + "Routing" : "Custom" + }, + "devices" :{ + "output" : [ "Headphone", "Speaker", "Lineout", "Default"], + "input" : [ "BT", "USB", "Linein", "Default"] + }, + "Routing" : { + "phone":{ + "Input" : "BT", + "Output" : "Speaker" + }, + "phone_headphone":{ + "Input" : "BT", + "Output" : "Headphone" + }, + "phone_aux":{ + "Input" : "BT", + "Output" : "Lineout" + }, + "usb":{ + "Input" : "USB", + "Output" : "Speaker" + }, + "usb_headphone":{ + "Input" : "USB", + "Output" : "Headphone" + }, + "usb_aux":{ + "Input" : "USB", + "Output" : "Lineout" + }, + "aux":{ + "Input" : "Linein", + "Output" : "Speaker" + }, + "aux_headphone":{ + "Input" : "Linein", + "Output" : "Headphone" + }, + "aux_aux":{ + "Input" : "Linein", + "Output" : "Lineout" + } + + } + }, + "System" : { + "Attributes" :{ + "Interruptible" : "Yes", + "Routing" : "All" + }, + "devices" :{ + "output" : [ "Speaker", "Headset", "Headphone", "Handset", "BT", "HDMI", "Lineout", "USB", "Default"], + "input" : [ "BT", "Linein", "USB", "Voice", "Default"] + } + } + } +}
\ No newline at end of file diff --git a/conf.d/project/json.d/onload-audio-control.json b/conf.d/project/json.d/onload-audio-control.json new file mode 100644 index 0000000..14d6970 --- /dev/null +++ b/conf.d/project/json.d/onload-audio-control.json @@ -0,0 +1,137 @@ +{ + "$schema": "ToBeDone", + "metadata": { + "label": "audio-policy-control-configuration", + "info": "Provide Default Audio Policy Control configuration", + "version": "1.0" + }, + "onload": [{ + "label": "onload-default", + "info": "onload initialisation config", + "plugin": { + "label" : "_MyPlug", + "sharelib": "ctl-audio-plugin-sample.ctlso", + "lua2c": ["Lua2cHelloWorld1", "Lua2cHelloWorld2"] + }, + "require": ["intel-hda"], + "actions": [ + { + "label": "onload-sample-cb", + "info": "Call control sharelib install entrypoint", + "callback": "SamplePolicyInit", + "args": { + "arg1": "first_arg", + "nextarg": "second arg value" + } + }, { + "label": "onload-sample-api", + "info": "Assert AlsaCore Presence", + "api": "alsacore", + "verb": "ping", + "args": {"data": "none"} + }, { + "label": "onload-hal-lua", + "info": "Load avaliable HALs", + "lua": "_Alsa_Get_Hal" + } + ] + }], + "controls": + [ + { + "label": "duckentertainment_lowprioritypriority", + "api": "intel-hda", + "verb": "SetRTPC", + "args": { + "value": "-30" + } + }, + + { + "label": "multimedia", + "actions": { + "label": "multimedia-control-lua", + "info": "Call Lua Script function Test_Lua_Engin", + "lua": "_Audio_Set_Multimedia" + } + }, { + "label": "navigation", + "actions": { + "label": "navigation-control-lua", + "info": "Call Lua Script to set Navigation", + "lua": "_Audio_Set_Navigation" + } + }, { + "label": "emergency", + "actions": { + "label": "emergency-control-ucm", + "lua": "_Audio_Set_Emergency" + } + }, { + "label": "multi-step-sample", + "info" : "all actions must succeed for control to be accepted", + "actions": [{ + "label": "multimedia-control-cb", + "info": "Call Sharelib Sample Callback", + "callback": "sampleControlNavigation", + "args": { + "arg1": "snoopy", + "arg2": "toto" + } + }, { + "label": "duckentertainment_lowprioritypriority", + "api": "hal", + "verb": "SetRTPC", + "args": { + "value": "-30" + } + }, { + "label": "navigation-control-lua", + "info": "Call Lua Script to set Navigation", + "lua": "_Audio_Set_Navigation" + }] + } + ], + "events": + [ + { + "label": "SampleEvent1", + "info": "define action when receiving a given event", + "actions": [ + { + "label": "Event Callback-1", + "callback": "SampleControlEvent", + "args": { + "arg": "action-1" + } + }, { + "label": "Event Callback-2", + "callback": "SampleControlEvent", + "args": { + "arg": "action-2" + } + } + ] + }, + { + "label": "SampleEvent2", + "info": "define action when receiving a given event", + "actions": [ + { + "label": "Event Callback-1", + "callback": "SampleControlEvent", + "args": { + "arg": "action-1" + } + }, { + "label": "Event Callback-2", + "callback": "SampleControlEvent", + "args": { + "arg": "action-2" + } + } + ] + } + ] +} + diff --git a/htdocs/CMakeLists.txt b/htdocs/CMakeLists.txt index e85ca3c..2e51461 100644 --- a/htdocs/CMakeLists.txt +++ b/htdocs/CMakeLists.txt @@ -25,18 +25,8 @@ PROJECT_TARGET_ADD(htdocs) file(GLOB SOURCE_FILES "*.html" "*.js" "*.jpg" "*.css") - add_custom_target(${TARGET_NAME} - DEPENDS ${PROJECT_PKG_BUILD_DIR}/${TARGET_NAME} - ) - - add_custom_command( - DEPENDS ${SOURCE_FILES} - OUTPUT ${PROJECT_PKG_BUILD_DIR}/${TARGET_NAME} - COMMAND mkdir -p ${PROJECT_PKG_BUILD_DIR}/${TARGET_NAME} - COMMAND touch ${PROJECT_PKG_BUILD_DIR}/${TARGET_NAME} - COMMAND cp -r ${SOURCE_FILES} ${PROJECT_PKG_BUILD_DIR}/${TARGET_NAME} - ) - + add_input_files("${SOURCE_FILES}") + SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES LABELS "HTDOCS" OUTPUT_NAME ${TARGET_NAME} diff --git a/htdocs/alsa-core.html b/htdocs/alsa-core.html index e946bdb..deca631 100644 --- a/htdocs/alsa-core.html +++ b/htdocs/alsa-core.html @@ -2,23 +2,25 @@ <head> <link rel="stylesheet" href="AudioBinding.css"> <title>Alsa Low Level Simple Test</title> - + <script type="text/javascript" src="AFB-websock.js"></script> <script type="text/javascript" src="AudioBinding.js"></script> </head> - -<body onload="init('alsa_registry','alsacore', 'getinfo')"> - - <button id="connected" onclick="init('alsa_registry','alsacore', 'getinfo');">Binder WS Fail</button> + +<body onload="init('alsa_registry','alsacore', 'infoget')"> + + <button id="connected" onclick="init('alsa_registry','alsacore', 'infoget');">Binder WS Fail</button> + <button id="mnitoring" onclick="window.open('/monitoring/monitor.html','_monitor_audio')">Debug/Monitoring</a></button> + <br><br> <b>Selected SndCard </b> <select id='alsa_registry'></select> <b>Select NUMID </b> - <select id='alsa_ctl_list' onclick="refresh_list(this, 'alsacore', 'getctl', {devid:sndcard, mode:1})"> + <select id='alsa_ctl_list' onclick="refresh_list(this, 'alsacore', 'ctlget', {devid:sndcard, mode:1})"> <option value='-1'>Refresh NUMID list</option> - </select> - + </select> + <b>API Verbosity </b> <select id='api_verbosity' onclick='mode=this.value'> <option value='0'>Quiet</option> @@ -26,37 +28,39 @@ <option value='2'>Verbose</option> <option value='3'>Full</option> </select> - + <br> <ol> - <li><button onclick="callbinder('alsacore','getctl', {devid:sndcard, mode:mode})">Get all Alsa Ctls</button></li> - <li><button onclick="callbinder('alsacore','getctl', {devid:sndcard, mode:mode, ctl:[numid]})">Get Alsa Ctls [numid]</button></li> - <li><button onclick="callbinder('alsacore','getctl', {devid:sndcard, mode:mode, ctl:[numid,numid+1]})">Get Alsa Ctls [numid,numid+1]</button></li> + <li><button onclick="callbinder('alsacore','ctlget', {devid:sndcard, mode:mode})">Get all Alsa Ctls</button></li> + <li><button onclick="callbinder('alsacore','ctlget', {devid:sndcard, mode:mode, ctl:numid})">Get Alsa Ctls [numid]</button></li> + <li><button onclick="callbinder('alsacore','ctlget', {devid:sndcard, mode:mode, ctl:[numid,numid+1]})">Get Alsa Ctls [numid,numid+1]</button></li> + <li><button onclick="callbinder('alsacore','infoget', {})">Get info</button></li> + <li><button onclick="callbinder('alsacore','hallist', {})">HAL list</button></li> + <br> + + + <li><button onclick="callbinder('alsacore','ctlset', {devid:sndcard, mode:mode, ctl:[[9,20]]})">Set Alsa Ctl ctl:[[9,20]]</button></li> + <li><button onclick="callbinder('alsacore','ctlset', {devid:sndcard, mode:mode, ctl:[{id:46,val:50}]})">Set Alsa Ctl ctl:[{id:46,val:50}]}</button></li> + <li><button onclick="callbinder('alsacore','ctlset', {devid:sndcard, mode:mode, ctl:[[6,[20,20]]]})">Set Alsa Ctl ctl:[[6,[20,20]]]</button></li> + <li><button onclick="callbinder('alsacore','ctlset', {devid:sndcard, mode:mode, ctl:[{id:6,val:[50,50]}]})">Set Alsa Ctl ctl:[{id:2,val:[50,50]}]</button></li> + <li><button onclick="callbinder('alsacore','ctlset', {devid:sndcard, mode:mode, ctl:[{id:6,val:[50,50]}, {id:9,val:50,50}]})">Set Alsa Ctl ctl:[{id:6,val:[50,50]}, {id:9,val:50,50}]</button></li> <br> - - - <li><button onclick="callbinder('alsacore','setctl', {devid:sndcard, mode:mode, ctl:[[9,20]]})">Set Alsa Ctl ctl:[[9,20]]</button></li> - <li><button onclick="callbinder('alsacore','setctl', {devid:sndcard, mode:mode, ctl:[{id:9,val:50}]})">Set Alsa Ctl ctl:[{id:9,val:50}]}</button></li> - <li><button onclick="callbinder('alsacore','setctl', {devid:sndcard, mode:mode, ctl:[[6,[20,20]]]})">Set Alsa Ctl ctl:[[6,[20,20]]]</button></li> - <li><button onclick="callbinder('alsacore','setctl', {devid:sndcard, mode:mode, ctl:[{id:6,val:[50,50]}]})">Set Alsa Ctl ctl:[{id:2,val:[50,50]}]</button></li> - <li><button onclick="callbinder('alsacore','setctl', {devid:sndcard, mode:mode, ctl:[{id:6,val:[50,50]}, {id:9,val:50,50}]})">Set Alsa Ctl ctl:[{id:6,val:[50,50]}, {id:9,val:50,50}]</button></li> - <br> <li><button onclick="callbinder('alsacore','ucmquery', {devid:sndcard, mode:mode})">List UCM verbs</button></li> <li><button onclick="callbinder('alsacore','ucmset' , {devid:sndcard, mode:mode, verb:'HiFi'})">Set UCM HiFi</button></li> <li><button onclick="callbinder('alsacore','ucmset' , {devid:sndcard, mode:mode, verb:'HiFi', dev:'Headphone'})">Set UCM HiFi+Headphone</button></li> <li><button onclick="callbinder('alsacore','ucmset' , {devid:sndcard, mode:mode, verb:'HiFi', dev:'Headphone', mod:'RecordMedia'})">Set UCM HiFi+Headphone+RecordMedia</button></li> - <br> + <br> <li><button onclick="callbinder('alsacore','ucmget' , {devid:sndcard, mode:mode, values:['OutputDspName','PlaybackPCM','CapturePCM']})">Get UCM OutputDspName+PlaybackPCM+CapturePCM (SET UCM)</button></li> - <br> + <br> <li><button onclick="callbinder('alsacore','subscribe', {devid:sndcard})">Subscribe AlsaCtl Events</button></li> <br> </ol> - + <div id="main" style="visibility:hidden"> <ol> <li>Question <pre id="question"></pre> <li>Response <pre id="output"></pre> <li>Events: <pre id="outevt"></pre> </ol> - </div> + </div> diff --git a/htdocs/audiohl-demo.html b/htdocs/audiohl-demo.html new file mode 100644 index 0000000..e222707 --- /dev/null +++ b/htdocs/audiohl-demo.html @@ -0,0 +1,47 @@ +<html> +<head> + <link rel="stylesheet" href="AudioBinding.css"> + <title>Audio High Level Test</title> + + <script type="text/javascript" src="AFB-websock.js"></script> + <script type="text/javascript" src="AudioBinding.js"></script> +</head> + +<body onload="init('audiohl')"> + + <button id="connected" onclick="init('audiohl');">Binder WS Fail</button> + + <li>Use Case 1: Automatic EndPoint Selection</li> + <br> + <ol> + <li><button onclick="callbinder('audiohl','stream_open', {audio_role:4,endpoint_type:1})">stream_open('Entertainment','Sink')</button></li> + <li><button onclick="callbinder('audiohl','set_endpoint_volume', {endpoint_type:1,endpoint_id:2,volume:'10+'})">set_endpoint_volume('Sink',2,'10+')</button></li> + <li><button onclick="callbinder('audiohl','stream_close', {stream_id:1})">stream_close(streamID)</button></li> + <br> + </ol> + + <li>Use Case 2: User Select EndPoint</li> + <br> + <ol> + <li><button onclick="callbinder('audiohl','get_sinks', {audio_role:4})">get_sinks('Entertainment')</button></li> + <li><button onclick="callbinder('audiohl','stream_open', {audio_role:4,endpoint_type:1,endpoint_id:10})">stream_open('Entertainment','Sink',sinkID)</button></li> + <li><button onclick="callbinder('audiohl','post_sound_event', {event_name:'PlaySound',media_name:'HomeButton.wav'})">post_sound_event('PlaySound','HomeButton.wav')</button></li> + <li><button onclick="callbinder('audiohl','stream_close', {stream_id:1})">stream_close(streamID)</button></li> + <br> + </ol> + + <li>Use Case 3: Select Routing</li> + <br> + <ol> + <li><button onclick="callbinder('audiohl','get_available_routings', {audio_role:4})">get_available_routings('Entertainment')</button></li> + <li><button onclick="callbinder('audiohl','add_routing', {audio_role:4,routing_id:2})">add_routing('Entertainment',routingID)</button></li> + <br> + </ol> + + <div id="main" style="visibility:hidden"> + <ol> + <li>Question <pre id="question"></pre> + <li>Response <pre id="output"></pre> + <li>Events: <pre id="outevt"></pre> + </ol> + </div> diff --git a/htdocs/audiohl.html b/htdocs/audiohl.html index 7cca6d8..31a28fe 100644 --- a/htdocs/audiohl.html +++ b/htdocs/audiohl.html @@ -11,31 +11,124 @@ <button id="connected" onclick="init('audiohl');">Binder WS Fail</button> + <b>Audio Role</b> + <select select_id='audiorole_list' onclick='ar=this.value'> + <option selected value=''>Select...</option> + <option value='warning' >Warning</option> + <option value='guidance'>Guidance</option> + <option value='notification'>Notification</option> + <option value='communication'>Communication</option> + <option value='entertainment'>Entertainment</option> + <option value='system'>System</option> + <option value='startup'>Startup</option> + <option value='shutdown'>Shutdown</option> + </select> + <b>Endpoint Type</b> + <select select_id='endpoint_type_list' onclick='ep_type=this.selectedIndex'> + <option value=0>Source</option> + <option value=1>Sink</option> + <option selected value=''>Select...</option> + </select> + <b>Endpoint ID</b> + <select select_id='endpoint_id_list' onclick='ep_id=this.selectedIndex'> + <option value='0'>0</option> + <option value='1'>1</option> + <option value='2'>2</option> + <option value='3'>3</option> + <option value='4'>4</option> + <option value='5'>5</option> + <option value='6'>6</option> + <option value='7'>7</option> + <option value='8'>8</option> + <option value='9'>9</option> + <option value='10'>10</option> + <option value='11'>11</option> + <option value='12'>12</option> + <option value='13'>13</option> + <option value='14'>14</option> + <option value='15'>15</option> + <option value='16'>16</option> + <option value='17'>17</option> + <option value='18'>18</option> + <option value='19'>19</option> + <option selected value=''>Select...</option> + </select> + <b>Stream ID</b> + <select select_id='stream_id_list' onclick='s_id=this.selectedIndex'> + <option value='0'>0</option> + <option value='1'>1</option> + <option value='2'>2</option> + <option value='3'>3</option> + <option value='4'>4</option> + <option value='5'>5</option> + <option value='6'>6</option> + <option value='7'>7</option> + <option value='8'>8</option> + <option value='9'>9</option> + <option value='10'>10</option> + <option value='11'>11</option> + <option value='12'>12</option> + <option value='13'>13</option> + <option value='14'>14</option> + <option value='15'>15</option> + <option value='16'>16</option> + <option value='17'>17</option> + <option value='18'>18</option> + <option value='19'>19</option> + <option selected value=''>Select...</option> + </select> + <b>Property</b> + <select select_id='property_name_list' onclick='p_name=this.value'> + <option value='balance'>Balance</option> + <option value='fade'>Fade</option> + <option value='eq_low'>EQ Low</option> + <option value='eq_mid'>EQ Mid</option> + <option value='eq_high'>EQ High</option> + <option selected value=''>Select...</option> + </select> + + <b>State Name</b> + <select select_id='state_name_list' onclick='st_name=this.value'> + <option value='mute'>Mute</option> + <option value='active'>Active</option> + <option selected value=''>Select...</option> + </select> + + <b>State Value</b> + <select select_id='state_value_list' onclick='st_val=this.value'> + <option value='on'>On</option> + <option value='off'>Off</option> + <option selected value=''>Select...</option> + </select> + <b>Volume Value</b> + <select select_id='value_list' onclick='val=this.value'> + <option value='0'>0</option> + <option value='20'>20</option> + <option value='40'>40</option> + <option value='60'>60</option> + <option value='80'>80</option> + <option value='100'>100</option> + <option selected value=''>Select...</option> + </select> + <br> <ol> - <li><button onclick="callbinder('audiohl','get_sources', {})">get_sources {}</button></li> - <li><button onclick="callbinder('audiohl','get_sources', {audio_role:4})">get_sources('Entertainment')</button></li> - <li><button onclick="callbinder('audiohl','get_sinks', {})">get_sinks</button></li> - <li><button onclick="callbinder('audiohl','get_sinks', {audio_role:4})">get_sinks('Entertainment')</button></li> - <li><button onclick="callbinder('audiohl','stream_open', {audio_role:4,endpoint_type:1})">stream_open('Entertainment','Sink')</button></li> - <li><button onclick="callbinder('audiohl','stream_open', {audio_role:4,endpoint_type:1,endpoint_id:0})">stream_open('Entertainment','Sink',sinkID)</button></li> - <li><button onclick="callbinder('audiohl','stream_close', {stream_id:3})">stream_close(streamID)</button></li> - <li><button onclick="callbinder('audiohl','get_available_routings', {})">get_available_routings</button></li> - <li><button onclick="callbinder('audiohl','get_available_routings', {audio_role:4})">get_available_routings('Entertainment')</button></li> - <li><button onclick="callbinder('audiohl','add_routing', {audio_role:4})">add_routing('Entertainment')</button></li> - <li><button onclick="callbinder('audiohl','add_routing', {audio_role:4,routing_id:2})">add_routing('Entertainment',routingID)</button></li> - <li><button onclick="callbinder('audiohl','remove_routing', {routing_id:3})">remove_routing(routingID)</button></li> - <li><button onclick="callbinder('audiohl','set_endpoint_volume', {endpoint_type:1,endpoint_id:1,volume:'10+'})">set_endpoint_volume('Sink',sinkID,'10+')</button></li> - <li><button onclick="callbinder('audiohl','set_endpoint_volume', {endpoint_type:1,endpoint_id:1,volume:'50%',ramp_time_ms:50})">set_endpoint_volume('Sink',sinkID,'50+',50)</button></li> - <li><button onclick="callbinder('audiohl','get_endpoint_volume', {endpoint_type:1,endpoint_id:1})">get_endpoint_volume('Sink',sinkID)</button></li> - <li><button onclick="callbinder('audiohl','set_endpoint_property', {endpoint_type:1,endpoint_id:1,property_name:'balance',value:'75'})">set_endpoint_property('Sink',sinkID,'balance','75')</button></li> - <li><button onclick="callbinder('audiohl','set_endpoint_property', {endpoint_type:1,endpoint_id:1,property_name:'balance',value:'75',ramp_time_ms:100})">set_endpoint_property('Sink',sinkID,'balance','75',100)</button></li> - <li><button onclick="callbinder('audiohl','get_endpoint_property', {endpoint_type:1,endpoint_id:1,property_name:'balance'})">get_endpoint_property('Sink',sinkID,'balance')</button></li> - <li><button onclick="callbinder('audiohl','set_endpoint_state', {endpoint_type:1,endpoint_id:1,state_name:'mute',state_value:'true'})">set_endpoint_state('Sink',sinkID,'mute','true')</button></li> - <li><button onclick="callbinder('audiohl','get_endpoint_state', {endpoint_type:1,endpoint_id:1,state_name:'suspended'})">get_endpoint_state('Sink',sinkID,'suspended')</button></li> - <li><button onclick="callbinder('audiohl','post_sound_event', {event_name:'PlaySound',media_name:'HomeButton.wav'})">post_sound_event('PlaySound','HomeButton.wav')</button></li> + <li><button onclick="callbinder('audiohl','get_sources', {audio_role:ar})">get_sources(audio_role)</button></li> + <li><button onclick="callbinder('audiohl','get_sinks', {audio_role:ar})">get_sinks(audio_role)</button></li> + <li><button onclick="callbinder('audiohl','stream_open', {audio_role:ar,endpoint_type:ep_type})">stream_open(audio_role,endpoint_type)</button></li> + <li><button onclick="callbinder('audiohl','stream_open', {audio_role:ar,endpoint_type:ep_type,endpoint_id:ep_id})">stream_open(audio_role,enpoint_type,endpoint_id)</button></li> + <li><button onclick="callbinder('audiohl','stream_close', {stream_id:s_id})">stream_close(stream_id)</button></li> + <li><button onclick="callbinder('audiohl','set_volume', {endpoint_type:ep_type,endpoint_id:ep_id,volume:val})">set_volume(endpoint_type,endpoint_id,value)</button></li> + <li><button onclick="callbinder('audiohl','get_volume', {endpoint_type:ep_type,endpoint_id:ep_id})">get_volume(endpoint_type,endpoint_id)</button></li> + <li><button onclick="callbinder('audiohl','set_property', {endpoint_type:ep_type,endpoint_id:ep_id,property_name:p_name,value:val})">set_property(endpoint_type,endpoint_id,property,value)</button></li> + <li><button onclick="callbinder('audiohl','get_property', {endpoint_type:ep_type,endpoint_id:ep_id,property_name:'balance'})">get_property(endpoint_type,endpoint_id,'balance')</button></li> + <li><button onclick="callbinder('audiohl','set_state', {endpoint_type:ep_type,endpoint_id:ep_id,state_name:st_name,state_value:st_val})">set_state(endpoint_type,endpoint_id,stateName,stateVal)</button></li> + <li><button onclick="callbinder('audiohl','get_state', {endpoint_type:ep_type,endpoint_id:ep_id,state_name:st_name})">get_state(endpoint_type,endpoint_id,stateName)</button></li> + <li><button onclick="callbinder('audiohl','post_sound_event', {event_name:'PlaySound',audio_role:'notification',media_name:'HomeButton.wav'})">post_sound_event('PlaySound','HomeButton.wav')</button></li> <li><button onclick="callbinder('audiohl','subscribe', {events:['SinkDeviceChanges','StreamStatusChanges']})">subscribe(['SinkDeviceChanges','StreamStatusChanges'])</button> + <li><button onclick="callbinder('audiohl','unsubscribe', {events:['SinkDeviceChanges','StreamStatusChanges']})">unsubscribe(['SinkDeviceChanges','StreamStatusChanges'])</button> </li> + <br> </ol> diff --git a/htdocs/index.html b/htdocs/index.html index 9c645cf..3c43f9b 100644 --- a/htdocs/index.html +++ b/htdocs/index.html @@ -7,4 +7,5 @@ <li><a href="alsa-core.html">AlsaCore Low Level Binding</a> <li><a href="alsa-hal.html" >AlsaHAL Hardware Abstraction Layer</a> <li><a href="audio-control.html">AudioControl Control/Policy API</a> - <li><a href="audiohl.html">High Level Audio API</a> + <li><a href="audiohl.html">High Level Audio API</a> + <li><a href="audiohl-demo.html">High Level Audio API Demo</a> diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cbfe5dc..c0f6ca9 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,11 +27,19 @@ macro(SET_TARGET_GENSKEL TARGET_NAME API_DEF_NAME) endmacro(SET_TARGET_GENSKEL) +FIND_PACKAGE(PkgConfig REQUIRED) +PKG_CHECK_MODULES(GLIB_PKG REQUIRED glib-2.0) + +# get_cmake_property(_variableNames VARIABLES) +# foreach (_variableName ${_variableNames}) +# message(STATUS "${_variableName}=${${_variableName}}") +# endforeach() + # Add target to project dependency list -PROJECT_TARGET_ADD(audiohighlevel-afb) +PROJECT_TARGET_ADD(audiohighlevel) # Define project Targets - ADD_LIBRARY(${TARGET_NAME} MODULE ahl-binding.c ahl-deviceenum.c) + ADD_LIBRARY(${TARGET_NAME} MODULE ahl-binding.c ahl-deviceenum.c ahl-config.c ahl-policy.c) # Generate API-v2 hat from OpenAPI json definition SET_TARGET_GENSKEL(${TARGET_NAME} ahl-apidef) @@ -44,9 +52,16 @@ PROJECT_TARGET_ADD(audiohighlevel-afb) OUTPUT_NAME ${TARGET_NAME} ) + # Define target includes + TARGET_INCLUDE_DIRECTORIES(${TARGET_NAME} + PUBLIC ${GLIB_PKG_INCLUDE_DIRS} + ) + # Library dependencies (include updates automatically) + # Find package for GLIB does not seem to export TARGET_LINK_LIBRARIES(${TARGET_NAME} - afb-utilities + afb-utilities + ${GLIB_PKG_LIBRARIES} ${link_libraries} ) diff --git a/src/ahl-apidef.h b/src/ahl-apidef.h index fa78681..9a39470 100644 --- a/src/ahl-apidef.h +++ b/src/ahl-apidef.h @@ -22,140 +22,118 @@ static const char _afb_description_v2_audiohl[] = "\"],\"properties\":{\"jtype\":{\"type\":\"string\",\"const\":\"afb-event" "\"},\"event\":{\"type\":\"string\"},\"data\":{\"type\":\"object\"}}},\"e" "ndpoint_info\":{\"type\":\"object\",\"required\":[\"endpoint_id\",\"type" - "\",\"name\"],\"properties\":{\"endpoint_id\":{\"type\":\"int\"},\"type\"" - ":{\"type\":\"enum\"},\"name\":{\"type\":\"string\"}}},\"stream_info\":{\"" - "type\":\"object\",\"required\":[\"stream_id\",\"pcm_name\",\"name\"],\"p" - "roperties\":{\"stream_id\":{\"type\":\"int\"},\"pcm_name\":{\"type\":\"s" - "tring\"},\"$ref\":\"#/components/schemas/endpoint_info\"}},\"routing_inf" - "o\":{\"type\":\"object\",\"required\":[\"routing_id\",\"source_id\",\"si" - "nk_id\"],\"properties\":{\"routing_id\":{\"type\":\"int\"},\"source_id\"" - ":{\"type\":\"int\"},\"sink_id\":{\"type\":\"int\"}}}},\"x-permissions\":" - "{\"streamcontrol\":{\"permission\":\"urn:AGL:permission:audio:public:str" - "eamcontrol\"},\"routingcontrol\":{\"permission\":\"urn:AGL:permission:au" - "dio:public:routingcontrol\"},\"soundevent\":{\"permission\":\"urn:AGL:pe" - "rmission:audio:public:soundevent\"}},\"responses\":{\"200\":{\"descripti" - "on\":\"A complex object array response\",\"content\":{\"application/json" - "\":{\"schema\":{\"$ref\":\"#/components/schemas/afb-reply\"}}}},\"400\":" - "{\"description\":\"Invalid arguments\"}}},\"paths\":{\"/get_sources\":{\"" - "description\":\"Retrieve array of available audio sources\",\"get\":{\"p" - "arameters\":[{\"in\":\"query\",\"name\":\"audio_role\",\"required\":fals" - "e,\"schema\":{\"type\":\"enum\"}}],\"responses\":{\"200\":{\"$ref\":\"#/" - "components/responses/200\",\"response\":{\"description\":\"Array of endp" - "oint info structures\",\"type\":\"array\",\"items\":{\"$ref\":\"#/compon" - "ents/schemas/endpoint_info\"}}},\"400\":{\"$ref\":\"#/components/respons" - "es/400\"}}}},\"/get_sinks\":{\"description\":\"Retrieve array of availab" - "le audio sinks\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"a" - "udio_role\",\"required\":false,\"schema\":{\"type\":\"enum\"}}],\"respon" - "ses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"response\":{\"" - "description\":\"Array of endpoint info structures\",\"type\":\"array\",\"" - "items\":{\"$ref\":\"#/components/schemas/endpoint_info\"}}},\"400\":{\"$" - "ref\":\"#/components/responses/400\"}}}},\"/stream_open\":{\"description" - "\":\"Request opening a stream\",\"get\":{\"x-permissions\":{\"$ref\":\"#" - "/components/x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"que" - "ry\",\"name\":\"audio_role\",\"required\":true,\"schema\":{\"type\":\"en" - "um\"}},{\"in\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\"" - "schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\"" - ",\"required\":false,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200" - "\":{\"$ref\":\"#/components/responses/200\",\"response\":{\"description\"" - ":\"Stream information structure\",\"$ref\":\"#/components/schemas/stream" - "_info\"}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/stream" - "_close\":{\"description\":\"Request closing a stream\",\"get\":{\"x-perm" - "issions\":{\"$ref\":\"#/components/x-permissions/streamcontrol\"},\"para" - "meters\":[{\"in\":\"query\",\"name\":\"stream_id\",\"required\":true,\"s" - "chema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/compon" - "ents/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}" - "}},\"/get_available_routings\":{\"description\":\"Retrieve array of avai" - "lable routing info structures\",\"get\":{\"parameters\":[{\"in\":\"query" - "\",\"name\":\"audio_role\",\"required\":false,\"schema\":{\"type\":\"enu" - "m\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"" - "response\":{\"description\":\"Array of routing info structures\",\"type\"" - ":\"array\",\"items\":{\"description\":\"Routing info structure {routingI" - "D, sourceID, sinkID }\",\"type\":\"object\"}}},\"400\":{\"$ref\":\"#/com" - "ponents/responses/400\"}}}},\"/add_routing\":{\"description\":\"Request " - "a routing connection between available devices\",\"get\":{\"x-permission" - "s\":{\"$ref\":\"#/components/x-permissions/routingcontrol\"},\"parameter" - "s\":[{\"in\":\"query\",\"name\":\"audio_role\",\"required\":true,\"schem" - "a\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"routing_id\",\"req" - "uired\":false,\"schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"" - "$ref\":\"#/components/responses/200\",\"response\":{\"description\":\"Ro" - "uting information structure\",\"$ref\":\"#/components/schemas/routing_in" - "fo\"}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/remove_ro" - "uting\":{\"description\":\"Request to remove a routing connection betwee" - "n devices\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permis" - "sions/routingcontrol\"},\"parameters\":[{\"in\":\"query\",\"name\":\"rou" - "ting_id\",\"required\":true,\"schema\":{\"type\":\"int\"}}],\"responses\"" - ":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"" - "#/components/responses/400\"}}}},\"/set_endpoint_volume\":{\"description" - "\":\"Set endpoint volume\",\"get\":{\"x-permissions\":{\"$ref\":\"#/comp" - "onents/x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"query\"," - "\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum" - "\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\"sche" - "ma\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"volume\",\"require" - "d\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"" - "ramp_time_ms\",\"required\":false,\"schema\":{\"type\":\"int\"}}],\"resp" - "onses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$r" - "ef\":\"#/components/responses/400\"}}}},\"/get_endpoint_volume\":{\"desc" - "ription\":\"Get endpoint volume\",\"get\":{\"parameters\":[{\"in\":\"que" - "ry\",\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"" - "enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\"" - "schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/compo" - "nents/responses/200\",\"response\":{\"description\":\"Endpoint volume va" - "lue\",\"type\":\"double\"}},\"400\":{\"$ref\":\"#/components/responses/4" - "00\"}}}},\"/set_endpoint_property\":{\"description\":\"Set endpoint prop" - "erty value\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permi" - "ssions/streamcontrol\"},\"parameters\":[{\"in\":\"query\",\"name\":\"end" - "point_type\",\"required\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"" - "query\",\"name\":\"endpoint_id\",\"required\":false,\"schema\":{\"type\"" - ":\"int\"}},{\"in\":\"query\",\"name\":\"property_name\",\"required\":tru" - "e,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"value\"" - ",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"" - "name\":\"ramp_time_ms\",\"required\":false,\"schema\":{\"type\":\"int\"}" - "}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"40" - "0\":{\"$ref\":\"#/components/responses/400\"}}}},\"/get_endpoint_propert" - "y\":{\"description\":\"Get endpoint property value\",\"get\":{\"paramete" - "rs\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\"s" - "chema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\"," - "\"required\":false,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"na" - "me\":\"property_name\",\"required\":true,\"schema\":{\"type\":\"string\"" + "\",\"device_name\",\"device_uri\"],\"properties\":{\"endpoint_id\":{\"ty" + "pe\":\"int\"},\"type\":{\"type\":\"enum\"},\"device_name\":{\"type\":\"s" + "tring\"},\"device_uri_type\":{\"type\":\"string\"}}},\"stream_info\":{\"" + "type\":\"object\",\"required\":[\"stream_id\",\"endpoint_info\"],\"prope" + "rties\":{\"stream_id\":{\"type\":\"int\"},\"$ref\":\"#/components/schema" + "s/endpoint_info\"}}},\"x-permissions\":{\"streamcontrol\":{\"permission\"" + ":\"urn:AGL:permission:audio:public:streamcontrol\"},\"routingcontrol\":{" + "\"permission\":\"urn:AGL:permission:audio:public:routingcontrol\"},\"sou" + "ndevent\":{\"permission\":\"urn:AGL:permission:audio:public:soundevent\"" + "}},\"responses\":{\"200\":{\"description\":\"A complex object array resp" + "onse\",\"content\":{\"application/json\":{\"schema\":{\"$ref\":\"#/compo" + "nents/schemas/afb-reply\"}}}},\"400\":{\"description\":\"Invalid argumen" + "ts\"}}},\"paths\":{\"/get_sources\":{\"description\":\"Retrieve array of" + " available audio sources\",\"get\":{\"parameters\":[{\"in\":\"query\",\"" + "name\":\"audio_role\",\"required\":true,\"schema\":{\"type\":\"string\"}" + "}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"res" + "ponse\":{\"description\":\"Array of endpoint info structures\",\"type\":" + "\"array\",\"items\":{\"$ref\":\"#/components/schemas/endpoint_info\"}}}," + "\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/get_sinks\":{\"d" + "escription\":\"Retrieve array of available audio sinks\",\"get\":{\"para" + "meters\":[{\"in\":\"query\",\"name\":\"audio_role\",\"required\":true,\"" + "schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/co" + "mponents/responses/200\",\"response\":{\"description\":\"Array of endpoi" + "nt info structures\",\"type\":\"array\",\"items\":{\"$ref\":\"#/componen" + "ts/schemas/endpoint_info\"}}},\"400\":{\"$ref\":\"#/components/responses" + "/400\"}}}},\"/stream_open\":{\"description\":\"Request opening a stream\"" + ",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permissions/strea" + "mcontrol\"},\"parameters\":[{\"in\":\"query\",\"name\":\"audio_role\",\"" + "required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"na" + "me\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum\"}}" + ",{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":false,\"schema\"" + ":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/re" + "sponses/200\",\"response\":{\"description\":\"Stream information structu" + "re\",\"$ref\":\"#/components/schemas/stream_info\"}},\"400\":{\"$ref\":\"" + "#/components/responses/400\"}}}},\"/stream_close\":{\"description\":\"Re" + "quest closing a stream\",\"get\":{\"x-permissions\":{\"$ref\":\"#/compon" + "ents/x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"query\",\"" + "name\":\"stream_id\",\"required\":true,\"schema\":{\"type\":\"int\"}}],\"" + "responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{" + "\"$ref\":\"#/components/responses/400\"}}}},\"/set_volume\":{\"descripti" + "on\":\"Set volume\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/" + "x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"query\",\"name\"" + ":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum\"}},{\"" + "in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\"schema\":{\"" + "type\":\"int\"}},{\"in\":\"query\",\"name\":\"volume\",\"required\":true" + ",\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"ramp_tim" + "e_ms\",\"required\":false,\"schema\":{\"type\":\"int\"}}],\"responses\":" + "{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#" + "/components/responses/400\"}}}},\"/get_volume\":{\"description\":\"Get v" + "olume\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"endpoint_t" + "ype\",\"required\":true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\"" + ",\"name\":\"endpoint_id\",\"required\":true,\"schema\":{\"type\":\"int\"" "}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"re" - "sponse\":{\"description\":\"Property value\",\"type\":\"double\"}},\"400" - "\":{\"$ref\":\"#/components/responses/400\"}}}},\"/set_endpoint_state\":" - "{\"description\":\"Set endpoint state\",\"get\":{\"x-permissions\":{\"$r" - "ef\":\"#/components/x-permissions/streamcontrol\"},\"parameters\":[{\"in" - "\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\"" + "sponse\":{\"description\":\"Endpoint volume value\",\"type\":\"double\"}" + "},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/set_property\"" + ":{\"description\":\"Set property value\",\"get\":{\"x-permissions\":{\"$" + "ref\":\"#/components/x-permissions/streamcontrol\"},\"parameters\":[{\"i" + "n\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\"" "type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\"" - ":true,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"state_" - "name\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"que" - "ry\",\"name\":\"state_value\",\"required\":true,\"schema\":{\"type\":\"s" - "tring\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200" - "\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/get_endpoint" - "_state\":{\"description\":\"Get endpoint state value\",\"get\":{\"parame" - "ters\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\"required\":true,\"" - "schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoint_id\"" - ",\"required\":true,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"na" - "me\":\"state_name\",\"required\":true,\"schema\":{\"type\":\"string\"}}]" - ",\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\",\"respo" - "nse\":{\"description\":\"Endpoint state value\",\"type\":\"string\"}},\"" - "400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/post_sound_event\"" - ":{\"description\":\"Post sound event\",\"get\":{\"x-permissions\":{\"$re" - "f\":\"#/components/x-permissions/soundevent\"},\"parameters\":[{\"in\":\"" - "query\",\"name\":\"event_name\",\"required\":true,\"schema\":{\"type\":\"" - "string\"}},{\"in\":\"query\",\"name\":\"audio_role\",\"required\":false," - "\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"media_name\"" - ",\"required\":false,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\"," - "\"name\":\"audio_context\",\"required\":false,\"schema\":{\"type\":\"obj" - "ect\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"" - "},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/subscribe\":{\"" - "description\":\"Subscribe to audio high level events\",\"get\":{\"parame" - "ters\":[{\"in\":\"query\",\"name\":\"events\",\"required\":true,\"schema" - "\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}}}],\"responses\":" + ":false,\"schema\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"prope" + "rty_name\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"" + "query\",\"name\":\"value\",\"required\":true,\"schema\":{\"type\":\"stri" + "ng\"}},{\"in\":\"query\",\"name\":\"ramp_time_ms\",\"required\":false,\"" + "schema\":{\"type\":\"int\"}}],\"responses\":{\"200\":{\"$ref\":\"#/compo" + "nents/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}" + "}}},\"/get_property\":{\"description\":\"Get property value\",\"get\":{\"" + "parameters\":[{\"in\":\"query\",\"name\":\"endpoint_type\",\"required\":" + "true,\"schema\":{\"type\":\"enum\"}},{\"in\":\"query\",\"name\":\"endpoi" + "nt_id\",\"required\":false,\"schema\":{\"type\":\"int\"}},{\"in\":\"quer" + "y\",\"name\":\"property_name\",\"required\":true,\"schema\":{\"type\":\"" + "string\"}}],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/20" + "0\",\"response\":{\"description\":\"Property value\",\"type\":\"double\"" + "}},\"400\":{\"$ref\":\"#/components/responses/400\"}}}},\"/set_state\":{" + "\"description\":\"Set state\",\"get\":{\"x-permissions\":{\"$ref\":\"#/c" + "omponents/x-permissions/streamcontrol\"},\"parameters\":[{\"in\":\"query" + "\",\"name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"e" + "num\"}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\"s" + "chema\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"state_name\",\"" + "required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"query\",\"na" + "me\":\"state_value\",\"required\":true,\"schema\":{\"type\":\"string\"}}" + "],\"responses\":{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400" + "\":{\"$ref\":\"#/components/responses/400\"}}}},\"/get_state\":{\"descri" + "ption\":\"Get state value\",\"get\":{\"parameters\":[{\"in\":\"query\",\"" + "name\":\"endpoint_type\",\"required\":true,\"schema\":{\"type\":\"enum\"" + "}},{\"in\":\"query\",\"name\":\"endpoint_id\",\"required\":true,\"schema" + "\":{\"type\":\"int\"}},{\"in\":\"query\",\"name\":\"state_name\",\"requi" + "red\":true,\"schema\":{\"type\":\"string\"}}],\"responses\":{\"200\":{\"" + "$ref\":\"#/components/responses/200\",\"response\":{\"description\":\"En" + "dpoint state value\",\"type\":\"string\"}},\"400\":{\"$ref\":\"#/compone" + "nts/responses/400\"}}}},\"/post_sound_event\":{\"description\":\"Post so" + "und event\",\"get\":{\"x-permissions\":{\"$ref\":\"#/components/x-permis" + "sions/soundevent\"},\"parameters\":[{\"in\":\"query\",\"name\":\"event_n" + "ame\",\"required\":true,\"schema\":{\"type\":\"string\"}},{\"in\":\"quer" + "y\",\"name\":\"audio_role\",\"required\":true,\"schema\":{\"type\":\"str" + "ing\"}},{\"in\":\"query\",\"name\":\"media_name\",\"required\":false,\"s" + "chema\":{\"type\":\"string\"}},{\"in\":\"query\",\"name\":\"audio_contex" + "t\",\"required\":false,\"schema\":{\"type\":\"object\"}}],\"responses\":" "{\"200\":{\"$ref\":\"#/components/responses/200\"},\"400\":{\"$ref\":\"#" - "/components/responses/400\"}}}}}}" + "/components/responses/400\"}}}},\"/subscribe\":{\"description\":\"Subscr" + "ibe to audio high level events\",\"get\":{\"parameters\":[{\"in\":\"quer" + "y\",\"name\":\"events\",\"required\":true,\"schema\":{\"type\":\"array\"" + ",\"items\":{\"type\":\"string\"}}}],\"responses\":{\"200\":{\"$ref\":\"#" + "/components/responses/200\"},\"400\":{\"$ref\":\"#/components/responses/" + "400\"}}}},\"/unsubscribe\":{\"description\":\"Unubscribe to audio high l" + "evel events\",\"get\":{\"parameters\":[{\"in\":\"query\",\"name\":\"even" + "ts\",\"required\":true,\"schema\":{\"type\":\"array\",\"items\":{\"type\"" + ":\"string\"}}}],\"responses\":{\"200\":{\"$ref\":\"#/components/response" + "s/200\"},\"400\":{\"$ref\":\"#/components/responses/400\"}}}}}}" ; static const struct afb_auth _afb_auths_v2_audiohl[] = { { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:streamcontrol" }, - { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:routingcontrol" }, { .type = afb_auth_Permission, .text = "urn:AGL:permission:audio:public:soundevent" } }; @@ -163,131 +141,121 @@ static const struct afb_auth _afb_auths_v2_audiohl[] = { void audiohlapi_get_sinks(struct afb_req req); void audiohlapi_stream_open(struct afb_req req); void audiohlapi_stream_close(struct afb_req req); - void audiohlapi_get_available_routings(struct afb_req req); - void audiohlapi_add_routing(struct afb_req req); - void audiohlapi_remove_routing(struct afb_req req); - void audiohlapi_set_endpoint_volume(struct afb_req req); - void audiohlapi_get_endpoint_volume(struct afb_req req); - void audiohlapi_set_endpoint_property(struct afb_req req); - void audiohlapi_get_endpoint_property(struct afb_req req); - void audiohlapi_set_endpoint_state(struct afb_req req); - void audiohlapi_get_endpoint_state(struct afb_req req); + void audiohlapi_set_volume(struct afb_req req); + void audiohlapi_get_volume(struct afb_req req); + void audiohlapi_set_property(struct afb_req req); + void audiohlapi_get_property(struct afb_req req); + void audiohlapi_set_state(struct afb_req req); + void audiohlapi_get_state(struct afb_req req); void audiohlapi_post_sound_event(struct afb_req req); void audiohlapi_subscribe(struct afb_req req); + void audiohlapi_unsubscribe(struct afb_req req); static const struct afb_verb_v2 _afb_verbs_v2_audiohl[] = { { .verb = "get_sources", .callback = audiohlapi_get_sources, .auth = NULL, - .info = NULL, + .info = "Retrieve array of available audio sources", .session = AFB_SESSION_NONE_V2 }, { .verb = "get_sinks", .callback = audiohlapi_get_sinks, .auth = NULL, - .info = NULL, + .info = "Retrieve array of available audio sinks", .session = AFB_SESSION_NONE_V2 }, { .verb = "stream_open", .callback = audiohlapi_stream_open, .auth = &_afb_auths_v2_audiohl[0], - .info = NULL, + .info = "Request opening a stream", .session = AFB_SESSION_NONE_V2 }, { .verb = "stream_close", .callback = audiohlapi_stream_close, .auth = &_afb_auths_v2_audiohl[0], - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "get_available_routings", - .callback = audiohlapi_get_available_routings, - .auth = NULL, - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "add_routing", - .callback = audiohlapi_add_routing, - .auth = &_afb_auths_v2_audiohl[1], - .info = NULL, - .session = AFB_SESSION_NONE_V2 - }, - { - .verb = "remove_routing", - .callback = audiohlapi_remove_routing, - .auth = &_afb_auths_v2_audiohl[1], - .info = NULL, + .info = "Request closing a stream", .session = AFB_SESSION_NONE_V2 }, { - .verb = "set_endpoint_volume", - .callback = audiohlapi_set_endpoint_volume, + .verb = "set_volume", + .callback = audiohlapi_set_volume, .auth = &_afb_auths_v2_audiohl[0], - .info = NULL, + .info = "Set volume", .session = AFB_SESSION_NONE_V2 }, { - .verb = "get_endpoint_volume", - .callback = audiohlapi_get_endpoint_volume, + .verb = "get_volume", + .callback = audiohlapi_get_volume, .auth = NULL, - .info = NULL, + .info = "Get volume", .session = AFB_SESSION_NONE_V2 }, { - .verb = "set_endpoint_property", - .callback = audiohlapi_set_endpoint_property, + .verb = "set_property", + .callback = audiohlapi_set_property, .auth = &_afb_auths_v2_audiohl[0], - .info = NULL, + .info = "Set property value", .session = AFB_SESSION_NONE_V2 }, { - .verb = "get_endpoint_property", - .callback = audiohlapi_get_endpoint_property, + .verb = "get_property", + .callback = audiohlapi_get_property, .auth = NULL, - .info = NULL, + .info = "Get property value", .session = AFB_SESSION_NONE_V2 }, { - .verb = "set_endpoint_state", - .callback = audiohlapi_set_endpoint_state, + .verb = "set_state", + .callback = audiohlapi_set_state, .auth = &_afb_auths_v2_audiohl[0], - .info = NULL, + .info = "Set state", .session = AFB_SESSION_NONE_V2 }, { - .verb = "get_endpoint_state", - .callback = audiohlapi_get_endpoint_state, + .verb = "get_state", + .callback = audiohlapi_get_state, .auth = NULL, - .info = NULL, + .info = "Get state value", .session = AFB_SESSION_NONE_V2 }, { .verb = "post_sound_event", .callback = audiohlapi_post_sound_event, - .auth = &_afb_auths_v2_audiohl[2], - .info = NULL, + .auth = &_afb_auths_v2_audiohl[1], + .info = "Post sound event", .session = AFB_SESSION_NONE_V2 }, { .verb = "subscribe", .callback = audiohlapi_subscribe, .auth = NULL, - .info = NULL, + .info = "Subscribe to audio high level events", .session = AFB_SESSION_NONE_V2 }, - { .verb = NULL } + { + .verb = "unsubscribe", + .callback = audiohlapi_unsubscribe, + .auth = NULL, + .info = "Unubscribe to audio high level events", + .session = AFB_SESSION_NONE_V2 + }, + { + .verb = NULL, + .callback = NULL, + .auth = NULL, + .info = NULL, + .session = 0 + } }; const struct afb_binding_v2 afbBindingV2 = { .api = "audiohl", .specification = _afb_description_v2_audiohl, - .info = NULL, + .info = "Audio high level API for AGL applications", .verbs = _afb_verbs_v2_audiohl, .preinit = NULL, .init = AhlBindingInit, diff --git a/src/ahl-apidef.json b/src/ahl-apidef.json index cbe25a9..45067ea 100644 --- a/src/ahl-apidef.json +++ b/src/ahl-apidef.json @@ -97,30 +97,21 @@ }, "endpoint_info": { "type": "object", - "required": [ "endpoint_id", "type", "name" ], + "required": [ "endpoint_id", "type", "device_name", "device_uri" ], "properties": { "endpoint_id": { "type": "int" }, "type": { "type": "enum" }, - "name": { "type": "string" } + "device_name": { "type": "string" }, + "device_uri_type": { "type": "string" } } }, "stream_info": { "type": "object", - "required": [ "stream_id", "pcm_name", "name" ], + "required": [ "stream_id", "endpoint_info" ], "properties": { "stream_id": { "type": "int" }, - "pcm_name": { "type": "string" }, "$ref": "#/components/schemas/endpoint_info" } - }, - "routing_info": { - "type": "object", - "required": [ "routing_id", "source_id", "sink_id" ], - "properties": { - "routing_id": { "type": "int" }, - "source_id": { "type": "int" }, - "sink_id": { "type": "int" } - } } }, "x-permissions": { @@ -158,8 +149,8 @@ { "in": "query", "name": "audio_role", - "required": false, - "schema": { "type": "enum" } + "required": true, + "schema": { "type": "string" } } ], "responses": { @@ -182,8 +173,8 @@ { "in": "query", "name": "audio_role", - "required": false, - "schema": { "type": "enum" } + "required": true, + "schema": { "type": "string" } } ], "responses": { @@ -209,7 +200,7 @@ "in": "query", "name": "audio_role", "required": true, - "schema": { "type": "enum" } + "schema": { "type": "string" } }, { "in": "query", @@ -254,83 +245,8 @@ } } }, - "/get_available_routings": { - "description": "Retrieve array of available routing info structures", - "get": { - "parameters": [ - { - "in": "query", - "name": "audio_role", - "required": false, - "schema": { "type": "enum" } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200", - "response": { - "description": "Array of routing info structures", - "type": "array", - "items": { - "description": "Routing info structure {routingID, sourceID, sinkID }", - "type": "object" - } - } - }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/add_routing": { - "description": "Request a routing connection between available devices", - "get": { - "x-permissions": { "$ref": "#/components/x-permissions/routingcontrol" }, - "parameters": [ - { - "in": "query", - "name": "audio_role", - "required": true, - "schema": { "type": "enum" } - }, - { - "in": "query", - "name": "routing_id", - "required": false, - "schema": { "type": "int" } - } - ], - "responses": { - "200": { - "$ref": "#/components/responses/200", - "response": { - "description": "Routing information structure", - "$ref": "#/components/schemas/routing_info" - } - }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/remove_routing": { - "description": "Request to remove a routing connection between devices", - "get": { - "x-permissions": { "$ref": "#/components/x-permissions/routingcontrol" }, - "parameters": [ - { - "in": "query", - "name": "routing_id", - "required": true, - "schema": { "type": "int" } - } - ], - "responses": { - "200": { "$ref": "#/components/responses/200" }, - "400": { "$ref": "#/components/responses/400" } - } - } - }, - "/set_endpoint_volume": { - "description": "Set endpoint volume", + "/set_volume": { + "description": "Set volume", "get": { "x-permissions": { "$ref": "#/components/x-permissions/streamcontrol" }, "parameters": [ @@ -365,8 +281,8 @@ } } }, - "/get_endpoint_volume": { - "description": "Get endpoint volume", + "/get_volume": { + "description": "Get volume", "get": { "parameters": [ { @@ -394,8 +310,8 @@ } } }, - "/set_endpoint_property": { - "description": "Set endpoint property value", + "/set_property": { + "description": "Set property value", "get": { "x-permissions": { "$ref": "#/components/x-permissions/streamcontrol" }, "parameters": [ @@ -436,8 +352,8 @@ } } }, - "/get_endpoint_property": { - "description": "Get endpoint property value", + "/get_property": { + "description": "Get property value", "get": { "parameters": [ { @@ -471,8 +387,8 @@ } } }, - "/set_endpoint_state": { - "description": "Set endpoint state", + "/set_state": { + "description": "Set state", "get": { "x-permissions": { "$ref": "#/components/x-permissions/streamcontrol" }, "parameters": [ @@ -507,8 +423,8 @@ } } }, - "/get_endpoint_state": { - "description": "Get endpoint state value", + "/get_state": { + "description": "Get state value", "get": { "parameters": [ { @@ -556,8 +472,8 @@ { "in": "query", "name": "audio_role", - "required": false, - "schema": { "type": "enum" } + "required": true, + "schema": { "type": "string" } }, { "in": "query", @@ -596,6 +512,32 @@ "400": { "$ref": "#/components/responses/400" } } } + }, + "/unsubscribe": { + "description": "Unubscribe to audio high level events", + "get": { + "parameters": [ + { + "in": "query", + "name": "events", + "required": true, + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "responses": { + "200": { + "$ref": "#/components/responses/200" + }, + "400": { + "$ref": "#/components/responses/400" + } + } + } } } } diff --git a/src/ahl-binding.c b/src/ahl-binding.c index 99236bd..4b075c0 100644 --- a/src/ahl-binding.c +++ b/src/ahl-binding.c @@ -18,87 +18,167 @@ #define _GNU_SOURCE #include <stdio.h> #include <string.h> -#include <time.h> #include "ahl-binding.h" #include "ahl-apidef.h" // Generated from JSON OpenAPI #include "wrap-json.h" -// TODO: json_object_put to free JSON objects? potential leaks currently +// Global high-level binding context +AHLCtxT g_AHLCtx; + +// Workshop development topics: +// - Associating streamID with a client id of afb. Has to be done with sessions? +// - Declaring dependencies on other binding services (examples) +// - json_object_put to free JSON objects? potential leaks currently +// - no events on new HAL registered to alsacore? +// - unregistering event subscription examples? +// - discussion how to use property and event system to dispatch external parameterization (e.g. Wwise/Fiberdyne) +// - discussion where to best isolate policy (controller or HLB plugin) +// - Other HLB attributes to pass through (e.g. interrupted behavior) +// - DBScale TLV warning +// - GLib (internal) dependency +// - HAL registration dependency (initialization order) +// - Binding startup arguments for config file path +// - Can we use the same HAL for different card numbers? +// - Example use of volume ramping in HAL? +// - Binding termination function +// - AGL persistence framework? +// - How to provide API services with config.xml (secrets and all) -// Helper macros/func for packaging JSON objects from C structures +// Helper macros/func for packaging JSON objects from C structures #define EndpointInfoStructToJSON(__JSON_OBJECT__, __ENDPOINTINFOSTRUCT__) \ - wrap_json_pack(&__JSON_OBJECT__, "{s:i,s:i,s:s}", "endpoint_id", __ENDPOINTINFOSTRUCT__.endpoint_id, "type", __ENDPOINTINFOSTRUCT__.type, "name", __ENDPOINTINFOSTRUCT__.name); - -#define RoutingInfoStructToJSON(__JSON_OBJECT__, __ROUTINGINFOSTRUCT__) \ - wrap_json_pack(&__JSON_OBJECT__, "{s:i,s:i,s:i}", "routing_id", __ROUTINGINFOSTRUCT__.routing_id, "source_id", __ROUTINGINFOSTRUCT__.source_id, "sink_id", __ROUTINGINFOSTRUCT__.sink_id); - + wrap_json_pack(&__JSON_OBJECT__, "{s:i,s:i,s:s,s:i}",\ + "endpoint_id", __ENDPOINTINFOSTRUCT__.endpointID, \ + "endpoint_type", __ENDPOINTINFOSTRUCT__.type, \ + "device_name", __ENDPOINTINFOSTRUCT__.deviceName, \ + "device_uri_type", __ENDPOINTINFOSTRUCT__.deviceURIType); + static void StreamInfoStructToJSON(json_object **streamInfoJ, StreamInfoT streamInfo) { json_object *endpointInfoJ; - EndpointInfoStructToJSON(endpointInfoJ,streamInfo.endpoint_info); - wrap_json_pack(streamInfoJ, "{s:i,s:s}", "stream_id", streamInfo.stream_id, "pcm_name", streamInfo.pcm_name); + EndpointInfoStructToJSON(endpointInfoJ,streamInfo.endpointInfo); + wrap_json_pack(streamInfoJ, "{s:i}", "stream_id", streamInfo.streamID); json_object_object_add(*streamInfoJ,"endpoint_info",endpointInfoJ); } +static streamID_t CreateNewStreamID() +{ + streamID_t newID = g_AHLCtx.nextStreamID; + g_AHLCtx.nextStreamID++; + return newID; +} + +static int FindRoleIndex( const char * in_pAudioRole) +{ + int index = -1; // Not found + for (int i = 0; i < g_AHLCtx.policyCtx.iNumberRoles; i++) + { + GString gs = g_array_index( g_AHLCtx.policyCtx.pAudioRoles, GString, i ); + if ( strcasecmp(gs.str,in_pAudioRole) == 0 ) + index = i; + } + return index; +} + +static EndpointInfoT * GetEndpointInfo(endpointID_t in_endpointID, EndpointTypeT in_endpointType) +{ + EndpointInfoT * pEndpointInfo = NULL; + for (int i = 0; i < g_AHLCtx.policyCtx.iNumberRoles; i++) + { + GArray * pRoleDeviceArray = NULL; + if (in_endpointType == ENDPOINTTYPE_SOURCE){ + pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, i ); + } + else{ + pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, i ); + } + for (int j = 0; j < pRoleDeviceArray->len; j++) { + EndpointInfoT * pCurEndpointInfo = &g_array_index(pRoleDeviceArray,EndpointInfoT,j); + if (pCurEndpointInfo->endpointID == in_endpointID) { + pEndpointInfo = pCurEndpointInfo; + break; + } + } + } + return pEndpointInfo; +} + // Binding initialization PUBLIC int AhlBindingInit() { - int errcount = 0; + int err = 0; + + // This API uses services from low level audio + err = afb_daemon_require_api_v2("alsacore",1) ; + if( err != 0 ) + { + AFB_ERROR("Audio high level API requires alsacore API to be available"); + return 1; + } + + // Parse high-level binding JSON configuration file (will build device lists) + errcount += ParseHLBConfig(); - // Initialize list of available sources/sinks using lower level services - errcount += EnumerateSources(); - errcount += EnumerateSinks(); + // Initialize list of active streams + g_AHLCtx.policyCtx.pActiveStreams = g_array_new(FALSE,TRUE,sizeof(StreamInfoT)); - // TODO: Register for device changes from lower level services + // TODO: Register for device changes ALSA low level audio service - // TODO: Parse high-level binding configuration file + // TODO: Perform other binding initialization tasks (e.g. broadcast service ready event?) - // TODO: Perform other binding initialization tasks + // TODO: Use AGL persistence framework to retrieve and set inital state/volumes/properties AFB_DEBUG("Audio high-level Binding success errcount=%d", errcount); return errcount; } +// TODO: AhlBindingTerm()? +// // TODO: Use AGL persistence framework to retrieve and set inital state/volumes + +// TODO: OnEventFunction +// if ALSA device availability change -> update source / sink list +// Call policy to attempt to assign role based on information (e.g. device name) +// Policy should also determine priority (insert device in right spot in the list) + PUBLIC void audiohlapi_get_sources(struct afb_req req) { json_object *sourcesJ = NULL; json_object *sourceJ = NULL; json_object *queryJ = NULL; - AudioRoleT audioRole = AUDIOROLE_MAXVALUE; - + char * audioRole = NULL; + queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s?i}", "audio_role", &audioRole); + int err = wrap_json_unpack(queryJ, "{s:s}", "audio_role", &audioRole); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } - if (audioRole != AUDIOROLE_MAXVALUE) - { - AFB_DEBUG("Filtering according to specified audio role=%d", audioRole); - } - - // Fake run-time data for test purposes - EndpointInfoT sources[3]; - sources[0].endpoint_id = 0; - sources[0].type = 0; - sources[0].name = "Source0"; - sources[1].endpoint_id = 1; - sources[1].type = 1; - sources[1].name = "Source1"; - sources[2].endpoint_id = 2; - sources[2].type = 2; - sources[2].name = "Source2"; - sourcesJ = json_object_new_array(); - for ( unsigned int i = 0 ; i < 3; i++) + + AFB_DEBUG("Filtering devices according to specified audio role=%s", audioRole); + // Check that the role requested exists in current configuration + int roleIndex = FindRoleIndex(audioRole); + if ( roleIndex == -1) { - EndpointInfoStructToJSON(sourceJ, sources[i]); - json_object_array_add(sourcesJ, sourceJ); + afb_req_fail_f(req, "Invalid arguments", "Requested audio role does not exist in current configuration -> %s", json_object_get_string(queryJ)); + return; } + // List device only for current audio role and device type (pick the role device list) + int iRoleIndex = FindRoleIndex(audioRole); + if (iRoleIndex >= 0) + { + GArray * pRoleSourceDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, iRoleIndex ); + int iNumberDevices = pRoleSourceDeviceArray->len; + for ( int j = 0 ; j < iNumberDevices; j++) + { + EndpointInfoT sourceInfo = g_array_index(pRoleSourceDeviceArray,EndpointInfoT,j); + EndpointInfoStructToJSON(sourceJ, sourceInfo); + json_object_array_add(sourcesJ, sourceJ); + } + } afb_req_success(req, sourcesJ, "List of sources"); } @@ -108,38 +188,38 @@ PUBLIC void audiohlapi_get_sinks(struct afb_req req) json_object *sinksJ = NULL; json_object *sinkJ = NULL; json_object *queryJ = NULL; - AudioRoleT audioRole = AUDIOROLE_MAXVALUE; + char * audioRole = NULL; queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s?i}", "audio_role", &audioRole); + int err = wrap_json_unpack(queryJ, "{s:s}", "audio_role", &audioRole); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } - if (audioRole != AUDIOROLE_MAXVALUE) - { - AFB_DEBUG("Filtering according to specified audio role=%d", audioRole); - } - - // Fake run-time data for test purposes - EndpointInfoT sinks[3]; - sinks[0].endpoint_id = 0; - sinks[0].type = 0; - sinks[0].name = "Sink0"; - sinks[1].endpoint_id = 1; - sinks[1].type = 1; - sinks[1].name = "Sink1"; - sinks[2].endpoint_id = 2; - sinks[2].type = 2; - sinks[2].name = "Sink2"; - sinksJ = json_object_new_array(); - for ( unsigned int i = 0 ; i < 3; i++) + + AFB_DEBUG("Filtering devices according to specified audio role=%s", audioRole); + // Check that the role requested exists in current configuration + int roleIndex = FindRoleIndex(audioRole); + if ( roleIndex == -1) { - EndpointInfoStructToJSON(sinkJ, sinks[i]); - json_object_array_add(sinksJ, sinkJ); + afb_req_fail_f(req, "Invalid arguments", "Requested audio role does not exist in current configuration -> %s", json_object_get_string(queryJ)); + return; } + // List device only for current audio role and device type (pick the role device list) + int iRoleIndex = FindRoleIndex(audioRole); + if (iRoleIndex >= 0) + { + GArray * pRoleSinkDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, iRoleIndex ); + int iNumberDevices = pRoleSinkDeviceArray->len; + for ( int j = 0 ; j < iNumberDevices; j++) + { + EndpointInfoT sinkInfo = g_array_index(pRoleSinkDeviceArray,EndpointInfoT,j); + EndpointInfoStructToJSON(sinkJ, sinkInfo); + json_object_array_add(sinksJ, sinkJ); + } + } afb_req_success(req, sinksJ, "List of sinks"); } @@ -149,30 +229,75 @@ PUBLIC void audiohlapi_stream_open(struct afb_req req) json_object *streamInfoJ = NULL; StreamInfoT streamInfo; json_object *queryJ = NULL; - AudioRoleT audioRole; + char * audioRole = NULL; EndpointTypeT endpointType; endpointID_t endpointID = UNDEFINED_ID; + int policyAllowed = 0; + EndpointInfoT endpointInfo; queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:i,s:i,s?i}", "audio_role", &audioRole, "endpoint_type", &endpointType, "endpoint_id", &endpointID); + int err = wrap_json_unpack(queryJ, "{s:s,s:i,s?i}", "audio_role", &audioRole, "endpoint_type", &endpointType, "endpoint_id", &endpointID); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } - AFB_DEBUG("Parsed input arguments = audio_role:%d endpointType:%d endpointID:%d", audioRole,endpointType,endpointID); + AFB_DEBUG("Parsed input arguments = audio_role:%s endpoint_type:%d endpoint_id:%d", audioRole,endpointType,endpointID); + + int iRoleIndex = FindRoleIndex(audioRole); + if (iRoleIndex < 0) { + afb_req_fail_f(req, "Invalid audio role", "Audio role was not found in configuration -> %s",audioRole); + return; + } + + GArray * pRoleDeviceArray = NULL; + if (endpointType == ENDPOINTTYPE_SOURCE){ + pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, iRoleIndex ); + } + else{ + pRoleDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, iRoleIndex ); + } + if (pRoleDeviceArray->len == 0) { + afb_req_fail_f(req, "No available devices", "No available devices for role:%s and device type:%d",audioRole,endpointType); + return; + } if (endpointID == UNDEFINED_ID) { - // TODO: Go through configuration and available devices to find best device for specified role - endpointID = 2; + // Assign a device based on configuration priority (first in the list for requested role and endpoint type) + endpointInfo = g_array_index(pRoleDeviceArray,EndpointInfoT,0); + } + else{ + // Find specified endpoint ID in list of devices + int iNumberDevices = pRoleDeviceArray->len; + int iEndpointFound = 0; + for ( int j = 0 ; j < iNumberDevices; j++) + { + endpointInfo = g_array_index(pRoleDeviceArray,EndpointInfoT,j); + if (endpointInfo.endpointID == endpointID) { + iEndpointFound = 1; + break; + } + } + if (iEndpointFound == 0) { + afb_req_fail_f(req, "Endpoint not available", "Specified endpoint not available for role:%s and device type:%d endpoint id %d",audioRole,endpointType,endpointID); + return; + } } - // Fake run-time data for test purposes - streamInfo.stream_id = 12; - streamInfo.pcm_name = "plug:Entertainment"; - streamInfo.endpoint_info.endpoint_id = endpointID; - streamInfo.endpoint_info.type = endpointType; - streamInfo.endpoint_info.name = "MainSpeakers"; + // Call policy to verify whether creating a new audio stream is allowed in current context and possibly take other actions + policyAllowed = Policy_OpenStream(audioRole, endpointType, endpointID); + if (policyAllowed == AUDIOHL_POLICY_REJECT) + { + afb_req_fail(req, "Audio policy violation", "Open stream not allowed in current context"); + return; + } + + // Create stream + streamInfo.streamID = CreateNewStreamID(); // create new ID + streamInfo.endpointInfo = endpointInfo; + + // Push stream on active stream list + g_array_append_val( g_AHLCtx.policyCtx.pActiveStreams, streamInfo ); StreamInfoStructToJSON(&streamInfoJ,streamInfo); @@ -194,124 +319,94 @@ PUBLIC void audiohlapi_stream_close(struct afb_req req) // TODO: Validate that the application ID from which the stream close is coming is the one that opened it, otherwise fail and do nothing + // Remove from active stream list (if present) + int iNumActiveStreams = g_AHLCtx.policyCtx.pActiveStreams->len; + int iStreamFound = 0; + for ( int i = 0; i < iNumActiveStreams ; i++ ) { + StreamInfoT streamInfo = g_array_index(g_AHLCtx.policyCtx.pActiveStreams,StreamInfoT,i); + if (streamInfo.streamID == streamID){ + g_array_remove_index(g_AHLCtx.policyCtx.pActiveStreams,i); + iStreamFound = 1; + break; + } + } + + if (iStreamFound == 0) { + afb_req_fail_f(req, "Stream not found", "Specified stream not currently active stream_id -> %d",streamID); + return; + } + afb_req_success(req, NULL, "Stream close completed"); } -// Routings -PUBLIC void audiohlapi_get_available_routings(struct afb_req req) +// Endpoints +PUBLIC void audiohlapi_set_volume(struct afb_req req) { - json_object *routingsJ; - json_object *routingJ; json_object *queryJ = NULL; - AudioRoleT audioRole = AUDIOROLE_MAXVALUE; + endpointID_t endpointID = UNDEFINED_ID; + EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; + char * volumeStr = NULL; + int rampTimeMS = 0; + int policyAllowed = 0; queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s?i}", "audio_role", &audioRole); + int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s?i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"volume",&volumeStr,"ramp_time_ms",&rampTimeMS); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } + AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d volume:%s ramp_time_ms: %d", endpointType,endpointID,volumeStr,rampTimeMS); + + // TODO: Parse volume string to support increment/absolute/percent notation + int vol = atoi(volumeStr); - if (audioRole != AUDIOROLE_MAXVALUE) + policyAllowed = Policy_SetVolume(endpointType, endpointID, volumeStr, rampTimeMS); // TODO: Potentially retrieve modified value by policy (e.g. volume limit) + if (!policyAllowed) { - AFB_DEBUG("Filtering according to specified audio role=%d", audioRole); - } - - // Fake run-time data for test purposes - RoutingInfoT routings[3]; - routings[0].source_id = 0; - routings[0].sink_id = 0; - routings[0].routing_id = 0; - routings[1].source_id = 1; - routings[1].sink_id = 1; - routings[1].routing_id = 1; - routings[2].source_id = 2; - routings[2].sink_id = 2; - routings[2].routing_id = 2; - - routingsJ = json_object_new_array(); - for (unsigned int i = 0; i < 3; i++) - { - RoutingInfoStructToJSON(routingJ, routings[i]); - json_object_array_add(routingsJ, routingJ); + afb_req_fail(req, "Audio policy violation", "Set volume not allowed in current context"); + return; } - afb_req_success(req, routingsJ, "List of available routings"); -} - -PUBLIC void audiohlapi_add_routing(struct afb_req req) -{ - json_object *queryJ = NULL; - AudioRoleT audioRole = AUDIOROLE_MAXVALUE; - routingID_t routingID = UNDEFINED_ID; - json_object *routingJ = NULL; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:i,s?i}", "audio_role", &audioRole,"routing_id",routingID); - if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); + // TODO: Cache during device enumeration for efficiency + EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); + if (pEndpointInfo == NULL) + { + afb_req_fail_f(req, "Endpoint not found", "Endpoint information not found for id:%d type%d",endpointID,endpointType); return; } - AFB_DEBUG("Parsed input arguments = audio_role:%d routing_id:%d", audioRole,routingID); - - // Fake run-time data for test purposes - RoutingInfoT routingInfo; - routingInfo.routing_id = routingID; - routingInfo.source_id = 3; - routingInfo.sink_id = 4; - RoutingInfoStructToJSON(routingJ,routingInfo); - - afb_req_success(req,routingJ, "Selected routing information"); -} + // Using audio role available from endpoint to target the right HAL control (build string based on convention) + char halControlName[AUDIOHL_MAXHALCONTROLNAME_LENGTH]; + strncpy(halControlName,pEndpointInfo->audioRole,AUDIOHL_MAXHALCONTROLNAME_LENGTH); + strcat(halControlName,"_Ramp"); // Or _Vol for direct control (no ramping) -PUBLIC void audiohlapi_remove_routing(struct afb_req req) -{ - json_object *queryJ = NULL; - routingID_t routingID = UNDEFINED_ID; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:i}", "routing_id", &routingID); + // Set endpoint volume using HAL services (leveraging ramps etc.) + json_object *j_response, *j_query = NULL; + + // Package query + err = wrap_json_pack(&j_query,"{s:s,s:i}","label",halControlName, "val",vol); if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); + afb_req_fail_f(req, "Invalid query for HAL ctlset", "Invalid query for HAL ctlset: %s",json_object_to_json_string(j_query)); return; } - AFB_DEBUG("Parsed input arguments = routing_id:%d", routingID); - // TODO: Validate that the application ID from which the stream close is coming is the one that opened it, otherwise fail and do nothing - - afb_req_success(req, NULL, "Remove routing completed"); -} - -// Endpoints -PUBLIC void audiohlapi_set_endpoint_volume(struct afb_req req) -{ - json_object *queryJ = NULL; - endpointID_t endpointID = UNDEFINED_ID; - EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; - char * volumeStr = NULL; - int rampTimeMS = 0; - - queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s?i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"volume",&volumeStr,"ramp_time_ms",&rampTimeMS); + // Set the volume using the HAL + err = afb_service_call_sync(pEndpointInfo->halAPIName, "ctlset", j_query, &j_response); if (err) { - afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); + afb_req_fail_f(req, "HAL ctlset failure", "Could not ctlset on HAL: %s",pEndpointInfo->halAPIName); return; } - AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d volume:%s ramp_time_ms: %d", endpointType,endpointID,volumeStr,rampTimeMS); - - // TODO: Parse volume string to support increment/absolute/percent notation - + AFB_DEBUG("HAL ctlset response=%s", json_object_to_json_string(j_response)); + afb_req_success(req, NULL, "Set volume completed"); } -PUBLIC void audiohlapi_get_endpoint_volume(struct afb_req req) +PUBLIC void audiohlapi_get_volume(struct afb_req req) { json_object *queryJ = NULL; endpointID_t endpointID = UNDEFINED_ID; EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; json_object *volumeJ; - double volume = 0.0; queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s:i,s:i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID); @@ -321,13 +416,55 @@ PUBLIC void audiohlapi_get_endpoint_volume(struct afb_req req) } AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d", endpointType,endpointID); - volume = 87.0; // TODO: Get actual volume value - volumeJ = json_object_new_double(volume); + // TODO: Cache during device enumeration for efficiency + EndpointInfoT * pEndpointInfo = GetEndpointInfo(endpointID,endpointType); + if (pEndpointInfo == NULL) + { + afb_req_fail_f(req, "Endpoint not found", "Endpoint information not found for id:%d type%d",endpointID,endpointType); + return; + } + + // Using audio role available from endpoint to target the right HAL control (build string based on convention) + char halControlName[AUDIOHL_MAXHALCONTROLNAME_LENGTH]; + strncpy(halControlName,pEndpointInfo->audioRole,AUDIOHL_MAXHALCONTROLNAME_LENGTH); + strcat(halControlName,"_Vol"); // Use current value, not ramp target + + // Set endpoint volume using HAL services (leveraging ramps etc.) + json_object *j_response, *j_query = NULL; + + // Package query + err = wrap_json_pack(&j_query,"{s:s}","label",halControlName); + if (err) { + afb_req_fail_f(req, "Invalid query for HAL ctlget", "Invalid query for HAL ctlget: %s",json_object_to_json_string(j_query)); + return; + } + + // Set the volume using the HAL + err = afb_service_call_sync(pEndpointInfo->halAPIName, "ctlget", j_query, &j_response); + if (err) { + afb_req_fail_f(req, "HAL ctlget failure", "Could not ctlget on HAL: %s",pEndpointInfo->halAPIName); + return; + } + AFB_INFO("HAL ctlget response=%s", json_object_to_json_string(j_response)); + + // Parse response + json_object * jRespObj = NULL; + json_object_object_get_ex(j_response, "response", &jRespObj); + json_object * jVal = NULL; + json_object_object_get_ex(jRespObj, "val", &jVal); + int val1 = 0, val2 = 0; // Why 2 values? + err = wrap_json_unpack(jVal, "[ii]", &val1, &val2); + if (err) { + afb_req_fail_f(req,"Volume retrieve failed", "Could not retrieve volume value -> %s", json_object_get_string(jVal)); + return; + } + + volumeJ = json_object_new_double((double)val1); afb_req_success(req, volumeJ, "Retrieved volume value"); } -PUBLIC void audiohlapi_set_endpoint_property(struct afb_req req) +PUBLIC void audiohlapi_set_property(struct afb_req req) { json_object *queryJ = NULL; endpointID_t endpointID = UNDEFINED_ID; @@ -335,6 +472,7 @@ PUBLIC void audiohlapi_set_endpoint_property(struct afb_req req) char * propertyName = NULL; char * propValueStr = NULL; int rampTimeMS = 0; + int policyAllowed = 0; queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s:s,s?i}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"property_name",&propertyName,"value",&propValueStr,"ramp_time_ms",&rampTimeMS); @@ -346,10 +484,23 @@ PUBLIC void audiohlapi_set_endpoint_property(struct afb_req req) // TODO: Parse property value string to support increment/absolute/percent notation + // Call policy to allow custom policy actions in current context + policyAllowed = Policy_SetProperty(endpointType, endpointID, propertyName, propValueStr, rampTimeMS); // TODO: Potentially retrieve modified value by policy (e.g. parameter limit) + if (!policyAllowed) + { + afb_req_fail(req, "Audio policy violation", "Set endpoint property not allowed in current context"); + return; + } + + // TODO: Set endpoint property (dispatch on right service target) + // Property targets (Internal,Wwise,Fiberdyne) (e.g. Wwise.ParamX, Fiberdyne.ParamY, Internal.ParamZ) + // Cache value in property list + // TBD + afb_req_success(req, NULL, "Set property completed"); } -PUBLIC void audiohlapi_get_endpoint_property(struct afb_req req) +PUBLIC void audiohlapi_get_property(struct afb_req req) { json_object *queryJ = NULL; endpointID_t endpointID = UNDEFINED_ID; @@ -366,19 +517,22 @@ PUBLIC void audiohlapi_get_endpoint_property(struct afb_req req) } AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d property_name:%s", endpointType,endpointID,propertyName); - value = 93.0; // TODO: Get actual property value + // TODO: Retrieve cached property value + + value = 93.0; // TODO: Get actual property value propertyValJ = json_object_new_double(value); afb_req_success(req, propertyValJ, "Retrieved property value"); } -PUBLIC void audiohlapi_set_endpoint_state(struct afb_req req) +PUBLIC void audiohlapi_set_state(struct afb_req req) { json_object *queryJ = NULL; endpointID_t endpointID = UNDEFINED_ID; EndpointTypeT endpointType = ENDPOINTTYPE_MAXVALUE; char * stateName = NULL; char * stateValue = NULL; + int policyAllowed = 0; queryJ = afb_req_json(req); int err = wrap_json_unpack(queryJ, "{s:i,s:i,s:s,s:s}", "endpoint_type", &endpointType,"endpoint_id",&endpointID,"state_name",&stateName,"state_value",&stateValue); @@ -388,10 +542,28 @@ PUBLIC void audiohlapi_set_endpoint_state(struct afb_req req) } AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d state_name:%s state_value:%s", endpointType,endpointID,stateName,stateValue); + // Check that state provided is within list of known state for this config + char * pDefaultStateValue = g_hash_table_lookup(g_AHLCtx.pDefaultStatesHT, stateName); + if (pDefaultStateValue == NULL) + { + afb_req_fail_f(req, "Invalid arguments", "State provided is not known to configuration query=%s", stateName); + return; + } + + // Call policy to allow custom policy actions in current context + policyAllowed = Policy_SetState(endpointType, endpointID, stateName, stateValue); // TODO: Potentially retrieve modified value by policy (e.g. state change) + if (!policyAllowed) + { + afb_req_fail(req, "Audio policy violation", "Set endpoint state not allowed in current context"); + return; + } + + // Change the state of the endpoint as requested + afb_req_success(req, NULL, "Set endpoint state completed"); } -PUBLIC void audiohlapi_get_endpoint_state(struct afb_req req) +PUBLIC void audiohlapi_get_state(struct afb_req req) { json_object *queryJ = NULL; endpointID_t endpointID = UNDEFINED_ID; @@ -409,6 +581,7 @@ PUBLIC void audiohlapi_get_endpoint_state(struct afb_req req) AFB_DEBUG("Parsed input arguments = endpoint_type:%d endpoint_id:%d state_name:%s", endpointType,endpointID,stateName); stateValJ = json_object_new_string(stateValue); + // return cached value afb_req_success(req, stateValJ, "Retrieved state value"); } @@ -419,18 +592,27 @@ PUBLIC void audiohlapi_post_sound_event(struct afb_req req) json_object *queryJ = NULL; char * eventName = NULL; char * mediaName = NULL; - AudioRoleT audioRole; + char * audioRole = NULL; json_object *audioContext = NULL; + int policyAllowed = 0; queryJ = afb_req_json(req); - int err = wrap_json_unpack(queryJ, "{s:s,s?i,s?s,s?o}", "event_name", &eventName,"audio_role",&audioRole,"media_name",&mediaName,"audio_context",&audioContext); + int err = wrap_json_unpack(queryJ, "{s:s,s:s,s?s,s?o}", "event_name", &eventName,"audio_role",&audioRole,"media_name",&mediaName,"audio_context",&audioContext); if (err) { afb_req_fail_f(req, "Invalid arguments", "Args not a valid json object query=%s", json_object_get_string(queryJ)); return; } - AFB_DEBUG("Parsed input arguments = event_name:%s audio_role:%d media_name:%s", eventName,audioRole,mediaName); + AFB_DEBUG("Parsed input arguments = event_name:%s audio_role:%s media_name:%s", eventName,audioRole,mediaName); - // TODO: Post sound event to rendering services + // Call policy to allow custom policy actions in current context (e.g. cancel playback) + policyAllowed = Policy_PostSoundEvent(eventName, audioRole, mediaName, (void*)audioContext); // TODO: Potentially retrieve modified value by policy (e.g. change media) + if (!policyAllowed) + { + afb_req_fail(req, "Audio policy violation", "Post sound event not allowed in current context"); + return; + } + + // TODO: Post sound event to rendering services (e.g. gstreamer file player wrapper or simple ALSA player) afb_req_success(req, NULL, "Posted sound event"); } @@ -448,3 +630,15 @@ PUBLIC void audiohlapi_subscribe(struct afb_req req) afb_req_success(req, NULL, "Subscribe to events finished"); } + +PUBLIC void audiohlapi_unsubscribe(struct afb_req req) +{ + // json_object *queryJ = NULL; + + // queryJ = afb_req_json(req); + + // TODO: Iterate through array length, parsing the string value to actual events + // TODO: Unsubscribe to appropriate events from other services + + afb_req_success(req, NULL, "Subscribe to events finished"); +} diff --git a/src/ahl-binding.h b/src/ahl-binding.h index 7bd0653..2422963 100644 --- a/src/ahl-binding.h +++ b/src/ahl-binding.h @@ -15,82 +15,85 @@ * limitations under the License. */ - #ifndef AHL_BINDING_INCLUDE #define AHL_BINDING_INCLUDE #define AFB_BINDING_VERSION 2 #include <afb/afb-binding.h> #include <json-c/json.h> +#include <glib.h> + +#include "ahl-interface.h" #ifndef PUBLIC #define PUBLIC #endif -#define UNDEFINED_ID -1 - -typedef int endpointID_t; -typedef int streamID_t; -typedef int routingID_t; - -typedef enum EndpointType { - ENDPOINTTYPE_SOURCE = 0, // source devices - ENDPOINTTYPE_SINK, // sink devices - ENDPOINTTYPE_MAXVALUE // Enum count, keep at the end -} EndpointTypeT; +/////////////// Binding private information ////////////////// -typedef enum AudioRole { - AUDIOROLE_WARNING = 0, // Safety-relevant or critical alerts/alarms - AUDIOROLE_GUIDANCE, // Important user information where user action is expected (e.g. navigation instruction) - AUDIOROLE_NOTIFICATION, // HMI or else notifications (e.g. touchscreen events, speech recognition on/off,...) - AUDIOROLE_COMMUNICATIONS, // Voice communications (e.g. handsfree, speech recognition) - AUDIOROLE_ENTERTAINMENT, // Multimedia content (e.g. tuner, media player, etc.) - AUDIOROLE_SYSTEM, // System level content - AUDIOROLE_DEFAULT, // No specific audio role (legacy applications) - AUDIOROLE_MAXVALUE // Enum count, keep at the end -} AudioRoleT; - -typedef enum AudioDeviceClass { - AUDIODEVICE_SPEAKERMAIN = 0, - AUDIODEVICE_SPEAKERHEADREST, - AUDIODEVICE_HEADSET, - AUDIODEVICE_HEADPHONE, - AUDIODEVICE_LINEOUT, - AUDIODEVICE_LINEIN, - AUDIODEVICE_BLUETOOTH, - AUDIODEVICE_HANDSET, - AUDIODEVICE_HDMI, - AUDIODEVICE_USB, - AUDIODEVICE_TONES, - AUDIODEVICE_VOICE, - AUDIODEVICE_PHONELINK, - AUDIODEVICE_DEFAULT, - AUDIODEVICE_MAXVALUE // Enum count, keep at the end -} AudioDeviceClassT; +#define AUDIOHL_MAX_DEVICE_URI_LENGTH 256 +#define AUDIOHL_MAX_DEVICE_NAME_LENGTH 256 +#define AUDIOHL_MAX_AUDIOROLE_LENGTH 128 +#define AUDIOHL_MAX_HALAPINAME_LENGTH 64 +#define AUDIOHL_POLICY_ACCEPT 1 +#define AUDIOHL_POLICY_REJECT 0 typedef struct EndpointInfo { - endpointID_t endpoint_id; - EndpointTypeT type; - char * name; - // TODO: Consider adding associated device class + endpointID_t endpointID; // Unique endpoint ID (per type) + EndpointTypeT type; // Source or sink device + char deviceName[AUDIOHL_MAX_DEVICE_NAME_LENGTH]; // Device name for applications to display + char deviceURI[AUDIOHL_MAX_DEVICE_URI_LENGTH]; // Associated URI + DeviceURITypeT deviceURIType; // Device URI type (includes audio domain information) + char audioRole[AUDIOHL_MAX_AUDIOROLE_LENGTH]; // Audio role that registered this endpoint -> private + char halAPIName[AUDIOHL_MAX_AUDIOROLE_LENGTH]; // HAL associated with the device (for volume control) -> private + int cardNum; // HW card number -> private + int deviceNum; // HW device number -> private + int subDeviceNum; // HW sub device number -> private + // Cached endpoint properties + GHashTable * pStatesHT; // Keep all known states in key value pairs } EndpointInfoT; typedef struct StreamInfo { - streamID_t stream_id; - char * pcm_name; - EndpointInfoT endpoint_info; + streamID_t streamID; + EndpointInfoT endpointInfo; } StreamInfoT; -typedef struct RoutingInfo { - routingID_t routing_id; - endpointID_t source_id; - endpointID_t sink_id; -} RoutingInfoT; +// Parts of the context that are visible to the policy (for state based decisions) +typedef struct AHLPolicyCtx { + GPtrArray * pSourceEndpoints; // Array of source end points for each audio role (GArray*) + GPtrArray * pSinkEndpoints; // Array of sink end points for each audio role (GArray*) + GArray * pRolePriority; // List of role priorities (int). TODO: Should be hash table with role name as key + GArray * pAudioRoles; // List of audio roles (GString) + GArray * pActiveStreams; // List of active streams (StreamInfoT) + int iNumberRoles; // Number of audio roles from configuration + // TODO: Global properties -> exposed to policy +} AHLPolicyCtxT; + +// Global binding context +typedef struct AHLCtx { + AHLPolicyCtxT policyCtx; + endpointID_t nextSourceEndpointID; // Counter to assign new ID + endpointID_t nextSinkEndpointID; // Counter to assign new ID + endpointID_t nextStreamID; // Counter to assign new ID + GArray * pHALList; // List of HAL dependencies + GHashTable * pDefaultStatesHT; // List of states and default values known to configuration +} AHLCtxT; PUBLIC int AhlBindingInit(); // ahl-deviceenum.c -PUBLIC int EnumerateSources(); -PUBLIC int EnumerateSinks(); +PUBLIC int EnumerateSources(json_object * in_jSourceArray, int in_iRoleIndex, char * in_pRoleName); +PUBLIC int EnumerateSinks(json_object * in_jSinkArray, int in_iRoleIndex, char * in_pRoleName); +// ahl-config.c +PUBLIC int ParseHLBConfig(); +// ahl-policy.c +PUBLIC int Policy_OpenStream(char *pAudioRole, EndpointTypeT endpointType, endpointID_t endpointID); +PUBLIC int Policy_SetVolume(EndpointTypeT endpointType, endpointID_t endpointID, char *volumeStr, int rampTimeMS); +PUBLIC int Policy_SetProperty(EndpointTypeT endpointType, endpointID_t endpointID, char *propertyName, char *propValueStr, int rampTimeMS); +PUBLIC int Policy_SetState(EndpointTypeT endpointType, endpointID_t endpointID, char *pStateName, char *pStateValue); +PUBLIC int Policy_PostSoundEvent(char *eventName, char *audioRole, char *mediaName, void *audioContext); +PUBLIC int Policy_AudioDeviceChange(); -#endif +#define AUDIOHL_MAXHALCONTROLNAME_LENGTH 128 + +#endif // AHL_BINDING_INCLUDE diff --git a/src/ahl-config.c b/src/ahl-config.c new file mode 100644 index 0000000..432910f --- /dev/null +++ b/src/ahl-config.c @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2017 "Audiokinetic Inc" + * Author Francois Thibault <fthibault@audiokinetic.com> + * + * 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. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> +#include <json-c/json.h> +#include "wrap-json.h" + +#include "ahl-binding.h" + +// TODO: Term() to free all allocs upon exit... + +extern AHLCtxT g_AHLCtx; + +PUBLIC int ParseHLBConfig() { + char * versionStr = NULL; + json_object * jAudioRoles = NULL; + json_object * jHALList = NULL; + json_object * jStateList = NULL; + + // TODO: This should be retrieve from binding startup arguments + char configfile_path[256]; + sprintf(configfile_path, "%s/opt/config/ahl-config.json", getenv("HOME")); + AFB_INFO("High-level config file -> %s\n", configfile_path); + + // Open configuration file + json_object *config_JFile=json_object_from_file(configfile_path); + if(config_JFile == NULL) + { + AFB_ERROR("Error: Can't open configuration file -> %s",configfile_path); + return 1; + } + + int err = wrap_json_unpack(config_JFile, "{s:s,s:o,s:o,s:o}", "version", &versionStr,"audio_roles",&jAudioRoles,"hal_list",&jHALList,"state_list",&jStateList); + if (err) { + AFB_ERROR("Invalid configuration file -> %s", configfile_path); + return 1; + } + + // Parse and populate list of known states + g_AHLCtx.pDefaultStatesHT = g_hash_table_new(g_str_hash, g_str_equal); + int iStateListLength = json_object_array_length(jStateList); + for ( int i = 0; i < iStateListLength; i++) + { + char * pStateName = NULL; + char * pStateVal = NULL; + json_object * jStateobject = json_object_array_get_idx(jStateList,i); + err = wrap_json_unpack(jStateobject, "{s:s,s:s}", "name", &pStateName,"default_val",&pStateVal); + if (err) { + AFB_ERROR("Invalid state object -> %s", json_object_get_string(jStateobject)); + return 1; + } + + g_hash_table_insert(g_AHLCtx.pDefaultStatesHT, pStateName, pStateVal); + } + + int iHALListLength = json_object_array_length(jHALList); + int iNumberOfRoles = json_object_array_length(jAudioRoles); + // Dynamically allocated based on number or roles found + g_AHLCtx.pHALList = g_array_sized_new(FALSE, TRUE, sizeof(GString), iHALListLength); + g_AHLCtx.policyCtx.pRolePriority = g_array_sized_new(FALSE, TRUE, sizeof(int), iNumberOfRoles); + g_AHLCtx.policyCtx.pAudioRoles = g_array_sized_new(FALSE, TRUE, sizeof(GString), iNumberOfRoles); + g_AHLCtx.policyCtx.pSourceEndpoints = g_ptr_array_sized_new(iNumberOfRoles); + g_AHLCtx.policyCtx.pSinkEndpoints = g_ptr_array_sized_new(iNumberOfRoles); + g_AHLCtx.policyCtx.iNumberRoles = iNumberOfRoles; + + for (unsigned int i = 0; i < iHALListLength; i++) + { + char * pHAL = NULL; + json_object * jHAL = json_object_array_get_idx(jHALList,i); + pHAL = (char *)json_object_get_string(jHAL); + GString * gHALName = g_string_new( pHAL ); + g_array_append_val( g_AHLCtx.pHALList, *gHALName ); + + // Set dependency on HAL + err = afb_daemon_require_api_v2(pHAL,1) ; + if( err != 0 ) + { + AFB_ERROR("Audio high level API could not set dependenvy on API: %s",pHAL); + return 1; + } + } + + for (unsigned int i = 0; i < iNumberOfRoles; i++) + { + int priority = 0; + json_object * jAudioRole = json_object_array_get_idx(jAudioRoles,i); + json_object * jOutputDevices = NULL; + json_object * jInputDevices = NULL; + char * pRoleName = NULL; + int iNumOutDevices = 0; + int iNumInDevices = 0; + + err = wrap_json_unpack(jAudioRole, "{s:s,s:i,s?o,s?o}", "name", &pRoleName,"priority",&priority,"output",&jOutputDevices,"input",&jInputDevices); + if (err) { + AFB_ERROR("Invalid audio role configuration : %s", json_object_to_json_string(jAudioRole)); + return 1; + } + + if (jOutputDevices) + iNumOutDevices = json_object_array_length(jOutputDevices); + if (jInputDevices) + iNumInDevices = json_object_array_length(jInputDevices); + + GString * gRoleName = g_string_new( pRoleName ); + g_array_append_val( g_AHLCtx.policyCtx.pAudioRoles, *gRoleName ); + g_array_append_val( g_AHLCtx.policyCtx.pRolePriority, priority ); + + // Sources + GArray * pRoleSourceDeviceArray = g_array_new(FALSE, TRUE, sizeof(EndpointInfoT)); + g_ptr_array_add(g_AHLCtx.policyCtx.pSourceEndpoints,pRoleSourceDeviceArray); + if (iNumInDevices) { + err = EnumerateSources(jInputDevices,i,pRoleName); + if (err) { + AFB_ERROR("Invalid input devices : %s", json_object_to_json_string(jInputDevices)); + return 1; + } + } + GArray * pRoleSinkDeviceArray = g_array_new(FALSE, TRUE, sizeof(EndpointInfoT)); + g_ptr_array_add(g_AHLCtx.policyCtx.pSinkEndpoints,pRoleSinkDeviceArray); + if (iNumOutDevices) { + err = EnumerateSinks(jOutputDevices,i,pRoleName); + if (err) { + AFB_ERROR("Invalid output devices : %s", json_object_to_json_string(jOutputDevices)); + return 1; + } + } + } + + // Build lists of all device URI referenced in config file (input/output) + AFB_DEBUG ("Audio high-level - Parse high-level audio configuration done"); + return 0; +} diff --git a/src/ahl-deviceenum.c b/src/ahl-deviceenum.c index 6629de2..b866d52 100644 --- a/src/ahl-deviceenum.c +++ b/src/ahl-deviceenum.c @@ -18,23 +18,417 @@ #define _GNU_SOURCE #include <stdio.h> #include <string.h> -#include <time.h> +#include <alsa/asoundlib.h> +#include <alsa/pcm.h> +#include <json-c/json.h> +#include "wrap-json.h" #include "ahl-binding.h" -PUBLIC int EnumerateSources() { - - // TODO: Use lower level services to build a list of available source devices +extern AHLCtxT g_AHLCtx; + +static endpointID_t CreateNewSourceID() +{ + endpointID_t newID = g_AHLCtx.nextSourceEndpointID; + g_AHLCtx.nextSourceEndpointID++; + return newID; +} + +static endpointID_t CreateNewSinkID() +{ + endpointID_t newID = g_AHLCtx.nextSinkEndpointID; + g_AHLCtx.nextSinkEndpointID++; + return newID; +} + +// Watchout: This function uses strtok and is destructive on the input string (use a copy) +static int SeparateDomainFromDeviceURI( char * in_pDeviceURI, char ** out_pDomain, char ** out_pDevice) +{ + *out_pDomain = strtok(in_pDeviceURI, "."); + if (*out_pDomain == NULL) + { + AFB_ERROR("Error tokenizing device URI -> %s",in_pDeviceURI); + return 1; + } + // TODO: Validate domain is known string (e.g. ALSA,Pulse,GStreamer) + *out_pDevice = strtok(NULL, "."); + if (*out_pDevice == NULL) + { + AFB_ERROR("Error tokenizing device URI -> %s",in_pDeviceURI); + return 1; + } + return 0; +} + +static int IsAlsaDomain(const char * in_pDomainStr) +{ + return (int) (strcasecmp(in_pDomainStr,AUDIOHL_DOMAIN_ALSA) == 0); +} + +static int IsPulseDomain(const char * in_pDomainStr) +{ + return (int) (strcasecmp(in_pDomainStr,AUDIOHL_DOMAIN_PULSE) == 0); +} + +static int IsGStreamerDomain(const char * in_pDomainStr) +{ + return (int) (strcasecmp(in_pDomainStr,AUDIOHL_DOMAIN_GSTREAMER) == 0); +} + +static int IsExternalDomain(const char * in_pDomainStr) +{ + return (int) (strcasecmp(in_pDomainStr,AUDIOHL_DOMAIN_EXTERNAL) == 0); +} + +static int FillALSAPCMInfo( snd_pcm_t * in_pPcmHandle, EndpointInfoT * out_pEndpointInfo ) +{ + snd_pcm_type_t pcmType = 0; + snd_pcm_info_t * pPcmInfo = NULL; + int iAlsaRet = 0; + const char * pDeviceName = NULL; + int retVal = 0; + + // retrieve PCM type + pcmType = snd_pcm_type(in_pPcmHandle); + switch (pcmType) { + case SND_PCM_TYPE_HW: + out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_HW; + break; + case SND_PCM_TYPE_DMIX: + out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_DMIX; + break; + case SND_PCM_TYPE_SOFTVOL: + out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_SOFTVOL; + break; + default: + out_pEndpointInfo->deviceURIType = DEVICEURITYPE_ALSA_OTHER; + break; + } + + iAlsaRet = snd_pcm_info_malloc(&pPcmInfo); + if (iAlsaRet < 0) + { + AFB_WARNING("Error allocating PCM info structure"); + retVal = 1; + goto End; + } + + iAlsaRet = snd_pcm_info(in_pPcmHandle,pPcmInfo); + if (iAlsaRet < 0) + { + AFB_WARNING("Error retrieving PCM device info"); + retVal = 1; + goto End; + } + + // Populate target device name (for application display) + pDeviceName = snd_pcm_info_get_name(pPcmInfo); + if (pDeviceName == NULL) + { + AFB_WARNING("No Alsa device name available"); + retVal = 1; + goto End; + // Could potentially assign a "default" name and carry on with this device + } + strncpy(out_pEndpointInfo->deviceName,pDeviceName,AUDIOHL_MAX_DEVICE_NAME_LENGTH); // Overwritten by HAL name if available + + // get card number + out_pEndpointInfo->cardNum = snd_pcm_info_get_card(pPcmInfo); + if ( out_pEndpointInfo->cardNum < 0 ) + { + AFB_WARNING("No Alsa card number available"); + retVal = 1; + goto End; + } + // get device number + out_pEndpointInfo->deviceNum = snd_pcm_info_get_device(pPcmInfo); + if ( out_pEndpointInfo->deviceNum < 0 ) + { + AFB_WARNING("No Alsa device number available"); + retVal = 1; + goto End; + } + + // get sub-device number + out_pEndpointInfo->subDeviceNum = snd_pcm_info_get_subdevice(pPcmInfo); + if ( out_pEndpointInfo->subDeviceNum < 0 ) + { + AFB_WARNING("No Alsa subdevice number available"); + retVal = 1; + goto End; + } + +End: + if(pPcmInfo) { + snd_pcm_info_free(pPcmInfo); + pPcmInfo = NULL; + } + + return retVal; +} + +static int RetrieveAssociatedHALAPIName(EndpointInfoT * io_pEndpointInfo) +{ + json_object *j_response, *j_query = NULL; + int err; + err = afb_service_call_sync("alsacore", "hallist", j_query, &j_response); + if (err) { + AFB_ERROR("Could not retrieve list of HAL from ALSA core"); + return 1; + } + AFB_DEBUG("ALSAcore hallist response=%s", json_object_to_json_string(j_response)); + + // Look through returned list for matching card + int found = 0; + json_object * jRespObj = NULL; + json_object_object_get_ex(j_response, "response", &jRespObj); + int iNumHAL = json_object_array_length(jRespObj); + for ( int i = 0 ; i < iNumHAL; i++) + { + json_object * jHAL = json_object_array_get_idx(jRespObj,i); + char * pDevIDStr = NULL; + char * pAPIName = NULL; + char * pShortName = NULL; + + int err = wrap_json_unpack(jHAL, "{s:s,s:s,s:s}", "devid", &pDevIDStr,"api", &pAPIName,"shortname",&pShortName); + if (err) { + AFB_ERROR("Could not retrieve devid string=%s", json_object_get_string(jHAL)); + return 1; + } + + // Retrieve card number (e.g. hw:0) + int iCardNum = atoi(pDevIDStr+3); + if (iCardNum == io_pEndpointInfo->cardNum) { + strncpy(io_pEndpointInfo->halAPIName,pAPIName,AUDIOHL_MAX_AUDIOROLE_LENGTH); + strncpy(io_pEndpointInfo->deviceName,pShortName,AUDIOHL_MAX_DEVICE_NAME_LENGTH); + found = 1; + break; + } + } + return !found; +} + +static int InitializeEndpointStates( EndpointInfoT * out_pEndpointInfo ) +{ + //out_pEndpointInfo = g_array_sized_new(FALSE,TRUE,sizeof) + // for list of known states + return 0; +} + +// For a given audio role +PUBLIC int EnumerateSources(json_object * in_jSourceArray, int in_iRoleIndex, char * in_pRoleName) { + + int iNumberDevices = json_object_array_length(in_jSourceArray); + + // Parse and validate list of available devices + for (unsigned int i = 0; i < iNumberDevices; i++) + { + char fullDeviceURI[AUDIOHL_MAX_DEVICE_URI_LENGTH]; // strtok is destructive + char * pDeviceURIDomain = NULL; + char * pFullDeviceURI = NULL; + char * pDeviceURIPCM = NULL; + int err = 0; + EndpointInfoT endpointInfo; + + json_object * jSource = json_object_array_get_idx(in_jSourceArray,i); + + // strip domain name from URI + pFullDeviceURI = (char *)json_object_get_string(jSource); + strncpy(fullDeviceURI,pFullDeviceURI,AUDIOHL_MAX_DEVICE_URI_LENGTH); + err = SeparateDomainFromDeviceURI(fullDeviceURI,&pDeviceURIDomain,&pDeviceURIPCM); + if (err) + { + AFB_WARNING("Invalid device URI string -> %s",fullDeviceURI); + continue; + } + + // non ALSA URI are simply passed to application (no validation) at this time + // In Non ALSA case devices in config are assumed to be available, if not application can fallback on explicit device selection + endpointInfo.cardNum = -1; + endpointInfo.deviceNum = -1; + endpointInfo.cardNum = -1; + strncpy(endpointInfo.deviceName,pDeviceURIPCM,AUDIOHL_MAX_DEVICE_NAME_LENGTH); // Overwritten by HAL name if available + + if (IsAlsaDomain(pDeviceURIDomain)) + { + // TODO: Missing support for loose name matching + // This will require using ALSA hints to get PCM names + // And would iterate over all available devices matching string (possibly all if no filtering is desired for a certain role) + + snd_pcm_t * pPcmHandle = NULL; + + // Get PCM handle + err = snd_pcm_open(&pPcmHandle, pDeviceURIPCM, SND_PCM_STREAM_CAPTURE, 0); + if (err < 0) + { + AFB_NOTICE("Alsa PCM device was not found -> %s", pDeviceURIPCM); + continue; + } + + err = FillALSAPCMInfo(pPcmHandle,&endpointInfo); + if (err) { + AFB_WARNING("Unable to retrieve PCM information for PCM -> %s",pDeviceURIPCM); + snd_pcm_close(pPcmHandle); + continue; + } + + snd_pcm_close(pPcmHandle); + + // Retrieve HAL API name + err = RetrieveAssociatedHALAPIName(&endpointInfo); + if (err) { + AFB_WARNING("SetVolume will fail without HAL association ->%s",endpointInfo.deviceURI); + // Choose not to skip anyhow... + } + } + else if (IsPulseDomain(pDeviceURIDomain)) { + // Pulse domain + // For now display name is device URI directly, could extrapolated using more heuristics or even usins Pulse API later on + endpointInfo.deviceURIType = DEVICEURITYPE_PULSE; + } + else if (IsGStreamerDomain(pDeviceURIDomain)){ + // GStreamer domain + // For now display name is device URI directly, could extrapolated using more heuristics or even usins GStreamer API later on + endpointInfo.deviceURIType = DEVICEURITYPE_GSTREAMER; + } + else if (IsExternalDomain(pDeviceURIDomain)){ + // External domain + endpointInfo.deviceURIType = DEVICEURITYPE_EXTERNAL; + } + else { + // Unknown domain + AFB_WARNING("Unknown domain in device URI string -> %s",fullDeviceURI); + continue; + } + + err = InitializeEndpointStates( &endpointInfo ); + if (err) { + AFB_ERROR("Cannot initialize endpoint states for URI -> %s",fullDeviceURI); + continue; + } + + strncpy(endpointInfo.deviceURI,pDeviceURIPCM,AUDIOHL_MAX_DEVICE_URI_LENGTH); + strncpy(endpointInfo.audioRole,in_pRoleName,AUDIOHL_MAX_AUDIOROLE_LENGTH); + endpointInfo.endpointID = CreateNewSourceID(); + endpointInfo.type = ENDPOINTTYPE_SOURCE; + + // add to structure to list of available source devices + GArray * pRoleSourceDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSourceEndpoints, in_iRoleIndex ); + g_array_append_val(pRoleSourceDeviceArray, endpointInfo); + + } // for all devices + AFB_DEBUG ("Audio high-level - Enumerate sources done"); return 0; } -PUBLIC int EnumerateSinks() { - - // TODO: Use lower level services to build a list of available sink devices - +// For a given audio role +PUBLIC int EnumerateSinks(json_object * in_jSinkArray, int in_iRoleIndex, char * in_pRoleName) { + + int iNumberDevices = json_object_array_length(in_jSinkArray); + + // Parse and validate list of available devices + for (unsigned int i = 0; i < iNumberDevices; i++) + { + char fullDeviceURI[AUDIOHL_MAX_DEVICE_URI_LENGTH]; // strtok is destructive + char * pDeviceURIDomain = NULL; + char * pFullDeviceURI = NULL; + char * pDeviceURIPCM = NULL; + int err = 0; + EndpointInfoT endpointInfo; + + json_object * jSink = json_object_array_get_idx(in_jSinkArray,i); + + // strip domain name from URI + pFullDeviceURI = (char*)json_object_get_string(jSink); + strncpy(fullDeviceURI,pFullDeviceURI,AUDIOHL_MAX_DEVICE_URI_LENGTH); + err = SeparateDomainFromDeviceURI(fullDeviceURI,&pDeviceURIDomain,&pDeviceURIPCM); + if (err) + { + AFB_WARNING("Invalid device URI string -> %s",fullDeviceURI); + continue; + } + + // non ALSA URI are simply passed to application (no validation) at this time + // In Non ALSA case devices in config are assumed to be available, if not application can fallback on explicit device selection + + endpointInfo.cardNum = -1; + endpointInfo.deviceNum = -1; + endpointInfo.cardNum = -1; + strncpy(endpointInfo.deviceName,pDeviceURIPCM,AUDIOHL_MAX_DEVICE_NAME_LENGTH); + + if (IsAlsaDomain(pDeviceURIDomain)) + { + // TODO: Missing support for loose name matching + // This will require using ALSA hints to get PCM names + // And would iterate over all available devices matching string (possibly all if no filtering is desired for a certain role) + + snd_pcm_t * pPcmHandle = NULL; + + // get PCM handle + err = snd_pcm_open(&pPcmHandle, pDeviceURIPCM, SND_PCM_STREAM_PLAYBACK, 0); + if (err < 0) + { + AFB_NOTICE("Alsa PCM device was not found -> %s", pDeviceURIPCM); + continue; + } + + err = FillALSAPCMInfo(pPcmHandle,&endpointInfo); + if (err) { + AFB_WARNING("Unable to retrieve PCM information for PCM -> %s",pDeviceURIPCM); + snd_pcm_close(pPcmHandle); + continue; + } + + snd_pcm_close(pPcmHandle); + + // Retrieve HAL API name + err = RetrieveAssociatedHALAPIName(&endpointInfo); + if (err) { + //AFB_WARNING("SetVolume w fail without HAL association ->%s",endpointInfo.deviceURI); + continue; + } + } + else if (IsPulseDomain(pDeviceURIDomain)) { + // Pulse domain + // For now display name is device URI directly, could extrapolated using more heuristics or even usins Pulse API later on + endpointInfo.deviceURIType = DEVICEURITYPE_PULSE; + + } + else if (IsGStreamerDomain(pDeviceURIDomain)){ + // GStreamer domain + // For now display name is device URI directly, could extrapolated using more heuristics or even usins GStreamer API later on + endpointInfo.deviceURIType = DEVICEURITYPE_GSTREAMER; + } + else if (IsExternalDomain(pDeviceURIDomain)){ + // External domain + + endpointInfo.deviceURIType = DEVICEURITYPE_EXTERNAL; + } + else { + // Unknown domain + AFB_WARNING("Unknown domain in device URI string -> %s",fullDeviceURI); + continue; + } + + err = InitializeEndpointStates( &endpointInfo ); + if (err) { + AFB_ERROR("Cannot initialize endpoint states for URI -> %s",fullDeviceURI); + continue; + } + + strncpy(endpointInfo.deviceURI,pDeviceURIPCM,AUDIOHL_MAX_DEVICE_URI_LENGTH); + strncpy(endpointInfo.audioRole,in_pRoleName,AUDIOHL_MAX_AUDIOROLE_LENGTH); + endpointInfo.endpointID = CreateNewSinkID(); + endpointInfo.type = ENDPOINTTYPE_SINK; + + // add to structure to list of available source devices + GArray * pRoleSinkDeviceArray = g_ptr_array_index( g_AHLCtx.policyCtx.pSinkEndpoints, in_iRoleIndex ); + g_array_append_val(pRoleSinkDeviceArray, endpointInfo); + + } // for all devices + AFB_DEBUG ("Audio high-level - Enumerate sinks done"); return 0; } - diff --git a/src/ahl-interface.h b/src/ahl-interface.h new file mode 100644 index 0000000..781bb05 --- /dev/null +++ b/src/ahl-interface.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2017 "Audiokinetic Inc" + * Author Francois Thibault <fthibault@audiokinetic.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef AHL_INTERFACE_INCLUDE +#define AHL_INTERFACE_INCLUDE + +#define UNDEFINED_ID -1 + +typedef int endpointID_t; +typedef int streamID_t; + +typedef enum EndpointType { + ENDPOINTTYPE_SOURCE = 0, // source devices + ENDPOINTTYPE_SINK, // sink devices + ENDPOINTTYPE_MAXVALUE // Enum count, keep at the end +} EndpointTypeT; + +// Standardized name for common audio roles (not enforced in any way, just helps system being more compatible) +#define AUDIOROLE_WARNING "warning" // Safety-relevant or critical alerts/alarms +#define AUDIOROLE_GUIDANCE "guidance" // Important user information where user action is expected (e.g. navigation instruction) +#define AUDIOROLE_NOTIFICATION "notification" // HMI or else notifications (e.g. touchscreen events, speech recognition on/off,...) +#define AUDIOROLE_COMMUNICATION "communications" // Voice communications (e.g. handsfree, speech recognition) +#define AUDIOROLE_ENTERTAINMENT "entertainment" // Multimedia content (e.g. tuner, media player, etc.) +#define AUDIOROLE_SYSTEM "system" // System level content or development +#define AUDIOROLE_STARTUP "startup" // Early (startup) sound +#define AUDIOROLE_SHUTDOWN "shutdown" // Late (shutdown) sound +#define AUDIOROLE_NONE "none" // Non-assigned / legacy applications + +typedef enum DeviceURIType { + DEVICEURITYPE_ALSA_HW = 0, // Alsa hardware device URI + DEVICEURITYPE_ALSA_DMIX, // Alsa Dmix device URI (only for playback devices) + DEVICEURITYPE_ALSA_DSNOOP, // Alsa DSnoop device URI (only for capture devices) + DEVICEURITYPE_ALSA_SOFTVOL, // Alsa softvol device URI + DEVICEURITYPE_ALSA_OTHER, // Alsa domain URI device of unspecified type + DEVICEURITYPE_PULSE, // Pulse device URI + DEVICEURITYPE_GSTREAMER, // GStreamer device URI + DEVICEURITYPE_EXTERNAL, // Device URI for external ECU device + DEVICEURITYPE_MAXVALUE // Enum count, keep at the end +} DeviceURITypeT; + +// Standardized list of properties (string used for extensibility) +#define AUDIOHL_PROPERTY_BALANCE "balance" +#define AUDIOHL_PROPERTY_FADE "fade" +#define AUDIOHL_PROPERTY_EQ_LOW "eq_low" +#define AUDIOHL_PROPERTY_EQ_MID "eq_mid" +#define AUDIOHL_PROPERTY_EQ_HIGH "eq_high" + +// Standardized list of state names/values (string used for extensibility) +#define AUDIOHL_STATE_NAME_ACTIVE "active" +#define AUDIOHL_STATE_NAME_MUTE "mute" +#define AUDIOHL_STATE_VALUE_ON "on" +#define AUDIOHL_STATE_VALUE_OFF "off" + +// Known audio domain string definitions (for configuration file format) +#define AUDIOHL_DOMAIN_ALSA "Alsa" +#define AUDIOHL_DOMAIN_PULSE "Pulse" +#define AUDIOHL_DOMAIN_GSTREAMER "GStreamer" +#define AUDIOHL_DOMAIN_EXTERNAL "External" + +#endif // AHL_INTERFACE_INCLUDE diff --git a/src/ahl-policy.c b/src/ahl-policy.c new file mode 100644 index 0000000..7741afd --- /dev/null +++ b/src/ahl-policy.c @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2017 "Audiokinetic Inc" + * Author Francois Thibault <fthibault@audiokinetic.com> + * + * 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. + */ + +#define _GNU_SOURCE +#include <stdio.h> +#include <string.h> + +#include "ahl-binding.h" + + +// This file provides example of custom, business logic driven policy actions that can affect behavior of the high level +// TODO: Currently only isolated in separate source file. Objective is to make this to at least a shared lib plug-in +// Going a step further would be to implement this within afb-controler policy plug-in (would require bi-directional access to HLB context) + +extern AHLCtxT g_AHLCtx; // TODO: Cannot stay if moved to external module + +// Attribute of high-level binding (parsed), policy enforced +typedef enum InterruptedBehavior { + INTERRUPTEDBEHAVIOR_CONTINUE = 0, // Continue to play when interrupted (e.g. media will be ducked) + INTERRUPTEDBEHAVIOR_CANCEL, // Abort playback when interrupted (e.g. non-important HMI feedback that does not make sense later) + INTERRUPTEDBEHAVIOR_PAUSE, // Pause source when interrupted, to be resumed afterwards (e.g. non-temporal guidance) + INTERRUPTEDBEHAVIOR_MAXVALUE, // Enum count, keep at the end +} InterruptedBehaviorT; + +PUBLIC int Policy_OpenStream(char *pAudioRole, EndpointTypeT endpointType, endpointID_t endpointID) +{ + return 1; // Policy allowed +} + +PUBLIC int Policy_SetVolume(EndpointTypeT endpointType, endpointID_t endpointID, char *volumeStr, int rampTimeMS) +{ + return 1; // Policy allowed +} + +PUBLIC int Policy_SetProperty(EndpointTypeT endpointType, endpointID_t endpointID, char *propertyName, char *propValueStr, int rampTimeMS) +{ + return 1; // Policy allowed +} + + +PUBLIC int Policy_SetState(EndpointTypeT endpointType, endpointID_t endpointID, char *pStateName, char *pStateValue) +{ + + + //Mute + if(strcmp(pStateName, AUDIOHL_STATE_NAME_MUTE) == 0) + { + if(strcmp(pStateName, AUDIOHL_STATE_NAME_MUTE) == 0) + + + return AUDIOHL_POLICY_ACCEPT; + } + + //Retrieve global context + + + //Active rule check + + //Ducking rule settings + + + return AUDIOHL_POLICY_ACCEPT; +} + +PUBLIC int Policy_PostSoundEvent(char *eventName, char *audioRole, char *mediaName, void *audioContext) +{ + return 1; // Policy allowed +} + +PUBLIC int Policy_AudioDeviceChange() +{ + // Allow or disallow a new audio device + // Raise events to engage device switching + // Policy can also assign audio role(s) for device + return 1; // Policy allowed +}
\ No newline at end of file |