Introduce to a project aurum accepted/tizen/unified/20191213.115146 submit/tizen/20191211.080758 submit/tizen_5.5/20191212.063236
authorWonki Kim <wonki_.kim@samsung.com>
Thu, 4 Jul 2019 08:34:17 +0000 (17:34 +0900)
committerWonki Kim <wonki_.kim@samsung.com>
Wed, 11 Dec 2019 08:02:57 +0000 (17:02 +0900)
This commit introduces a project which has designed for
Automation framework for Ui testing,
Au{79},
Aurum.

Change-Id: I3407b40d4e5340f88fac170c9bb278356a236d4c

118 files changed:
.clang-format [new file with mode: 0644]
.gitignore [new file with mode: 0644]
.gitmodules [new file with mode: 0644]
AUTHORS [new file with mode: 0644]
COPYING [new file with mode: 0644]
NOTICE [new file with mode: 0644]
bootstrap/meson.build [new file with mode: 0644]
bootstrap/server/aurum-bootstrap.service [new file with mode: 0644]
bootstrap/server/inc/AurumServiceImpl.h [new file with mode: 0644]
bootstrap/server/inc/Commands/ClearCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/ClickCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/CloseAppCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/Command.h [new file with mode: 0644]
bootstrap/server/inc/Commands/Commands.h [new file with mode: 0644]
bootstrap/server/inc/Commands/FindElementCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/FlickCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/GetAppInfoCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/GetAttributeCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/GetDeviceTimeCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/GetLocationCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/GetSizeCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/GetValueCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/InstallAppCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/KillServerCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/LaunchAppCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/LongClickCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/PostCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/PreCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/RemoveAppCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/SendKeyCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/SetValueCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/SyncCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/TouchDownCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/TouchMoveCommand.h [new file with mode: 0644]
bootstrap/server/inc/Commands/TouchUpCommand.h [new file with mode: 0644]
bootstrap/server/inc/ObjectMapper.h [new file with mode: 0644]
bootstrap/server/meson.build [new file with mode: 0644]
bootstrap/server/src/AurumServiceImpl.cc [new file with mode: 0644]
bootstrap/server/src/BootstrapServer.cc [new file with mode: 0644]
bootstrap/server/src/Commands/ClearCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/ClickCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/CloseAppCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/Command.cc [new file with mode: 0644]
bootstrap/server/src/Commands/FindElementCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/FlickCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/GetAppInfoCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/GetAttributeCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/GetDeviceTimeCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/GetLocationCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/GetSizeCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/GetValueCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/InstallAppCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/KillServerCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/LaunchAppCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/LongClickCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/PostCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/PreCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/RemoveAppCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/SendKeyCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/SetValueCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/SyncCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/TouchDownCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/TouchMoveCommand.cc [new file with mode: 0644]
bootstrap/server/src/Commands/TouchUpCommand.cc [new file with mode: 0644]
bootstrap/server/src/ObjectMapper.cc [new file with mode: 0644]
libaurum/inc/Accessible.h [new file with mode: 0644]
libaurum/inc/AccessibleNode.h [new file with mode: 0644]
libaurum/inc/Comparer.h [new file with mode: 0644]
libaurum/inc/DeviceImpl/TM1Impl.h [new file with mode: 0644]
libaurum/inc/IDevice.h [new file with mode: 0644]
libaurum/inc/ISearchable.h [new file with mode: 0644]
libaurum/inc/PartialMatch.h [new file with mode: 0644]
libaurum/inc/Sel.h [new file with mode: 0644]
libaurum/inc/UiDevice.h [new file with mode: 0644]
libaurum/inc/UiObject.h [new file with mode: 0644]
libaurum/inc/UiSelector.h [new file with mode: 0644]
libaurum/inc/Until.h [new file with mode: 0644]
libaurum/inc/Waiter.h [new file with mode: 0644]
libaurum/meson.build [new file with mode: 0644]
libaurum/src/Accessible.cc [new file with mode: 0644]
libaurum/src/AccessibleNode.cc [new file with mode: 0644]
libaurum/src/Comparer.cc [new file with mode: 0644]
libaurum/src/DeviceImpl/TM1Impl.cc [new file with mode: 0644]
libaurum/src/PartialMatch.cc [new file with mode: 0644]
libaurum/src/Sel.cc [new file with mode: 0644]
libaurum/src/UiDevice.cc [new file with mode: 0644]
libaurum/src/UiObject.cc [new file with mode: 0644]
libaurum/src/UiSelector.cc [new file with mode: 0644]
libaurum/src/Until.cc [new file with mode: 0644]
libaurum/src/Waiter.cc [new file with mode: 0644]
libloguru/inc/loguru.hpp [new file with mode: 0644]
libloguru/meson.build [new file with mode: 0644]
libloguru/src/loguru.cpp [new file with mode: 0644]
meson.build [new file with mode: 0644]
meson_options.txt [new file with mode: 0644]
misc/down_grpc.sh [new file with mode: 0755]
misc/env.sh [new file with mode: 0644]
misc/setup_device.sh [new file with mode: 0755]
packaging/aurum.manifest [new file with mode: 0644]
packaging/aurum.spec [new file with mode: 0644]
protocol/aurum.proto [new file with mode: 0644]
protocol/empty.cpp [new file with mode: 0644]
protocol/examples/node/README [new file with mode: 0644]
protocol/examples/node/client.js [new file with mode: 0644]
protocol/examples/node/gen.sh [new file with mode: 0755]
protocol/examples/node/package-lock.json [new file with mode: 0644]
protocol/examples/node/package.json [new file with mode: 0644]
protocol/examples/python/README [new file with mode: 0644]
protocol/examples/python/gen.sh [new file with mode: 0755]
protocol/examples/python/killServer.py [new file with mode: 0644]
protocol/examples/python/org.tizen.uicomponents.arm.tpk [new file with mode: 0644]
protocol/examples/python/requirements.txt [new file with mode: 0644]
protocol/examples/python/sample01.py [new file with mode: 0644]
protocol/examples/python/sample02.py [new file with mode: 0644]
protocol/examples/python/sample03.py [new file with mode: 0644]
protocol/meson.build [new file with mode: 0644]
tests/meson.build [new file with mode: 0644]
tests/ua_test.cpp [new file with mode: 0644]

diff --git a/.clang-format b/.clang-format
new file mode 100644 (file)
index 0000000..10f4c7a
--- /dev/null
@@ -0,0 +1,90 @@
+---
+Language:        Cpp
+BasedOnStyle:  Google
+AccessModifierOffset: -4
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: false
+AlignConsecutiveDeclarations: true
+AlignEscapedNewlinesLeft: true
+AlignOperands:   true
+AlignTrailingComments: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: true
+AllowShortLoopsOnASingleLine: true
+AlwaysBreakAfterDefinitionReturnType: None
+AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: true
+AlwaysBreakTemplateDeclarations: true
+BinPackArguments: true
+BinPackParameters: true
+BraceWrapping:
+  AfterClass:      false
+  AfterControlStatement: false
+  AfterEnum:       false
+  AfterFunction:   false
+  AfterNamespace:  false
+  AfterObjCDeclaration: false
+  AfterStruct:     false
+  AfterUnion:      false
+  BeforeCatch:     false
+  BeforeElse:      false
+  IndentBraces:    false
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: WebKit
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+ColumnLimit:     80
+CommentPragmas:  '^ IWYU pragma:'
+ConstructorInitializerAllOnOneLineOrOnePerLine: true
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: true
+DisableFormat:   false
+ExperimentalAutoDetectBinPacking: false
+ForEachMacros:   [ foreach, Q_FOREACH, BOOST_FOREACH ]
+IncludeCategories:
+  - Regex:           '^<.*\.h>'
+    Priority:        1
+  - Regex:           '^<.*'
+    Priority:        2
+  - Regex:           '.*'
+    Priority:        3
+IndentCaseLabels: false
+IndentWidth:     4
+IndentWrappedFunctionNames: false
+KeepEmptyLinesAtTheStartOfBlocks: false
+MacroBlockBegin: ''
+MacroBlockEnd:   ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBlockIndentWidth: 2
+ObjCSpaceAfterProperty: false
+ObjCSpaceBeforeProtocolList: false
+PenaltyBreakBeforeFirstCallParameter: 1
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 200
+PointerAlignment: Right
+ReflowComments:  true
+SortIncludes:    true
+SpaceAfterCStyleCast: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeParens: ControlStatements
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 2
+SpacesInAngles:  false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard:        Auto
+TabWidth:        4
+UseTab:          Never
+...
+
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..4c102df
--- /dev/null
@@ -0,0 +1,11 @@
+build*/
+.vscode/
+misc/rpm/
+node_modules/
+*_pb.js
+__pycache__
+*.pyc
+*.pyo
+*.pc
+v
+aurum_pb2*.py
diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/AUTHORS b/AUTHORS
new file mode 100644 (file)
index 0000000..e183b0c
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1 @@
+Wonki Kim <wonki_.kim@samsung.com>
diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,202 @@
+
+                                 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 [yyyy] [name of copyright owner]
+
+   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/NOTICE b/NOTICE
new file mode 100644 (file)
index 0000000..dcf3561
--- /dev/null
+++ b/NOTICE
@@ -0,0 +1,3 @@
+Copyright (c) 2019 Samsung Electronics Co., Ltd. All rights reserved.
+Except as noted, this software is licensed under Apache License, Version 2.
+Please, see the COPYING file for Apache License terms and conditions.
diff --git a/bootstrap/meson.build b/bootstrap/meson.build
new file mode 100644 (file)
index 0000000..874ed25
--- /dev/null
@@ -0,0 +1 @@
+subdir('server')
diff --git a/bootstrap/server/aurum-bootstrap.service b/bootstrap/server/aurum-bootstrap.service
new file mode 100644 (file)
index 0000000..eafa1b2
--- /dev/null
@@ -0,0 +1,7 @@
+[Unit]
+Description=Server for testing UI testing
+
+[Service]
+Type=simple
+ExecStart=/usr/bin/aurum_bootstrap
+EnvironmentFile=-/run/tizen-system-env
diff --git a/bootstrap/server/inc/AurumServiceImpl.h b/bootstrap/server/inc/AurumServiceImpl.h
new file mode 100644 (file)
index 0000000..935094b
--- /dev/null
@@ -0,0 +1,91 @@
+#ifndef aurum_IMPL_H
+#define aurum_IMPL_H
+
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "aurum.grpc.pb.h"
+
+class aurumServiceImpl final : public aurum::Bootstrap::Service {
+public:
+    aurumServiceImpl();
+    virtual ~aurumServiceImpl();
+
+protected:
+    ::grpc::Status execute(Command &cmd);
+
+public:
+    ::grpc::Status sync(::grpc::ServerContext *  context,
+                        const ::aurum::ReqEmpty *request,
+                        ::aurum::RspEmpty *      response) override;
+    ::grpc::Status killServer(::grpc::ServerContext *  context,
+                              const ::aurum::ReqEmpty *request,
+                              ::aurum::RspEmpty *      response) override;
+
+    ::grpc::Status findElement(::grpc::ServerContext *        context,
+                               const ::aurum::ReqFindElement *request,
+                               ::aurum::RspFindElement *response) override;
+
+    ::grpc::Status getValue(::grpc::ServerContext *     context,
+                            const ::aurum::ReqGetValue *request,
+                            ::aurum::RspGetValue *      response) override;
+    ::grpc::Status setValue(::grpc::ServerContext *     context,
+                            const ::aurum::ReqSetValue *request,
+                            ::aurum::RspSetValue *      response) override;
+    ::grpc::Status getAttribute(::grpc::ServerContext *         context,
+                                const ::aurum::ReqGetAttribute *request,
+                                ::aurum::RspGetAttribute *response) override;
+    ::grpc::Status getSize(::grpc::ServerContext *    context,
+                           const ::aurum::ReqGetSize *request,
+                           ::aurum::RspGetSize *      response) override;
+    ::grpc::Status clear(::grpc::ServerContext *  context,
+                         const ::aurum::ReqClear *request,
+                         ::aurum::RspClear *      response) override;
+    ::grpc::Status installApp(
+        ::grpc::ServerContext *                        context,
+        ::grpc::ServerReader< ::aurum::ReqInstallApp> *reader,
+        ::aurum::RspInstallApp *                       response) override;
+    ::grpc::Status removeApp(::grpc::ServerContext *      context,
+                             const ::aurum::ReqRemoveApp *request,
+                             ::aurum::RspRemoveApp *      response) override;
+    ::grpc::Status getAppInfo(::grpc::ServerContext *       context,
+                              const ::aurum::ReqGetAppInfo *request,
+                              ::aurum::RspGetAppInfo *      response) override;
+    ::grpc::Status launchApp(::grpc::ServerContext *      context,
+                             const ::aurum::ReqLaunchApp *request,
+                             ::aurum::RspLaunchApp *      response) override;
+    ::grpc::Status closeApp(::grpc::ServerContext *     context,
+                            const ::aurum::ReqCloseApp *request,
+                            ::aurum::RspCloseApp *      response) override;
+
+    ::grpc::Status click(::grpc::ServerContext *  context,
+                         const ::aurum::ReqClick *request,
+                         ::aurum::RspClick *      response) override;
+    ::grpc::Status longClick(::grpc::ServerContext *  context,
+                             const ::aurum::ReqClick *request,
+                             ::aurum::RspClick *      response) override;
+    ::grpc::Status flick(::grpc::ServerContext *  context,
+                         const ::aurum::ReqFlick *request,
+                         ::aurum::RspFlick *      response) override;
+
+    ::grpc::Status touchDown(::grpc::ServerContext *      context,
+                             const ::aurum::ReqTouchDown *request,
+                             ::aurum::RspTouchDown *      response) override;
+    ::grpc::Status touchUp(::grpc::ServerContext *    context,
+                           const ::aurum::ReqTouchUp *request,
+                           ::aurum::RspTouchUp *      response) override;
+    ::grpc::Status touchMove(::grpc::ServerContext *      context,
+                             const ::aurum::ReqTouchMove *request,
+                             ::aurum::RspTouchMove *      response) override;
+
+    ::grpc::Status getDeviceTime(::grpc::ServerContext *          context,
+                                 const ::aurum::ReqGetDeviceTime *request,
+                                 ::aurum::RspGetDeviceTime *response) override;
+    ::grpc::Status getLocation(::grpc::ServerContext *        context,
+                               const ::aurum::ReqGetLocation *request,
+                               ::aurum::RspGetLocation *response) override;
+    ::grpc::Status sendKey(::grpc::ServerContext *context,
+                           const ::aurum::ReqKey *request,
+                           ::aurum::RspKey *      response) override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/ClearCommand.h b/bootstrap/server/inc/Commands/ClearCommand.h
new file mode 100644 (file)
index 0000000..7883290
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef CLEAR_COMMAND_H
+#define CLEAR_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class ClearCommand : public Command {
+private:
+    const ::aurum::ReqClear* mRequest;
+    ::aurum::RspClear*       mResponse;
+
+public:
+    ClearCommand(const ::aurum::ReqClear* request, ::aurum::RspClear* response);
+    ;
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/ClickCommand.h b/bootstrap/server/inc/Commands/ClickCommand.h
new file mode 100644 (file)
index 0000000..d731dd6
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef CLICK_COMMAND_H
+#define CLICK_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class ClickCommand : public Command {
+private:
+    const ::aurum::ReqClick* mRequest;
+    ::aurum::RspClick*       mResponse;
+
+public:
+    ClickCommand(const ::aurum::ReqClick* request, ::aurum::RspClick* response);
+    ;
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/CloseAppCommand.h b/bootstrap/server/inc/Commands/CloseAppCommand.h
new file mode 100644 (file)
index 0000000..e4eb3d3
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef CLOSE_APP_COMMAND_H
+#define CLOSE_APP_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class CloseAppCommand : public Command {
+private:
+    const ::aurum::ReqCloseApp* mRequest;
+    ::aurum::RspCloseApp*       mResponse;
+
+public:
+    CloseAppCommand(const ::aurum::ReqCloseApp* request,
+                    ::aurum::RspCloseApp*       response);
+    ;
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/Command.h b/bootstrap/server/inc/Commands/Command.h
new file mode 100644 (file)
index 0000000..d443856
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef COMMAND_H
+#define COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+
+#include "config.h"
+
+class Command {
+public:
+    virtual ~Command(){};
+    virtual ::grpc::Status execute() = 0;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/Commands.h b/bootstrap/server/inc/Commands/Commands.h
new file mode 100644 (file)
index 0000000..7a5acd8
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef COMMANDS_H
+#define COMMANDS_H
+
+#include "Commands/KillServerCommand.h"
+#include "Commands/SyncCommand.h"
+
+#include "Commands/FindElementCommand.h"
+
+#include "Commands/GetAttributeCommand.h"
+#include "Commands/GetValueCommand.h"
+#include "Commands/SetValueCommand.h"
+
+#include "Commands/ClearCommand.h"
+#include "Commands/GetSizeCommand.h"
+
+#include "Commands/ClickCommand.h"
+#include "Commands/FlickCommand.h"
+#include "Commands/LongClickCommand.h"
+
+#include "Commands/TouchDownCommand.h"
+#include "Commands/TouchMoveCommand.h"
+#include "Commands/TouchUpCommand.h"
+
+#include "Commands/CloseAppCommand.h"
+#include "Commands/GetAppInfoCommand.h"
+#include "Commands/InstallAppCommand.h"
+#include "Commands/LaunchAppCommand.h"
+#include "Commands/RemoveAppCommand.h"
+
+#include "Commands/GetDeviceTimeCommand.h"
+#include "Commands/GetLocationCommand.h"
+#include "Commands/SendKeyCommand.h"
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/FindElementCommand.h b/bootstrap/server/inc/Commands/FindElementCommand.h
new file mode 100644 (file)
index 0000000..989ad81
--- /dev/null
@@ -0,0 +1,25 @@
+#ifndef FIND_ELEMENT_COMMAND_H
+#define FIND_ELEMENT_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class FindElementCommand : public Command {
+private:
+    const ::aurum::ReqFindElement* mRequest;
+    ::aurum::RspFindElement*       mResponse;
+
+private:
+    ObjectMapper* mObjMap;
+
+public:
+    FindElementCommand(const ::aurum::ReqFindElement* request,
+                       ::aurum::RspFindElement*       response);
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/FlickCommand.h b/bootstrap/server/inc/Commands/FlickCommand.h
new file mode 100644 (file)
index 0000000..f78e1e4
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef FLICK_COMMAND_H
+#define FLICK_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class FlickCommand : public Command {
+private:
+    const ::aurum::ReqFlick* mRequest;
+    ::aurum::RspFlick*       mResponse;
+
+public:
+    FlickCommand(const ::aurum::ReqFlick* request, ::aurum::RspFlick* response);
+    ;
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/GetAppInfoCommand.h b/bootstrap/server/inc/Commands/GetAppInfoCommand.h
new file mode 100644 (file)
index 0000000..1a9a146
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef IS_APP_INSTALLED_COMMAND_H
+#define IS_APP_INSTALLED_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class GetAppInfoCommand : public Command {
+private:
+    const ::aurum::ReqGetAppInfo* mRequest;
+    ::aurum::RspGetAppInfo*       mResponse;
+
+public:
+    GetAppInfoCommand(const ::aurum::ReqGetAppInfo* request,
+                      ::aurum::RspGetAppInfo*       response);
+    ;
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/GetAttributeCommand.h b/bootstrap/server/inc/Commands/GetAttributeCommand.h
new file mode 100644 (file)
index 0000000..5bed89e
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef GET_ATTRIBUTE_COMMAND_H
+#define GET_ATTRIBUTE_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class GetAttributeCommand : public Command {
+private:
+    const ::aurum::ReqGetAttribute* mRequest;
+    ::aurum::RspGetAttribute*       mResponse;
+
+public:
+    GetAttributeCommand(const ::aurum::ReqGetAttribute* request,
+                        ::aurum::RspGetAttribute*       response);
+    ;
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/GetDeviceTimeCommand.h b/bootstrap/server/inc/Commands/GetDeviceTimeCommand.h
new file mode 100644 (file)
index 0000000..4521709
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef GET_DEVICE_TIME_COMMAND_H
+#define GET_DEVICE_TIME_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class GetDeviceTimeCommand : public Command {
+private:
+    const ::aurum::ReqGetDeviceTime* mRequest;
+    ::aurum::RspGetDeviceTime*       mResponse;
+
+public:
+    GetDeviceTimeCommand(const ::aurum::ReqGetDeviceTime* request,
+                         ::aurum::RspGetDeviceTime*       response);
+    ;
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/GetLocationCommand.h b/bootstrap/server/inc/Commands/GetLocationCommand.h
new file mode 100644 (file)
index 0000000..2cb1341
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef GET_LOCATION_COMMAND_H
+#define GET_LOCATION_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class GetLocationCommand : public Command {
+private:
+    const ::aurum::ReqGetLocation* mRequest;
+    ::aurum::RspGetLocation*       mResponse;
+
+public:
+    GetLocationCommand(const ::aurum::ReqGetLocation* request,
+                       ::aurum::RspGetLocation*       response);
+    ;
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/GetSizeCommand.h b/bootstrap/server/inc/Commands/GetSizeCommand.h
new file mode 100644 (file)
index 0000000..d6c8a33
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef GET_SIZE_COMMAND_H
+#define GET_SIZE_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class GetSizeCommand : public Command {
+private:
+    const ::aurum::ReqGetSize* mRequest;
+    ::aurum::RspGetSize*       mResponse;
+
+public:
+    GetSizeCommand(const ::aurum::ReqGetSize* request,
+                   ::aurum::RspGetSize*       response);
+    ;
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/GetValueCommand.h b/bootstrap/server/inc/Commands/GetValueCommand.h
new file mode 100644 (file)
index 0000000..ccbd6ec
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef GET_VALUE_COMMAND_H
+#define GET_VALUE_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class GetValueCommand : public Command {
+private:
+    const ::aurum::ReqGetValue* mRequest;
+    ::aurum::RspGetValue*       mResponse;
+
+public:
+    GetValueCommand(const ::aurum::ReqGetValue* request,
+                    ::aurum::RspGetValue*       response);
+    ;
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/InstallAppCommand.h b/bootstrap/server/inc/Commands/InstallAppCommand.h
new file mode 100644 (file)
index 0000000..4ddc3e2
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef INSTALL_APP_COMMAND_H
+#define INSTALL_APP_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class InstallAppCommand : public Command {
+private:
+    ::grpc::ServerReader< ::aurum::ReqInstallApp>* mRequest;
+    ::aurum::RspInstallApp*                        mResponse;
+
+public:
+    InstallAppCommand(::grpc::ServerReader< ::aurum::ReqInstallApp>* request,
+                      ::aurum::RspInstallApp*                        response);
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/KillServerCommand.h b/bootstrap/server/inc/Commands/KillServerCommand.h
new file mode 100644 (file)
index 0000000..3121ac1
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef KILL_SERVER_COMMAND_H
+#define KILL_SERVER_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class KillServerCommand : public Command {
+private:
+    const ::aurum::ReqEmpty* mRequest;
+    ::aurum::RspEmpty*       mResponse;
+
+public:
+    KillServerCommand(const ::aurum::ReqEmpty* request,
+                      ::aurum::RspEmpty*       response);
+    ;
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/LaunchAppCommand.h b/bootstrap/server/inc/Commands/LaunchAppCommand.h
new file mode 100644 (file)
index 0000000..bf5352f
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef LAUNCH_APP_COMMAND_H
+#define LAUNCH_APP_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class LaunchAppCommand : public Command {
+private:
+    const ::aurum::ReqLaunchApp* mRequest;
+    ::aurum::RspLaunchApp*       mResponse;
+
+public:
+    LaunchAppCommand(const ::aurum::ReqLaunchApp* request,
+                     ::aurum::RspLaunchApp*       response);
+    ;
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/LongClickCommand.h b/bootstrap/server/inc/Commands/LongClickCommand.h
new file mode 100644 (file)
index 0000000..73e1ed2
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef LONG_CLICK_COMMAND_H
+#define LONG_CLICK_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class LongClickCommand : public Command {
+private:
+    const ::aurum::ReqClick* mRequest;
+    ::aurum::RspClick*       mResponse;
+
+public:
+    LongClickCommand(const ::aurum::ReqClick* request,
+                     ::aurum::RspClick*       response);
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/PostCommand.h b/bootstrap/server/inc/Commands/PostCommand.h
new file mode 100644 (file)
index 0000000..757b4ba
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef POST_COMMAND_H
+#define POST_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class PostCommand : public Command {
+private:
+    Command *mCommand;
+    PostCommand();
+
+public:
+    PostCommand(Command *cmd);
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/PreCommand.h b/bootstrap/server/inc/Commands/PreCommand.h
new file mode 100644 (file)
index 0000000..0005ee2
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef PRE_COMMAND_H
+#define PRE_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class PreCommand : public Command {
+private:
+    Command *mCommand;
+    PreCommand();
+
+public:
+    PreCommand(Command *cmd);
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/RemoveAppCommand.h b/bootstrap/server/inc/Commands/RemoveAppCommand.h
new file mode 100644 (file)
index 0000000..9ab28ba
--- /dev/null
@@ -0,0 +1,23 @@
+#ifndef REMOVE_APP_COMMAND_H
+#define REMOVE_APP_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class RemoveAppCommand : public Command {
+private:
+    const ::aurum::ReqRemoveApp* mRequest;
+    ::aurum::RspRemoveApp*       mResponse;
+
+public:
+    RemoveAppCommand(const ::aurum::ReqRemoveApp* request,
+                     ::aurum::RspRemoveApp*       response);
+    ;
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/SendKeyCommand.h b/bootstrap/server/inc/Commands/SendKeyCommand.h
new file mode 100644 (file)
index 0000000..96fd071
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef SEND_KEY_COMMAND_H
+#define SEND_KEY_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class SendKeyCommand : public Command {
+private:
+    const ::aurum::ReqKey* mRequest;
+    ::aurum::RspKey*       mResponse;
+
+public:
+    SendKeyCommand(const ::aurum::ReqKey* request, ::aurum::RspKey* response);
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/SetValueCommand.h b/bootstrap/server/inc/Commands/SetValueCommand.h
new file mode 100644 (file)
index 0000000..13cf245
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef SET_VALUE_COMMAND_H
+#define SET_VALUE_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class SetValueCommand : public Command {
+private:
+    const ::aurum::ReqSetValue* mRequest;
+    ::aurum::RspSetValue*       mResponse;
+
+public:
+    SetValueCommand(const ::aurum::ReqSetValue* request,
+                    ::aurum::RspSetValue*       response);
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/SyncCommand.h b/bootstrap/server/inc/Commands/SyncCommand.h
new file mode 100644 (file)
index 0000000..ecc7d6a
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef SYNC_COMMAND_H
+#define SYNC_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class SyncCommand : public Command {
+private:
+    const ::aurum::ReqEmpty* mRequest;
+    ::aurum::RspEmpty*       mResponse;
+
+public:
+    SyncCommand(const ::aurum::ReqEmpty* request, ::aurum::RspEmpty* response);
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/TouchDownCommand.h b/bootstrap/server/inc/Commands/TouchDownCommand.h
new file mode 100644 (file)
index 0000000..29b796b
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef TOUCH_DOWN_COMMAND_H
+#define TOUCH_DOWN_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class TouchDownCommand : public Command {
+private:
+    const ::aurum::ReqTouchDown* mRequest;
+    ::aurum::RspTouchDown*       mResponse;
+
+public:
+    TouchDownCommand(const ::aurum::ReqTouchDown* request,
+                     ::aurum::RspTouchDown*       response);
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/TouchMoveCommand.h b/bootstrap/server/inc/Commands/TouchMoveCommand.h
new file mode 100644 (file)
index 0000000..12b719a
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef TOUCH_MOVE_COMMAND_H
+#define TOUCH_MOVE_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class TouchMoveCommand : public Command {
+private:
+    const ::aurum::ReqTouchMove* mRequest;
+    ::aurum::RspTouchMove*       mResponse;
+
+public:
+    TouchMoveCommand(const ::aurum::ReqTouchMove* request,
+                     ::aurum::RspTouchMove*       response);
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/Commands/TouchUpCommand.h b/bootstrap/server/inc/Commands/TouchUpCommand.h
new file mode 100644 (file)
index 0000000..7ddc9e0
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef TOUCH_UP_COMMAND_H
+#define TOUCH_UP_COMMAND_H
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "Commands/Command.h"
+#include "ObjectMapper.h"
+#include "aurum.grpc.pb.h"
+#include "config.h"
+
+class TouchUpCommand : public Command {
+private:
+    const ::aurum::ReqTouchUp* mRequest;
+    ::aurum::RspTouchUp*       mResponse;
+
+public:
+    TouchUpCommand(const ::aurum::ReqTouchUp* request,
+                   ::aurum::RspTouchUp*       response);
+    ::grpc::Status execute() override;
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/inc/ObjectMapper.h b/bootstrap/server/inc/ObjectMapper.h
new file mode 100644 (file)
index 0000000..b1f127f
--- /dev/null
@@ -0,0 +1,26 @@
+#ifndef OBJECT_MAPPER_H
+#define OBJECT_MAPPER_H
+
+#include <map>
+#include <string>
+
+#include "UiObject.h"
+
+class ObjectMapper {
+private:
+    std::map<std::string, std::unique_ptr<UiObject>> mObjectMap;
+    unsigned long long                               mObjCounter;
+
+private:
+    ObjectMapper();
+
+public:
+    ~ObjectMapper();
+
+public:
+    static ObjectMapper *getInstance();
+    std::string          addElement(std::unique_ptr<UiObject> object);
+    UiObject *           getElement(const std::string &key);
+};
+
+#endif
\ No newline at end of file
diff --git a/bootstrap/server/meson.build b/bootstrap/server/meson.build
new file mode 100644 (file)
index 0000000..fecdb11
--- /dev/null
@@ -0,0 +1,74 @@
+bootstrap_svr_inc = [
+   libaurum_inc,
+   root_inc,
+   include_directories('inc'),
+   include_directories('inc/Commands'),
+   loguru_inc,
+]
+
+bootstrap_svr_src = [
+   files('src/BootstrapServer.cc'),
+   files('src/AurumServiceImpl.cc'),
+   files('src/ObjectMapper.cc'),
+]
+
+bootstrap_svr_src += [
+   files('src/Commands/ClearCommand.cc'),
+   files('src/Commands/ClickCommand.cc'),
+   files('src/Commands/CloseAppCommand.cc'),
+   files('src/Commands/FindElementCommand.cc'),
+   files('src/Commands/FlickCommand.cc'),
+   files('src/Commands/GetAttributeCommand.cc'),
+   files('src/Commands/GetDeviceTimeCommand.cc'),
+   files('src/Commands/GetLocationCommand.cc'),
+   files('src/Commands/GetSizeCommand.cc'),
+   files('src/Commands/GetValueCommand.cc'),
+   files('src/Commands/InstallAppCommand.cc'),
+   files('src/Commands/GetAppInfoCommand.cc'),
+   files('src/Commands/LaunchAppCommand.cc'),
+   files('src/Commands/LongClickCommand.cc'),
+   files('src/Commands/RemoveAppCommand.cc'),
+   files('src/Commands/SendKeyCommand.cc'),
+   files('src/Commands/SetValueCommand.cc'),
+   files('src/Commands/TouchDownCommand.cc'),
+   files('src/Commands/TouchMoveCommand.cc'),
+   files('src/Commands/TouchUpCommand.cc'),
+   files('src/Commands/SyncCommand.cc'),
+   files('src/Commands/KillServerCommand.cc'),
+   files('src/Commands/Command.cc'),
+   files('src/Commands/PreCommand.cc'),
+   files('src/Commands/PostCommand.cc'),
+]
+
+bootstrap_svr_dep = [
+   libaurum,
+   grpc_deps,
+   loguru_deps,
+]
+
+if get_option('tizen') == true
+bootstrap_svr_dep += [
+   dependency('capi-appfw-app-control'),
+   dependency('capi-appfw-app-manager'),
+   dependency('capi-appfw-package-manager'),
+]
+endif
+
+bootstrap_svr_bin = executable(
+   'aurum_bootstrap',
+   [bootstrap_svr_src, grpc_src, grpc_pb_src],
+   dependencies: bootstrap_svr_dep,
+   include_directories: bootstrap_svr_inc,
+   link_with: libloguru,
+   install:true,
+)
+
+system_unit_dir = ''
+user_unit_dir = ''
+
+systemd = dependency('systemd', required: false)
+if systemd.found()
+   user_unit_dir = systemd.get_pkgconfig_variable('systemduserunitdir')
+endif
+
+install_data('aurum-bootstrap.service', install_dir:user_unit_dir)
diff --git a/bootstrap/server/src/AurumServiceImpl.cc b/bootstrap/server/src/AurumServiceImpl.cc
new file mode 100644 (file)
index 0000000..fc15be0
--- /dev/null
@@ -0,0 +1,182 @@
+#include "AurumServiceImpl.h"
+
+#include <Accessible.h>
+#include "Commands/Commands.h"
+#include "Commands/PostCommand.h"
+#include "Commands/PreCommand.h"
+
+#include "config.h"
+#include "loguru.hpp"
+
+using namespace grpc;
+using namespace aurum;
+
+aurumServiceImpl::aurumServiceImpl()
+{
+    Accessible::getInstance();
+}
+
+::grpc::Status aurumServiceImpl::execute(Command& cmd)
+{
+    PreCommand     proxyPreCmd{&cmd};
+    PostCommand    proxyPostCmd{&proxyPreCmd};
+    ::grpc::Status rst = proxyPostCmd.execute();
+    return rst;
+}
+
+aurumServiceImpl::~aurumServiceImpl() {}
+::grpc::Status aurumServiceImpl::aurumServiceImpl::sync(
+    ::grpc::ServerContext* context, const ::aurum::ReqEmpty* request,
+    ::aurum::RspEmpty* response)
+{
+    SyncCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::aurumServiceImpl::killServer(
+    ::grpc::ServerContext* context, const ::aurum::ReqEmpty* request,
+    ::aurum::RspEmpty* response)
+{
+    KillServerCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::aurumServiceImpl::findElement(
+    ::grpc::ServerContext* context, const ::aurum::ReqFindElement* request,
+    ::aurum::RspFindElement* response)
+{
+    FindElementCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::aurumServiceImpl::click(
+    ::grpc::ServerContext* context, const ::aurum::ReqClick* request,
+    ::aurum::RspClick* response)
+{
+    ClickCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::getValue(::grpc::ServerContext*      context,
+                                          const ::aurum::ReqGetValue* request,
+                                          ::aurum::RspGetValue*       response)
+{
+    GetValueCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::setValue(::grpc::ServerContext*      context,
+                                          const ::aurum::ReqSetValue* request,
+                                          ::aurum::RspSetValue*       response)
+{
+    SetValueCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::getAttribute(
+    ::grpc::ServerContext* context, const ::aurum::ReqGetAttribute* request,
+    ::aurum::RspGetAttribute* response)
+{
+    GetAttributeCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::getSize(::grpc::ServerContext*     context,
+                                         const ::aurum::ReqGetSize* request,
+                                         ::aurum::RspGetSize*       response)
+{
+    GetSizeCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::clear(::grpc::ServerContext*   context,
+                                       const ::aurum::ReqClear* request,
+                                       ::aurum::RspClear*       response)
+{
+    ClearCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::installApp(
+    ::grpc::ServerContext*                         context,
+    ::grpc::ServerReader< ::aurum::ReqInstallApp>* request,
+    ::aurum::RspInstallApp*                        response)
+{
+    InstallAppCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::removeApp(::grpc::ServerContext*       context,
+                                           const ::aurum::ReqRemoveApp* request,
+                                           ::aurum::RspRemoveApp* response)
+{
+    RemoveAppCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::getAppInfo(
+    ::grpc::ServerContext* context, const ::aurum::ReqGetAppInfo* request,
+    ::aurum::RspGetAppInfo* response)
+{
+    GetAppInfoCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::launchApp(::grpc::ServerContext*       context,
+                                           const ::aurum::ReqLaunchApp* request,
+                                           ::aurum::RspLaunchApp* response)
+{
+    LaunchAppCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::closeApp(::grpc::ServerContext*      context,
+                                          const ::aurum::ReqCloseApp* request,
+                                          ::aurum::RspCloseApp*       response)
+{
+    CloseAppCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::touchDown(::grpc::ServerContext*       context,
+                                           const ::aurum::ReqTouchDown* request,
+                                           ::aurum::RspTouchDown* response)
+{
+    TouchDownCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::touchUp(::grpc::ServerContext*     context,
+                                         const ::aurum::ReqTouchUp* request,
+                                         ::aurum::RspTouchUp*       response)
+{
+    TouchUpCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::touchMove(::grpc::ServerContext*       context,
+                                           const ::aurum::ReqTouchMove* request,
+                                           ::aurum::RspTouchMove* response)
+{
+    TouchMoveCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::longClick(::grpc::ServerContext*   context,
+                                           const ::aurum::ReqClick* request,
+                                           ::aurum::RspClick*       response)
+{
+    LongClickCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::flick(::grpc::ServerContext*   context,
+                                       const ::aurum::ReqFlick* request,
+                                       ::aurum::RspFlick*       response)
+{
+    FlickCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::getDeviceTime(
+    ::grpc::ServerContext* context, const ::aurum::ReqGetDeviceTime* request,
+    ::aurum::RspGetDeviceTime* response)
+{
+    GetDeviceTimeCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::getLocation(
+    ::grpc::ServerContext* context, const ::aurum::ReqGetLocation* request,
+    ::aurum::RspGetLocation* response)
+{
+    GetLocationCommand cmd(request, response);
+    return execute(cmd);
+}
+::grpc::Status aurumServiceImpl::sendKey(::grpc::ServerContext* context,
+                                         const ::aurum::ReqKey* request,
+                                         ::aurum::RspKey*       response)
+{
+    SendKeyCommand cmd(request, response);
+    return execute(cmd);
+}
diff --git a/bootstrap/server/src/BootstrapServer.cc b/bootstrap/server/src/BootstrapServer.cc
new file mode 100644 (file)
index 0000000..ca344e4
--- /dev/null
@@ -0,0 +1,32 @@
+#include <iostream>
+
+#include <gio/gio.h>
+#include <grpcpp/grpcpp.h>
+#include "aurum.grpc.pb.h"
+
+#include "AurumServiceImpl.h"
+#include "config.h"
+#include "loguru.hpp"
+
+using namespace grpc;
+
+int main(int argc, char **argv)
+{
+    const char *logPath = "/tmp/ua.log";
+    loguru::init(argc, argv);
+    loguru::g_preamble = false;
+    loguru::add_file(logPath, loguru::Append, loguru::Verbosity_MAX);
+    LOG_SCOPE_F(INFO, "Log : %s", logPath);
+
+    std::string binding("0.0.0.0:50051");
+    aurumServiceImpl service;
+    ServerBuilder builder;
+
+    LOG_F(INFO, "Server Listening on %s", binding.c_str());
+    builder.AddListeningPort(binding, grpc::InsecureServerCredentials());
+    builder.RegisterService(&service);
+    std::unique_ptr<Server> server(builder.BuildAndStart());
+    server->Wait();
+
+    return 0;
+}
diff --git a/bootstrap/server/src/Commands/ClearCommand.cc b/bootstrap/server/src/Commands/ClearCommand.cc
new file mode 100644 (file)
index 0000000..ed26d1b
--- /dev/null
@@ -0,0 +1,20 @@
+#include "ClearCommand.h"
+#include <UiObject.h>
+#include <loguru.hpp>
+
+ClearCommand::ClearCommand(const ::aurum::ReqClear* request,
+                           ::aurum::RspClear*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status ClearCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "Clear --------------- ");
+    ObjectMapper* mObjMap = ObjectMapper::getInstance();
+    UiObject*     obj = mObjMap->getElement(mRequest->elementid());
+    if (obj) {
+        ;
+    }
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/ClickCommand.cc b/bootstrap/server/src/Commands/ClickCommand.cc
new file mode 100644 (file)
index 0000000..b8e24b9
--- /dev/null
@@ -0,0 +1,34 @@
+#include "ClickCommand.h"
+
+#include "UiObject.h"
+
+#include <loguru.hpp>
+
+ClickCommand::ClickCommand(const ::aurum::ReqClick* request,
+                           ::aurum::RspClick*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status ClickCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "Click --------------- ");
+
+    ObjectMapper*                 mObjMap = ObjectMapper::getInstance();
+    ::aurum::ReqClick_RequestType type = mRequest->type();
+
+    if (type == ::aurum::ReqClick_RequestType_ELEMENTID) {
+        UiObject* obj = mObjMap->getElement(mRequest->elementid());
+        if (obj) {
+            obj->click();
+            mResponse->set_status(::aurum::RspStatus::OK);
+        } else
+            mResponse->set_status(::aurum::RspStatus::ERROR);
+    } else if (type == ::aurum::ReqClick_RequestType_COORD) {
+        mResponse->set_status(::aurum::RspStatus::ERROR);
+    } else if (type == ::aurum::ReqClick_RequestType_ATSPI) {
+        mResponse->set_status(::aurum::RspStatus::ERROR);
+    }
+
+    return grpc::Status::OK;
+}
diff --git a/bootstrap/server/src/Commands/CloseAppCommand.cc b/bootstrap/server/src/Commands/CloseAppCommand.cc
new file mode 100644 (file)
index 0000000..9cb72df
--- /dev/null
@@ -0,0 +1,36 @@
+#include "CloseAppCommand.h"
+#include <loguru.hpp>
+#ifdef GBSBUILD
+#include <app_manager_extension.h>
+#endif
+
+CloseAppCommand::CloseAppCommand(const ::aurum::ReqCloseApp* request,
+                                 ::aurum::RspCloseApp*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status CloseAppCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "CloseApp --------------- ");
+#ifdef GBSBUILD
+    std::string   packageName = mRequest->packagename();
+    app_context_h app_context = NULL;
+
+    LOG_F(INFO, "close req : %s", packageName.c_str());
+
+    int ret = app_manager_get_app_context(packageName.c_str(), &app_context);
+    if (ret) {
+        LOG_SCOPE_F(INFO, "Terminate Failed(1/2) Err Code : %d", ret);
+        mResponse->set_status(::aurum::RspStatus::ERROR);
+        return grpc::Status::OK;
+    }
+    ret = app_manager_terminate_app(app_context);
+    if (ret) {
+        LOG_SCOPE_F(INFO, "Terminate Failed(2/2) Err Code : %d", ret);
+        mResponse->set_status(::aurum::RspStatus::ERROR);
+        return grpc::Status::OK;
+    }
+#endif
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/Command.cc b/bootstrap/server/src/Commands/Command.cc
new file mode 100644 (file)
index 0000000..5d4690e
--- /dev/null
@@ -0,0 +1,2 @@
+#include "Command.h"
+#include <loguru.hpp>
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/FindElementCommand.cc b/bootstrap/server/src/Commands/FindElementCommand.cc
new file mode 100644 (file)
index 0000000..22e950a
--- /dev/null
@@ -0,0 +1,53 @@
+#include "FindElementCommand.h"
+
+#include "ISearchable.h"
+
+#include "Sel.h"
+#include "UiDevice.h"
+#include "UiObject.h"
+#include "UiSelector.h"
+
+#include <loguru.hpp>
+
+FindElementCommand::FindElementCommand(const ::aurum::ReqFindElement* request,
+                                       ::aurum::RspFindElement*       response)
+    : mRequest{request}, mResponse{response}
+{
+    mObjMap = ObjectMapper::getInstance();
+}
+
+::grpc::Status FindElementCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "findElement --------------- ");
+
+    bool         fromObject = mRequest->elementid().empty() == false;
+    ISearchable* searchableObj = nullptr;
+
+    LOG_SCOPE_F(INFO, "fromObject:%d ei:%s tf:%s", fromObject,
+                mRequest->elementid().c_str(), mRequest->textfield().c_str());
+
+    if (fromObject) searchableObj = mObjMap->getElement(mRequest->elementid());
+
+    if (searchableObj == nullptr)
+        searchableObj = UiDevice::getInstance(DeviceType::DEFAULT);
+
+    std::unique_ptr<UiSelector> sel = Sel::text(mRequest->textfield());
+    sel->type(mRequest->widgettype());
+
+    std::vector<std::unique_ptr<UiObject>> founds =
+        searchableObj->findObjects(sel.get());
+
+    if (founds.size() > 0) {
+        for (auto& found : founds) {
+            UiObject*   obj = found.get();
+            std::string key = mObjMap->addElement(std::move(found));
+            LOG_F(INFO, "found object : %s key:%s",
+                  obj->getResourceName().c_str(), key.c_str());
+            ::aurum::Element* elm = mResponse->add_elements();
+            elm->set_elementid(key);
+        }
+        mResponse->set_status(::aurum::RspStatus::OK);
+    }
+
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/FlickCommand.cc b/bootstrap/server/src/Commands/FlickCommand.cc
new file mode 100644 (file)
index 0000000..a0592f7
--- /dev/null
@@ -0,0 +1,26 @@
+#include "FlickCommand.h"
+#include <loguru.hpp>
+
+#include <UiDevice.h>
+
+FlickCommand::FlickCommand(const ::aurum::ReqFlick *request,
+                           ::aurum::RspFlick *      response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status FlickCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "Flick --------------- ");
+    // ObjectMapper *mObjMap = ObjectMapper::getInstance();
+
+    const ::aurum::Point &startPoint = mRequest->startpoint();
+    const ::aurum::Point &endPoint = mRequest->endpoint();
+    int                   durationMs = mRequest->durationms();
+
+    UiDevice *device = UiDevice::getInstance(DeviceType::DEFAULT);
+
+    device->drag(10, 200, 400, 400, durationMs);
+
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/GetAppInfoCommand.cc b/bootstrap/server/src/Commands/GetAppInfoCommand.cc
new file mode 100644 (file)
index 0000000..b2a8397
--- /dev/null
@@ -0,0 +1,55 @@
+#include "GetAppInfoCommand.h"
+#include <loguru.hpp>
+#ifdef GBSBUILD
+#include <app_manager_extension.h>
+#include <package_manager.h>
+#endif
+
+GetAppInfoCommand::GetAppInfoCommand(const ::aurum::ReqGetAppInfo* request,
+                                     ::aurum::RspGetAppInfo*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status GetAppInfoCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "GetAppInfo --------------- ");
+#ifdef GBSBUILD
+    std::string packageName = mRequest->packagename();
+
+    app_context_h  app_context;
+    package_info_h package_info;
+    app_state_e    appState;
+
+    char* label = nullptr;
+    bool  terminated = false;
+    int   ret = -1;
+
+    mResponse->set_status(::aurum::RspStatus::OK);
+
+    package_manager_get_package_info(packageName.c_str(), &package_info);
+    package_info_get_label(package_info, &label);
+    if (label) {
+        free(label);
+        mResponse->set_isinstalled(true);
+    } else {
+        mResponse->set_isinstalled(false);
+        return grpc::Status::OK;
+    }
+
+    ret = app_manager_get_app_context(packageName.c_str(), &app_context);
+    if (ret) {
+        mResponse->set_isrunning(false);
+        return grpc::Status::OK;
+    }
+
+    ret = app_context_get_app_state(app_context, &appState);
+    if (ret) {
+        mResponse->set_status(::aurum::RspStatus::ERROR);
+        return grpc::Status::OK;
+    }
+    mResponse->set_isrunning(!(appState & APP_STATE_TERMINATED));
+    mResponse->set_isfocused(appState & APP_STATE_FOREGROUND);
+#endif
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/GetAttributeCommand.cc b/bootstrap/server/src/Commands/GetAttributeCommand.cc
new file mode 100644 (file)
index 0000000..9e4d50b
--- /dev/null
@@ -0,0 +1,15 @@
+#include "GetAttributeCommand.h"
+#include <loguru.hpp>
+
+GetAttributeCommand::GetAttributeCommand(
+    const ::aurum::ReqGetAttribute* request, ::aurum::RspGetAttribute* response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status GetAttributeCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "GetAttribute --------------- ");
+    // ObjectMapper *mObjMap = ObjectMapper::getInstance();
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/GetDeviceTimeCommand.cc b/bootstrap/server/src/Commands/GetDeviceTimeCommand.cc
new file mode 100644 (file)
index 0000000..6f9c2a6
--- /dev/null
@@ -0,0 +1,16 @@
+#include "GetDeviceTimeCommand.h"
+#include <loguru.hpp>
+
+GetDeviceTimeCommand::GetDeviceTimeCommand(
+    const ::aurum::ReqGetDeviceTime* request,
+    ::aurum::RspGetDeviceTime*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status GetDeviceTimeCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "GetDeviceTime --------------- ");
+    // ObjectMapper *mObjMap = ObjectMapper::getInstance();
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/GetLocationCommand.cc b/bootstrap/server/src/Commands/GetLocationCommand.cc
new file mode 100644 (file)
index 0000000..a8a5aff
--- /dev/null
@@ -0,0 +1,15 @@
+#include "GetLocationCommand.h"
+#include <loguru.hpp>
+
+GetLocationCommand::GetLocationCommand(const ::aurum::ReqGetLocation* request,
+                                       ::aurum::RspGetLocation*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status GetLocationCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "CliGetLocation --------------- ");
+    // ObjectMapper *mObjMap = ObjectMapper::getInstance();
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/GetSizeCommand.cc b/bootstrap/server/src/Commands/GetSizeCommand.cc
new file mode 100644 (file)
index 0000000..d831453
--- /dev/null
@@ -0,0 +1,15 @@
+#include "GetSizeCommand.h"
+#include <loguru.hpp>
+
+GetSizeCommand::GetSizeCommand(const ::aurum::ReqGetSize* request,
+                               ::aurum::RspGetSize*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status GetSizeCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "GetSize --------------- ");
+    // ObjectMapper *mObjMap = ObjectMapper::getInstance();
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/GetValueCommand.cc b/bootstrap/server/src/Commands/GetValueCommand.cc
new file mode 100644 (file)
index 0000000..60033a5
--- /dev/null
@@ -0,0 +1,15 @@
+#include "GetValueCommand.h"
+#include <loguru.hpp>
+
+GetValueCommand::GetValueCommand(const ::aurum::ReqGetValue* request,
+                                 ::aurum::RspGetValue*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status GetValueCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "GetValue --------------- ");
+    // ObjectMapper *mObjMap = ObjectMapper::getInstance();
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/InstallAppCommand.cc b/bootstrap/server/src/Commands/InstallAppCommand.cc
new file mode 100644 (file)
index 0000000..454bda5
--- /dev/null
@@ -0,0 +1,37 @@
+#include "InstallAppCommand.h"
+#include <fstream>
+#include <loguru.hpp>
+#ifdef GBSBUILD
+#include <package_manager.h>
+#endif
+
+InstallAppCommand::InstallAppCommand(
+    ::grpc::ServerReader<::aurum::ReqInstallApp> *request,
+    ::aurum::RspInstallApp *                      response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status InstallAppCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "InstallApp --------------- ");
+#ifdef GBSBUILD
+    ::aurum::ReqInstallApp chunk;
+
+    std::ofstream outfile("/tmp/app.tpk", std::ofstream::binary);
+
+    while (mRequest->Read(&chunk)) {
+        std::size_t size = chunk.package().length();
+        const char *bufptr = chunk.package().c_str();
+        outfile.write(bufptr, size);
+    }
+    outfile.close();
+
+    package_manager_request_h pkgRequest;
+    int                       id;
+
+    package_manager_request_create(&pkgRequest);
+    package_manager_request_install(pkgRequest, "/tmp/app.tpk", &id);
+#endif
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/KillServerCommand.cc b/bootstrap/server/src/Commands/KillServerCommand.cc
new file mode 100644 (file)
index 0000000..2e51682
--- /dev/null
@@ -0,0 +1,17 @@
+#include "KillServerCommand.h"
+#include <loguru.hpp>
+
+KillServerCommand::KillServerCommand(const ::aurum::ReqEmpty* request,
+                                     ::aurum::RspEmpty*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status KillServerCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "Kill Server ");
+
+    exit(1);
+
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/LaunchAppCommand.cc b/bootstrap/server/src/Commands/LaunchAppCommand.cc
new file mode 100644 (file)
index 0000000..020044c
--- /dev/null
@@ -0,0 +1,45 @@
+#include "LaunchAppCommand.h"
+#include <loguru.hpp>
+#ifdef GBSBUILD
+#include <app_control.h>
+#endif
+
+LaunchAppCommand::LaunchAppCommand(const ::aurum::ReqLaunchApp* request,
+                                   ::aurum::RspLaunchApp*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status LaunchAppCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "LaunchApp --------------- ");
+#ifdef GBSBUILD
+    app_control_h appControl;
+    std::string   packageName = mRequest->packagename();
+    int           ret = -1;
+
+    if (packageName.empty()) return grpc::Status::OK;
+
+    ret = app_control_create(&appControl);
+    if (ret) {
+        LOG_SCOPE_F(INFO, "Launch Failed(1/3) Err Code : %ull", ret);
+        mResponse->set_status(::aurum::RspStatus::ERROR);
+        return grpc::Status::OK;
+    }
+
+    ret = app_control_set_app_id(appControl, packageName.c_str());
+    if (ret) {
+        LOG_SCOPE_F(INFO, "Launch Failed(2/3) Err Code : %ull", ret);
+        mResponse->set_status(::aurum::RspStatus::ERROR);
+        return grpc::Status::OK;
+    }
+
+    ret = app_control_send_launch_request(appControl, NULL, NULL);
+    if (ret) {
+        LOG_SCOPE_F(INFO, "Launch Failed(3/3) Err Code : %ull", ret);
+        mResponse->set_status(::aurum::RspStatus::ERROR);
+        return grpc::Status::OK;
+    }
+#endif
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/LongClickCommand.cc b/bootstrap/server/src/Commands/LongClickCommand.cc
new file mode 100644 (file)
index 0000000..8197dd6
--- /dev/null
@@ -0,0 +1,15 @@
+#include "LongClickCommand.h"
+#include <loguru.hpp>
+
+LongClickCommand::LongClickCommand(const ::aurum::ReqClick* request,
+                                   ::aurum::RspClick*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status LongClickCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "LongClick --------------- ");
+    // ObjectMapper *mObjMap = ObjectMapper::getInstance();
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/PostCommand.cc b/bootstrap/server/src/Commands/PostCommand.cc
new file mode 100644 (file)
index 0000000..31c4200
--- /dev/null
@@ -0,0 +1,14 @@
+#include "PostCommand.h"
+#include <atspi/atspi.h>
+#include <loguru.hpp>
+
+PostCommand::PostCommand() {}
+PostCommand::PostCommand(Command *cmd) : mCommand{cmd} {}
+
+::grpc::Status PostCommand::execute()
+{
+    ::grpc::Status rst = mCommand->execute();
+    LOG_SCOPE_F(INFO, "PostCommand --------------- ");
+    // do post-command
+    return rst;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/PreCommand.cc b/bootstrap/server/src/Commands/PreCommand.cc
new file mode 100644 (file)
index 0000000..bea3aa5
--- /dev/null
@@ -0,0 +1,18 @@
+#include "PreCommand.h"
+#include <atspi/atspi.h>
+#include <loguru.hpp>
+
+PreCommand::PreCommand() {}
+PreCommand::PreCommand(Command *cmd) : mCommand{cmd} {}
+
+::grpc::Status PreCommand::execute()
+{
+    {
+        LOG_SCOPE_F(INFO, "PreCommand --------------- ");
+        AtspiAccessible *n = atspi_get_desktop(0);
+        free(atspi_accessible_get_name(n, NULL));
+        g_object_unref(n);
+    }
+
+    return mCommand->execute();
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/RemoveAppCommand.cc b/bootstrap/server/src/Commands/RemoveAppCommand.cc
new file mode 100644 (file)
index 0000000..0fa6816
--- /dev/null
@@ -0,0 +1,27 @@
+#include "RemoveAppCommand.h"
+#include <loguru.hpp>
+#ifdef GBSBUILD
+#include <package_manager.h>
+#endif
+
+RemoveAppCommand::RemoveAppCommand(const ::aurum::ReqRemoveApp* request,
+                                   ::aurum::RspRemoveApp*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status RemoveAppCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "RemoveApp --------------- ");
+#ifdef GBSBUILD
+    package_manager_request_h pkgRequest;
+    std::string               name = mRequest->packagename();
+    int                       id;
+    LOG_F(INFO, "package name :%s", name.c_str());
+
+    package_manager_request_create(&pkgRequest);
+    package_manager_request_uninstall(pkgRequest, name.c_str(), &id);
+#endif
+
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/SendKeyCommand.cc b/bootstrap/server/src/Commands/SendKeyCommand.cc
new file mode 100644 (file)
index 0000000..b519133
--- /dev/null
@@ -0,0 +1,28 @@
+#include "SendKeyCommand.h"
+#include <loguru.hpp>
+
+#include <UiDevice.h>
+
+SendKeyCommand::SendKeyCommand(const ::aurum::ReqKey* request,
+                               ::aurum::RspKey*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status SendKeyCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "SendKey --------------- ");
+    UiDevice* mDevice = UiDevice::getInstance(DeviceType::DEFAULT);
+    ::aurum::ReqKey_KeyType type = mRequest->type();
+
+    if (type == ::aurum::ReqKey_KeyType::ReqKey_KeyType_BACK)
+        mDevice->pressBack();
+    else if (type == ::aurum::ReqKey_KeyType::ReqKey_KeyType_HOME)
+        mDevice->pressHome();
+    else if (type == ::aurum::ReqKey_KeyType::ReqKey_KeyType_MENU)
+        mDevice->pressMenu();
+    else {
+        // TODO : handle keycode
+    }
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/SetValueCommand.cc b/bootstrap/server/src/Commands/SetValueCommand.cc
new file mode 100644 (file)
index 0000000..eeb5a54
--- /dev/null
@@ -0,0 +1,19 @@
+#include "SetValueCommand.h"
+#include <loguru.hpp>
+
+SetValueCommand::SetValueCommand(const ::aurum::ReqSetValue* request,
+                                 ::aurum::RspSetValue*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status SetValueCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "SetValue --------------- ");
+    ObjectMapper* mObjMap = ObjectMapper::getInstance();
+    UiObject*     obj = mObjMap->getElement(mRequest->elementid());
+    obj->setText(const_cast<std::string&>(mRequest->stringvalue()));
+    LOG_F(INFO, "%p %s", obj, mRequest->stringvalue().c_str());
+
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/SyncCommand.cc b/bootstrap/server/src/Commands/SyncCommand.cc
new file mode 100644 (file)
index 0000000..4b31ca7
--- /dev/null
@@ -0,0 +1,28 @@
+#include "SyncCommand.h"
+#include <loguru.hpp>
+
+#include <Accessible.h>
+#include <AccessibleNode.h>
+
+SyncCommand::SyncCommand(const ::aurum::ReqEmpty *request,
+                         ::aurum::RspEmpty *      response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status SyncCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "Sync Command ");
+    const Accessible *accObj = Accessible::getInstance();
+
+    AccessibleNode *root = accObj->getRootNode();
+    AccessibleNode *top = accObj->getTopNode();
+    LOG_F(INFO, "%p(%p) %p(%p)", root, root->getAccessible(), top,
+          top->getAccessible());
+
+    root->print(0, 2);
+    LOG_F(INFO, "---------");
+    top->print(0, 2);
+
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/TouchDownCommand.cc b/bootstrap/server/src/Commands/TouchDownCommand.cc
new file mode 100644 (file)
index 0000000..4864cc5
--- /dev/null
@@ -0,0 +1,19 @@
+#include "TouchDownCommand.h"
+#include <UiDevice.h>
+#include <loguru.hpp>
+
+TouchDownCommand::TouchDownCommand(const ::aurum::ReqTouchDown* request,
+                                   ::aurum::RspTouchDown*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status TouchDownCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "TouchDown --------------- ");
+    const aurum::Point& point_ = mRequest->coordination();
+    mResponse->set_seqid(0);
+    UiDevice::getInstance(DeviceType::DEFAULT)
+        ->touchDown(point_.x(), point_.y());
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/TouchMoveCommand.cc b/bootstrap/server/src/Commands/TouchMoveCommand.cc
new file mode 100644 (file)
index 0000000..18fbeb9
--- /dev/null
@@ -0,0 +1,17 @@
+#include "TouchMoveCommand.h"
+#include <UiDevice.h>
+#include <loguru.hpp>
+
+TouchMoveCommand::TouchMoveCommand(const ::aurum::ReqTouchMove* request,
+                                   ::aurum::RspTouchMove*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status TouchMoveCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "TouchMove --------------- ");
+    const aurum::Point& point = mRequest->coordination();
+    UiDevice::getInstance(DeviceType::DEFAULT)->touchMove(point.x(), point.y());
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/Commands/TouchUpCommand.cc b/bootstrap/server/src/Commands/TouchUpCommand.cc
new file mode 100644 (file)
index 0000000..20d8083
--- /dev/null
@@ -0,0 +1,17 @@
+#include "TouchUpCommand.h"
+#include <UiDevice.h>
+#include <loguru.hpp>
+
+TouchUpCommand::TouchUpCommand(const ::aurum::ReqTouchUp* request,
+                               ::aurum::RspTouchUp*       response)
+    : mRequest{request}, mResponse{response}
+{
+}
+
+::grpc::Status TouchUpCommand::execute()
+{
+    LOG_SCOPE_F(INFO, "TouchUp --------------- ");
+    const aurum::Point& point = mRequest->coordination();
+    UiDevice::getInstance(DeviceType::DEFAULT)->touchUp(point.x(), point.y());
+    return grpc::Status::OK;
+}
\ No newline at end of file
diff --git a/bootstrap/server/src/ObjectMapper.cc b/bootstrap/server/src/ObjectMapper.cc
new file mode 100644 (file)
index 0000000..4519066
--- /dev/null
@@ -0,0 +1,31 @@
+#include "ObjectMapper.h"
+
+ObjectMapper::ObjectMapper() : mObjectMap{}, mObjCounter{0} {}
+
+ObjectMapper::~ObjectMapper() {}
+
+ObjectMapper *ObjectMapper::getInstance()
+{
+    static ObjectMapper *mInstance = new ObjectMapper();
+    return mInstance;
+}
+
+std::string ObjectMapper::addElement(std::unique_ptr<UiObject> object)
+{
+    ++mObjCounter;
+    std::string key = std::to_string(mObjCounter);
+    mObjectMap[key] = std::move(object);
+    return key;
+}
+
+UiObject *ObjectMapper::getElement(const std::string &key)
+{
+    unsigned long long keyCnt = std::stoi(key);
+    if (keyCnt <= 0 || keyCnt > mObjCounter) return nullptr;
+    if (mObjectMap.count(key)) {
+        UiObject *obj = mObjectMap[key].get();
+        const_cast<const UiObject *>(obj)->refresh();
+        return obj;
+    }
+    return nullptr;
+}
diff --git a/libaurum/inc/Accessible.h b/libaurum/inc/Accessible.h
new file mode 100644 (file)
index 0000000..9bb5f30
--- /dev/null
@@ -0,0 +1,66 @@
+#ifndef ACCESSIBLE_H
+#define ACCESSIBLE_H
+
+#include <atspi/atspi.h>
+#include "AccessibleNode.h"
+
+#include <list>
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <gio/gio.h>
+#include <mutex>
+#include <shared_mutex>
+#include "config.h"
+
+enum class WindowActivateInfoType {
+    DEFAULT_LABEL_ENALBED = 0x00,
+    DEFAULT_LABEL_ENALBED_WITHOUT_WINDOW = 0x01,
+    DEFAULT_LABEL_DISABLED = 0x02,
+    KEYBOARD = 0x04,
+};
+
+class IAtspiEvents {
+public:
+    virtual ~IAtspiEvents() {}
+    virtual void onActivate(AtspiAccessible *      node,
+                            WindowActivateInfoType type) const = 0;
+    virtual void onDeactivate(AtspiAccessible *node) const = 0;
+    virtual void onVisibilityChanged(AtspiAccessible *node,
+                                     bool             visible) const = 0;
+    virtual void onObjectDefunct(AtspiAccessible *node) const = 0;
+};
+
+class Accessible : public IAtspiEvents {
+private:
+    Accessible();
+
+public:
+    static const Accessible *getInstance();
+    virtual ~Accessible();
+
+public:
+    AccessibleNode *getRootNode() const;
+    AccessibleNode *getTopNode() const;
+
+    void onActivate(AtspiAccessible *      node,
+                    WindowActivateInfoType type) const override;
+    void onDeactivate(AtspiAccessible *node) const override;
+    void onVisibilityChanged(AtspiAccessible *node,
+                             bool             visible) const override;
+    void onObjectDefunct(AtspiAccessible *node) const override;
+
+private:
+    void        clearWindowList() const;
+    static void onAtspiWindowEvent(AtspiEvent *event, void *user_data);
+
+private:
+    static AtspiEventListener *                   listener;
+    mutable std::list<AtspiAccessible *>          mWindowList;
+    GDBusProxy *                                  mDbusProxy;
+    std::map<AtspiAccessible *, AccessibleNode *> mAccessibleNode;
+    mutable std::mutex                            mLock;
+};
+
+#endif
diff --git a/libaurum/inc/AccessibleNode.h b/libaurum/inc/AccessibleNode.h
new file mode 100644 (file)
index 0000000..cc19f2c
--- /dev/null
@@ -0,0 +1,158 @@
+#ifndef ACCESSIBLE_NODE_H
+#define ACCESSIBLE_NODE_H
+#include "config.h"
+
+#include <atspi/atspi.h>
+#include <map>
+#include <memory>
+#include <string>
+#include <vector>
+
+enum class AccessibleNodeInterface {
+    ACTION = 0x0001,
+    COLLECTION = 0X0002,
+    COMPONENT = 0X0004,
+    DOCUMENT = 0X0008,
+
+    EDITABLETEXT = 0X0010,
+    HYPERTEXT = 0X0020,
+    IMAGE = 0X0040,
+    SELECTION = 0X0080,
+
+    TEXT = 0X0100,
+    VALUE = 0X0200,
+    ACCESSIBLE = 0X0400,
+    TABLE = 0X0800,
+
+    TABLECELL = 0X1000,
+};
+
+enum class NodeFeatureProperties {
+    CHECKABLE = 0x0001,
+    CHECKED = 0X0002,
+    CLICKABLE = 0X0004,
+    ENABLED = 0X0008,
+    FOCUSABLE = 0X0010,
+    FOCUSED = 0X0020,
+    LONGCLICKABLE = 0X0040,
+    SCROLLABLE = 0X0080,
+
+    SELECTABLE = 0X0100,
+    SELECTED = 0X0200,
+};
+
+template <typename T>
+class Point2D {
+public:
+    Point2D() : x{0}, y{0} {}
+    Point2D(const Point2D &src)
+    {
+        x = src.x;
+        y = src.y;
+    }
+    Point2D(const T &x, const T &y)
+    {
+        this->x = x;
+        this->y = y;
+    }
+    T x;
+    T y;
+};
+
+template <typename T>
+class Rect {
+public:
+    Rect() : mTopLeft{0, 0}, mBottomRight{0, 0} {}
+    Rect(const Point2D<T> &tl, const Point2D<T> &br)
+        : mTopLeft(tl), mBottomRight(br)
+    {
+    }
+    Rect(const T &x1, const T &y1, const T &x2, const T &y2)
+        : mTopLeft{x1, y1}, mBottomRight{x2, y2}
+    {
+    }
+    Rect(const Rect<T> &src)
+
+    {
+        this->mTopLeft = Point2D<int>{src.mTopLeft};
+        this->mBottomRight = Point2D<int>{src.mBottomRight};
+    }
+    Point2D<T> midPoint() const
+    {
+        return Point2D<T>{mTopLeft.x + static_cast<T>(width() / 2),
+                          mTopLeft.y + static_cast<T>(height() / 2)};
+    }
+    T width() const { return mBottomRight.x - mTopLeft.x; }
+
+    T          height() const { return mBottomRight.y - mTopLeft.y; }
+    Point2D<T> mTopLeft;
+    Point2D<T> mBottomRight;
+};
+
+class AccessibleNode {
+private:
+    AccessibleNode();
+    AccessibleNode(AtspiAccessible *node);
+
+public:
+    static AccessibleNode *get(AtspiAccessible *node);
+    ~AccessibleNode();
+
+public:
+    int              getChildCount() const;
+    AccessibleNode * getChildAt(int index) const;
+    AccessibleNode * getParent() const;
+    AtspiAccessible *getAccessible();
+
+public:
+    std::string getDesc() const;
+    std::string getText() const;
+    std::string getPkg() const;
+    std::string getRes() const;
+    std::string getType() const;
+    std::string getStyle() const;
+    Rect<int>   getBoundingBox() const;
+
+    bool isCheckable() const;
+    bool isChecked() const;
+    bool isClickable() const;
+    bool isEnabled() const;
+    bool isFocusable() const;
+    bool isFocused() const;
+    bool isLongClickable() const;
+    bool isScrollable() const;
+    bool isSelectable() const;
+    bool isSelected() const;
+
+public:
+    void print(int) const;
+    void print(int, int) const;
+    void refresh() const;
+
+    void setValue(std::string &text) const;
+
+private:
+    bool isSupporting(AccessibleNodeInterface thisIface) const;
+    bool hasFeatureProperty(NodeFeatureProperties prop) const;
+    void setFeatureProperty(NodeFeatureProperties prop, bool has);
+    static std::map<AtspiAccessible *, AccessibleNode *> mNodeMap;
+
+private:
+    AtspiAccessible *mNode;
+
+    mutable std::string mText;
+    mutable std::string mPkg;
+    mutable std::string mRole;
+    mutable std::string mDesc;
+    mutable std::string mRes;
+    mutable std::string mType;
+    mutable std::string mStyle;
+
+    mutable Rect<int> mBoundingBox;
+
+    int  mSupportingIfaces;
+    int  mFeatureProperty;
+    bool mIsAlive;
+};
+
+#endif
diff --git a/libaurum/inc/Comparer.h b/libaurum/inc/Comparer.h
new file mode 100644 (file)
index 0000000..ce662ef
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef COMPARER_H
+#define COMPARER_H
+#include "config.h"
+
+#include "AccessibleNode.h"
+#include "UiDevice.h"
+#include "UiSelector.h"
+
+#include "PartialMatch.h"
+
+#include <list>
+#include <memory>
+#include <vector>
+
+class Comparer {
+private:
+    Comparer(const UiDevice *device, const UiSelector *selector,
+             const bool &earlyReturn);
+    ~Comparer();
+
+public:
+    static AccessibleNode *              findObject(const UiDevice *      device,
+                                                    const UiSelector *    selector,
+                                                    const AccessibleNode *root);
+    static std::vector<AccessibleNode *> findObjects(
+        const UiDevice *device, const UiSelector *selector,
+        const AccessibleNode *root);
+
+private:
+    std::vector<AccessibleNode *> findObjects(const AccessibleNode *root);
+    std::vector<AccessibleNode *> findObjects(
+        const AccessibleNode *root, const int &index, const int &depth,
+        std::list<std::shared_ptr<PartialMatch>> &partialMatches);
+
+private:
+    std::unique_ptr<PartialMatch> accept(const AccessibleNode *node,
+                                         const UiSelector *    selector,
+                                         const int &index, const int &depth,
+                                         const int &relDepth);
+
+private:
+    const UiDevice *  mDevice;
+    const UiSelector *mSelector;
+    bool              mEarlyReturn;
+};
+
+#endif
diff --git a/libaurum/inc/DeviceImpl/TM1Impl.h b/libaurum/inc/DeviceImpl/TM1Impl.h
new file mode 100644 (file)
index 0000000..d24f14f
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef DEVICE_GENERAL_H
+#define DEVICE_GENERAL_H
+#include "config.h"
+
+#include "IDevice.h"
+
+#ifdef GBS_BUILD
+#include <efl_util.h>
+#endif
+
+class TM1Impl : public IDevice {
+public:
+    TM1Impl();
+    ~TM1Impl();
+
+    bool click(const int x, const int y) override;
+    bool drag(const int sx, const int sy, const int ex, const int ey,
+              const int steps) override;
+
+    bool touchDown(const int x, const int y) override;
+    bool touchMove(const int x, const int y) override;
+    bool touchUp(const int x, const int y) override;
+
+    bool pressBack() override;
+    bool pressHome() override;
+    bool pressMenu() override;
+    bool pressVolUp() override;
+    bool pressVolDown() override;
+    bool pressPower() override;
+    bool pressKeyCode(std::string keycode) override;
+    bool takeScreenshot(std::string path, float scale, int quality) override;
+
+private:
+#ifdef GBS_BUILD
+    efl_util_inputgen_h mFakeTouchHandle;
+    efl_util_inputgen_h mFakeKeyboardHandle;
+
+#endif
+};
+
+#endif
\ No newline at end of file
diff --git a/libaurum/inc/IDevice.h b/libaurum/inc/IDevice.h
new file mode 100644 (file)
index 0000000..32abd39
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef IDEVICE_H
+#define IDEVICE_H
+#include "config.h"
+
+#include <string>
+
+class IDevice {
+public:
+    virtual ~IDevice() {}
+
+    virtual bool click(const int x, const int y) = 0;
+    virtual bool drag(const int sx, const int sy, const int ex, const int ey,
+                      const int steps) = 0;
+
+    virtual bool touchDown(const int x, const int y) = 0;
+    virtual bool touchMove(const int x, const int y) = 0;
+    virtual bool touchUp(const int x, const int y) = 0;
+
+    virtual bool pressBack() = 0;
+    virtual bool pressHome() = 0;
+    virtual bool pressMenu() = 0;
+    virtual bool pressVolUp() = 0;
+    virtual bool pressVolDown() = 0;
+    virtual bool pressPower() = 0;
+    virtual bool pressKeyCode(std::string keycode) = 0;
+
+    virtual bool takeScreenshot(std::string path, float scale, int quality) = 0;
+};
+#endif
\ No newline at end of file
diff --git a/libaurum/inc/ISearchable.h b/libaurum/inc/ISearchable.h
new file mode 100644 (file)
index 0000000..43b5483
--- /dev/null
@@ -0,0 +1,22 @@
+#ifndef ISEARCHABLE_H
+#define ISEARCHABLE_H
+#include "config.h"
+
+#include "UiSelector.h"
+
+#include <memory>
+#include <vector>
+
+class UiObject;
+
+class ISearchable {
+public:
+    virtual ~ISearchable() {}
+    virtual bool hasObject(const UiSelector *selector) const = 0;
+    virtual std::unique_ptr<UiObject> findObject(
+        const UiSelector *selector) const = 0;
+    virtual std::vector<std::unique_ptr<UiObject>> findObjects(
+        const UiSelector *selector) const = 0;
+};
+
+#endif
\ No newline at end of file
diff --git a/libaurum/inc/PartialMatch.h b/libaurum/inc/PartialMatch.h
new file mode 100644 (file)
index 0000000..467310e
--- /dev/null
@@ -0,0 +1,39 @@
+#ifndef PARTIAL_MATCH_H
+#define PARTIAL_MATCH_H
+
+#include <list>
+#include <memory>
+#include <vector>
+
+#include "AccessibleNode.h"
+#include "UiSelector.h"
+
+class PartialMatch {
+private:
+    PartialMatch();
+
+    const UiSelector *                       mSelector;
+    const int                                mDepth;
+    std::list<std::shared_ptr<PartialMatch>> mPartialMatches;
+
+public:
+    PartialMatch(const UiSelector *selector, const int absDepth);
+    void update(const AccessibleNode *node, int index, int depth,
+                std::list<std::shared_ptr<PartialMatch>> &partialMatches);
+    bool finalizeMatch();
+
+public:
+    static std::shared_ptr<PartialMatch> accept(const AccessibleNode *node,
+                                                const UiSelector *    selector,
+                                                int index, int depth);
+    static std::shared_ptr<PartialMatch> accept(const AccessibleNode *node,
+                                                const UiSelector *    selector,
+                                                int index, int absoluteDepth,
+                                                int relativeDepth);
+
+private:
+    static bool checkCriteria(const UiSelector *    sel,
+                              const AccessibleNode *node);
+};
+
+#endif
\ No newline at end of file
diff --git a/libaurum/inc/Sel.h b/libaurum/inc/Sel.h
new file mode 100644 (file)
index 0000000..cc67193
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef SEL_H
+#define SEL_H
+#include "config.h"
+
+#include <memory>
+#include <string>
+
+#include "UiSelector.h"
+
+class Sel {
+public:
+    static std::unique_ptr<UiSelector> text(const std::string &text);
+    static std::unique_ptr<UiSelector> type(const std::string &text);
+    static std::unique_ptr<UiSelector> depth(const int &depth);
+};
+
+#endif
\ No newline at end of file
diff --git a/libaurum/inc/UiDevice.h b/libaurum/inc/UiDevice.h
new file mode 100644 (file)
index 0000000..56d5cec
--- /dev/null
@@ -0,0 +1,73 @@
+#ifndef UIDEVICE_H
+#define UIDEVICE_H
+#include "config.h"
+
+#include "UiObject.h"
+#include "UiSelector.h"
+
+#include "IDevice.h"
+#include "ISearchable.h"
+
+#include "AccessibleNode.h"
+#include "Waiter.h"
+
+#include <functional>
+#include <string>
+
+enum class DeviceType {
+    DEFAULT,
+    NUM_DEVICETYPE,
+};
+
+class UiDevice : public IDevice, public ISearchable {
+public:
+    bool click(const int x, const int y) override;
+    bool drag(const int sx, const int sy, const int ex, const int ey,
+              const int steps) override;
+
+    bool touchDown(const int x, const int y) override;
+    bool touchMove(const int x, const int y) override;
+    bool touchUp(const int x, const int y) override;
+
+    bool pressBack() override;
+    bool pressHome() override;
+    bool pressMenu() override;
+    bool pressVolUp() override;
+    bool pressVolDown() override;
+    bool pressPower() override;
+
+    bool pressKeyCode(std::string keycode) override;
+
+    bool takeScreenshot(std::string path, float scale, int quality);
+
+public:
+    bool hasObject(const UiSelector *selector) const override;
+    std::unique_ptr<UiObject> findObject(
+        const UiSelector *selector) const override;
+    std::vector<std::unique_ptr<UiObject>> findObjects(
+        const UiSelector *selector) const override;
+
+    bool waitFor(
+        const std::function<bool(const ISearchable *)> condition) const;
+    std::unique_ptr<UiObject> waitFor(
+        const std::function<std::unique_ptr<UiObject>(const ISearchable *)>
+            condition) const;
+
+public:
+    static UiDevice *getInstance(DeviceType type);
+
+private:
+    const AccessibleNode *getWindowRoot() const;
+
+private:
+    UiDevice();
+    UiDevice(DeviceType type, IDevice *impl);
+    virtual ~UiDevice();
+
+private:
+    DeviceType    mType;
+    IDevice *     mDeviceImpl;
+    const Waiter *mWaiter;
+};
+
+#endif
\ No newline at end of file
diff --git a/libaurum/inc/UiObject.h b/libaurum/inc/UiObject.h
new file mode 100644 (file)
index 0000000..a776f92
--- /dev/null
@@ -0,0 +1,74 @@
+#ifndef UI_OBJECT_H
+#define UI_OBJECT_H
+#include "config.h"
+
+#include "AccessibleNode.h"
+#include "ISearchable.h"
+#include "UiSelector.h"
+#include "Waiter.h"
+
+#include <memory>
+#include <vector>
+
+class UiDevice;
+
+class UiObject : public ISearchable {
+public:
+    UiObject(const UiDevice *device, const UiSelector *selector,
+             const AccessibleNode *node);
+    UiObject(const UiObject &src);  // copy constroctur
+    UiObject(UiObject &&src);       // move constructor
+
+    virtual ~UiObject();
+
+    bool hasObject(const UiSelector *selector) const override;
+    std::unique_ptr<UiObject> findObject(
+        const UiSelector *selector) const override;
+    std::vector<std::unique_ptr<UiObject>> findObjects(
+        const UiSelector *selector) const override;
+
+    bool waitFor(
+        const std::function<bool(const ISearchable *)> condition) const;
+    std::unique_ptr<UiObject> waitFor(
+        const std::function<std::unique_ptr<UiObject>(const ISearchable *)>
+            condition) const;
+    bool waitFor(const std::function<bool(const UiObject *)> condition) const;
+
+public:
+    UiObject *                             getParent() const;
+    int                                    getChildCount() const;
+    std::vector<std::unique_ptr<UiObject>> getChildren() const;
+
+    std::string getContentDescription() const;
+    std::string getApplicationPackage() const;
+    std::string getResourceName() const;
+
+    std::string getText() const;
+    void        setText(std::string &text);
+
+    bool isCheckable() const;
+    bool isChecked() const;
+    bool isClickable() const;
+    bool isEnabled() const;
+    bool isFocusable() const;
+    bool isFocused() const;
+    bool isLongClickable() const;
+    bool isScrollable() const;
+    bool isSelectable() const;
+    bool isSelected() const;
+
+    void click() const;
+    void refresh() const;
+
+private:
+    UiObject();
+    const AccessibleNode *getAccessibleNode() const;
+
+private:
+    const UiDevice *      mDevice;
+    const UiSelector *    mSelector;
+    const AccessibleNode *mNode;
+    const Waiter *        mWaiter;
+};
+
+#endif
\ No newline at end of file
diff --git a/libaurum/inc/UiSelector.h b/libaurum/inc/UiSelector.h
new file mode 100644 (file)
index 0000000..997ad79
--- /dev/null
@@ -0,0 +1,38 @@
+#ifndef UI_SELECTOR_H
+#define UI_SELECTOR_H
+#include "config.h"
+
+#include <string>
+#include <vector>
+
+class UiSelector {
+public:
+    UiSelector();
+    UiSelector(UiSelector &selector);
+
+    // UiSelector(const UiSelector &src);
+    //        UiSelector &operator= (const UiSelector& src);
+
+public:
+    UiSelector *text(const std::string &text);
+    UiSelector *pkg(const std::string &text);
+    UiSelector *res(const std::string &text);
+    UiSelector *desc(const std::string &text);
+    UiSelector *type(const std::string &text);
+
+    UiSelector *depth(int depth);
+
+    UiSelector *hasChild(UiSelector *child);
+
+public:
+    std::string mText;
+    std::string mPkg;
+    std::string mRes;
+    std::string mDesc;
+    std::string mType;
+    int         mDepth;
+
+    std::vector<UiSelector *> mChild;
+};
+
+#endif
\ No newline at end of file
diff --git a/libaurum/inc/Until.h b/libaurum/inc/Until.h
new file mode 100644 (file)
index 0000000..fc80795
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef UNTIL_H
+#define UNTIL_H
+
+#include <functional>
+#include "ISearchable.h"
+#include "UiSelector.h"
+
+class Until {
+private:
+    Until();
+    Until(const UiSelector *selector);
+    Until(const Until &src);
+    Until(const Until &&src);
+
+public:
+    ~Until();
+
+public:
+    static std::function<bool(const ISearchable *)> hasObject(
+        const UiSelector *selector);
+    static std::function<std::unique_ptr<UiObject>(const ISearchable *)>
+                                                 findObject(const UiSelector *selector);
+    static std::function<bool(const UiObject *)> checkable(
+        const bool isCheckable);
+};
+#endif
+
+// Until::hasObject(Sel::text("text").get())
\ No newline at end of file
diff --git a/libaurum/inc/Waiter.h b/libaurum/inc/Waiter.h
new file mode 100644 (file)
index 0000000..d2ff893
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef WAITER_H
+#define WAITER_H
+
+#include "ISearchable.h"
+
+#include <functional>
+
+class Waiter {
+private:
+    Waiter();
+
+public:
+    Waiter(const ISearchable *searchableObject,
+           const UiObject *   uiObject = nullptr);  // : mObject{object}
+    ~Waiter();
+
+public:
+    template <typename R>
+    R waitFor(const std::function<R(const ISearchable *)> condition) const;
+    template <typename R>
+    R waitFor(const std::function<R(const UiObject *)> object) const;
+
+private:
+    const ISearchable *mSearchableObject;
+    const UiObject *   mUiObject;
+    const int          WAIT_INTERVAL_MS;
+    const int          WAIT_TIMEOUT_MS;
+};
+
+#endif
\ No newline at end of file
diff --git a/libaurum/meson.build b/libaurum/meson.build
new file mode 100644 (file)
index 0000000..d437503
--- /dev/null
@@ -0,0 +1,53 @@
+libaurum_inc = [
+  include_directories('./inc'),
+  root_inc,
+  loguru_inc,
+]
+
+libaurum_src = [
+  files('src/Sel.cc'),
+  files('src/UiDevice.cc'),
+  files('src/UiObject.cc'),
+  files('src/UiSelector.cc'),
+  files('src/Accessible.cc'),
+  files('src/AccessibleNode.cc'),
+  files('src/Comparer.cc'),
+  files('src/Until.cc'),
+  files('src/Waiter.cc'),
+  files('src/PartialMatch.cc'),
+]
+
+if get_option('tizen') == true
+libaurum_src +=[
+  files('src/DeviceImpl/TM1Impl.cc'),
+]
+endif
+
+libaurum_dep = [
+  dependency('atspi-2'),
+  dependency('gio-2.0'),
+  dependency('threads'),
+  loguru_deps,
+]
+
+if get_option('tizen') == true
+  libaurum_dep += [
+    dependency('capi-system-info'),
+    dependency('dlog'),
+    dependency('capi-ui-efl-util'),
+    dependency('elementary'),
+  ]
+endif
+
+libaurum_lib = library('aurum', libaurum_src,
+                        dependencies: libaurum_dep,
+                        include_directories: libaurum_inc,
+                        link_with: libloguru,
+                        install: true,
+                        version: meson.project_version(),
+               )
+
+libaurum =  declare_dependency(link_with: libaurum_lib,
+                               dependencies: libaurum_dep,
+                               include_directories: libaurum_inc,
+            )
diff --git a/libaurum/src/Accessible.cc b/libaurum/src/Accessible.cc
new file mode 100644 (file)
index 0000000..7275c64
--- /dev/null
@@ -0,0 +1,254 @@
+
+#include "Accessible.h"
+
+#include <string.h>
+#include <iostream>
+#include <utility>
+
+#include "loguru.hpp"
+
+AtspiEventListener *Accessible::listener = nullptr;
+
+static bool iShowingNode(AtspiAccessible *node)
+{
+    char *name = atspi_accessible_get_name(node, NULL);
+    char *pname = atspi_accessible_get_name(
+        atspi_accessible_get_parent(node, NULL), NULL);
+
+    LOG_SCOPE_F(INFO, "isShowing %s %s", name, pname);
+
+    if (!strcmp(name, "Keyboard") && !strcmp(pname, "ise-default")) {
+        free(name);
+        free(pname);
+        return false;
+    }
+    free(name);
+    free(pname);
+
+    AtspiStateSet *stateSet = atspi_accessible_get_state_set(node);
+    if (atspi_state_set_contains(stateSet, ATSPI_STATE_ACTIVE) &&
+        atspi_state_set_contains(stateSet, ATSPI_STATE_SHOWING)) {
+        g_object_unref(stateSet);
+        return true;
+    }
+    return false;
+}
+
+static AtspiAccessible *findActiveNode(AtspiAccessible *node, int depth,
+                                       int max_depth)
+{
+    if (depth >= max_depth) return NULL;
+
+    if (iShowingNode(node)) {
+        g_object_ref(node);
+
+        char *name = atspi_accessible_get_name(node, NULL);
+        char *pname = atspi_accessible_get_name(
+            atspi_accessible_get_parent(node, NULL), NULL);
+        LOG_SCOPE_F(INFO, "%s %s", name, pname);
+        return node;
+    }
+
+    int nchild = atspi_accessible_get_child_count(node, NULL);
+    for (int i = 0; i < nchild; i++) {
+        AtspiAccessible *child =
+            atspi_accessible_get_child_at_index(node, i, NULL);
+        AtspiAccessible *active = findActiveNode(child, depth + 1, max_depth);
+        g_object_unref(child);
+        if (active) return active;
+    }
+
+    return NULL;
+}
+
+Accessible::Accessible() : mWindowList{}
+{
+    GVariant *enabled_variant = nullptr, *result = nullptr;
+    GError *  error = nullptr;
+    atspi_init();
+
+    mDbusProxy = g_dbus_proxy_new_for_bus_sync(
+        G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE,
+        NULL, /* GDBusInterfaceInfo */
+        "org.a11y.Bus", "/org/a11y/bus", "org.freedesktop.DBus.Properties",
+        NULL, &error);
+
+    enabled_variant = g_variant_new_boolean(true);
+    result = g_dbus_proxy_call_sync(
+        mDbusProxy, "Set",
+        g_variant_new("(ssv)", "org.a11y.Status", "IsEnabled", enabled_variant),
+        G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
+
+    g_variant_unref(enabled_variant);
+    g_variant_unref(result);
+
+    listener =
+        atspi_event_listener_new(Accessible::onAtspiWindowEvent, this, NULL);
+    atspi_event_listener_register(listener, "window:", NULL);
+    atspi_event_listener_register(listener, "object:", NULL);
+}
+
+Accessible::~Accessible()
+{
+    for (auto it = mAccessibleNode.begin(); it != mAccessibleNode.end(); ++it) {
+        g_object_unref(it->first);
+        delete it->second;
+    }
+    atspi_event_listener_deregister(listener, "window:", NULL);
+    atspi_event_listener_deregister(listener, "object:", NULL);
+
+    g_object_unref(listener);
+    g_object_unref(mDbusProxy);
+
+    atspi_event_quit();
+    atspi_exit();
+}
+
+AccessibleNode *Accessible::getRootNode() const
+{
+    AtspiAccessible *node = atspi_get_desktop(0);
+    AccessibleNode * accNode = AccessibleNode::get(node);
+    if (node) g_object_unref(node);
+    return accNode;
+}
+
+AccessibleNode *Accessible::getTopNode() const
+{
+    AtspiAccessible *topNode = nullptr, *activeNode = nullptr,
+                    *rootNode = nullptr;
+    {
+        std::unique_lock<std::mutex> lock(mLock);
+        if (!mWindowList.empty()) {
+            topNode = mWindowList.front();
+        }
+    }
+
+#ifdef TIZEN
+    char *name, *pname;
+    name = atspi_accessible_get_name(topNode, NULL);
+    pname = atspi_accessible_get_parent(topNode, NULL)
+                ? atspi_accessible_get_name(
+                      atspi_accessible_get_parent(topNode, NULL), NULL)
+                : NULL;
+
+    LOG_F(INFO, "topNode unique id: %s  /  name: %s pname :%s",
+          atspi_accessible_get_unique_id(topNode, NULL), name, pname);
+
+    free(name);
+    free(pname);
+#endif
+
+    if (topNode) {
+        if (iShowingNode(topNode)) {
+            AccessibleNode *node = AccessibleNode::get(topNode);
+            // g_object_unref(activeNode);
+            return node;
+        }
+    }
+
+    rootNode = atspi_get_desktop(0);
+
+    if (rootNode) {
+        activeNode = findActiveNode(rootNode, 0, 2);
+
+#ifdef TIZEN
+        char *name, *pname;
+        name = atspi_accessible_get_name(activeNode, NULL);
+        pname = atspi_accessible_get_parent(activeNode, NULL)
+                    ? atspi_accessible_get_name(
+                          atspi_accessible_get_parent(activeNode, NULL), NULL)
+                    : NULL;
+
+        LOG_F(INFO, "activeNode unique id: %s  /   name: %s pname:%s",
+              atspi_accessible_get_unique_id(activeNode, NULL), name, pname);
+        free(name);
+        free(pname);
+#endif
+
+        if (activeNode) {
+            AccessibleNode *node = AccessibleNode::get(activeNode);
+            return node;
+        }
+        AccessibleNode *node = AccessibleNode::get(rootNode);
+        return node;
+    }
+    return nullptr;
+}
+
+void Accessible::onAtspiWindowEvent(AtspiEvent *event, void *user_data)
+{
+    char *              name, *pname;
+    const IAtspiEvents *instance = (IAtspiEvents *)user_data;
+
+    AtspiAccessible *p = atspi_accessible_get_parent(event->source, NULL);
+
+    name = atspi_accessible_get_name(event->source, NULL);
+    pname = atspi_accessible_get_name(p, NULL);
+
+    LOG_SCOPE_F(INFO, "event:%s, src:%p(%s), p:%p(%s), d1:%p d2:%p instance:%p",
+                event->type, event->source, name, p, pname, event->detail1,
+                event->detail2, instance);
+
+    if (!strcmp(event->type, "window:activate")) {
+        instance->onActivate(
+            static_cast<AtspiAccessible *>(event->source),
+            static_cast<WindowActivateInfoType>(event->detail1));
+    } else if (!strcmp(event->type, "window:deactivate")) {
+        instance->onDeactivate(static_cast<AtspiAccessible *>(event->source));
+    } else if (!strcmp(event->type, "object:state-changed:visible")) {
+        instance->onVisibilityChanged(
+            static_cast<AtspiAccessible *>(event->source),
+            (event->detail1 != 0));
+    } else if (!strcmp(event->type, "object:state-changed:defunct")) {
+        instance->onObjectDefunct(
+            static_cast<AtspiAccessible *>(event->source));
+    }
+}
+
+const Accessible *Accessible::getInstance()
+{
+    static Accessible *mInstance = nullptr;
+    if (!mInstance) mInstance = new Accessible();
+    return mInstance;
+}
+
+void Accessible::clearWindowList() const
+{
+    std::unique_lock<std::mutex> lock(mLock);
+    while (!mWindowList.empty()) {
+        AtspiAccessible *n = mWindowList.front();
+        mWindowList.pop_front();
+        g_object_unref(n);
+    }
+}
+
+void Accessible::onActivate(AtspiAccessible *      node,
+                            WindowActivateInfoType type) const
+{
+    std::unique_lock<std::mutex> lock(mLock);
+
+    char *name = atspi_accessible_get_name(node, NULL);
+    char *pname = atspi_accessible_get_name(
+        atspi_accessible_get_parent(node, NULL), NULL);
+    LOG_SCOPE_F(INFO, "%s %s", name, pname);
+
+    mWindowList.remove_if([&](auto &n) { return n == node; });
+    if (!strcmp(name, "Keyboard") && !strcmp(pname, "ise-default")) return;
+    mWindowList.push_front(node);
+}
+
+void Accessible::onDeactivate(AtspiAccessible *node) const
+{
+    std::unique_lock<std::mutex> lock(mLock);
+    mWindowList.remove_if([&](auto &n) { return n == node; });
+    mWindowList.push_back(node);
+}
+
+void Accessible::onVisibilityChanged(AtspiAccessible *node, bool visible) const
+{
+}
+
+void Accessible::onObjectDefunct(AtspiAccessible *node) const
+{
+    LOG_SCOPE_F(INFO, "object defuncted %p", node);
+}
\ No newline at end of file
diff --git a/libaurum/src/AccessibleNode.cc b/libaurum/src/AccessibleNode.cc
new file mode 100644 (file)
index 0000000..b0ef5cb
--- /dev/null
@@ -0,0 +1,301 @@
+#include "AccessibleNode.h"
+#include <string.h>
+#include <iostream>
+
+#include "loguru.hpp"
+
+#include "config.h"
+
+std::map<AtspiAccessible *, AccessibleNode *> AccessibleNode::mNodeMap{};
+
+AccessibleNode::~AccessibleNode()
+{
+    g_object_unref(mNode);
+}
+
+AccessibleNode::AccessibleNode()
+{
+    // No meaning without AtspiAccessbile object
+    // prohibited to create this object with this constructor
+}
+
+AccessibleNode::AccessibleNode(AtspiAccessible *node)
+    : mNode(node), mSupportingIfaces(0), mIsAlive(true)
+{
+    // prohibited to create this object this constructor
+    // better to use AccessibleNode::get factory method.
+    LOG_SCOPE_F(1, "AccessibleNode constructor %p", node);
+    g_object_ref(node);
+    GArray *ifaces = atspi_accessible_get_interfaces(mNode);
+    if (ifaces) {
+        for (unsigned int i = 0; i < ifaces->len; i++) {
+            char *iface = g_array_index(ifaces, char *, i);
+            if (!strcmp(iface, "Action"))
+                mSupportingIfaces |=
+                    static_cast<int>(AccessibleNodeInterface::ACTION);
+            else if (!strcmp(iface, "Collection"))
+                mSupportingIfaces |=
+                    static_cast<int>(AccessibleNodeInterface::COLLECTION);
+            else if (!strcmp(iface, "Component"))
+                mSupportingIfaces |=
+                    static_cast<int>(AccessibleNodeInterface::COMPONENT);
+            else if (!strcmp(iface, "Document"))
+                mSupportingIfaces |=
+                    static_cast<int>(AccessibleNodeInterface::DOCUMENT);
+            else if (!strcmp(iface, "EditableText"))
+                mSupportingIfaces |=
+                    static_cast<int>(AccessibleNodeInterface::EDITABLETEXT);
+            else if (!strcmp(iface, "Hypertext"))
+                mSupportingIfaces |=
+                    static_cast<int>(AccessibleNodeInterface::HYPERTEXT);
+            else if (!strcmp(iface, "Image"))
+                mSupportingIfaces |=
+                    static_cast<int>(AccessibleNodeInterface::IMAGE);
+            else if (!strcmp(iface, "Selection"))
+                mSupportingIfaces |=
+                    static_cast<int>(AccessibleNodeInterface::SELECTION);
+            else if (!strcmp(iface, "Text"))
+                mSupportingIfaces |=
+                    static_cast<int>(AccessibleNodeInterface::TEXT);
+            else if (!strcmp(iface, "Value"))
+                mSupportingIfaces |=
+                    static_cast<int>(AccessibleNodeInterface::VALUE);
+            else if (!strcmp(iface, "Accessible"))
+                mSupportingIfaces |=
+                    static_cast<int>(AccessibleNodeInterface::ACCESSIBLE);
+            else if (!strcmp(iface, "Table"))
+                mSupportingIfaces |=
+                    static_cast<int>(AccessibleNodeInterface::TABLE);
+            else if (!strcmp(iface, "TableCell"))
+                mSupportingIfaces |=
+                    static_cast<int>(AccessibleNodeInterface::TABLECELL);
+            else
+                LOG_F(WARNING, "Not Supported interface found %s", iface);
+        }
+    }
+    this->refresh();
+}
+
+AccessibleNode *AccessibleNode::get(AtspiAccessible *node)
+{
+    LOG_SCOPE_F(9, "Accessible Node Factory %p", node);
+    if (node == nullptr) return nullptr;
+
+    AccessibleNode *cache = mNodeMap[node];
+    LOG_F(9, "Cache hit ? %p", cache);
+
+    if (!cache)
+        mNodeMap[node] = new AccessibleNode(node);
+    else
+        cache->refresh();
+
+    return mNodeMap[node];
+}
+
+void AccessibleNode::refresh() const
+{
+    gchar *rolename = atspi_accessible_get_role_name(mNode, NULL);
+    mRole = rolename;
+    g_free(rolename);
+
+#ifdef GBS_BUILD
+    gchar *uID = atspi_accessible_get_unique_id(mNode, NULL);
+    mRes = uID;
+    g_free(uID);
+#else
+    mRes = "Not_Supported";
+#endif
+
+    /*
+        GHashTable *attributes = atspi_accessible_get_attributes(mNode, NULL);
+        char *t = (char*)g_hash_table_lookup(attributes, "type");
+        char *s = (char*)g_hash_table_lookup(attributes, "style");
+
+        //LOG_F(INFO, "%s %s", t, s);
+
+        if (t) mType =  std::string(t);
+        if (s) mStyle = std::string(s);
+
+        free(t);
+        free(s);
+
+        g_hash_table_unref(attributes);
+    */
+}
+
+int AccessibleNode::getChildCount() const
+{
+    return atspi_accessible_get_child_count(mNode, NULL);
+}
+
+AccessibleNode *AccessibleNode::getChildAt(int index) const
+{
+    AtspiAccessible *child =
+        atspi_accessible_get_child_at_index(mNode, index, NULL);
+    AccessibleNode *node = AccessibleNode::get(child);
+    if (child) g_object_unref(child);
+    return node;
+}
+
+AccessibleNode *AccessibleNode::getParent() const
+{
+    AtspiAccessible *parent = atspi_accessible_get_parent(mNode, NULL);
+    AccessibleNode * node = AccessibleNode::get(parent);
+    if (parent) g_object_unref(parent);
+    return node;
+}
+
+void AccessibleNode::print(int d, int m) const
+{
+    if (m <= 0 || d > m) return;
+
+    int             n = 0;
+    AccessibleNode *child = nullptr;
+
+    this->print(d);
+    n = getChildCount();
+
+    for (int i = 0; i < n; i++) {
+        child = getChildAt(i);
+        if (child) child->print(d + 1, m);
+    }
+}
+
+void AccessibleNode::print(int d) const
+{
+    char *name = atspi_accessible_get_name(mNode, NULL);
+    char *role = atspi_accessible_get_role_name(mNode, NULL);
+    LOG_F(INFO, "%s - %p(%s)  /  role:%s, pkg:%s, text:%s",
+          std::string(d, ' ').c_str(), mNode, name, role, getPkg().c_str(),
+          getText().c_str());
+    free(name);
+    free(role);
+}
+
+bool AccessibleNode::isSupporting(AccessibleNodeInterface thisIface) const
+{
+    return (mSupportingIfaces & static_cast<int>(thisIface)) != 0;
+}
+
+bool AccessibleNode::hasFeatureProperty(NodeFeatureProperties prop) const
+{
+    return (mFeatureProperty & static_cast<int>(prop)) != 0;
+}
+
+void AccessibleNode::setFeatureProperty(NodeFeatureProperties prop, bool has)
+{
+    if (has)
+        mFeatureProperty |= static_cast<int>(prop);
+    else
+        mFeatureProperty &= ~static_cast<int>(prop);
+}
+
+std::string AccessibleNode::getDesc() const
+{
+    return mDesc;
+}
+
+std::string AccessibleNode::getText() const
+{
+    gchar *name = atspi_accessible_get_name(mNode, NULL);
+    mText = name;
+    mPkg = name;
+    g_free(name);
+
+    return mText;
+}
+
+std::string AccessibleNode::getPkg() const
+{
+    return mPkg;
+}
+
+std::string AccessibleNode::getRes() const
+{
+    return mRes;
+}
+std::string AccessibleNode::getType() const
+{
+    return mType;
+}
+Rect<int> AccessibleNode::getBoundingBox() const
+{
+    AtspiComponent *component = atspi_accessible_get_component_iface(mNode);
+    if (component) {
+        AtspiRect *extent = atspi_component_get_extents(
+            component, ATSPI_COORD_TYPE_SCREEN, NULL);
+        if (extent) {
+            mBoundingBox =
+                Rect<int>{extent->x, extent->y, extent->x + extent->width,
+                          extent->y + extent->height};
+            g_free(extent);
+        }
+        g_object_unref(component);
+    }
+
+    return mBoundingBox;
+}
+
+bool AccessibleNode::isCheckable() const
+{
+    return hasFeatureProperty(NodeFeatureProperties::CHECKABLE);
+}
+
+bool AccessibleNode::isChecked() const
+{
+    return hasFeatureProperty(NodeFeatureProperties::CHECKED);
+}
+
+bool AccessibleNode::isClickable() const
+{
+    return hasFeatureProperty(NodeFeatureProperties::CLICKABLE);
+}
+
+bool AccessibleNode::isEnabled() const
+{
+    return hasFeatureProperty(NodeFeatureProperties::ENABLED);
+}
+
+bool AccessibleNode::isFocusable() const
+{
+    return hasFeatureProperty(NodeFeatureProperties::FOCUSABLE);
+}
+
+bool AccessibleNode::isFocused() const
+{
+    return hasFeatureProperty(NodeFeatureProperties::FOCUSED);
+}
+
+bool AccessibleNode::isLongClickable() const
+{
+    return hasFeatureProperty(NodeFeatureProperties::LONGCLICKABLE);
+}
+
+bool AccessibleNode::isScrollable() const
+{
+    return hasFeatureProperty(NodeFeatureProperties::SCROLLABLE);
+}
+
+bool AccessibleNode::isSelectable() const
+{
+    return hasFeatureProperty(NodeFeatureProperties::SELECTABLE);
+}
+
+bool AccessibleNode::isSelected() const
+{
+    return hasFeatureProperty(NodeFeatureProperties::SELECTED);
+}
+
+AtspiAccessible *AccessibleNode::getAccessible()
+{
+    return mNode;
+}
+
+void AccessibleNode::setValue(std::string &text) const
+{
+    AtspiEditableText *iface = atspi_accessible_get_editable_text(mNode);
+    if (iface) {
+        atspi_editable_text_insert_text(iface, 0, text.c_str(), text.length(),
+                                        NULL);
+    }
+}
\ No newline at end of file
diff --git a/libaurum/src/Comparer.cc b/libaurum/src/Comparer.cc
new file mode 100644 (file)
index 0000000..9a1e777
--- /dev/null
@@ -0,0 +1,72 @@
+#include "Comparer.h"
+
+#include "loguru.hpp"
+
+Comparer::Comparer(const UiDevice *device, const UiSelector *selector,
+                   const bool &earlyReturn)
+    : mDevice(device), mSelector(selector), mEarlyReturn(earlyReturn)
+{
+}
+
+Comparer::~Comparer() {}
+
+AccessibleNode *Comparer::findObject(const UiDevice *      device,
+                                     const UiSelector *    selector,
+                                     const AccessibleNode *root)
+{
+    Comparer                      comparer(device, selector, true);
+    std::vector<AccessibleNode *> ret = comparer.findObjects(root);
+    if (ret.size() > 0)
+        return ret[0];
+    else
+        return nullptr;
+}
+
+std::vector<AccessibleNode *> Comparer::findObjects(const UiDevice *  device,
+                                                    const UiSelector *selector,
+                                                    const AccessibleNode *root)
+{
+    Comparer                      comparer(device, selector, false);
+    std::vector<AccessibleNode *> ret = comparer.findObjects(root);
+    return std::move(ret);
+}
+
+std::vector<AccessibleNode *> Comparer::findObjects(const AccessibleNode *root)
+{
+    std::list<std::shared_ptr<PartialMatch>> partialList{};
+    std::vector<AccessibleNode *> ret = findObjects(root, 0, 0, partialList);
+    return std::move(ret);
+}
+
+std::vector<AccessibleNode *> Comparer::findObjects(
+    const AccessibleNode *root, const int &index, const int &depth,
+    std::list<std::shared_ptr<PartialMatch>> &partialMatches)
+{
+    std::vector<AccessibleNode *> ret;
+    root->refresh();
+
+    // LOG_F(INFO, "%p %s / i:%d d:%d", root, root->getText().c_str(), index,
+    // depth);
+
+    for (auto match : partialMatches)
+        match->update(root, index, depth, partialMatches);
+
+    std::shared_ptr<PartialMatch> currentMatch =
+        PartialMatch::accept(root, mSelector, index, depth);
+    if (currentMatch) partialMatches.push_front(currentMatch);
+
+    int childCnt = root->getChildCount();
+    for (int i = 0; i < childCnt; i++) {
+        AccessibleNode *              childNode = root->getChildAt(i);
+        std::vector<AccessibleNode *> childret =
+            findObjects(childNode, i, depth + 1, partialMatches);
+        ret.insert(ret.end(), childret.begin(), childret.end());
+
+        if (!ret.empty() && mEarlyReturn) return ret;
+    }
+
+    if (currentMatch && currentMatch->finalizeMatch())
+        ret.push_back(const_cast<AccessibleNode *>(root));
+
+    return ret;
+}
\ No newline at end of file
diff --git a/libaurum/src/DeviceImpl/TM1Impl.cc b/libaurum/src/DeviceImpl/TM1Impl.cc
new file mode 100644 (file)
index 0000000..f55c427
--- /dev/null
@@ -0,0 +1,141 @@
+#include "DeviceImpl/TM1Impl.h"
+
+#include <stdlib.h>
+#include <iostream>
+
+#include <stdlib.h>
+#include "loguru.hpp"
+
+TM1Impl::TM1Impl()
+{
+    LOG_SCOPE_F(INFO, "device implementation init");
+#ifdef GBSBUILD
+    mFakeTouchHandle =
+        efl_util_input_initialize_generator(EFL_UTIL_INPUT_DEVTYPE_TOUCHSCREEN);
+    mFakeKeyboardHandle =
+        efl_util_input_initialize_generator(EFL_UTIL_INPUT_DEVTYPE_KEYBOARD);
+#endif
+}
+
+TM1Impl::~TM1Impl()
+{
+#ifdef GBSBUILD
+    efl_util_input_deinitialize_generator(mFakeTouchHandle);
+    efl_util_input_deinitialize_generator(mFakeKeyboardHandle);
+#endif
+}
+
+bool TM1Impl::click(const int x, const int y)
+{
+    LOG_SCOPE_F(INFO, "click at (%d, %d)", x, y);
+#ifdef GBSBUILD
+    efl_util_input_generate_touch(mFakeTouchHandle, 0,
+                                  EFL_UTIL_INPUT_TOUCH_BEGIN, x, y);
+    usleep(50);
+    efl_util_input_generate_touch(mFakeTouchHandle, 0, EFL_UTIL_INPUT_TOUCH_END,
+                                  x, y);
+    return true;
+#else
+    return false;
+#endif
+}
+
+bool TM1Impl::touchDown(const int x, const int y)
+{
+#ifdef GBSBUILD
+    LOG_F(INFO, "%d %d", x, y);
+    efl_util_input_generate_touch(mFakeTouchHandle, 0,
+                                  EFL_UTIL_INPUT_TOUCH_BEGIN, x, y);
+#endif
+    return true;
+}
+
+bool TM1Impl::touchMove(const int x, const int y)
+{
+#ifdef GBSBUILD
+    LOG_F(INFO, "%d %d", x, y);
+    efl_util_input_generate_touch(mFakeTouchHandle, 0,
+                                  EFL_UTIL_INPUT_TOUCH_UPDATE, x, y);
+#endif
+    return true;
+}
+
+bool TM1Impl::touchUp(const int x, const int y)
+{
+#ifdef GBSBUILD
+    LOG_F(INFO, "%d %d", x, y);
+    efl_util_input_generate_touch(mFakeTouchHandle, 0, EFL_UTIL_INPUT_TOUCH_END,
+                                  x, y);
+#endif
+    return true;
+}
+
+bool TM1Impl::drag(const int sx, const int sy, const int ex, const int ey,
+                         const int duration)
+{
+#ifdef GBSBUILD
+    int i, j;
+
+    // TODO fixed fps implementation
+
+    i = sx, j = sy;
+    LOG_SCOPE_F(INFO, "flicking (%d, %d)", i, j);
+
+    efl_util_input_generate_touch(mFakeTouchHandle, 0,
+                                  EFL_UTIL_INPUT_TOUCH_BEGIN, i, j);
+    for (; i <= ex && j <= ey; i += (ex - sx) / 10, j += (ey - sy) / 10) {
+        efl_util_input_generate_touch(mFakeTouchHandle, 0,
+                                      EFL_UTIL_INPUT_TOUCH_UPDATE, i, j);
+        usleep(duration * 1000);
+        LOG_SCOPE_F(INFO, "flicking (%d, %d)", i, j);
+    }
+    LOG_SCOPE_F(INFO, "flicking (%d, %d)", i, j);
+    efl_util_input_generate_touch(mFakeTouchHandle, 0, EFL_UTIL_INPUT_TOUCH_END,
+                                  i, j);
+#endif
+    return true;
+}
+
+bool TM1Impl::pressBack()
+{
+    return pressKeyCode("XF86Back");
+}
+
+bool TM1Impl::pressHome()
+{
+    return pressKeyCode("XF86Home");
+}
+
+bool TM1Impl::pressMenu()
+{
+    return pressKeyCode("XF86Menu");
+}
+
+bool TM1Impl::pressVolUp()
+{
+    return pressKeyCode("XF86AudioRaiseVolume");
+}
+
+bool TM1Impl::pressVolDown()
+{
+    return pressKeyCode("XF86AudioLowerVolume");
+}
+
+bool TM1Impl::pressPower()
+{
+    return pressKeyCode("XF86PowerOff");
+}
+
+bool TM1Impl::pressKeyCode(std::string keycode)
+{
+#ifdef GBSBUILD
+    efl_util_input_generate_key(mFakeKeyboardHandle, keycode.c_str(), 1);
+    efl_util_input_generate_key(mFakeKeyboardHandle, keycode.c_str(), 0);
+#endif
+    return true;
+}
+
+bool TM1Impl::takeScreenshot(std::string path, float scale, int quality)
+{
+    return false;
+}
\ No newline at end of file
diff --git a/libaurum/src/PartialMatch.cc b/libaurum/src/PartialMatch.cc
new file mode 100644 (file)
index 0000000..7d57236
--- /dev/null
@@ -0,0 +1,94 @@
+#include "PartialMatch.h"
+
+#include <iostream>
+#include <set>
+
+#include "loguru.hpp"
+
+bool PartialMatch::checkCriteria(const UiSelector *    sel,
+                                 const AccessibleNode *node)
+{
+    if (sel->mPkg.length() > 0 && sel->mPkg.compare(node->getPkg()))
+        return false;
+    if (sel->mRes.length() > 0 && sel->mRes.compare(node->getRes()))
+        return false;
+    if (sel->mText.length() > 0 && sel->mText.compare(node->getText()))
+        return false;
+    if (sel->mDesc.length() > 0 && sel->mDesc.compare(node->getDesc()))
+        return false;
+    if (sel->mType.length() > 0 && sel->mType.compare(node->getType()))
+        return false;
+
+    LOG_F(INFO, "node mPkg :%s, sel->desc :%s | %ld", node->getPkg().c_str(),
+          sel->mPkg.c_str(), sel->mPkg.length());
+    LOG_F(INFO, "node mRes :%s, sel->desc :%s | %ld", node->getRes().c_str(),
+          sel->mRes.c_str(), sel->mRes.length());
+    LOG_F(INFO, "node mText :%s, sel->desc :%s | %ld", node->getText().c_str(),
+          sel->mText.c_str(), sel->mText.length());
+    LOG_F(INFO, "node mDesc :%s, sel->desc :%s | %ld", node->getDesc().c_str(),
+          sel->mDesc.c_str(), sel->mDesc.length());
+    LOG_F(INFO, "node mType :%s, sel->type :%s | %ld", node->getType().c_str(),
+          sel->mType.c_str(), sel->mType.length());
+
+    return true;
+}
+
+PartialMatch::PartialMatch() : mSelector{nullptr}, mDepth{-1}, mPartialMatches{}
+{
+}
+
+PartialMatch::PartialMatch(const UiSelector *selector, const int absDepth)
+    : mSelector{selector}, mDepth{absDepth}, mPartialMatches{}
+{
+}
+
+std::shared_ptr<PartialMatch> PartialMatch::accept(const AccessibleNode *node,
+                                                   const UiSelector *selector,
+                                                   int index, int depth)
+{
+    return PartialMatch::accept(node, selector, index, depth, depth);
+}
+
+std::shared_ptr<PartialMatch> PartialMatch::accept(const AccessibleNode *node,
+                                                   const UiSelector *selector,
+                                                   int index, int absoluteDepth,
+                                                   int relativeDepth)
+{
+    PartialMatch *match = nullptr;
+
+    if (PartialMatch::checkCriteria(selector, node)) {
+        LOG_SCOPE_F(INFO, "New Match found %p %d", selector, absoluteDepth);
+        match = new PartialMatch(selector, absoluteDepth);
+    }
+
+    return std::shared_ptr<PartialMatch>(match);
+}
+
+void PartialMatch::update(
+    const AccessibleNode *node, int index, int depth,
+    std::list<std::shared_ptr<PartialMatch>> &partialMatches)
+{
+    for (auto childSelector : mSelector->mChild) {
+        auto match = PartialMatch::accept(node, childSelector, index, depth,
+                                          depth - mDepth);
+        if (match) {
+            mPartialMatches.push_back(match);
+            partialMatches.push_front(match);
+        }
+    }
+}
+
+bool PartialMatch::finalizeMatch()
+{
+    std::set<UiSelector *> matches;
+    for (auto match : mPartialMatches) {
+        if (match->finalizeMatch()) {
+            matches.insert(const_cast<UiSelector *>(match->mSelector));
+        }
+    }
+
+    for (auto sel : mSelector->mChild) {
+        if (!matches.count(sel)) return false;
+    }
+    return true;
+}
diff --git a/libaurum/src/Sel.cc b/libaurum/src/Sel.cc
new file mode 100644 (file)
index 0000000..38be464
--- /dev/null
@@ -0,0 +1,23 @@
+#include "Sel.h"
+#include <utility>
+
+std::unique_ptr<UiSelector> Sel::text(const std::string &text)
+{
+    std::unique_ptr<UiSelector> sel = std::make_unique<UiSelector>();
+    sel->text(text);
+    return sel;
+}
+
+std::unique_ptr<UiSelector> Sel::type(const std::string &text)
+{
+    std::unique_ptr<UiSelector> sel = std::make_unique<UiSelector>();
+    sel->type(text);
+    return sel;
+}
+
+std::unique_ptr<UiSelector> Sel::depth(const int &depth)
+{
+    std::unique_ptr<UiSelector> sel = std::make_unique<UiSelector>();
+    sel->depth(depth);
+    return sel;
+}
\ No newline at end of file
diff --git a/libaurum/src/UiDevice.cc b/libaurum/src/UiDevice.cc
new file mode 100644 (file)
index 0000000..afc9398
--- /dev/null
@@ -0,0 +1,145 @@
+#include "UiDevice.h"
+#include "Accessible.h"
+#include "Comparer.h"
+#include "DeviceImpl/TM1Impl.h"
+
+#include <unistd.h>
+#include <utility>
+#include <vector>
+
+UiDevice::UiDevice() : UiDevice(DeviceType::DEFAULT, nullptr) {}
+
+UiDevice::UiDevice(DeviceType type, IDevice *impl)
+    : mType(type), mDeviceImpl(impl), mWaiter(new Waiter{this})
+{
+}
+
+UiDevice::~UiDevice()
+{
+    delete mDeviceImpl;
+    delete mWaiter;
+}
+
+UiDevice *UiDevice::getInstance(DeviceType type)
+{
+    static UiDevice *device = nullptr;
+#ifdef TIZEN
+    if (!device) device = new UiDevice(type, new TM1Impl());
+#endif
+    return device;
+}
+
+const AccessibleNode *UiDevice::getWindowRoot() const
+{
+    AccessibleNode *root = Accessible::getInstance()->getTopNode();
+    // root->print(0,6);
+    return root;
+}
+
+bool UiDevice::hasObject(const UiSelector *selector) const
+{
+    AccessibleNode *node =
+        Comparer::findObject(this, selector, getWindowRoot());
+    if (node != nullptr) return true;
+    return false;
+}
+
+std::unique_ptr<UiObject> UiDevice::findObject(const UiSelector *selector) const
+{
+    AccessibleNode *node =
+        Comparer::findObject(this, selector, getWindowRoot());
+    if (node)
+        return std::make_unique<UiObject>(this, selector, node);
+    else
+        return std::unique_ptr<UiObject>{nullptr};
+}
+
+std::vector<std::unique_ptr<UiObject>> UiDevice::findObjects(
+    const UiSelector *selector) const
+{
+    std::vector<std::unique_ptr<UiObject>> ret{};
+    std::vector<AccessibleNode *>          nodes =
+        Comparer::findObjects(this, selector, getWindowRoot());
+    for (const AccessibleNode *node : nodes)
+        ret.push_back(std::make_unique<UiObject>(this, selector, node));
+
+    return std::move(ret);
+}
+bool UiDevice::waitFor(
+    const std::function<bool(const ISearchable *)> condition) const
+{
+    return mWaiter->waitFor(condition);
+}
+
+std::unique_ptr<UiObject> UiDevice::waitFor(
+    const std::function<std::unique_ptr<UiObject>(const ISearchable *)>
+        condition) const
+{
+    return mWaiter->waitFor(condition);
+}
+
+bool UiDevice::click(const int x, const int y)
+{
+    return mDeviceImpl->click(x, y);
+}
+
+bool UiDevice::drag(const int sx, const int sy, const int ex, const int ey,
+                    const int steps)
+{
+    return mDeviceImpl->drag(sx, sy, ex, ey, steps);
+}
+
+bool UiDevice::touchDown(const int x, const int y)
+{
+    return mDeviceImpl->touchDown(x, y);
+}
+
+bool UiDevice::touchMove(const int x, const int y)
+{
+    return mDeviceImpl->touchMove(x, y);
+}
+
+bool UiDevice::touchUp(const int x, const int y)
+{
+    return mDeviceImpl->touchUp(x, y);
+}
+
+bool UiDevice::pressBack()
+{
+    return mDeviceImpl->pressBack();
+}
+
+bool UiDevice::pressHome()
+{
+    return mDeviceImpl->pressHome();
+}
+
+bool UiDevice::pressMenu()
+{
+    return mDeviceImpl->pressMenu();
+}
+
+bool UiDevice::pressVolUp()
+{
+    return mDeviceImpl->pressVolUp();
+}
+
+bool UiDevice::pressVolDown()
+{
+    return mDeviceImpl->pressVolDown();
+}
+
+bool UiDevice::pressPower()
+{
+    return mDeviceImpl->pressPower();
+}
+
+bool UiDevice::pressKeyCode(std::string keycode)
+{
+    return mDeviceImpl->pressKeyCode(keycode);
+}
+
+bool UiDevice::takeScreenshot(std::string path, float scale, int quality)
+{
+    return mDeviceImpl->takeScreenshot(path, scale, quality);
+}
\ No newline at end of file
diff --git a/libaurum/src/UiObject.cc b/libaurum/src/UiObject.cc
new file mode 100644 (file)
index 0000000..85e349c
--- /dev/null
@@ -0,0 +1,212 @@
+#include "UiObject.h"
+#include "Comparer.h"
+#include "Sel.h"
+
+#include <iostream>
+#include <utility>
+
+#include "loguru.hpp"
+
+UiObject::UiObject() {}
+
+UiObject::~UiObject()
+{
+    if (mWaiter) delete mWaiter;
+}
+
+UiObject::UiObject(const UiDevice *device, const UiSelector *selector,
+                   const AccessibleNode *node)
+    : mDevice(device),
+      mSelector(selector),
+      mNode(node),
+      mWaiter(new Waiter{this, this})
+{
+    // tood interface to interact with input interface
+    // mInputImple = mDevice->getInputInterface();
+}
+
+UiObject::UiObject(const UiObject &src)
+    : mDevice(src.mDevice),
+      mSelector(src.mSelector),
+      mNode(src.mNode),
+      mWaiter{src.mWaiter}
+{
+}
+
+UiObject::UiObject(UiObject &&src)
+    : mDevice(src.mDevice),
+      mSelector(src.mSelector),
+      mNode(src.mNode),
+      mWaiter{src.mWaiter}
+{
+    src.mDevice = nullptr;
+    src.mSelector = nullptr;
+    src.mNode = nullptr;
+    src.mWaiter = nullptr;
+}
+
+bool UiObject::hasObject(const UiSelector *selector) const
+{
+    AccessibleNode *node =
+        Comparer::findObject(mDevice, selector, getAccessibleNode());
+    if (node != nullptr) {
+        // todo : what is this node.recycle()
+        return true;
+    }
+    return false;
+}
+
+std::unique_ptr<UiObject> UiObject::findObject(const UiSelector *selector) const
+{
+    AccessibleNode *node =
+        Comparer::findObject(mDevice, selector, getAccessibleNode());
+    if (node)
+        return std::make_unique<UiObject>(mDevice, selector, node);
+    else
+        return std::unique_ptr<UiObject>{nullptr};
+}
+
+std::vector<std::unique_ptr<UiObject>> UiObject::findObjects(
+    const UiSelector *selector) const
+{
+    return std::vector<std::unique_ptr<UiObject>>{};
+}
+
+bool UiObject::waitFor(
+    const std::function<bool(const ISearchable *)> condition) const
+{
+    return mWaiter->waitFor(condition);
+}
+
+std::unique_ptr<UiObject> UiObject::waitFor(
+    const std::function<std::unique_ptr<UiObject>(const ISearchable *)>
+        condition) const
+{
+    return mWaiter->waitFor(condition);
+}
+
+bool UiObject::waitFor(
+    const std::function<bool(const UiObject *)> condition) const
+{
+    LOG_F(INFO, "asdf");
+    return mWaiter->waitFor(condition);
+}
+
+UiObject *UiObject::getParent() const
+{
+    AccessibleNode *node = getAccessibleNode()->getParent();
+    if (!node) return nullptr;
+    return new UiObject(mDevice, mSelector, node);
+}
+
+int UiObject::getChildCount() const
+{
+    return getAccessibleNode()->getChildCount();
+}
+
+std::vector<std::unique_ptr<UiObject>> UiObject::getChildren() const
+{
+    return findObjects(Sel::depth(1).get());
+}
+
+std::string UiObject::getContentDescription() const
+{
+    return getAccessibleNode()->getDesc();
+}
+
+std::string UiObject::getApplicationPackage() const
+{
+    return getAccessibleNode()->getPkg();
+}
+
+std::string UiObject::getResourceName() const
+{
+    return getAccessibleNode()->getRes();
+}
+
+std::string UiObject::getText() const
+{
+    return getAccessibleNode()->getText();
+}
+
+void UiObject::setText(std::string &text)
+{
+    getAccessibleNode()->setValue(text);
+}
+
+bool UiObject::isCheckable() const
+{
+    return getAccessibleNode()->isCheckable();
+}
+
+bool UiObject::isChecked() const
+{
+    return getAccessibleNode()->isChecked();
+}
+
+bool UiObject::isClickable() const
+{
+    return getAccessibleNode()->isClickable();
+}
+
+bool UiObject::isEnabled() const
+{
+    return getAccessibleNode()->isEnabled();
+}
+
+bool UiObject::isFocusable() const
+{
+    return getAccessibleNode()->isFocusable();
+}
+
+bool UiObject::isFocused() const
+{
+    return getAccessibleNode()->isFocused();
+}
+
+bool UiObject::isLongClickable() const
+{
+    return getAccessibleNode()->isLongClickable();
+}
+
+bool UiObject::isScrollable() const
+{
+    return getAccessibleNode()->isScrollable();
+}
+
+bool UiObject::isSelectable() const
+{
+    return getAccessibleNode()->isSelectable();
+}
+
+bool UiObject::isSelected() const
+{
+    return getAccessibleNode()->isSelected();
+}
+
+void UiObject::refresh() const
+{
+    mNode->refresh();
+}
+
+void UiObject::click() const
+{
+    LOG_SCOPE_F(INFO, "click on obj %p", this);
+    mNode->refresh();
+    const Rect<int> rect = mNode->getBoundingBox();
+    std::cout << rect.mTopLeft.x << ", " << rect.mTopLeft.y << std::endl;
+    const Point2D<int> midPoint = rect.midPoint();
+    const_cast<UiDevice *>(mDevice)->click(midPoint.x, midPoint.y);
+    // todo click implementation
+}
+
+const AccessibleNode *UiObject::getAccessibleNode() const
+{
+    if (mNode == nullptr) throw;
+
+    // TODO : wait for animation and refresh current node
+    // mDevice->waitForIdle();
+    // mNode->refresh();
+
+    return mNode;
+}
\ No newline at end of file
diff --git a/libaurum/src/UiSelector.cc b/libaurum/src/UiSelector.cc
new file mode 100644 (file)
index 0000000..c8dece4
--- /dev/null
@@ -0,0 +1,62 @@
+#include "UiSelector.h"
+#include <string>
+
+UiSelector::UiSelector()
+    : mText{""}, mPkg{""}, mRes{""}, mDesc{""}, mType{""}, mDepth{-1}, mChild{}
+{
+}
+/*
+UiSelector::UiSelector(const UiSelector &src)
+{
+    // 복사 생성자
+}
+
+UiSelector& UiSelector::operator= (const UiSelector& src)
+{
+    // = 오버라이드
+    if (this == &src) return *this; // 자기 대입 방지
+    return *this;
+}
+*/
+
+UiSelector *UiSelector::desc(const std::string &text)
+{
+    this->mDesc = text;
+    return this;
+}
+
+UiSelector *UiSelector::text(const std::string &text)
+{
+    this->mText = text;
+    return this;
+}
+
+UiSelector *UiSelector::pkg(const std::string &text)
+{
+    this->mPkg = text;
+    return this;
+}
+
+UiSelector *UiSelector::res(const std::string &text)
+{
+    this->mRes = text;
+    return this;
+}
+
+UiSelector *UiSelector::type(const std::string &text)
+{
+    this->mType = text;
+    return this;
+}
+
+UiSelector *UiSelector::depth(int depth)
+{
+    this->mDepth = depth;
+    return this;
+}
+
+UiSelector *UiSelector::hasChild(UiSelector *child)
+{
+    mChild.push_back(child);
+    return this;
+}
diff --git a/libaurum/src/Until.cc b/libaurum/src/Until.cc
new file mode 100644 (file)
index 0000000..c67fa2b
--- /dev/null
@@ -0,0 +1,33 @@
+#include <Until.h>
+
+#include <UiObject.h>
+#include <loguru.hpp>
+
+std::function<bool(const ISearchable *)> Until::hasObject(
+    const UiSelector *selector)
+{
+    return [=](const ISearchable *searchable) -> bool {
+        LOG_SCOPE_F(INFO, "sel:%p, search:%p", selector, searchable);
+        std::unique_ptr<UiObject> obj = searchable->findObject(selector);
+        return obj.get() != nullptr;
+    };
+}
+
+std::function<std::unique_ptr<UiObject>(const ISearchable *)> Until::findObject(
+    const UiSelector *selector)
+{
+    return [=](const ISearchable *searchable) -> std::unique_ptr<UiObject> {
+        LOG_SCOPE_F(INFO, "sel:%p, search:%p", selector, searchable);
+        std::unique_ptr<UiObject> obj = searchable->findObject(selector);
+        return obj;
+    };
+}
+
+std::function<bool(const UiObject *)> Until::checkable(const bool isCheckable)
+{
+    return [=](const UiObject *object) -> bool {
+        LOG_SCOPE_F(INFO, "waitfor ischeckable %d for obj %p", isCheckable,
+                    object);
+        return object->isClickable() == isCheckable;
+    };
+}
\ No newline at end of file
diff --git a/libaurum/src/Waiter.cc b/libaurum/src/Waiter.cc
new file mode 100644 (file)
index 0000000..4e4dbec
--- /dev/null
@@ -0,0 +1,72 @@
+#include "Waiter.h"
+#include <unistd.h>
+#include <chrono>
+#include <thread>
+
+#include "ISearchable.h"
+#include "UiObject.h"
+#include "loguru.hpp"
+
+Waiter::Waiter() : Waiter(nullptr) {}
+
+Waiter::~Waiter() {}
+
+Waiter::Waiter(const ISearchable *searchableObject, const UiObject *uiObject)
+    : mSearchableObject{searchableObject},
+      mUiObject{uiObject},
+      WAIT_INTERVAL_MS{500},
+      WAIT_TIMEOUT_MS{5000}
+{
+}
+
+template bool Waiter::waitFor(
+    const std::function<bool(const ISearchable *)> condition) const;
+
+template std::unique_ptr<UiObject> Waiter::waitFor(
+    const std::function<std::unique_ptr<UiObject>(const ISearchable *)>
+        condition) const;
+
+template bool Waiter::waitFor(
+    const std::function<bool(const UiObject *)> condition) const;
+
+template <typename R>
+R Waiter::waitFor(const std::function<R(const ISearchable *)> condition) const
+{
+    // startTime = currentTime();
+    std::chrono::system_clock::time_point start =
+        std::chrono::system_clock::now();
+    R result = condition(mSearchableObject);
+    while (!result) {
+        if ((std::chrono::system_clock::now() - start) >
+            std::chrono::milliseconds{WAIT_TIMEOUT_MS})
+            break;
+        std::this_thread::sleep_for(
+            std::chrono::milliseconds{WAIT_INTERVAL_MS});
+        result = condition(mSearchableObject);
+    }
+    return result;
+}
+
+template <typename R>
+R Waiter::waitFor(const std::function<R(const UiObject *)> condition) const
+{
+    LOG_F(INFO, "1");
+    if (mUiObject) {
+        LOG_F(INFO, "2");
+        std::chrono::system_clock::time_point start =
+            std::chrono::system_clock::now();
+        R result = condition(mUiObject);
+        LOG_F(INFO, "3 : %d", result);
+        while (!result) {
+            if ((std::chrono::system_clock::now() - start) >
+                std::chrono::milliseconds{WAIT_TIMEOUT_MS})
+                break;
+            std::this_thread::sleep_for(
+                std::chrono::milliseconds{WAIT_INTERVAL_MS});
+            result = condition(mUiObject);
+            LOG_F(INFO, "4 : %d", result);
+        }
+        return result;
+    }
+    return R();
+}
\ No newline at end of file
diff --git a/libloguru/inc/loguru.hpp b/libloguru/inc/loguru.hpp
new file mode 100644 (file)
index 0000000..24eca7a
--- /dev/null
@@ -0,0 +1,1335 @@
+/*
+Loguru logging library for C++, by Emil Ernerfeldt.
+www.github.com/emilk/loguru
+If you find Loguru useful, please let me know on twitter or in a mail!
+Twitter: @ernerfeldt
+Mail:    emil.ernerfeldt@gmail.com
+Website: www.ilikebigbits.com
+
+# License
+       This software is in the public domain. Where that dedication is not
+       recognized, you are granted a perpetual, irrevocable license to
+       copy, modify and distribute it as you see fit.
+
+# Inspiration
+       Much of Loguru was inspired by GLOG, https://code.google.com/p/google-glog/.
+       The choice of public domain is fully due Sean T. Barrett
+       and his wonderful stb libraries at https://github.com/nothings/stb.
+
+# Version history
+       * Version 0.1.0 - 2015-03-22 - Works great on Mac.
+       * Version 0.2.0 - 2015-09-17 - Removed the only dependency.
+       * Version 0.3.0 - 2015-10-02 - Drop-in replacement for most of GLOG
+       * Version 0.4.0 - 2015-10-07 - Single-file!
+       * Version 0.5.0 - 2015-10-17 - Improved file logging
+       * Version 0.6.0 - 2015-10-24 - Add stack traces
+       * Version 0.7.0 - 2015-10-27 - Signals
+       * Version 0.8.0 - 2015-10-30 - Color logging.
+       * Version 0.9.0 - 2015-11-26 - ABORT_S and proper handling of FATAL
+       * Version 1.0.0 - 2016-02-14 - ERROR_CONTEXT
+       * Version 1.1.0 - 2016-02-19 - -v OFF, -v INFO etc
+       * Version 1.1.1 - 2016-02-20 - textprintf vs strprintf
+       * Version 1.1.2 - 2016-02-22 - Remove g_alsologtostderr
+       * Version 1.1.3 - 2016-02-29 - ERROR_CONTEXT as linked list
+       * Version 1.2.0 - 2016-03-19 - Add get_thread_name()
+       * Version 1.2.1 - 2016-03-20 - Minor fixes
+       * Version 1.2.2 - 2016-03-29 - Fix issues with set_fatal_handler throwing an exception
+       * Version 1.2.3 - 2016-05-16 - Log current working directory in loguru::init().
+       * Version 1.2.4 - 2016-05-18 - Custom replacement for -v in loguru::init() by bjoernpollex
+       * Version 1.2.5 - 2016-05-18 - Add ability to print ERROR_CONTEXT of parent thread.
+       * Version 1.2.6 - 2016-05-19 - Bug fix regarding VLOG verbosity argument lacking ().
+       * Version 1.2.7 - 2016-05-23 - Fix PATH_MAX problem.
+       * Version 1.2.8 - 2016-05-26 - Add shutdown() and remove_all_callbacks()
+       * Version 1.2.9 - 2016-06-09 - Use a monotonic clock for uptime.
+       * Version 1.3.0 - 2016-07-20 - Fix issues with callback flush/close not being called.
+       * Version 1.3.1 - 2016-07-20 - Add LOGURU_UNSAFE_SIGNAL_HANDLER to toggle stacktrace on signals.
+       * Version 1.3.2 - 2016-07-20 - Add loguru::arguments()
+       * Version 1.4.0 - 2016-09-15 - Semantic versioning + add loguru::create_directories
+       * Version 1.4.1 - 2016-09-29 - Customize formating with LOGURU_FILENAME_WIDTH
+       * Version 1.5.0 - 2016-12-22 - LOGURU_USE_FMTLIB by kolis and LOGURU_WITH_FILEABS by scinart
+       * Version 1.5.1 - 2017-08-08 - Terminal colors on Windows 10 thanks to looki
+       * Version 1.6.0 - 2018-01-03 - Add LOGURU_RTTI and LOGURU_STACKTRACES settings
+       * Version 1.7.0 - 2018-01-03 - Add ability to turn off the preamble with loguru::g_preamble
+       * Version 1.7.1 - 2018-04-05 - Add function get_fatal_handler
+       * Version 1.7.2 - 2018-04-22 - Fix a bug where large file names could cause stack corruption (thanks @ccamporesi)
+       * Version 1.8.0 - 2018-04-23 - Shorten long file names to keep preamble fixed width
+       * Version 1.9.0 - 2018-09-22 - Adjust terminal colors, add LOGURU_VERBOSE_SCOPE_ENDINGS, add LOGURU_SCOPE_TIME_PRECISION, add named log levels
+       * Version 2.0.0 - 2018-09-22 - Split loguru.hpp into loguru.hpp and loguru.cpp
+
+# Compiling
+       Just include <loguru.hpp> where you want to use Loguru.
+       Then, in one .cpp file #include <loguru.cpp>
+       Make sure you compile with -std=c++11 -lstdc++ -lpthread -ldl
+
+# Usage
+       For details, please see the official documentation at emilk.github.io/loguru
+
+       #include <loguru.hpp>
+
+       int main(int argc, char* argv[]) {
+               loguru::init(argc, argv);
+
+               // Put every log message in "everything.log":
+               loguru::add_file("everything.log", loguru::Append, loguru::Verbosity_MAX);
+
+               LOG_F(INFO, "The magic number is %d", 42);
+       }
+
+*/
+
+#if defined(LOGURU_IMPLEMENTATION)
+       #warning "You are defining LOGURU_IMPLEMENTATION. This is for older versions of Loguru. You should now instead include loguru.cpp (or build it and link with it)"
+#endif
+
+// Disable all warnings from gcc/clang:
+#if defined(__clang__)
+       #pragma clang system_header
+#elif defined(__GNUC__)
+       #pragma GCC system_header
+#endif
+
+#ifndef LOGURU_HAS_DECLARED_FORMAT_HEADER
+#define LOGURU_HAS_DECLARED_FORMAT_HEADER
+
+// Semantic versioning. Loguru version can be printed with printf("%d.%d.%d", LOGURU_VERSION_MAJOR, LOGURU_VERSION_MINOR, LOGURU_VERSION_PATCH);
+#define LOGURU_VERSION_MAJOR 2
+#define LOGURU_VERSION_MINOR 0
+#define LOGURU_VERSION_PATCH 0
+
+#if defined(_MSC_VER)
+#include <sal.h>       // Needed for _In_z_ etc annotations
+#endif
+
+// ----------------------------------------------------------------------------
+
+#ifndef LOGURU_EXPORT
+       // Define to your project's export declaration if needed for use in a shared library.
+       #define LOGURU_EXPORT
+#endif
+
+#ifndef LOGURU_SCOPE_TEXT_SIZE
+       // Maximum length of text that can be printed by a LOG_SCOPE.
+       // This should be long enough to get most things, but short enough not to clutter the stack.
+       #define LOGURU_SCOPE_TEXT_SIZE 196
+#endif
+
+#ifndef LOGURU_FILENAME_WIDTH
+       // Width of the column containing the file name
+       #define LOGURU_FILENAME_WIDTH 23
+#endif
+
+#ifndef LOGURU_THREADNAME_WIDTH
+       // Width of the column containing the thread name
+       #define LOGURU_THREADNAME_WIDTH 16
+#endif
+
+#ifndef LOGURU_SCOPE_TIME_PRECISION
+       // Resolution of scope timers. 3=ms, 6=us, 9=ns
+       #define LOGURU_SCOPE_TIME_PRECISION 3
+#endif
+
+#ifndef LOGURU_CATCH_SIGABRT
+       // Should Loguru catch SIGABRT to print stack trace etc?
+       #define LOGURU_CATCH_SIGABRT 1
+#endif
+
+#ifndef LOGURU_VERBOSE_SCOPE_ENDINGS
+       // Show milliseconds and scope name at end of scope.
+       #define LOGURU_VERBOSE_SCOPE_ENDINGS 1
+#endif
+
+#ifndef LOGURU_REDEFINE_ASSERT
+       #define LOGURU_REDEFINE_ASSERT 0
+#endif
+
+#ifndef LOGURU_WITH_STREAMS
+       #define LOGURU_WITH_STREAMS 0
+#endif
+
+#ifndef LOGURU_REPLACE_GLOG
+       #define LOGURU_REPLACE_GLOG 0
+#endif
+
+#if LOGURU_REPLACE_GLOG
+       #undef LOGURU_WITH_STREAMS
+       #define LOGURU_WITH_STREAMS 1
+#endif
+
+#ifndef LOGURU_UNSAFE_SIGNAL_HANDLER
+       #define LOGURU_UNSAFE_SIGNAL_HANDLER 1
+#endif
+
+#if LOGURU_IMPLEMENTATION
+       #undef LOGURU_WITH_STREAMS
+       #define LOGURU_WITH_STREAMS 1
+#endif
+
+#ifndef LOGURU_USE_FMTLIB
+       #define LOGURU_USE_FMTLIB 0
+#endif
+
+#ifndef LOGURU_WITH_FILEABS
+       #define LOGURU_WITH_FILEABS 0
+#endif
+
+#ifndef LOGURU_RTTI
+#if defined(__clang__)
+       #if __has_feature(cxx_rtti)
+               #define LOGURU_RTTI 1
+       #endif
+#elif defined(__GNUG__)
+       #if defined(__GXX_RTTI)
+               #define LOGURU_RTTI 1
+       #endif
+#elif defined(_MSC_VER)
+       #if defined(_CPPRTTI)
+               #define LOGURU_RTTI 1
+       #endif
+#endif
+#endif
+
+// --------------------------------------------------------------------
+// Utility macros
+
+#define LOGURU_CONCATENATE_IMPL(s1, s2) s1 ## s2
+#define LOGURU_CONCATENATE(s1, s2) LOGURU_CONCATENATE_IMPL(s1, s2)
+
+#ifdef __COUNTER__
+#   define LOGURU_ANONYMOUS_VARIABLE(str) LOGURU_CONCATENATE(str, __COUNTER__)
+#else
+#   define LOGURU_ANONYMOUS_VARIABLE(str) LOGURU_CONCATENATE(str, __LINE__)
+#endif
+
+#if defined(__clang__) || defined(__GNUC__)
+       // Helper macro for declaring functions as having similar signature to printf.
+       // This allows the compiler to catch format errors at compile-time.
+       #define LOGURU_PRINTF_LIKE(fmtarg, firstvararg) __attribute__((__format__ (__printf__, fmtarg, firstvararg)))
+       #define LOGURU_FORMAT_STRING_TYPE const char*
+#elif defined(_MSC_VER)
+       #define LOGURU_PRINTF_LIKE(fmtarg, firstvararg)
+       #define LOGURU_FORMAT_STRING_TYPE _In_z_ _Printf_format_string_ const char*
+#else
+       #define LOGURU_PRINTF_LIKE(fmtarg, firstvararg)
+       #define LOGURU_FORMAT_STRING_TYPE const char*
+#endif
+
+// Used to mark log_and_abort for the benefit of the static analyzer and optimizer.
+#if defined(_MSC_VER)
+#define LOGURU_NORETURN __declspec(noreturn)
+#else
+#define LOGURU_NORETURN __attribute__((noreturn))
+#endif
+
+#if defined(_MSC_VER)
+#define LOGURU_PREDICT_FALSE(x) (x)
+#define LOGURU_PREDICT_TRUE(x)  (x)
+#else
+#define LOGURU_PREDICT_FALSE(x) (__builtin_expect(x,     0))
+#define LOGURU_PREDICT_TRUE(x)  (__builtin_expect(!!(x), 1))
+#endif
+
+#if LOGURU_USE_FMTLIB
+       #include <fmt/format.h>
+#endif
+
+#ifdef _WIN32
+       #define STRDUP(str) _strdup(str)
+#else
+       #define STRDUP(str) strdup(str)
+#endif
+
+// --------------------------------------------------------------------
+
+namespace loguru
+{
+       // Simple RAII ownership of a char*.
+       class LOGURU_EXPORT Text
+       {
+       public:
+               explicit Text(char* owned_str) : _str(owned_str) {}
+               ~Text();
+               Text(Text&& t)
+               {
+                       _str = t._str;
+                       t._str = nullptr;
+               }
+               Text(Text& t) = delete;
+               Text& operator=(Text& t) = delete;
+               void operator=(Text&& t) = delete;
+
+               const char* c_str() const { return _str; }
+               bool empty() const { return _str == nullptr || *_str == '\0'; }
+
+               char* release()
+               {
+                       auto result = _str;
+                       _str = nullptr;
+                       return result;
+               }
+
+       private:
+               char* _str;
+       };
+
+       // Like printf, but returns the formated text.
+       LOGURU_EXPORT
+       Text textprintf(LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(1, 2);
+
+       // Overloaded for variadic template matching.
+       LOGURU_EXPORT
+       Text textprintf();
+
+       using Verbosity = int;
+
+#undef FATAL
+#undef ERROR
+#undef WARNING
+#undef INFO
+#undef MAX
+
+       enum NamedVerbosity : Verbosity
+       {
+               // Used to mark an invalid verbosity. Do not log to this level.
+               Verbosity_INVALID = -10, // Never do LOG_F(INVALID)
+
+               // You may use Verbosity_OFF on g_stderr_verbosity, but for nothing else!
+               Verbosity_OFF     = -9, // Never do LOG_F(OFF)
+
+               // Prefer to use ABORT_F or ABORT_S over LOG_F(FATAL) or LOG_S(FATAL).
+               Verbosity_FATAL   = -3,
+               Verbosity_ERROR   = -2,
+               Verbosity_WARNING = -1,
+
+               // Normal messages. By default written to stderr.
+               Verbosity_INFO    =  0,
+
+               // Same as Verbosity_INFO in every way.
+               Verbosity_0       =  0,
+
+               // Verbosity levels 1-9 are generally not written to stderr, but are written to file.
+               Verbosity_1       = +1,
+               Verbosity_2       = +2,
+               Verbosity_3       = +3,
+               Verbosity_4       = +4,
+               Verbosity_5       = +5,
+               Verbosity_6       = +6,
+               Verbosity_7       = +7,
+               Verbosity_8       = +8,
+               Verbosity_9       = +9,
+
+               // Don not use higher verbosity levels, as that will make grepping log files harder.
+               Verbosity_MAX     = +9,
+       };
+
+       struct Message
+       {
+               // You would generally print a Message by just concating the buffers without spacing.
+               // Optionally, ignore preamble and indentation.
+               Verbosity   verbosity;   // Already part of preamble
+               const char* filename;    // Already part of preamble
+               unsigned    line;        // Already part of preamble
+               const char* preamble;    // Date, time, uptime, thread, file:line, verbosity.
+               const char* indentation; // Just a bunch of spacing.
+               const char* prefix;      // Assertion failure info goes here (or "").
+               const char* message;     // User message goes here.
+       };
+
+       /* Everything with a verbosity equal or greater than g_stderr_verbosity will be
+       written to stderr. You can set this in code or via the -v argument.
+       Set to loguru::Verbosity_OFF to write nothing to stderr.
+       Default is 0, i.e. only log ERROR, WARNING and INFO are written to stderr.
+       */
+       LOGURU_EXPORT extern Verbosity g_stderr_verbosity;
+       LOGURU_EXPORT extern bool      g_colorlogtostderr; // True by default.
+       LOGURU_EXPORT extern unsigned  g_flush_interval_ms; // 0 (unbuffered) by default.
+       LOGURU_EXPORT extern bool      g_preamble; // Prefix each log line with date, time etc? True by default.
+
+       /* Specify the verbosity used by loguru to log its info messages including the header
+       logged when logged::init() is called or on exit. Default is 0 (INFO).
+       */
+       LOGURU_EXPORT extern Verbosity g_internal_verbosity;
+
+       // Turn off individual parts of the preamble
+       LOGURU_EXPORT extern bool      g_preamble_date; // The date field
+       LOGURU_EXPORT extern bool      g_preamble_time; // The time of the current day
+       LOGURU_EXPORT extern bool      g_preamble_uptime; // The time since init call
+       LOGURU_EXPORT extern bool      g_preamble_thread; // The logging thread
+       LOGURU_EXPORT extern bool      g_preamble_file; // The file from which the log originates from
+       LOGURU_EXPORT extern bool      g_preamble_verbose; // The verbosity field
+       LOGURU_EXPORT extern bool      g_preamble_pipe; // The pipe symbol right before the message
+
+       // May not throw!
+       typedef void (*log_handler_t)(void* user_data, const Message& message);
+       typedef void (*close_handler_t)(void* user_data);
+       typedef void (*flush_handler_t)(void* user_data);
+
+       // May throw if that's how you'd like to handle your errors.
+       typedef void (*fatal_handler_t)(const Message& message);
+
+       // Given a verbosity level, return the level's name or nullptr.
+       typedef const char* (*verbosity_to_name_t)(Verbosity verbosity);
+
+       // Given a verbosity level name, return the verbosity level or
+       // Verbosity_INVALID if name is not recognized.
+       typedef Verbosity (*name_to_verbosity_t)(const char* name);
+
+       /*  Should be called from the main thread.
+               You don't *need* to call this, but if you do you get:
+                       * Signal handlers installed
+                       * Program arguments logged
+                       * Working dir logged
+                       * Optional -v verbosity flag parsed
+                       * Main thread name set to "main thread"
+                       * Explanation of the preamble (date, threanmae etc) logged
+
+               loguru::init() will look for arguments meant for loguru and remove them.
+               Arguments meant for loguru are:
+                       -v n   Set loguru::g_stderr_verbosity level. Examples:
+                               -v 3        Show verbosity level 3 and lower.
+                               -v 0        Only show INFO, WARNING, ERROR, FATAL (default).
+                               -v INFO     Only show INFO, WARNING, ERROR, FATAL (default).
+                               -v WARNING  Only show WARNING, ERROR, FATAL.
+                               -v ERROR    Only show ERROR, FATAL.
+                               -v FATAL    Only show FATAL.
+                               -v OFF      Turn off logging to stderr.
+
+               Tip: You can set g_stderr_verbosity before calling loguru::init.
+               That way you can set the default but have the user override it with the -v flag.
+               Note that -v does not affect file logging (see loguru::add_file).
+
+               You can use something else instead of "-v" via verbosity_flag.
+               You can also set verbosity_flag to nullptr.
+       */
+       LOGURU_EXPORT
+       void init(int& argc, char* argv[], const char* verbosity_flag = "-v");
+
+       // Will call remove_all_callbacks(). After calling this, logging will still go to stderr.
+       // You generally don't need to call this.
+       LOGURU_EXPORT
+       void shutdown();
+
+       // What ~ will be replaced with, e.g. "/home/your_user_name/"
+       LOGURU_EXPORT
+       const char* home_dir();
+
+       /* Returns the name of the app as given in argv[0] but without leading path.
+          That is, if argv[0] is "../foo/app" this will return "app".
+       */
+       LOGURU_EXPORT
+       const char* argv0_filename();
+
+       // Returns all arguments given to loguru::init(), but escaped with a single space as separator.
+       LOGURU_EXPORT
+       const char* arguments();
+
+       // Returns the path to the current working dir when loguru::init() was called.
+       LOGURU_EXPORT
+       const char* current_dir();
+
+       // Returns the part of the path after the last / or \ (if any).
+       LOGURU_EXPORT
+       const char* filename(const char* path);
+
+       // e.g. "foo/bar/baz.ext" will create the directories "foo/" and "foo/bar/"
+       LOGURU_EXPORT
+       bool create_directories(const char* file_path_const);
+
+       // Writes date and time with millisecond precision, e.g. "20151017_161503.123"
+       LOGURU_EXPORT
+       void write_date_time(char* buff, unsigned buff_size);
+
+       // Helper: thread-safe version strerror
+       LOGURU_EXPORT
+       Text errno_as_text();
+
+       /* Given a prefix of e.g. "~/loguru/" this might return
+          "/home/your_username/loguru/app_name/20151017_161503.123.log"
+
+          where "app_name" is a sanitized version of argv[0].
+       */
+       LOGURU_EXPORT
+       void suggest_log_path(const char* prefix, char* buff, unsigned buff_size);
+
+       enum FileMode { Truncate, Append };
+
+       /*  Will log to a file at the given path.
+               Any logging message with a verbosity lower or equal to
+               the given verbosity will be included.
+               The function will create all directories in 'path' if needed.
+               If path starts with a ~, it will be replaced with loguru::home_dir()
+               To stop the file logging, just call loguru::remove_callback(path) with the same path.
+       */
+       LOGURU_EXPORT
+       bool add_file(const char* path, FileMode mode, Verbosity verbosity);
+
+       /*  Will be called right before abort().
+               You can for instance use this to print custom error messages, or throw an exception.
+               Feel free to call LOG:ing function from this, but not FATAL ones! */
+       LOGURU_EXPORT
+       void set_fatal_handler(fatal_handler_t handler);
+
+       // Get the current fatal handler, if any. Default value is nullptr.
+       LOGURU_EXPORT
+       fatal_handler_t get_fatal_handler();
+
+       /*  Will be called on each log messages with a verbosity less or equal to the given one.
+               Useful for displaying messages on-screen in a game, for example.
+               The given on_close is also expected to flush (if desired).
+       */
+       LOGURU_EXPORT
+       void add_callback(
+               const char*     id,
+               log_handler_t   callback,
+               void*           user_data,
+               Verbosity       verbosity,
+               close_handler_t on_close = nullptr,
+               flush_handler_t on_flush = nullptr);
+
+       /*  Set a callback that returns custom verbosity level names. If callback
+               is nullptr or returns nullptr, default log names will be used.
+       */
+       LOGURU_EXPORT
+       void set_verbosity_to_name_callback(verbosity_to_name_t callback);
+
+       /*  Set a callback that returns the verbosity level matching a name. The
+               callback should return Verbosity_INVALID if the name is not
+               recognized.
+       */
+       LOGURU_EXPORT
+       void set_name_to_verbosity_callback(name_to_verbosity_t callback);
+
+       /*  Get a custom name for a specific verbosity, if one exists, or nullptr. */
+       LOGURU_EXPORT
+       const char* get_verbosity_name(Verbosity verbosity);
+
+       /*  Get the verbosity enum value from a custom 4-character level name, if one exists.
+               If the name does not match a custom level name, Verbosity_INVALID is returned.
+       */
+       LOGURU_EXPORT
+       Verbosity get_verbosity_from_name(const char* name);
+
+       // Returns true iff the callback was found (and removed).
+       LOGURU_EXPORT
+       bool remove_callback(const char* id);
+
+       // Shut down all file logging and any other callback hooks installed.
+       LOGURU_EXPORT
+       void remove_all_callbacks();
+
+       // Returns the maximum of g_stderr_verbosity and all file/custom outputs.
+       LOGURU_EXPORT
+       Verbosity current_verbosity_cutoff();
+
+#if LOGURU_USE_FMTLIB
+       // Actual logging function. Use the LOG macro instead of calling this directly.
+       LOGURU_EXPORT
+       void log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, fmt::ArgList args);
+       FMT_VARIADIC(void, log, Verbosity, const char*, unsigned, LOGURU_FORMAT_STRING_TYPE)
+
+       // Log without any preamble or indentation.
+       LOGURU_EXPORT
+       void raw_log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, fmt::ArgList args);
+       FMT_VARIADIC(void, raw_log, Verbosity, const char*, unsigned, LOGURU_FORMAT_STRING_TYPE)
+#else // LOGURU_USE_FMTLIB?
+       // Actual logging function. Use the LOG macro instead of calling this directly.
+       LOGURU_EXPORT
+       void log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(4, 5);
+
+       // Log without any preamble or indentation.
+       LOGURU_EXPORT
+       void raw_log(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(4, 5);
+#endif // !LOGURU_USE_FMTLIB
+
+       // Helper class for LOG_SCOPE_F
+       class LOGURU_EXPORT LogScopeRAII
+       {
+       public:
+               LogScopeRAII() : _file(nullptr) {} // No logging
+               LogScopeRAII(Verbosity verbosity, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(5, 6);
+               ~LogScopeRAII();
+
+#if defined(_MSC_VER) && _MSC_VER > 1800
+               // older MSVC default move ctors close the scope on move. See
+               // issue #43
+               LogScopeRAII(LogScopeRAII&& other)
+                       : _verbosity(other._verbosity)
+                       , _file(other._file)
+                       , _line(other._line)
+                       , _indent_stderr(other._indent_stderr)
+                       , _start_time_ns(other._start_time_ns)
+               {
+                       // Make sure the tmp object's destruction doesn't close the scope:
+                       other._file = nullptr;
+
+                       for (unsigned int i = 0; i < LOGURU_SCOPE_TEXT_SIZE; ++i) {
+                               _name[i] = other._name[i];
+                       }
+               }
+#else
+               LogScopeRAII(LogScopeRAII&&) = default;
+#endif
+
+       private:
+               LogScopeRAII(const LogScopeRAII&) = delete;
+               LogScopeRAII& operator=(const LogScopeRAII&) = delete;
+               void operator=(LogScopeRAII&&) = delete;
+
+               Verbosity   _verbosity;
+               const char* _file; // Set to null if we are disabled due to verbosity
+               unsigned    _line;
+               bool        _indent_stderr; // Did we?
+               long long   _start_time_ns;
+               char        _name[LOGURU_SCOPE_TEXT_SIZE];
+       };
+
+       // Marked as 'noreturn' for the benefit of the static analyzer and optimizer.
+       // stack_trace_skip is the number of extrace stack frames to skip above log_and_abort.
+       LOGURU_EXPORT
+       LOGURU_NORETURN void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line, LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(5, 6);
+       LOGURU_EXPORT
+       LOGURU_NORETURN void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line);
+
+       // Flush output to stderr and files.
+       // If g_flush_interval_ms is set to non-zero, this will be called automatically this often.
+       // If not set, you do not need to call this at all.
+       LOGURU_EXPORT
+       void flush();
+
+       template<class T> inline Text format_value(const T&)                    { return textprintf("N/A");     }
+       template<>        inline Text format_value(const char& v)               { return textprintf("%c",   v); }
+       template<>        inline Text format_value(const int& v)                { return textprintf("%d",   v); }
+       template<>        inline Text format_value(const unsigned int& v)       { return textprintf("%u",   v); }
+       template<>        inline Text format_value(const long& v)               { return textprintf("%lu",  v); }
+       template<>        inline Text format_value(const unsigned long& v)      { return textprintf("%ld",  v); }
+       template<>        inline Text format_value(const long long& v)          { return textprintf("%llu", v); }
+       template<>        inline Text format_value(const unsigned long long& v) { return textprintf("%lld", v); }
+       template<>        inline Text format_value(const float& v)              { return textprintf("%f",   v); }
+       template<>        inline Text format_value(const double& v)             { return textprintf("%f",   v); }
+
+       /* Thread names can be set for the benefit of readable logs.
+          If you do not set the thread name, a hex id will be shown instead.
+          These thread names may or may not be the same as the system thread names,
+          depending on the system.
+          Try to limit the thread name to 15 characters or less. */
+       LOGURU_EXPORT
+       void set_thread_name(const char* name);
+
+       /* Returns the thread name for this thread.
+          On OSX this will return the system thread name (settable from both within and without Loguru).
+          On other systems it will return whatever you set in set_thread_name();
+          If no thread name is set, this will return a hexadecimal thread id.
+          length should be the number of bytes available in the buffer.
+          17 is a good number for length.
+          right_align_hext_id means any hexadecimal thread id will be written to the end of buffer.
+       */
+       LOGURU_EXPORT
+       void get_thread_name(char* buffer, unsigned long long length, bool right_align_hext_id);
+
+       /* Generates a readable stacktrace as a string.
+          'skip' specifies how many stack frames to skip.
+          For instance, the default skip (1) means:
+          don't include the call to loguru::stacktrace in the stack trace. */
+       LOGURU_EXPORT
+       Text stacktrace(int skip = 1);
+
+       /*  Add a string to be replaced with something else in the stack output.
+
+               For instance, instead of having a stack trace look like this:
+                       0x41f541 some_function(std::basic_ofstream<char, std::char_traits<char> >&)
+               You can clean it up with:
+                       auto verbose_type_name = loguru::demangle(typeid(std::ofstream).name());
+                       loguru::add_stack_cleanup(verbose_type_name.c_str(); "std::ofstream");
+               So the next time you will instead see:
+                       0x41f541 some_function(std::ofstream&)
+
+               `replace_with_this` must be shorter than `find_this`.
+       */
+       LOGURU_EXPORT
+       void add_stack_cleanup(const char* find_this, const char* replace_with_this);
+
+       // Example: demangle(typeid(std::ofstream).name()) -> "std::basic_ofstream<char, std::char_traits<char> >"
+       LOGURU_EXPORT
+       Text demangle(const char* name);
+
+       // ------------------------------------------------------------------------
+       /*
+       Not all terminals support colors, but if they do, and g_colorlogtostderr
+       is set, Loguru will write them to stderr to make errors in red, etc.
+
+       You also have the option to manually use them, via the function below.
+
+       Note, however, that if you do, the color codes could end up in your logfile!
+
+       This means if you intend to use them functions you should either:
+               * Use them on the stderr/stdout directly (bypass Loguru).
+               * Don't add file outputs to Loguru.
+               * Expect some \e[1m things in your logfile.
+
+       Usage:
+               printf("%sRed%sGreen%sBold green%sClear again\n",
+                          loguru::terminal_red(), loguru::terminal_green(),
+                          loguru::terminal_bold(), loguru::terminal_reset());
+
+       If the terminal at hand does not support colors the above output
+       will just not have funky \e[1m things showing.
+       */
+
+       // Do the output terminal support colors?
+       LOGURU_EXPORT
+       bool terminal_has_color();
+
+       // Colors
+       LOGURU_EXPORT const char* terminal_black();
+       LOGURU_EXPORT const char* terminal_red();
+       LOGURU_EXPORT const char* terminal_green();
+       LOGURU_EXPORT const char* terminal_yellow();
+       LOGURU_EXPORT const char* terminal_blue();
+       LOGURU_EXPORT const char* terminal_purple();
+       LOGURU_EXPORT const char* terminal_cyan();
+       LOGURU_EXPORT const char* terminal_light_gray();
+       LOGURU_EXPORT const char* terminal_light_red();
+       LOGURU_EXPORT const char* terminal_white();
+
+       // Formating
+       LOGURU_EXPORT const char* terminal_bold();
+       LOGURU_EXPORT const char* terminal_underline();
+
+       // You should end each line with this!
+       LOGURU_EXPORT const char* terminal_reset();
+
+       // --------------------------------------------------------------------
+       // Error context related:
+
+       struct StringStream;
+
+       // Use this in your EcEntryBase::print_value overload.
+       LOGURU_EXPORT
+       void stream_print(StringStream& out_string_stream, const char* text);
+
+       class LOGURU_EXPORT EcEntryBase
+       {
+       public:
+               EcEntryBase(const char* file, unsigned line, const char* descr);
+               ~EcEntryBase();
+               EcEntryBase(const EcEntryBase&) = delete;
+               EcEntryBase(EcEntryBase&&) = delete;
+               EcEntryBase& operator=(const EcEntryBase&) = delete;
+               EcEntryBase& operator=(EcEntryBase&&) = delete;
+
+               virtual void print_value(StringStream& out_string_stream) const = 0;
+
+               EcEntryBase* previous() const { return _previous; }
+
+       // private:
+               const char*  _file;
+               unsigned     _line;
+               const char*  _descr;
+               EcEntryBase* _previous;
+       };
+
+       template<typename T>
+       class EcEntryData : public EcEntryBase
+       {
+       public:
+               using Printer = Text(*)(T data);
+
+               EcEntryData(const char* file, unsigned line, const char* descr, T data, Printer&& printer)
+                       : EcEntryBase(file, line, descr), _data(data), _printer(printer) {}
+
+               virtual void print_value(StringStream& out_string_stream) const override
+               {
+                       const auto str = _printer(_data);
+                       stream_print(out_string_stream, str.c_str());
+               }
+
+       private:
+               T       _data;
+               Printer _printer;
+       };
+
+       // template<typename Printer>
+       // class EcEntryLambda : public EcEntryBase
+       // {
+       // public:
+       //      EcEntryLambda(const char* file, unsigned line, const char* descr, Printer&& printer)
+       //              : EcEntryBase(file, line, descr), _printer(std::move(printer)) {}
+
+       //      virtual void print_value(StringStream& out_string_stream) const override
+       //      {
+       //              const auto str = _printer();
+       //              stream_print(out_string_stream, str.c_str());
+       //      }
+
+       // private:
+       //      Printer _printer;
+       // };
+
+       // template<typename Printer>
+       // EcEntryLambda<Printer> make_ec_entry_lambda(const char* file, unsigned line, const char* descr, Printer&& printer)
+       // {
+       //      return {file, line, descr, std::move(printer)};
+       // }
+
+       template <class T>
+       struct decay_char_array { using type = T; };
+
+       template <unsigned long long  N>
+       struct decay_char_array<const char(&)[N]> { using type = const char*; };
+
+       template <class T>
+       struct make_const_ptr { using type = T; };
+
+       template <class T>
+       struct make_const_ptr<T*> { using type = const T*; };
+
+       template <class T>
+       struct make_ec_type { using type = typename make_const_ptr<typename decay_char_array<T>::type>::type; };
+
+       /*      A stack trace gives you the names of the function at the point of a crash.
+               With ERROR_CONTEXT, you can also get the values of select local variables.
+               Usage:
+
+               void process_customers(const std::string& filename)
+               {
+                       ERROR_CONTEXT("Processing file", filename.c_str());
+                       for (int customer_index : ...)
+                       {
+                               ERROR_CONTEXT("Customer index", customer_index);
+                               ...
+                       }
+               }
+
+               The context is in effect during the scope of the ERROR_CONTEXT.
+               Use loguru::get_error_context() to get the contents of the active error contexts.
+
+               Example result:
+
+               ------------------------------------------------
+               [ErrorContext]                main.cpp:416   Processing file:    "customers.json"
+               [ErrorContext]                main.cpp:417   Customer index:     42
+               ------------------------------------------------
+
+               Error contexts are printed automatically on crashes, and only on crashes.
+               This makes them much faster than logging the value of a variable.
+       */
+       #define ERROR_CONTEXT(descr, data)                                             \
+               const loguru::EcEntryData<loguru::make_ec_type<decltype(data)>::type>      \
+                       LOGURU_ANONYMOUS_VARIABLE(error_context_scope_)(                       \
+                               __FILE__, __LINE__, descr, data,                                   \
+                               static_cast<loguru::EcEntryData<loguru::make_ec_type<decltype(data)>::type>::Printer>(loguru::ec_to_text) ) // For better error messages
+
+/*
+       #define ERROR_CONTEXT(descr, data)                                 \
+               const auto LOGURU_ANONYMOUS_VARIABLE(error_context_scope_)(    \
+                       loguru::make_ec_entry_lambda(__FILE__, __LINE__, descr,    \
+                               [=](){ return loguru::ec_to_text(data); }))
+*/
+
+       using EcHandle = const EcEntryBase*;
+
+       /*
+               Get a light-weight handle to the error context stack on this thread.
+               The handle is valid as long as the current thread has no changes to its error context stack.
+               You can pass the handle to loguru::get_error_context on another thread.
+               This can be very useful for when you have a parent thread spawning several working threads,
+               and you want the error context of the parent thread to get printed (too) when there is an
+               error on the child thread. You can accomplish this thusly:
+
+               void foo(const char* parameter)
+               {
+                       ERROR_CONTEXT("parameter", parameter)
+                       const auto parent_ec_handle = loguru::get_thread_ec_handle();
+
+                       std::thread([=]{
+                               loguru::set_thread_name("child thread");
+                               ERROR_CONTEXT("parent context", parent_ec_handle);
+                               dangerous_code();
+                       }.join();
+               }
+
+       */
+       LOGURU_EXPORT
+       EcHandle get_thread_ec_handle();
+
+       // Get a string describing the current stack of error context. Empty string if there is none.
+       LOGURU_EXPORT
+       Text get_error_context();
+
+       // Get a string describing the error context of the given thread handle.
+       LOGURU_EXPORT
+       Text get_error_context_for(EcHandle ec_handle);
+
+       // ------------------------------------------------------------------------
+
+       LOGURU_EXPORT Text ec_to_text(const char* data);
+       LOGURU_EXPORT Text ec_to_text(char data);
+       LOGURU_EXPORT Text ec_to_text(int data);
+       LOGURU_EXPORT Text ec_to_text(unsigned int data);
+       LOGURU_EXPORT Text ec_to_text(long data);
+       LOGURU_EXPORT Text ec_to_text(unsigned long data);
+       LOGURU_EXPORT Text ec_to_text(long long data);
+       LOGURU_EXPORT Text ec_to_text(unsigned long long data);
+       LOGURU_EXPORT Text ec_to_text(float data);
+       LOGURU_EXPORT Text ec_to_text(double data);
+       LOGURU_EXPORT Text ec_to_text(long double data);
+       LOGURU_EXPORT Text ec_to_text(EcHandle);
+
+       /*
+       You can add ERROR_CONTEXT support for your own types by overloading ec_to_text. Here's how:
+
+       some.hpp:
+               namespace loguru {
+                       Text ec_to_text(MySmallType data)
+                       Text ec_to_text(const MyBigType* data)
+               } // namespace loguru
+
+       some.cpp:
+               namespace loguru {
+                       Text ec_to_text(MySmallType small_value)
+                       {
+                               // Called only when needed, i.e. on a crash.
+                               std::string str = small_value.as_string(); // Format 'small_value' here somehow.
+                               return Text{STRDUP(str.c_str())};
+                       }
+
+                       Text ec_to_text(const MyBigType* big_value)
+                       {
+                               // Called only when needed, i.e. on a crash.
+                               std::string str = big_value->as_string(); // Format 'big_value' here somehow.
+                               return Text{STRDUP(str.c_str())};
+                       }
+               } // namespace loguru
+
+       Any file that include some.hpp:
+               void foo(MySmallType small, const MyBigType& big)
+               {
+                       ERROR_CONTEXT("Small", small); // Copy ´small` by value.
+                       ERROR_CONTEXT("Big",   &big);  // `big` should not change during this scope!
+                       ....
+               }
+       */
+} // namespace loguru
+
+// --------------------------------------------------------------------
+// Logging macros
+
+// LOG_F(2, "Only logged if verbosity is 2 or higher: %d", some_number);
+#define VLOG_F(verbosity, ...)                                                                     \
+       ((verbosity) > loguru::current_verbosity_cutoff()) ? (void)0                                   \
+                                                                         : loguru::log(verbosity, __FILE__, __LINE__, __VA_ARGS__)
+
+// LOG_F(INFO, "Foo: %d", some_number);
+#define LOG_F(verbosity_name, ...) VLOG_F(loguru::Verbosity_ ## verbosity_name, __VA_ARGS__)
+
+#define VLOG_IF_F(verbosity, cond, ...)                                                            \
+       ((verbosity) > loguru::current_verbosity_cutoff() || (cond) == false)                          \
+               ? (void)0                                                                                  \
+               : loguru::log(verbosity, __FILE__, __LINE__, __VA_ARGS__)
+
+#define LOG_IF_F(verbosity_name, cond, ...)                                                        \
+       VLOG_IF_F(loguru::Verbosity_ ## verbosity_name, cond, __VA_ARGS__)
+
+#define VLOG_SCOPE_F(verbosity, ...)                                                               \
+       loguru::LogScopeRAII LOGURU_ANONYMOUS_VARIABLE(error_context_RAII_) =                          \
+       ((verbosity) > loguru::current_verbosity_cutoff()) ? loguru::LogScopeRAII() :                  \
+       loguru::LogScopeRAII(verbosity, __FILE__, __LINE__, __VA_ARGS__)
+
+// Raw logging - no preamble, no indentation. Slightly faster than full logging.
+#define RAW_VLOG_F(verbosity, ...)                                                                 \
+       ((verbosity) > loguru::current_verbosity_cutoff()) ? (void)0                                   \
+                                                                         : loguru::raw_log(verbosity, __FILE__, __LINE__, __VA_ARGS__)
+
+#define RAW_LOG_F(verbosity_name, ...) RAW_VLOG_F(loguru::Verbosity_ ## verbosity_name, __VA_ARGS__)
+
+// Use to book-end a scope. Affects logging on all threads.
+#define LOG_SCOPE_F(verbosity_name, ...)                                                           \
+       VLOG_SCOPE_F(loguru::Verbosity_ ## verbosity_name, __VA_ARGS__)
+
+#define LOG_SCOPE_FUNCTION(verbosity_name) LOG_SCOPE_F(verbosity_name, __func__)
+
+// -----------------------------------------------
+// ABORT_F macro. Usage:  ABORT_F("Cause of error: %s", error_str);
+
+// Message is optional
+#define ABORT_F(...) loguru::log_and_abort(0, "ABORT: ", __FILE__, __LINE__, __VA_ARGS__)
+
+// --------------------------------------------------------------------
+// CHECK_F macros:
+
+#define CHECK_WITH_INFO_F(test, info, ...)                                                         \
+       LOGURU_PREDICT_TRUE((test) == true) ? (void)0 : loguru::log_and_abort(0, "CHECK FAILED:  " info "  ", __FILE__,      \
+                                                                                                          __LINE__, ##__VA_ARGS__)
+
+/* Checked at runtime too. Will print error, then call fatal_handler (if any), then 'abort'.
+   Note that the test must be boolean.
+   CHECK_F(ptr); will not compile, but CHECK_F(ptr != nullptr); will. */
+#define CHECK_F(test, ...) CHECK_WITH_INFO_F(test, #test, ##__VA_ARGS__)
+
+#define CHECK_NOTNULL_F(x, ...) CHECK_WITH_INFO_F((x) != nullptr, #x " != nullptr", ##__VA_ARGS__)
+
+#define CHECK_OP_F(expr_left, expr_right, op, ...)                                                 \
+       do                                                                                             \
+       {                                                                                              \
+               auto val_left = expr_left;                                                                 \
+               auto val_right = expr_right;                                                               \
+               if (! LOGURU_PREDICT_TRUE(val_left op val_right))                                          \
+               {                                                                                          \
+                       auto str_left = loguru::format_value(val_left);                                        \
+                       auto str_right = loguru::format_value(val_right);                                      \
+                       auto fail_info = loguru::textprintf("CHECK FAILED:  %s %s %s  (%s %s %s)  ",           \
+                               #expr_left, #op, #expr_right, str_left.c_str(), #op, str_right.c_str());           \
+                       auto user_msg = loguru::textprintf(__VA_ARGS__);                                       \
+                       loguru::log_and_abort(0, fail_info.c_str(), __FILE__, __LINE__,                        \
+                                             "%s", user_msg.c_str());                                         \
+               }                                                                                          \
+       } while (false)
+
+#ifndef LOGURU_DEBUG_LOGGING
+       #ifndef NDEBUG
+               #define LOGURU_DEBUG_LOGGING 1
+       #else
+               #define LOGURU_DEBUG_LOGGING 0
+       #endif
+#endif
+
+#if LOGURU_DEBUG_LOGGING
+       // Debug logging enabled:
+       #define DLOG_F(verbosity_name, ...)     LOG_F(verbosity_name, __VA_ARGS__)
+       #define DVLOG_F(verbosity, ...)         VLOG_F(verbosity, __VA_ARGS__)
+       #define DLOG_IF_F(verbosity_name, ...)  LOG_IF_F(verbosity_name, __VA_ARGS__)
+       #define DVLOG_IF_F(verbosity, ...)      VLOG_IF_F(verbosity, __VA_ARGS__)
+       #define DRAW_LOG_F(verbosity_name, ...) RAW_LOG_F(verbosity_name, __VA_ARGS__)
+       #define DRAW_VLOG_F(verbosity, ...)     RAW_VLOG_F(verbosity, __VA_ARGS__)
+#else
+       // Debug logging disabled:
+       #define DLOG_F(verbosity_name, ...)
+       #define DVLOG_F(verbosity, ...)
+       #define DLOG_IF_F(verbosity_name, ...)
+       #define DVLOG_IF_F(verbosity, ...)
+       #define DRAW_LOG_F(verbosity_name, ...)
+       #define DRAW_VLOG_F(verbosity, ...)
+#endif
+
+#define CHECK_EQ_F(a, b, ...) CHECK_OP_F(a, b, ==, ##__VA_ARGS__)
+#define CHECK_NE_F(a, b, ...) CHECK_OP_F(a, b, !=, ##__VA_ARGS__)
+#define CHECK_LT_F(a, b, ...) CHECK_OP_F(a, b, < , ##__VA_ARGS__)
+#define CHECK_GT_F(a, b, ...) CHECK_OP_F(a, b, > , ##__VA_ARGS__)
+#define CHECK_LE_F(a, b, ...) CHECK_OP_F(a, b, <=, ##__VA_ARGS__)
+#define CHECK_GE_F(a, b, ...) CHECK_OP_F(a, b, >=, ##__VA_ARGS__)
+
+#ifndef LOGURU_DEBUG_CHECKS
+       #ifndef NDEBUG
+               #define LOGURU_DEBUG_CHECKS 1
+       #else
+               #define LOGURU_DEBUG_CHECKS 0
+       #endif
+#endif
+
+#if LOGURU_DEBUG_CHECKS
+       // Debug checks enabled:
+       #define DCHECK_F(test, ...)             CHECK_F(test, ##__VA_ARGS__)
+       #define DCHECK_NOTNULL_F(x, ...)        CHECK_NOTNULL_F(x, ##__VA_ARGS__)
+       #define DCHECK_EQ_F(a, b, ...)          CHECK_EQ_F(a, b, ##__VA_ARGS__)
+       #define DCHECK_NE_F(a, b, ...)          CHECK_NE_F(a, b, ##__VA_ARGS__)
+       #define DCHECK_LT_F(a, b, ...)          CHECK_LT_F(a, b, ##__VA_ARGS__)
+       #define DCHECK_LE_F(a, b, ...)          CHECK_LE_F(a, b, ##__VA_ARGS__)
+       #define DCHECK_GT_F(a, b, ...)          CHECK_GT_F(a, b, ##__VA_ARGS__)
+       #define DCHECK_GE_F(a, b, ...)          CHECK_GE_F(a, b, ##__VA_ARGS__)
+#else
+       // Debug checks disabled:
+       #define DCHECK_F(test, ...)
+       #define DCHECK_NOTNULL_F(x, ...)
+       #define DCHECK_EQ_F(a, b, ...)
+       #define DCHECK_NE_F(a, b, ...)
+       #define DCHECK_LT_F(a, b, ...)
+       #define DCHECK_LE_F(a, b, ...)
+       #define DCHECK_GT_F(a, b, ...)
+       #define DCHECK_GE_F(a, b, ...)
+#endif // NDEBUG
+
+
+#if LOGURU_REDEFINE_ASSERT
+       #undef assert
+       #ifndef NDEBUG
+               // Debug:
+               #define assert(test) CHECK_WITH_INFO_F(!!(test), #test) // HACK
+       #else
+               #define assert(test)
+       #endif
+#endif // LOGURU_REDEFINE_ASSERT
+
+#endif // LOGURU_HAS_DECLARED_FORMAT_HEADER
+
+// ----------------------------------------------------------------------------
+// .dP"Y8 888888 88""Yb 888888    db    8b    d8 .dP"Y8
+// `Ybo."   88   88__dP 88__     dPYb   88b  d88 `Ybo."
+// o.`Y8b   88   88"Yb  88""    dP__Yb  88YbdP88 o.`Y8b
+// 8bodP'   88   88  Yb 888888 dP""""Yb 88 YY 88 8bodP'
+
+#if LOGURU_WITH_STREAMS
+#ifndef LOGURU_HAS_DECLARED_STREAMS_HEADER
+#define LOGURU_HAS_DECLARED_STREAMS_HEADER
+
+/* This file extends loguru to enable std::stream-style logging, a la Glog.
+   It's an optional feature behind the LOGURU_WITH_STREAMS settings
+   because including it everywhere will slow down compilation times.
+*/
+
+#include <cstdarg>
+#include <sstream> // Adds about 38 kLoC on clang.
+#include <string>
+
+namespace loguru
+{
+       // Like sprintf, but returns the formated text.
+       LOGURU_EXPORT
+       std::string strprintf(LOGURU_FORMAT_STRING_TYPE format, ...) LOGURU_PRINTF_LIKE(1, 2);
+
+       // Like vsprintf, but returns the formated text.
+       LOGURU_EXPORT
+       std::string vstrprintf(LOGURU_FORMAT_STRING_TYPE format, va_list) LOGURU_PRINTF_LIKE(1, 0);
+
+       class LOGURU_EXPORT StreamLogger
+       {
+       public:
+               StreamLogger(Verbosity verbosity, const char* file, unsigned line) : _verbosity(verbosity), _file(file), _line(line) {}
+               ~StreamLogger() noexcept(false);
+
+               template<typename T>
+               StreamLogger& operator<<(const T& t)
+               {
+                       _ss << t;
+                       return *this;
+               }
+
+               // std::endl and other iomanip:s.
+               StreamLogger& operator<<(std::ostream&(*f)(std::ostream&))
+               {
+                       f(_ss);
+                       return *this;
+               }
+
+       private:
+               Verbosity   _verbosity;
+               const char* _file;
+               unsigned    _line;
+               std::ostringstream _ss;
+       };
+
+       class LOGURU_EXPORT AbortLogger
+       {
+       public:
+               AbortLogger(const char* expr, const char* file, unsigned line) : _expr(expr), _file(file), _line(line) { }
+               LOGURU_NORETURN ~AbortLogger() noexcept(false);
+
+               template<typename T>
+               AbortLogger& operator<<(const T& t)
+               {
+                       _ss << t;
+                       return *this;
+               }
+
+               // std::endl and other iomanip:s.
+               AbortLogger& operator<<(std::ostream&(*f)(std::ostream&))
+               {
+                       f(_ss);
+                       return *this;
+               }
+
+       private:
+               const char*        _expr;
+               const char*        _file;
+               unsigned           _line;
+               std::ostringstream _ss;
+       };
+
+       class LOGURU_EXPORT Voidify
+       {
+       public:
+               Voidify() {}
+               // This has to be an operator with a precedence lower than << but higher than ?:
+               void operator&(const StreamLogger&) { }
+               void operator&(const AbortLogger&)  { }
+       };
+
+       /*  Helper functions for CHECK_OP_S macro.
+               GLOG trick: The (int, int) specialization works around the issue that the compiler
+               will not instantiate the template version of the function on values of unnamed enum type. */
+       #define DEFINE_CHECK_OP_IMPL(name, op)                                                             \
+               template <typename T1, typename T2>                                                            \
+               inline std::string* name(const char* expr, const T1& v1, const char* op_str, const T2& v2)     \
+               {                                                                                              \
+                       if (LOGURU_PREDICT_TRUE(v1 op v2)) { return NULL; }                                        \
+                       std::ostringstream ss;                                                                     \
+                       ss << "CHECK FAILED:  " << expr << "  (" << v1 << " " << op_str << " " << v2 << ")  ";     \
+                       return new std::string(ss.str());                                                          \
+               }                                                                                              \
+               inline std::string* name(const char* expr, int v1, const char* op_str, int v2)                 \
+               {                                                                                              \
+                       return name<int, int>(expr, v1, op_str, v2);                                               \
+               }
+
+       DEFINE_CHECK_OP_IMPL(check_EQ_impl, ==)
+       DEFINE_CHECK_OP_IMPL(check_NE_impl, !=)
+       DEFINE_CHECK_OP_IMPL(check_LE_impl, <=)
+       DEFINE_CHECK_OP_IMPL(check_LT_impl, < )
+       DEFINE_CHECK_OP_IMPL(check_GE_impl, >=)
+       DEFINE_CHECK_OP_IMPL(check_GT_impl, > )
+       #undef DEFINE_CHECK_OP_IMPL
+
+       /*  GLOG trick: Function is overloaded for integral types to allow static const integrals
+               declared in classes and not defined to be used as arguments to CHECK* macros. */
+       template <class T>
+       inline const T&           referenceable_value(const T&           t) { return t; }
+       inline char               referenceable_value(char               t) { return t; }
+       inline unsigned char      referenceable_value(unsigned char      t) { return t; }
+       inline signed char        referenceable_value(signed char        t) { return t; }
+       inline short              referenceable_value(short              t) { return t; }
+       inline unsigned short     referenceable_value(unsigned short     t) { return t; }
+       inline int                referenceable_value(int                t) { return t; }
+       inline unsigned int       referenceable_value(unsigned int       t) { return t; }
+       inline long               referenceable_value(long               t) { return t; }
+       inline unsigned long      referenceable_value(unsigned long      t) { return t; }
+       inline long long          referenceable_value(long long          t) { return t; }
+       inline unsigned long long referenceable_value(unsigned long long t) { return t; }
+} // namespace loguru
+
+// -----------------------------------------------
+// Logging macros:
+
+// usage:  LOG_STREAM(INFO) << "Foo " << std::setprecision(10) << some_value;
+#define VLOG_IF_S(verbosity, cond)                                                                 \
+       ((verbosity) > loguru::current_verbosity_cutoff() || (cond) == false)                          \
+               ? (void)0                                                                                  \
+               : loguru::Voidify() & loguru::StreamLogger(verbosity, __FILE__, __LINE__)
+#define LOG_IF_S(verbosity_name, cond) VLOG_IF_S(loguru::Verbosity_ ## verbosity_name, cond)
+#define VLOG_S(verbosity)              VLOG_IF_S(verbosity, true)
+#define LOG_S(verbosity_name)          VLOG_S(loguru::Verbosity_ ## verbosity_name)
+
+// -----------------------------------------------
+// ABORT_S macro. Usage:  ABORT_S() << "Causo of error: " << details;
+
+#define ABORT_S() loguru::Voidify() & loguru::AbortLogger("ABORT: ", __FILE__, __LINE__)
+
+// -----------------------------------------------
+// CHECK_S macros:
+
+#define CHECK_WITH_INFO_S(cond, info)                                                              \
+       LOGURU_PREDICT_TRUE((cond) == true)                                                            \
+               ? (void)0                                                                                  \
+               : loguru::Voidify() & loguru::AbortLogger("CHECK FAILED:  " info "  ", __FILE__, __LINE__)
+
+#define CHECK_S(cond) CHECK_WITH_INFO_S(cond, #cond)
+#define CHECK_NOTNULL_S(x) CHECK_WITH_INFO_S((x) != nullptr, #x " != nullptr")
+
+#define CHECK_OP_S(function_name, expr1, op, expr2)                                                \
+       while (auto error_string = loguru::function_name(#expr1 " " #op " " #expr2,                    \
+                                                                                                        loguru::referenceable_value(expr1), #op,      \
+                                                                                                        loguru::referenceable_value(expr2)))          \
+               loguru::AbortLogger(error_string->c_str(), __FILE__, __LINE__)
+
+#define CHECK_EQ_S(expr1, expr2) CHECK_OP_S(check_EQ_impl, expr1, ==, expr2)
+#define CHECK_NE_S(expr1, expr2) CHECK_OP_S(check_NE_impl, expr1, !=, expr2)
+#define CHECK_LE_S(expr1, expr2) CHECK_OP_S(check_LE_impl, expr1, <=, expr2)
+#define CHECK_LT_S(expr1, expr2) CHECK_OP_S(check_LT_impl, expr1, < , expr2)
+#define CHECK_GE_S(expr1, expr2) CHECK_OP_S(check_GE_impl, expr1, >=, expr2)
+#define CHECK_GT_S(expr1, expr2) CHECK_OP_S(check_GT_impl, expr1, > , expr2)
+
+#if LOGURU_DEBUG_LOGGING
+       // Debug logging enabled:
+       #define DVLOG_IF_S(verbosity, cond)     VLOG_IF_S(verbosity, cond)
+       #define DLOG_IF_S(verbosity_name, cond) LOG_IF_S(verbosity_name, cond)
+       #define DVLOG_S(verbosity)              VLOG_S(verbosity)
+       #define DLOG_S(verbosity_name)          LOG_S(verbosity_name)
+#else
+       // Debug logging disabled:
+       #define DVLOG_IF_S(verbosity, cond)                                                     \
+               (true || (verbosity) > loguru::current_verbosity_cutoff() || (cond) == false)       \
+                       ? (void)0                                                                       \
+                       : loguru::Voidify() & loguru::StreamLogger(verbosity, __FILE__, __LINE__)
+
+       #define DLOG_IF_S(verbosity_name, cond) DVLOG_IF_S(loguru::Verbosity_ ## verbosity_name, cond)
+       #define DVLOG_S(verbosity)              DVLOG_IF_S(verbosity, true)
+       #define DLOG_S(verbosity_name)          DVLOG_S(loguru::Verbosity_ ## verbosity_name)
+#endif
+
+#if LOGURU_DEBUG_CHECKS
+       // Debug checks enabled:
+       #define DCHECK_S(cond)                  CHECK_S(cond)
+       #define DCHECK_NOTNULL_S(x)             CHECK_NOTNULL_S(x)
+       #define DCHECK_EQ_S(a, b)               CHECK_EQ_S(a, b)
+       #define DCHECK_NE_S(a, b)               CHECK_NE_S(a, b)
+       #define DCHECK_LT_S(a, b)               CHECK_LT_S(a, b)
+       #define DCHECK_LE_S(a, b)               CHECK_LE_S(a, b)
+       #define DCHECK_GT_S(a, b)               CHECK_GT_S(a, b)
+       #define DCHECK_GE_S(a, b)               CHECK_GE_S(a, b)
+#else
+// Debug checks disabled:
+       #define DCHECK_S(cond)                  CHECK_S(true || (cond))
+       #define DCHECK_NOTNULL_S(x)             CHECK_S(true || (x) != nullptr)
+       #define DCHECK_EQ_S(a, b)               CHECK_S(true || (a) == (b))
+       #define DCHECK_NE_S(a, b)               CHECK_S(true || (a) != (b))
+       #define DCHECK_LT_S(a, b)               CHECK_S(true || (a) <  (b))
+       #define DCHECK_LE_S(a, b)               CHECK_S(true || (a) <= (b))
+       #define DCHECK_GT_S(a, b)               CHECK_S(true || (a) >  (b))
+       #define DCHECK_GE_S(a, b)               CHECK_S(true || (a) >= (b))
+#endif
+
+#if LOGURU_REPLACE_GLOG
+       #undef LOG
+       #undef VLOG
+       #undef LOG_IF
+       #undef VLOG_IF
+       #undef CHECK
+       #undef CHECK_NOTNULL
+       #undef CHECK_EQ
+       #undef CHECK_NE
+       #undef CHECK_LT
+       #undef CHECK_LE
+       #undef CHECK_GT
+       #undef CHECK_GE
+       #undef DLOG
+       #undef DVLOG
+       #undef DLOG_IF
+       #undef DVLOG_IF
+       #undef DCHECK
+       #undef DCHECK_NOTNULL
+       #undef DCHECK_EQ
+       #undef DCHECK_NE
+       #undef DCHECK_LT
+       #undef DCHECK_LE
+       #undef DCHECK_GT
+       #undef DCHECK_GE
+       #undef VLOG_IS_ON
+
+       #define LOG            LOG_S
+       #define VLOG           VLOG_S
+       #define LOG_IF         LOG_IF_S
+       #define VLOG_IF        VLOG_IF_S
+       #define CHECK(cond)    CHECK_S(!!(cond))
+       #define CHECK_NOTNULL  CHECK_NOTNULL_S
+       #define CHECK_EQ       CHECK_EQ_S
+       #define CHECK_NE       CHECK_NE_S
+       #define CHECK_LT       CHECK_LT_S
+       #define CHECK_LE       CHECK_LE_S
+       #define CHECK_GT       CHECK_GT_S
+       #define CHECK_GE       CHECK_GE_S
+       #define DLOG           DLOG_S
+       #define DVLOG          DVLOG_S
+       #define DLOG_IF        DLOG_IF_S
+       #define DVLOG_IF       DVLOG_IF_S
+       #define DCHECK         DCHECK_S
+       #define DCHECK_NOTNULL DCHECK_NOTNULL_S
+       #define DCHECK_EQ      DCHECK_EQ_S
+       #define DCHECK_NE      DCHECK_NE_S
+       #define DCHECK_LT      DCHECK_LT_S
+       #define DCHECK_LE      DCHECK_LE_S
+       #define DCHECK_GT      DCHECK_GT_S
+       #define DCHECK_GE      DCHECK_GE_S
+       #define VLOG_IS_ON(verbosity) ((verbosity) <= loguru::current_verbosity_cutoff())
+
+#endif // LOGURU_REPLACE_GLOG
+
+#endif // LOGURU_WITH_STREAMS
+
+#endif // LOGURU_HAS_DECLARED_STREAMS_HEADER
\ No newline at end of file
diff --git a/libloguru/meson.build b/libloguru/meson.build
new file mode 100644 (file)
index 0000000..6c01d58
--- /dev/null
@@ -0,0 +1,20 @@
+loguru_inc = [
+   include_directories('inc'),
+]
+
+loguru_src = [
+   files('src/loguru.cpp'),
+]
+
+loguru_deps = [
+    dependency('threads'),
+    meson.get_compiler('cpp').find_library('dl', required : true),
+]
+
+libloguru = static_library(
+    'libloguru',
+    loguru_src,
+    dependencies: loguru_deps,
+    include_directories: loguru_inc,
+    install:false
+)
\ No newline at end of file
diff --git a/libloguru/src/loguru.cpp b/libloguru/src/loguru.cpp
new file mode 100644 (file)
index 0000000..a2d17ec
--- /dev/null
@@ -0,0 +1,1795 @@
+#ifndef _WIN32
+// Disable all warnings from gcc/clang:
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpragmas"
+
+#pragma GCC diagnostic ignored "-Wc++98-compat"
+#pragma GCC diagnostic ignored "-Wc++98-compat-pedantic"
+#pragma GCC diagnostic ignored "-Wexit-time-destructors"
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+#pragma GCC diagnostic ignored "-Wglobal-constructors"
+#pragma GCC diagnostic ignored "-Wgnu-zero-variadic-macro-arguments"
+#pragma GCC diagnostic ignored "-Wmissing-prototypes"
+#pragma GCC diagnostic ignored "-Wpadded"
+#pragma GCC diagnostic ignored "-Wsign-compare"
+#pragma GCC diagnostic ignored "-Wsign-conversion"
+#pragma GCC diagnostic ignored "-Wunknown-pragmas"
+#pragma GCC diagnostic ignored "-Wunused-macros"
+#pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant"
+#endif
+
+#include "loguru.hpp"
+
+#ifndef LOGURU_HAS_BEEN_IMPLEMENTED
+#define LOGURU_HAS_BEEN_IMPLEMENTED
+
+#define LOGURU_PREAMBLE_WIDTH (53 + LOGURU_THREADNAME_WIDTH + LOGURU_FILENAME_WIDTH)
+
+#undef min
+#undef max
+
+#include <algorithm>
+#include <atomic>
+#include <chrono>
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <mutex>
+#include <regex>
+#include <string>
+#include <thread>
+#include <vector>
+
+#ifdef _WIN32
+       #include <direct.h>
+
+       #define localtime_r(a, b) localtime_s(b, a) // No localtime_r with MSVC, but arguments are swapped for localtime_s
+#else
+       #include <signal.h>
+       #include <sys/stat.h> // mkdir
+       #include <unistd.h>   // STDERR_FILENO
+#endif
+
+#ifdef __linux__
+       #include <linux/limits.h> // PATH_MAX
+#elif !defined(_WIN32)
+       #include <limits.h> // PATH_MAX
+#endif
+
+#ifndef PATH_MAX
+       #define PATH_MAX 1024
+#endif
+
+#ifdef __APPLE__
+       #include "TargetConditionals.h"
+#endif
+
+// TODO: use defined(_POSIX_VERSION) for some of these things?
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+       #define LOGURU_PTHREADS    0
+       #define LOGURU_WINTHREADS  1
+       #ifndef LOGURU_STACKTRACES
+               #define LOGURU_STACKTRACES 0
+       #endif
+#elif defined(__rtems__) || defined(__ANDROID__)
+       #define LOGURU_PTHREADS    1
+       #define LOGURU_WINTHREADS  0
+       #ifndef LOGURU_STACKTRACES
+               #define LOGURU_STACKTRACES 0
+       #endif
+#else
+       #define LOGURU_PTHREADS    1
+       #define LOGURU_WINTHREADS  0
+       #ifndef LOGURU_STACKTRACES
+               #define LOGURU_STACKTRACES 1
+       #endif
+#endif
+
+#if LOGURU_STACKTRACES
+       #include <cxxabi.h>    // for __cxa_demangle
+       #include <dlfcn.h>     // for dladdr
+       #include <execinfo.h>  // for backtrace
+#endif // LOGURU_STACKTRACES
+
+#if LOGURU_PTHREADS
+       #include <pthread.h>
+       #if defined(__FreeBSD__)
+               #include <pthread_np.h>
+               #include <sys/thr.h>
+       #elif defined(__OpenBSD__)
+               #include <pthread_np.h>
+       #endif
+
+       #ifdef __linux__
+               /* On Linux, the default thread name is the same as the name of the binary.
+                  Additionally, all new threads inherit the name of the thread it got forked from.
+                  For this reason, Loguru use the pthread Thread Local Storage
+                  for storing thread names on Linux. */
+               #define LOGURU_PTLS_NAMES 1
+       #endif
+#endif
+
+#if LOGURU_WINTHREADS
+       #ifndef _WIN32_WINNT
+               #define _WIN32_WINNT 0x0502
+       #endif
+       #define WIN32_LEAN_AND_MEAN
+       #define NOMINMAX
+       #include <windows.h>
+#endif
+
+#ifndef LOGURU_PTLS_NAMES
+   #define LOGURU_PTLS_NAMES 0
+#endif
+
+namespace loguru
+{
+       using namespace std::chrono;
+
+#if LOGURU_WITH_FILEABS
+       struct FileAbs
+       {
+               char path[PATH_MAX];
+               char mode_str[4];
+               Verbosity verbosity;
+               struct stat st;
+               FILE* fp;
+               bool is_reopening = false; // to prevent recursive call in file_reopen.
+               decltype(steady_clock::now()) last_check_time = steady_clock::now();
+       };
+#else
+       typedef FILE* FileAbs;
+#endif
+
+       struct Callback
+       {
+               std::string     id;
+               log_handler_t   callback;
+               void*           user_data;
+               Verbosity       verbosity; // Does not change!
+               close_handler_t close;
+               flush_handler_t flush;
+               unsigned        indentation;
+       };
+
+       using CallbackVec = std::vector<Callback>;
+
+       using StringPair     = std::pair<std::string, std::string>;
+       using StringPairList = std::vector<StringPair>;
+
+       const auto s_start_time = steady_clock::now();
+
+       Verbosity g_stderr_verbosity  = Verbosity_0;
+       bool      g_colorlogtostderr  = true;
+       unsigned  g_flush_interval_ms = 0;
+       bool      g_preamble          = true;
+
+       Verbosity g_internal_verbosity = Verbosity_0;
+
+       // Preamble details
+       bool      g_preamble_date     = true;
+       bool      g_preamble_time     = true;
+       bool      g_preamble_uptime   = true;
+       bool      g_preamble_thread   = true;
+       bool      g_preamble_file     = true;
+       bool      g_preamble_verbose  = true;
+       bool      g_preamble_pipe     = true;
+
+       static std::recursive_mutex  s_mutex;
+       static Verbosity             s_max_out_verbosity = Verbosity_OFF;
+       static std::string           s_argv0_filename;
+       static std::string           s_arguments;
+       static char                  s_current_dir[PATH_MAX];
+       static CallbackVec           s_callbacks;
+       static fatal_handler_t       s_fatal_handler   = nullptr;
+       static verbosity_to_name_t   s_verbosity_to_name_callback = nullptr;
+       static name_to_verbosity_t   s_name_to_verbosity_callback = nullptr;
+       static StringPairList        s_user_stack_cleanups;
+       static bool                  s_strip_file_path = true;
+       static std::atomic<unsigned> s_stderr_indentation { 0 };
+
+       // For periodic flushing:
+       static std::thread* s_flush_thread   = nullptr;
+       static bool         s_needs_flushing = false;
+
+       static const bool s_terminal_has_color = [](){
+               #ifdef _WIN32
+                       #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
+                       #define ENABLE_VIRTUAL_TERMINAL_PROCESSING  0x0004
+                       #endif
+
+                       HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
+                       if (hOut != INVALID_HANDLE_VALUE) {
+                               DWORD dwMode = 0;
+                               GetConsoleMode(hOut, &dwMode);
+                               dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
+                               return SetConsoleMode(hOut, dwMode) != 0;
+                       }
+                       return false;
+               #else
+                       if (const char* term = getenv("TERM")) {
+                               return 0 == strcmp(term, "cygwin")
+                                       || 0 == strcmp(term, "linux")
+                                       || 0 == strcmp(term, "rxvt-unicode-256color")
+                                       || 0 == strcmp(term, "screen")
+                                       || 0 == strcmp(term, "screen-256color")
+                                       || 0 == strcmp(term, "screen.xterm-256color")
+                                       || 0 == strcmp(term, "tmux-256color")
+                                       || 0 == strcmp(term, "xterm")
+                                       || 0 == strcmp(term, "xterm-256color")
+                                       || 0 == strcmp(term, "xterm-termite")
+                                       || 0 == strcmp(term, "xterm-color");
+                       } else {
+                               return false;
+                       }
+               #endif
+       }();
+
+       static void print_preamble_header(char* out_buff, size_t out_buff_size);
+
+       #if LOGURU_PTLS_NAMES
+               static pthread_once_t s_pthread_key_once = PTHREAD_ONCE_INIT;
+               static pthread_key_t  s_pthread_key_name;
+
+               void make_pthread_key_name()
+               {
+                       (void)pthread_key_create(&s_pthread_key_name, free);
+               }
+       #endif
+
+       // ------------------------------------------------------------------------------
+       // Colors
+
+       bool terminal_has_color() { return s_terminal_has_color; }
+
+       // Colors
+
+#ifdef _WIN32
+#define VTSEQ(ID) ("\x1b[1;" #ID "m")
+#else
+#define VTSEQ(ID) ("\x1b[" #ID "m")
+#endif
+
+       const char* terminal_black()      { return s_terminal_has_color ? VTSEQ(30) : ""; }
+       const char* terminal_red()        { return s_terminal_has_color ? VTSEQ(31) : ""; }
+       const char* terminal_green()      { return s_terminal_has_color ? VTSEQ(32) : ""; }
+       const char* terminal_yellow()     { return s_terminal_has_color ? VTSEQ(33) : ""; }
+       const char* terminal_blue()       { return s_terminal_has_color ? VTSEQ(34) : ""; }
+       const char* terminal_purple()     { return s_terminal_has_color ? VTSEQ(35) : ""; }
+       const char* terminal_cyan()       { return s_terminal_has_color ? VTSEQ(36) : ""; }
+       const char* terminal_light_gray() { return s_terminal_has_color ? VTSEQ(37) : ""; }
+       const char* terminal_white()      { return s_terminal_has_color ? VTSEQ(37) : ""; }
+       const char* terminal_light_red()  { return s_terminal_has_color ? VTSEQ(91) : ""; }
+       const char* terminal_dim()        { return s_terminal_has_color ? VTSEQ(2)  : ""; }
+
+       // Formating
+       const char* terminal_bold()       { return s_terminal_has_color ? VTSEQ(1) : ""; }
+       const char* terminal_underline()  { return s_terminal_has_color ? VTSEQ(4) : ""; }
+
+       // You should end each line with this!
+       const char* terminal_reset()      { return s_terminal_has_color ? VTSEQ(0) : ""; }
+
+       // ------------------------------------------------------------------------------
+#if LOGURU_WITH_FILEABS
+       void file_reopen(void* user_data);
+       inline FILE* to_file(void* user_data) { return reinterpret_cast<FileAbs*>(user_data)->fp; }
+#else
+       inline FILE* to_file(void* user_data) { return reinterpret_cast<FILE*>(user_data); }
+#endif
+
+       void file_log(void* user_data, const Message& message)
+       {
+#if LOGURU_WITH_FILEABS
+               FileAbs* file_abs = reinterpret_cast<FileAbs*>(user_data);
+               if (file_abs->is_reopening) {
+                       return;
+               }
+               // It is better checking file change every minute/hour/day,
+               // instead of doing this every time we log.
+               // Here check_interval is set to zero to enable checking every time;
+               const auto check_interval = seconds(0);
+               if (duration_cast<seconds>(steady_clock::now() - file_abs->last_check_time) > check_interval) {
+                       file_abs->last_check_time = steady_clock::now();
+                       file_reopen(user_data);
+               }
+               FILE* file = to_file(user_data);
+               if (!file) {
+                       return;
+               }
+#else
+               FILE* file = to_file(user_data);
+#endif
+               fprintf(file, "%s%s%s%s\n",
+                       message.preamble, message.indentation, message.prefix, message.message);
+               if (g_flush_interval_ms == 0) {
+                       fflush(file);
+               }
+       }
+
+       void file_close(void* user_data)
+       {
+               FILE* file = to_file(user_data);
+               if (file) {
+                       fclose(file);
+               }
+#if LOGURU_WITH_FILEABS
+               delete reinterpret_cast<FileAbs*>(user_data);
+#endif
+       }
+
+       void file_flush(void* user_data)
+       {
+               FILE* file = to_file(user_data);
+               fflush(file);
+       }
+
+#if LOGURU_WITH_FILEABS
+       void file_reopen(void* user_data)
+       {
+               FileAbs * file_abs = reinterpret_cast<FileAbs*>(user_data);
+               struct stat st;
+               int ret;
+               if (!file_abs->fp || (ret = stat(file_abs->path, &st)) == -1 || (st.st_ino != file_abs->st.st_ino)) {
+                       file_abs->is_reopening = true;
+                       if (file_abs->fp) {
+                               fclose(file_abs->fp);
+                       }
+                       if (!file_abs->fp) {
+                               VLOG_F(g_internal_verbosity, "Reopening file '%s' due to previous error", file_abs->path);
+                       }
+                       else if (ret < 0) {
+                               const auto why = errno_as_text();
+                               VLOG_F(g_internal_verbosity, "Reopening file '%s' due to '%s'", file_abs->path, why.c_str());
+                       } else {
+                               VLOG_F(g_internal_verbosity, "Reopening file '%s' due to file changed", file_abs->path);
+                       }
+                       // try reopen current file.
+                       if (!create_directories(file_abs->path)) {
+                               LOG_F(ERROR, "Failed to create directories to '%s'", file_abs->path);
+                       }
+                       file_abs->fp = fopen(file_abs->path, file_abs->mode_str);
+                       if (!file_abs->fp) {
+                               LOG_F(ERROR, "Failed to open '%s'", file_abs->path);
+                       } else {
+                               stat(file_abs->path, &file_abs->st);
+                       }
+                       file_abs->is_reopening = false;
+               }
+       }
+#endif
+       // ------------------------------------------------------------------------------
+
+       // Helpers:
+
+       Text::~Text() { free(_str); }
+
+       LOGURU_PRINTF_LIKE(1, 0)
+       static Text vtextprintf(const char* format, va_list vlist)
+       {
+#ifdef _WIN32
+               int bytes_needed = _vscprintf(format, vlist);
+               CHECK_F(bytes_needed >= 0, "Bad string format: '%s'", format);
+               char* buff = (char*)malloc(bytes_needed+1);
+               vsnprintf(buff, bytes_needed+1, format, vlist);
+               return Text(buff);
+#else
+               char* buff = nullptr;
+               int result = vasprintf(&buff, format, vlist);
+               CHECK_F(result >= 0, "Bad string format: '%s'", format);
+               return Text(buff);
+#endif
+       }
+
+       Text textprintf(const char* format, ...)
+       {
+               va_list vlist;
+               va_start(vlist, format);
+               auto result = vtextprintf(format, vlist);
+               va_end(vlist);
+               return result;
+       }
+
+       // Overloaded for variadic template matching.
+       Text textprintf()
+       {
+               return Text(static_cast<char*>(calloc(1, 1)));
+       }
+
+       static const char* indentation(unsigned depth)
+       {
+               static const char buff[] =
+               ".   .   .   .   .   .   .   .   .   .   " ".   .   .   .   .   .   .   .   .   .   "
+               ".   .   .   .   .   .   .   .   .   .   " ".   .   .   .   .   .   .   .   .   .   "
+               ".   .   .   .   .   .   .   .   .   .   " ".   .   .   .   .   .   .   .   .   .   "
+               ".   .   .   .   .   .   .   .   .   .   " ".   .   .   .   .   .   .   .   .   .   "
+               ".   .   .   .   .   .   .   .   .   .   " ".   .   .   .   .   .   .   .   .   .   ";
+               static const size_t INDENTATION_WIDTH = 4;
+               static const size_t NUM_INDENTATIONS = (sizeof(buff) - 1) / INDENTATION_WIDTH;
+               depth = std::min<unsigned>(depth, NUM_INDENTATIONS);
+               return buff + INDENTATION_WIDTH * (NUM_INDENTATIONS - depth);
+       }
+
+       static void parse_args(int& argc, char* argv[], const char* verbosity_flag)
+       {
+               int arg_dest = 1;
+               int out_argc = argc;
+
+               for (int arg_it = 1; arg_it < argc; ++arg_it) {
+                       auto cmd = argv[arg_it];
+                       auto arg_len = strlen(verbosity_flag);
+                       if (strncmp(cmd, verbosity_flag, arg_len) == 0 && !std::isalpha(cmd[arg_len], std::locale(""))) {
+                               out_argc -= 1;
+                               auto value_str = cmd + arg_len;
+                               if (value_str[0] == '\0') {
+                                       // Value in separate argument
+                                       arg_it += 1;
+                                       CHECK_LT_F(arg_it, argc, "Missing verbosiy level after %s", verbosity_flag);
+                                       value_str = argv[arg_it];
+                                       out_argc -= 1;
+                               }
+                               if (*value_str == '=') { value_str += 1; }
+
+                               auto req_verbosity = get_verbosity_from_name(value_str);
+                               if (req_verbosity != Verbosity_INVALID) {
+                                       g_stderr_verbosity = req_verbosity;
+                               } else {
+                                       char* end = 0;
+                                       g_stderr_verbosity = static_cast<int>(strtol(value_str, &end, 10));
+                                       CHECK_F(end && *end == '\0',
+                                               "Invalid verbosity. Expected integer, INFO, WARNING, ERROR or OFF, got '%s'", value_str);
+                               }
+                       } else {
+                               argv[arg_dest++] = argv[arg_it];
+                       }
+               }
+
+               argc = out_argc;
+               argv[argc] = nullptr;
+       }
+
+       static long long now_ns()
+       {
+               return duration_cast<nanoseconds>(high_resolution_clock::now().time_since_epoch()).count();
+       }
+
+       // Returns the part of the path after the last / or \ (if any).
+       const char* filename(const char* path)
+       {
+               for (auto ptr = path; *ptr; ++ptr) {
+                       if (*ptr == '/' || *ptr == '\\') {
+                               path = ptr + 1;
+                       }
+               }
+               return path;
+       }
+
+       // ------------------------------------------------------------------------------
+
+       static void on_atexit()
+       {
+               VLOG_F(g_internal_verbosity, "atexit");
+               flush();
+       }
+
+       static void install_signal_handlers();
+
+       static void write_hex_digit(std::string& out, unsigned num)
+       {
+               DCHECK_LT_F(num, 16u);
+               if (num < 10u) { out.push_back(char('0' + num)); }
+               else { out.push_back(char('A' + num - 10)); }
+       }
+
+       static void write_hex_byte(std::string& out, uint8_t n)
+       {
+               write_hex_digit(out, n >> 4u);
+               write_hex_digit(out, n & 0x0f);
+       }
+
+       static void escape(std::string& out, const std::string& str)
+       {
+               for (char c : str) {
+                       /**/ if (c == '\a') { out += "\\a";  }
+                       else if (c == '\b') { out += "\\b";  }
+                       else if (c == '\f') { out += "\\f";  }
+                       else if (c == '\n') { out += "\\n";  }
+                       else if (c == '\r') { out += "\\r";  }
+                       else if (c == '\t') { out += "\\t";  }
+                       else if (c == '\v') { out += "\\v";  }
+                       else if (c == '\\') { out += "\\\\"; }
+                       else if (c == '\'') { out += "\\\'"; }
+                       else if (c == '\"') { out += "\\\""; }
+                       else if (c == ' ')  { out += "\\ ";  }
+                       else if (0 <= c && c < 0x20) { // ASCI control character:
+                       // else if (c < 0x20 || c != (c & 127)) { // ASCII control character or UTF-8:
+                               out += "\\x";
+                               write_hex_byte(out, static_cast<uint8_t>(c));
+                       } else { out += c; }
+               }
+       }
+
+       Text errno_as_text()
+       {
+               char buff[256];
+       #if defined(__GLIBC__) && defined(_GNU_SOURCE)
+               // GNU Version
+               return Text(STRDUP(strerror_r(errno, buff, sizeof(buff))));
+       #elif defined(__APPLE__) || _POSIX_C_SOURCE >= 200112L
+               // XSI Version
+               strerror_r(errno, buff, sizeof(buff));
+               return Text(strdup(buff));
+       #elif defined(_WIN32)
+               strerror_s(buff, sizeof(buff), errno);
+               return Text(STRDUP(buff));
+       #else
+               // Not thread-safe.
+               return Text(STRDUP(strerror(errno)));
+       #endif
+       }
+
+       void init(int& argc, char* argv[], const char* verbosity_flag)
+       {
+               CHECK_GT_F(argc,       0,       "Expected proper argc/argv");
+               CHECK_EQ_F(argv[argc], nullptr, "Expected proper argc/argv");
+
+               s_argv0_filename = filename(argv[0]);
+
+               #ifdef _WIN32
+                       #define getcwd _getcwd
+               #endif
+
+               if (!getcwd(s_current_dir, sizeof(s_current_dir)))
+               {
+                       const auto error_text = errno_as_text();
+                       LOG_F(WARNING, "Failed to get current working directory: %s", error_text.c_str());
+               }
+
+               s_arguments = "";
+               for (int i = 0; i < argc; ++i) {
+                       escape(s_arguments, argv[i]);
+                       if (i + 1 < argc) {
+                               s_arguments += " ";
+                       }
+               }
+
+               if (verbosity_flag) {
+                       parse_args(argc, argv, verbosity_flag);
+               }
+
+               #if LOGURU_PTLS_NAMES || LOGURU_WINTHREADS
+                       set_thread_name("main thread");
+               #elif LOGURU_PTHREADS
+                       char old_thread_name[16] = {0};
+                       auto this_thread = pthread_self();
+                       #if defined(__APPLE__) || defined(__linux__)
+                               pthread_getname_np(this_thread, old_thread_name, sizeof(old_thread_name));
+                       #endif
+                       if (old_thread_name[0] == 0) {
+                               #ifdef __APPLE__
+                                       pthread_setname_np("main thread");
+                               #elif defined(__FreeBSD__) || defined(__OpenBSD__)
+                                       pthread_set_name_np(this_thread, "main thread");
+                               #elif defined(__linux__)
+                                       pthread_setname_np(this_thread, "main thread");
+                               #endif
+                       }
+               #endif // LOGURU_PTHREADS
+
+               if (g_stderr_verbosity >= Verbosity_INFO) {
+                       if (g_preamble) {
+                               char preamble_explain[LOGURU_PREAMBLE_WIDTH];
+                               print_preamble_header(preamble_explain, sizeof(preamble_explain));
+                               if (g_colorlogtostderr && s_terminal_has_color) {
+                                       fprintf(stderr, "%s%s%s\n", terminal_reset(), terminal_dim(), preamble_explain);
+                               } else {
+                                       fprintf(stderr, "%s\n", preamble_explain);
+                               }
+                       }
+                       fflush(stderr);
+               }
+               VLOG_F(g_internal_verbosity, "arguments: %s", s_arguments.c_str());
+               if (strlen(s_current_dir) != 0)
+               {
+                       VLOG_F(g_internal_verbosity, "Current dir: %s", s_current_dir);
+               }
+               VLOG_F(g_internal_verbosity, "stderr verbosity: %d", g_stderr_verbosity);
+               VLOG_F(g_internal_verbosity, "-----------------------------------");
+
+               install_signal_handlers();
+
+               atexit(on_atexit);
+       }
+
+       void shutdown()
+       {
+               VLOG_F(g_internal_verbosity, "loguru::shutdown()");
+               remove_all_callbacks();
+               set_fatal_handler(nullptr);
+               set_verbosity_to_name_callback(nullptr);
+               set_name_to_verbosity_callback(nullptr);
+       }
+
+       void write_date_time(char* buff, size_t buff_size)
+       {
+               auto now = system_clock::now();
+               long long ms_since_epoch = duration_cast<milliseconds>(now.time_since_epoch()).count();
+               time_t sec_since_epoch = time_t(ms_since_epoch / 1000);
+               tm time_info;
+               localtime_r(&sec_since_epoch, &time_info);
+               snprintf(buff, buff_size, "%04d%02d%02d_%02d%02d%02d.%03lld",
+                       1900 + time_info.tm_year, 1 + time_info.tm_mon, time_info.tm_mday,
+                       time_info.tm_hour, time_info.tm_min, time_info.tm_sec, ms_since_epoch % 1000);
+       }
+
+       const char* argv0_filename()
+       {
+               return s_argv0_filename.c_str();
+       }
+
+       const char* arguments()
+       {
+               return s_arguments.c_str();
+       }
+
+       const char* current_dir()
+       {
+               return s_current_dir;
+       }
+
+       const char* home_dir()
+       {
+               #ifdef _WIN32
+                       auto user_profile = getenv("USERPROFILE");
+                       CHECK_F(user_profile != nullptr, "Missing USERPROFILE");
+                       return user_profile;
+               #else // _WIN32
+                       auto home = getenv("HOME");
+                       CHECK_F(home != nullptr, "Missing HOME");
+                       return home;
+               #endif // _WIN32
+       }
+
+       void suggest_log_path(const char* prefix, char* buff, unsigned buff_size)
+       {
+               if (prefix[0] == '~') {
+                       snprintf(buff, buff_size - 1, "%s%s", home_dir(), prefix + 1);
+               } else {
+                       snprintf(buff, buff_size - 1, "%s", prefix);
+               }
+
+               // Check for terminating /
+               size_t n = strlen(buff);
+               if (n != 0) {
+                       if (buff[n - 1] != '/') {
+                               CHECK_F(n + 2 < buff_size, "Filename buffer too small");
+                               buff[n] = '/';
+                               buff[n + 1] = '\0';
+                       }
+               }
+
+               strncat(buff, s_argv0_filename.c_str(), buff_size - strlen(buff) - 1);
+               strncat(buff, "/",                      buff_size - strlen(buff) - 1);
+               write_date_time(buff + strlen(buff),    buff_size - strlen(buff));
+               strncat(buff, ".log",                   buff_size - strlen(buff) - 1);
+       }
+
+       bool create_directories(const char* file_path_const)
+       {
+               CHECK_F(file_path_const && *file_path_const);
+               char* file_path = STRDUP(file_path_const);
+               for (char* p = strchr(file_path + 1, '/'); p; p = strchr(p + 1, '/')) {
+                       *p = '\0';
+
+       #ifdef _WIN32
+                       if (_mkdir(file_path) == -1) {
+       #else
+                       if (mkdir(file_path, 0755) == -1) {
+       #endif
+                               if (errno != EEXIST) {
+                                       LOG_F(ERROR, "Failed to create directory '%s'", file_path);
+                                       LOG_IF_F(ERROR, errno == EACCES,       "EACCES");
+                                       LOG_IF_F(ERROR, errno == ENAMETOOLONG, "ENAMETOOLONG");
+                                       LOG_IF_F(ERROR, errno == ENOENT,       "ENOENT");
+                                       LOG_IF_F(ERROR, errno == ENOTDIR,      "ENOTDIR");
+                                       LOG_IF_F(ERROR, errno == ELOOP,        "ELOOP");
+
+                                       *p = '/';
+                                       free(file_path);
+                                       return false;
+                               }
+                       }
+                       *p = '/';
+               }
+               free(file_path);
+               return true;
+       }
+       bool add_file(const char* path_in, FileMode mode, Verbosity verbosity)
+       {
+               char path[PATH_MAX];
+               if (path_in[0] == '~') {
+                       snprintf(path, sizeof(path) - 1, "%s%s", home_dir(), path_in + 1);
+               } else {
+                       snprintf(path, sizeof(path) - 1, "%s", path_in);
+               }
+
+               if (!create_directories(path)) {
+                       LOG_F(ERROR, "Failed to create directories to '%s'", path);
+               }
+
+               const char* mode_str = (mode == FileMode::Truncate ? "w" : "a");
+               auto file = fopen(path, mode_str);
+               if (!file) {
+                       LOG_F(ERROR, "Failed to open '%s'", path);
+                       return false;
+               }
+#if LOGURU_WITH_FILEABS
+               FileAbs* file_abs = new FileAbs(); // this is deleted in file_close;
+               snprintf(file_abs->path, sizeof(file_abs->path) - 1, "%s", path);
+               snprintf(file_abs->mode_str, sizeof(file_abs->mode_str) - 1, "%s", mode_str);
+               stat(file_abs->path, &file_abs->st);
+               file_abs->fp = file;
+               file_abs->verbosity = verbosity;
+               add_callback(path_in, file_log, file_abs, verbosity, file_close, file_flush);
+#else
+               add_callback(path_in, file_log, file, verbosity, file_close, file_flush);
+#endif
+
+               if (mode == FileMode::Append) {
+                       fprintf(file, "\n\n\n\n\n");
+               }
+               if (!s_arguments.empty()) {
+                       fprintf(file, "arguments: %s\n", s_arguments.c_str());
+               }
+               if (strlen(s_current_dir) != 0) {
+                       fprintf(file, "Current dir: %s\n", s_current_dir);
+               }
+               fprintf(file, "File verbosity level: %d\n", verbosity);
+               if (g_preamble) {
+                       char preamble_explain[LOGURU_PREAMBLE_WIDTH];
+                       print_preamble_header(preamble_explain, sizeof(preamble_explain));
+                       fprintf(file, "%s\n", preamble_explain);
+               }
+               fflush(file);
+
+               VLOG_F(g_internal_verbosity, "Logging to '%s', mode: '%s', verbosity: %d", path, mode_str, verbosity);
+               return true;
+       }
+
+       // Will be called right before abort().
+       void set_fatal_handler(fatal_handler_t handler)
+       {
+               s_fatal_handler = handler;
+       }
+
+       fatal_handler_t get_fatal_handler()
+       {
+               return s_fatal_handler;
+       }
+
+       void set_verbosity_to_name_callback(verbosity_to_name_t callback)
+       {
+               s_verbosity_to_name_callback = callback;
+       }
+
+       void set_name_to_verbosity_callback(name_to_verbosity_t callback)
+       {
+               s_name_to_verbosity_callback = callback;
+       }
+
+       void add_stack_cleanup(const char* find_this, const char* replace_with_this)
+       {
+               if (strlen(find_this) <= strlen(replace_with_this)) {
+                       LOG_F(WARNING, "add_stack_cleanup: the replacement should be shorter than the pattern!");
+                       return;
+               }
+
+               s_user_stack_cleanups.push_back(StringPair(find_this, replace_with_this));
+       }
+
+       static void on_callback_change()
+       {
+               s_max_out_verbosity = Verbosity_OFF;
+               for (const auto& callback : s_callbacks) {
+                       s_max_out_verbosity = std::max(s_max_out_verbosity, callback.verbosity);
+               }
+       }
+
+       void add_callback(
+               const char*     id,
+               log_handler_t   callback,
+               void*           user_data,
+               Verbosity       verbosity,
+               close_handler_t on_close,
+               flush_handler_t on_flush)
+       {
+               std::lock_guard<std::recursive_mutex> lock(s_mutex);
+               s_callbacks.push_back(Callback{id, callback, user_data, verbosity, on_close, on_flush, 0});
+               on_callback_change();
+       }
+
+       // Returns a custom verbosity name if one is available, or nullptr.
+       // See also set_verbosity_to_name_callback.
+       const char* get_verbosity_name(Verbosity verbosity)
+       {
+               auto name = s_verbosity_to_name_callback
+                               ? (*s_verbosity_to_name_callback)(verbosity)
+                               : nullptr;
+
+               // Use standard replacements if callback fails:
+               if (!name)
+               {
+                       if (verbosity <= Verbosity_FATAL) {
+                               name = "FATL";
+                       } else if (verbosity == Verbosity_ERROR) {
+                               name = "ERR";
+                       } else if (verbosity == Verbosity_WARNING) {
+                               name = "WARN";
+                       } else if (verbosity == Verbosity_INFO) {
+                               name = "INFO";
+                       }
+               }
+
+               return name;
+       }
+
+       // Returns Verbosity_INVALID if the name is not found.
+       // See also set_name_to_verbosity_callback.
+       Verbosity get_verbosity_from_name(const char* name)
+       {
+               auto verbosity = s_name_to_verbosity_callback
+                               ? (*s_name_to_verbosity_callback)(name)
+                               : Verbosity_INVALID;
+
+               // Use standard replacements if callback fails:
+               if (verbosity == Verbosity_INVALID) {
+                       if (strcmp(name, "OFF") == 0) {
+                               verbosity = Verbosity_OFF;
+                       } else if (strcmp(name, "INFO") == 0) {
+                               verbosity = Verbosity_INFO;
+                       } else if (strcmp(name, "WARNING") == 0) {
+                               verbosity = Verbosity_WARNING;
+                       } else if (strcmp(name, "ERROR") == 0) {
+                               verbosity = Verbosity_ERROR;
+                       } else if (strcmp(name, "FATAL") == 0) {
+                               verbosity = Verbosity_FATAL;
+                       }
+               }
+
+               return verbosity;
+       }
+
+       bool remove_callback(const char* id)
+       {
+               std::lock_guard<std::recursive_mutex> lock(s_mutex);
+               auto it = std::find_if(begin(s_callbacks), end(s_callbacks), [&](const Callback& c) { return c.id == id; });
+               if (it != s_callbacks.end()) {
+                       if (it->close) { it->close(it->user_data); }
+                       s_callbacks.erase(it);
+                       on_callback_change();
+                       return true;
+               } else {
+                       LOG_F(ERROR, "Failed to locate callback with id '%s'", id);
+                       return false;
+               }
+       }
+
+       void remove_all_callbacks()
+       {
+               std::lock_guard<std::recursive_mutex> lock(s_mutex);
+               for (auto& callback : s_callbacks) {
+                       if (callback.close) {
+                               callback.close(callback.user_data);
+                       }
+               }
+               s_callbacks.clear();
+               on_callback_change();
+       }
+
+       // Returns the maximum of g_stderr_verbosity and all file/custom outputs.
+       Verbosity current_verbosity_cutoff()
+       {
+               return g_stderr_verbosity > s_max_out_verbosity ?
+                          g_stderr_verbosity : s_max_out_verbosity;
+       }
+
+#if LOGURU_WINTHREADS
+       char* get_thread_name_win32()
+       {
+               __declspec( thread ) static char thread_name[LOGURU_THREADNAME_WIDTH + 1] = {0};
+               return &thread_name[0];
+       }
+#endif // LOGURU_WINTHREADS
+
+       void set_thread_name(const char* name)
+       {
+               #if LOGURU_PTLS_NAMES
+                       (void)pthread_once(&s_pthread_key_once, make_pthread_key_name);
+                       (void)pthread_setspecific(s_pthread_key_name, STRDUP(name));
+
+               #elif LOGURU_PTHREADS
+                       #ifdef __APPLE__
+                               pthread_setname_np(name);
+                       #elif defined(__FreeBSD__) || defined(__OpenBSD__)
+                               pthread_set_name_np(pthread_self(), name);
+                       #elif defined(__linux__)
+                               pthread_setname_np(pthread_self(), name);
+                       #endif
+               #elif LOGURU_WINTHREADS
+                       strncpy_s(get_thread_name_win32(), LOGURU_THREADNAME_WIDTH + 1, name, _TRUNCATE);
+               #else // LOGURU_PTHREADS
+                       (void)name;
+               #endif // LOGURU_PTHREADS
+       }
+
+#if LOGURU_PTLS_NAMES
+       const char* get_thread_name_ptls()
+       {
+               (void)pthread_once(&s_pthread_key_once, make_pthread_key_name);
+               return static_cast<const char*>(pthread_getspecific(s_pthread_key_name));
+       }
+#endif // LOGURU_PTLS_NAMES
+
+       void get_thread_name(char* buffer, unsigned long long length, bool right_align_hext_id)
+       {
+#ifdef _WIN32
+               (void)right_align_hext_id;
+#endif
+               CHECK_NE_F(length, 0u, "Zero length buffer in get_thread_name");
+               CHECK_NOTNULL_F(buffer, "nullptr in get_thread_name");
+#if LOGURU_PTHREADS
+               auto thread = pthread_self();
+               #if LOGURU_PTLS_NAMES
+                       if (const char* name = get_thread_name_ptls()) {
+                               snprintf(buffer, length, "%s", name);
+                       } else {
+                               buffer[0] = 0;
+                       }
+               #elif defined(__APPLE__) || defined(__linux__)
+                       pthread_getname_np(thread, buffer, length);
+               #else
+                       buffer[0] = 0;
+               #endif
+
+               if (buffer[0] == 0) {
+                       #ifdef __APPLE__
+                               uint64_t thread_id;
+                               pthread_threadid_np(thread, &thread_id);
+                       #elif defined(__FreeBSD__)
+                               long thread_id;
+                               (void)thr_self(&thread_id);
+                       #elif defined(__OpenBSD__)
+                               unsigned thread_id = -1;
+                       #else
+                               uint64_t thread_id = thread;
+                       #endif
+                       if (right_align_hext_id) {
+                               snprintf(buffer, length, "%*X", static_cast<int>(length - 1), static_cast<unsigned>(thread_id));
+                       } else {
+                               snprintf(buffer, length, "%X", static_cast<unsigned>(thread_id));
+                       }
+               }
+#elif LOGURU_WINTHREADS
+               if (const char* name = get_thread_name_win32()) {
+                       snprintf(buffer, (size_t)length, "%s", name);
+               } else {
+                       buffer[0] = 0;
+               }
+#else // !LOGURU_WINTHREADS && !LOGURU_WINTHREADS
+               buffer[0] = 0;
+#endif
+
+       }
+
+       // ------------------------------------------------------------------------
+       // Stack traces
+
+#if LOGURU_STACKTRACES
+       Text demangle(const char* name)
+       {
+               int status = -1;
+               char* demangled = abi::__cxa_demangle(name, 0, 0, &status);
+               Text result{status == 0 ? demangled : STRDUP(name)};
+               return result;
+       }
+
+       #if LOGURU_RTTI
+               template <class T>
+               std::string type_name()
+               {
+                       auto demangled = demangle(typeid(T).name());
+                       return demangled.c_str();
+               }
+       #endif // LOGURU_RTTI
+
+       static const StringPairList REPLACE_LIST = {
+               #if LOGURU_RTTI
+                       { type_name<std::string>(),    "std::string"    },
+                       { type_name<std::wstring>(),   "std::wstring"   },
+                       { type_name<std::u16string>(), "std::u16string" },
+                       { type_name<std::u32string>(), "std::u32string" },
+               #endif // LOGURU_RTTI
+               { "std::__1::",                "std::"          },
+               { "__thiscall ",               ""               },
+               { "__cdecl ",                  ""               },
+       };
+
+       void do_replacements(const StringPairList& replacements, std::string& str)
+       {
+               for (auto&& p : replacements) {
+                       if (p.first.size() <= p.second.size()) {
+                               // On gcc, "type_name<std::string>()" is "std::string"
+                               continue;
+                       }
+
+                       size_t it;
+                       while ((it=str.find(p.first)) != std::string::npos) {
+                               str.replace(it, p.first.size(), p.second);
+                       }
+               }
+       }
+
+       std::string prettify_stacktrace(const std::string& input)
+       {
+               std::string output = input;
+
+               do_replacements(s_user_stack_cleanups, output);
+               do_replacements(REPLACE_LIST, output);
+
+               try {
+                       std::regex std_allocator_re(R"(,\s*std::allocator<[^<>]+>)");
+                       output = std::regex_replace(output, std_allocator_re, std::string(""));
+
+                       std::regex template_spaces_re(R"(<\s*([^<> ]+)\s*>)");
+                       output = std::regex_replace(output, template_spaces_re, std::string("<$1>"));
+               } catch (std::regex_error&) {
+                       // Probably old GCC.
+               }
+
+               return output;
+       }
+
+       std::string stacktrace_as_stdstring(int skip)
+       {
+               // From https://gist.github.com/fmela/591333
+               void* callstack[128];
+               const auto max_frames = sizeof(callstack) / sizeof(callstack[0]);
+               int num_frames = backtrace(callstack, max_frames);
+               char** symbols = backtrace_symbols(callstack, num_frames);
+
+               std::string result;
+               // Print stack traces so the most relevant ones are written last
+               // Rationale: http://yellerapp.com/posts/2015-01-22-upside-down-stacktraces.html
+               for (int i = num_frames - 1; i >= skip; --i) {
+                       char buf[1024];
+                       Dl_info info;
+                       if (dladdr(callstack[i], &info) && info.dli_sname) {
+                               char* demangled = NULL;
+                               int status = -1;
+                               if (info.dli_sname[0] == '_') {
+                                       demangled = abi::__cxa_demangle(info.dli_sname, 0, 0, &status);
+                               }
+                               snprintf(buf, sizeof(buf), "%-3d %*p %s + %zd\n",
+                                                i - skip, int(2 + sizeof(void*) * 2), callstack[i],
+                                                status == 0 ? demangled :
+                                                info.dli_sname == 0 ? symbols[i] : info.dli_sname,
+                                                static_cast<char*>(callstack[i]) - static_cast<char*>(info.dli_saddr));
+                               free(demangled);
+                       } else {
+                               snprintf(buf, sizeof(buf), "%-3d %*p %s\n",
+                                                i - skip, int(2 + sizeof(void*) * 2), callstack[i], symbols[i]);
+                       }
+                       result += buf;
+               }
+               free(symbols);
+
+               if (num_frames == max_frames) {
+                       result = "[truncated]\n" + result;
+               }
+
+               if (!result.empty() && result[result.size() - 1] == '\n') {
+                       result.resize(result.size() - 1);
+               }
+
+               return prettify_stacktrace(result);
+       }
+
+#else // LOGURU_STACKTRACES
+       Text demangle(const char* name)
+       {
+               return Text(STRDUP(name));
+       }
+
+       std::string stacktrace_as_stdstring(int)
+       {
+               // No stacktraces available on this platform"
+               return "";
+       }
+
+#endif // LOGURU_STACKTRACES
+
+       Text stacktrace(int skip)
+       {
+               auto str = stacktrace_as_stdstring(skip + 1);
+               return Text(STRDUP(str.c_str()));
+       }
+
+       // ------------------------------------------------------------------------
+
+       static void print_preamble_header(char* out_buff, size_t out_buff_size)
+       {
+               if (out_buff_size == 0) { return; }
+               out_buff[0] = '\0';
+               long pos = 0;
+               if (g_preamble_date && pos < out_buff_size) {
+                       pos += snprintf(out_buff + pos, out_buff_size - pos, "date       ");
+               }
+               if (g_preamble_time && pos < out_buff_size) {
+                       pos += snprintf(out_buff + pos, out_buff_size - pos, "time         ");
+               }
+               if (g_preamble_uptime && pos < out_buff_size) {
+                       pos += snprintf(out_buff + pos, out_buff_size - pos, "( uptime  ) ");
+               }
+               if (g_preamble_thread && pos < out_buff_size) {
+                       pos += snprintf(out_buff + pos, out_buff_size - pos, "[%-*s]", LOGURU_THREADNAME_WIDTH, " thread name/id");
+               }
+               if (g_preamble_file && pos < out_buff_size) {
+                       pos += snprintf(out_buff + pos, out_buff_size - pos, "%*s:line  ", LOGURU_FILENAME_WIDTH, "file");
+               }
+               if (g_preamble_verbose && pos < out_buff_size) {
+                       pos += snprintf(out_buff + pos, out_buff_size - pos, "   v");
+               }
+               if (g_preamble_pipe && pos < out_buff_size) {
+                       pos += snprintf(out_buff + pos, out_buff_size - pos, "| ");
+               }
+       }
+
+       static void print_preamble(char* out_buff, size_t out_buff_size, Verbosity verbosity, const char* file, unsigned line)
+       {
+               if (out_buff_size == 0) { return; }
+               out_buff[0] = '\0';
+               if (!g_preamble) { return; }
+               long long ms_since_epoch = duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
+               time_t sec_since_epoch = time_t(ms_since_epoch / 1000);
+               tm time_info;
+               localtime_r(&sec_since_epoch, &time_info);
+
+               auto uptime_ms = duration_cast<milliseconds>(steady_clock::now() - s_start_time).count();
+               auto uptime_sec = uptime_ms / 1000.0;
+
+               char thread_name[LOGURU_THREADNAME_WIDTH + 1] = {0};
+               get_thread_name(thread_name, LOGURU_THREADNAME_WIDTH + 1, true);
+
+               if (s_strip_file_path) {
+                       file = filename(file);
+               }
+
+               char level_buff[6];
+               const char* custom_level_name = get_verbosity_name(verbosity);
+               if (custom_level_name) {
+                       snprintf(level_buff, sizeof(level_buff) - 1, "%s", custom_level_name);
+               } else {
+                       snprintf(level_buff, sizeof(level_buff) - 1, "% 4d", verbosity);
+               }
+
+               long pos = 0;
+
+               if (g_preamble_date && pos < out_buff_size) {
+                       pos += snprintf(out_buff + pos, out_buff_size - pos, "%04d-%02d-%02d ",
+                                            1900 + time_info.tm_year, 1 + time_info.tm_mon, time_info.tm_mday);
+               }
+               if (g_preamble_time && pos < out_buff_size) {
+                       pos += snprintf(out_buff + pos, out_buff_size - pos, "%02d:%02d:%02d.%03lld ",
+                                      time_info.tm_hour, time_info.tm_min, time_info.tm_sec, ms_since_epoch % 1000);
+               }
+               if (g_preamble_uptime && pos < out_buff_size) {
+                       pos += snprintf(out_buff + pos, out_buff_size - pos, "(%8.3fs) ",
+                                      uptime_sec);
+               }
+               if (g_preamble_thread && pos < out_buff_size) {
+                       pos += snprintf(out_buff + pos, out_buff_size - pos, "[%-*s]",
+                                      LOGURU_THREADNAME_WIDTH, thread_name);
+               }
+               if (g_preamble_file && pos < out_buff_size) {
+                       char shortened_filename[LOGURU_FILENAME_WIDTH + 1];
+                       snprintf(shortened_filename, LOGURU_FILENAME_WIDTH + 1, "%s", file);
+                       pos += snprintf(out_buff + pos, out_buff_size - pos, "%*s:%-5u ",
+                                      LOGURU_FILENAME_WIDTH, shortened_filename, line);
+               }
+               if (g_preamble_verbose && pos < out_buff_size) {
+                       pos += snprintf(out_buff + pos, out_buff_size - pos, "%4s",
+                                      level_buff);
+               }
+               if (g_preamble_pipe && pos < out_buff_size) {
+                       pos += snprintf(out_buff + pos, out_buff_size - pos, "| ");
+               }
+       }
+
+       // stack_trace_skip is just if verbosity == FATAL.
+       static void log_message(int stack_trace_skip, Message& message, bool with_indentation, bool abort_if_fatal)
+       {
+               const auto verbosity = message.verbosity;
+               std::lock_guard<std::recursive_mutex> lock(s_mutex);
+
+               if (message.verbosity == Verbosity_FATAL) {
+                       auto st = loguru::stacktrace(stack_trace_skip + 2);
+                       if (!st.empty()) {
+                               RAW_LOG_F(ERROR, "Stack trace:\n%s", st.c_str());
+                       }
+
+                       auto ec = loguru::get_error_context();
+                       if (!ec.empty()) {
+                               RAW_LOG_F(ERROR, "%s", ec.c_str());
+                       }
+               }
+
+               if (with_indentation) {
+                       message.indentation = indentation(s_stderr_indentation);
+               }
+
+               if (verbosity <= g_stderr_verbosity) {
+                       if (g_colorlogtostderr && s_terminal_has_color) {
+                               if (verbosity > Verbosity_WARNING) {
+                                       fprintf(stderr, "%s%s%s%s%s%s%s%s\n",
+                                               terminal_reset(),
+                                               terminal_dim(),
+                                               message.preamble,
+                                               message.indentation,
+                                               verbosity == Verbosity_INFO ? terminal_reset() : "", // un-dim for info
+                                               message.prefix,
+                                               message.message,
+                                               terminal_reset());
+                               } else {
+                                       fprintf(stderr, "%s%s%s%s%s%s%s\n",
+                                               terminal_reset(),
+                                               verbosity == Verbosity_WARNING ? terminal_yellow() : terminal_red(),
+                                               message.preamble,
+                                               message.indentation,
+                                               message.prefix,
+                                               message.message,
+                                               terminal_reset());
+                               }
+                       } else {
+                               fprintf(stderr, "%s%s%s%s\n",
+                                       message.preamble, message.indentation, message.prefix, message.message);
+                       }
+
+                       if (g_flush_interval_ms == 0) {
+                               fflush(stderr);
+                       } else {
+                               s_needs_flushing = true;
+                       }
+               }
+
+               for (auto& p : s_callbacks) {
+                       if (verbosity <= p.verbosity) {
+                               if (with_indentation) {
+                                       message.indentation = indentation(p.indentation);
+                               }
+                               p.callback(p.user_data, message);
+                               if (g_flush_interval_ms == 0) {
+                                       if (p.flush) { p.flush(p.user_data); }
+                               } else {
+                                       s_needs_flushing = true;
+                               }
+                       }
+               }
+
+               if (g_flush_interval_ms > 0 && !s_flush_thread) {
+                       s_flush_thread = new std::thread([](){
+                               for (;;) {
+                                       if (s_needs_flushing) {
+                                               flush();
+                                       }
+                                       std::this_thread::sleep_for(std::chrono::milliseconds(g_flush_interval_ms));
+                               }
+                       });
+               }
+
+               if (message.verbosity == Verbosity_FATAL) {
+                       flush();
+
+                       if (s_fatal_handler) {
+                               s_fatal_handler(message);
+                               flush();
+                       }
+
+                       if (abort_if_fatal) {
+#if LOGURU_CATCH_SIGABRT && !defined(_WIN32)
+                               // Make sure we don't catch our own abort:
+                               signal(SIGABRT, SIG_DFL);
+#endif
+                               abort();
+                       }
+               }
+       }
+
+       // stack_trace_skip is just if verbosity == FATAL.
+       void log_to_everywhere(int stack_trace_skip, Verbosity verbosity,
+                              const char* file, unsigned line,
+                              const char* prefix, const char* buff)
+       {
+               char preamble_buff[LOGURU_PREAMBLE_WIDTH];
+               print_preamble(preamble_buff, sizeof(preamble_buff), verbosity, file, line);
+               auto message = Message{verbosity, file, line, preamble_buff, "", prefix, buff};
+               log_message(stack_trace_skip + 1, message, true, true);
+       }
+
+#if LOGURU_USE_FMTLIB
+       void log(Verbosity verbosity, const char* file, unsigned line, const char* format, fmt::ArgList args)
+       {
+               auto formatted = fmt::format(format, args);
+               log_to_everywhere(1, verbosity, file, line, "", formatted.c_str());
+       }
+
+       void raw_log(Verbosity verbosity, const char* file, unsigned line, const char* format, fmt::ArgList args)
+       {
+               auto formatted = fmt::format(format, args);
+               auto message = Message{verbosity, file, line, "", "", "", formatted.c_str()};
+               log_message(1, message, false, true);
+       }
+
+#else
+       void log(Verbosity verbosity, const char* file, unsigned line, const char* format, ...)
+       {
+               va_list vlist;
+               va_start(vlist, format);
+               auto buff = vtextprintf(format, vlist);
+               log_to_everywhere(1, verbosity, file, line, "", buff.c_str());
+               va_end(vlist);
+       }
+
+       void raw_log(Verbosity verbosity, const char* file, unsigned line, const char* format, ...)
+       {
+               va_list vlist;
+               va_start(vlist, format);
+               auto buff = vtextprintf(format, vlist);
+               auto message = Message{verbosity, file, line, "", "", "", buff.c_str()};
+               log_message(1, message, false, true);
+               va_end(vlist);
+       }
+#endif
+
+       void flush()
+       {
+               std::lock_guard<std::recursive_mutex> lock(s_mutex);
+               fflush(stderr);
+               for (const auto& callback : s_callbacks)
+               {
+                       if (callback.flush) {
+                               callback.flush(callback.user_data);
+                       }
+               }
+               s_needs_flushing = false;
+       }
+
+       LogScopeRAII::LogScopeRAII(Verbosity verbosity, const char* file, unsigned line, const char* format, ...)
+               : _verbosity(verbosity), _file(file), _line(line)
+       {
+               if (verbosity <= current_verbosity_cutoff()) {
+                       std::lock_guard<std::recursive_mutex> lock(s_mutex);
+                       _indent_stderr = (verbosity <= g_stderr_verbosity);
+                       _start_time_ns = now_ns();
+                       va_list vlist;
+                       va_start(vlist, format);
+                       vsnprintf(_name, sizeof(_name), format, vlist);
+                       log_to_everywhere(1, _verbosity, file, line, "{ ", _name);
+                       va_end(vlist);
+
+                       if (_indent_stderr) {
+                               ++s_stderr_indentation;
+                       }
+
+                       for (auto& p : s_callbacks) {
+                               if (verbosity <= p.verbosity) {
+                                       ++p.indentation;
+                               }
+                       }
+               } else {
+                       _file = nullptr;
+               }
+       }
+
+       LogScopeRAII::~LogScopeRAII()
+       {
+               if (_file) {
+                       std::lock_guard<std::recursive_mutex> lock(s_mutex);
+                       if (_indent_stderr && s_stderr_indentation > 0) {
+                               --s_stderr_indentation;
+                       }
+                       for (auto& p : s_callbacks) {
+                               // Note: Callback indentation cannot change!
+                               if (_verbosity <= p.verbosity) {
+                                       // in unlikely case this callback is new
+                                       if (p.indentation > 0) {
+                                               --p.indentation;
+                                       }
+                               }
+                       }
+#if LOGURU_VERBOSE_SCOPE_ENDINGS
+                       auto duration_sec = (now_ns() - _start_time_ns) / 1e9;
+                       auto buff = textprintf("%.*f s: %s", LOGURU_SCOPE_TIME_PRECISION, duration_sec, _name);
+                       log_to_everywhere(1, _verbosity, _file, _line, "} ", buff.c_str());
+#else
+                       log_to_everywhere(1, _verbosity, _file, _line, "}", "");
+#endif
+               }
+       }
+
+       void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line, const char* format, ...)
+       {
+               va_list vlist;
+               va_start(vlist, format);
+               auto buff = vtextprintf(format, vlist);
+               log_to_everywhere(stack_trace_skip + 1, Verbosity_FATAL, file, line, expr, buff.c_str());
+               va_end(vlist);
+               abort(); // log_to_everywhere already does this, but this makes the analyzer happy.
+       }
+
+       void log_and_abort(int stack_trace_skip, const char* expr, const char* file, unsigned line)
+       {
+               log_and_abort(stack_trace_skip + 1, expr, file, line, " ");
+       }
+
+       // ----------------------------------------------------------------------------
+       // Streams:
+
+       std::string vstrprintf(const char* format, va_list vlist)
+       {
+               auto text = vtextprintf(format, vlist);
+               std::string result = text.c_str();
+               return result;
+       }
+
+       std::string strprintf(const char* format, ...)
+       {
+               va_list vlist;
+               va_start(vlist, format);
+               auto result = vstrprintf(format, vlist);
+               va_end(vlist);
+               return result;
+       }
+
+       #if LOGURU_WITH_STREAMS
+
+       StreamLogger::~StreamLogger() noexcept(false)
+       {
+               auto message = _ss.str();
+               log(_verbosity, _file, _line, "%s", message.c_str());
+       }
+
+       AbortLogger::~AbortLogger() noexcept(false)
+       {
+               auto message = _ss.str();
+               loguru::log_and_abort(1, _expr, _file, _line, "%s", message.c_str());
+       }
+
+       #endif // LOGURU_WITH_STREAMS
+
+       // ----------------------------------------------------------------------------
+       // 888888 88""Yb 88""Yb  dP"Yb  88""Yb      dP""b8  dP"Yb  88b 88 888888 888888 Yb  dP 888888
+       // 88__   88__dP 88__dP dP   Yb 88__dP     dP   `" dP   Yb 88Yb88   88   88__    YbdP    88
+       // 88""   88"Yb  88"Yb  Yb   dP 88"Yb      Yb      Yb   dP 88 Y88   88   88""    dPYb    88
+       // 888888 88  Yb 88  Yb  YbodP  88  Yb      YboodP  YbodP  88  Y8   88   888888 dP  Yb   88
+       // ----------------------------------------------------------------------------
+
+       struct StringStream
+       {
+               std::string str;
+       };
+
+       // Use this in your EcPrinter implementations.
+       void stream_print(StringStream& out_string_stream, const char* text)
+       {
+               out_string_stream.str += text;
+       }
+
+       // ----------------------------------------------------------------------------
+
+       using ECPtr = EcEntryBase*;
+
+#if defined(_WIN32) || (defined(__APPLE__) && !TARGET_OS_IPHONE)
+       #ifdef __APPLE__
+               #define LOGURU_THREAD_LOCAL __thread
+       #else
+               #define LOGURU_THREAD_LOCAL thread_local
+       #endif
+       static LOGURU_THREAD_LOCAL ECPtr thread_ec_ptr = nullptr;
+
+       ECPtr& get_thread_ec_head_ref()
+       {
+               return thread_ec_ptr;
+       }
+#else // !thread_local
+       static pthread_once_t s_ec_pthread_once = PTHREAD_ONCE_INIT;
+       static pthread_key_t  s_ec_pthread_key;
+
+       void free_ec_head_ref(void* io_error_context)
+       {
+               delete reinterpret_cast<ECPtr*>(io_error_context);
+       }
+
+       void ec_make_pthread_key()
+       {
+               (void)pthread_key_create(&s_ec_pthread_key, free_ec_head_ref);
+       }
+
+       ECPtr& get_thread_ec_head_ref()
+       {
+               (void)pthread_once(&s_ec_pthread_once, ec_make_pthread_key);
+               auto ec = reinterpret_cast<ECPtr*>(pthread_getspecific(s_ec_pthread_key));
+               if (ec == nullptr) {
+                       ec = new ECPtr(nullptr);
+                       (void)pthread_setspecific(s_ec_pthread_key, ec);
+               }
+               return *ec;
+       }
+#endif // !thread_local
+
+       // ----------------------------------------------------------------------------
+
+       EcHandle get_thread_ec_handle()
+       {
+               return get_thread_ec_head_ref();
+       }
+
+       Text get_error_context()
+       {
+               return get_error_context_for(get_thread_ec_head_ref());
+       }
+
+       Text get_error_context_for(const EcEntryBase* ec_head)
+       {
+               std::vector<const EcEntryBase*> stack;
+               while (ec_head) {
+                       stack.push_back(ec_head);
+                       ec_head = ec_head->_previous;
+               }
+               std::reverse(stack.begin(), stack.end());
+
+               StringStream result;
+               if (!stack.empty()) {
+                       result.str += "------------------------------------------------\n";
+                       for (auto entry : stack) {
+                               const auto description = std::string(entry->_descr) + ":";
+                               auto prefix = textprintf("[ErrorContext] %*s:%-5u %-20s ",
+                                       LOGURU_FILENAME_WIDTH, filename(entry->_file), entry->_line, description.c_str());
+                               result.str += prefix.c_str();
+                               entry->print_value(result);
+                               result.str += "\n";
+                       }
+                       result.str += "------------------------------------------------";
+               }
+               return Text(STRDUP(result.str.c_str()));
+       }
+
+       EcEntryBase::EcEntryBase(const char* file, unsigned line, const char* descr)
+               : _file(file), _line(line), _descr(descr)
+       {
+               EcEntryBase*& ec_head = get_thread_ec_head_ref();
+               _previous = ec_head;
+               ec_head = this;
+       }
+
+       EcEntryBase::~EcEntryBase()
+       {
+               get_thread_ec_head_ref() = _previous;
+       }
+
+       // ------------------------------------------------------------------------
+
+       Text ec_to_text(const char* value)
+       {
+               // Add quotes around the string to make it obvious where it begin and ends.
+               // This is great for detecting erroneous leading or trailing spaces in e.g. an identifier.
+               auto str = "\"" + std::string(value) + "\"";
+               return Text{STRDUP(str.c_str())};
+       }
+
+       Text ec_to_text(char c)
+       {
+               // Add quotes around the character to make it obvious where it begin and ends.
+               std::string str = "'";
+
+               auto write_hex_digit = [&](unsigned num)
+               {
+                       if (num < 10u) { str += char('0' + num); }
+                       else           { str += char('a' + num - 10); }
+               };
+
+               auto write_hex_16 = [&](uint16_t n)
+               {
+                       write_hex_digit((n >> 12u) & 0x0f);
+                       write_hex_digit((n >>  8u) & 0x0f);
+                       write_hex_digit((n >>  4u) & 0x0f);
+                       write_hex_digit((n >>  0u) & 0x0f);
+               };
+
+               if      (c == '\\') { str += "\\\\"; }
+               else if (c == '\"') { str += "\\\""; }
+               else if (c == '\'') { str += "\\\'"; }
+               else if (c == '\0') { str += "\\0";  }
+               else if (c == '\b') { str += "\\b";  }
+               else if (c == '\f') { str += "\\f";  }
+               else if (c == '\n') { str += "\\n";  }
+               else if (c == '\r') { str += "\\r";  }
+               else if (c == '\t') { str += "\\t";  }
+               else if (0 <= c && c < 0x20) {
+                       str += "\\u";
+                       write_hex_16(static_cast<uint16_t>(c));
+               } else { str += c; }
+
+               str += "'";
+
+               return Text{STRDUP(str.c_str())};
+       }
+
+       #define DEFINE_EC(Type)                        \
+               Text ec_to_text(Type value)                \
+               {                                          \
+                       auto str = std::to_string(value);      \
+                       return Text{STRDUP(str.c_str())};      \
+               }
+
+       DEFINE_EC(int)
+       DEFINE_EC(unsigned int)
+       DEFINE_EC(long)
+       DEFINE_EC(unsigned long)
+       DEFINE_EC(long long)
+       DEFINE_EC(unsigned long long)
+       DEFINE_EC(float)
+       DEFINE_EC(double)
+       DEFINE_EC(long double)
+
+       #undef DEFINE_EC
+
+       Text ec_to_text(EcHandle ec_handle)
+       {
+               Text parent_ec = get_error_context_for(ec_handle);
+               char* with_newline = reinterpret_cast<char*>(malloc(strlen(parent_ec.c_str()) + 2));
+               with_newline[0] = '\n';
+               strcpy(with_newline + 1, parent_ec.c_str());
+               return Text(with_newline);
+       }
+
+       // ----------------------------------------------------------------------------
+
+} // namespace loguru
+
+// ----------------------------------------------------------------------------
+// .dP"Y8 88  dP""b8 88b 88    db    88     .dP"Y8
+// `Ybo." 88 dP   `" 88Yb88   dPYb   88     `Ybo."
+// o.`Y8b 88 Yb  "88 88 Y88  dP__Yb  88  .o o.`Y8b
+// 8bodP' 88  YboodP 88  Y8 dP""""Yb 88ood8 8bodP'
+// ----------------------------------------------------------------------------
+
+#ifdef _WIN32
+namespace loguru {
+       void install_signal_handlers()
+       {
+               #if defined(_MSC_VER)
+               #pragma message ( "No signal handlers on Win32" )
+               #else
+               #warning "No signal handlers on Win32"
+               #endif
+       }
+} // namespace loguru
+
+#else // _WIN32
+
+namespace loguru
+{
+       struct Signal
+       {
+               int         number;
+               const char* name;
+       };
+       const Signal ALL_SIGNALS[] = {
+#if LOGURU_CATCH_SIGABRT
+               { SIGABRT, "SIGABRT" },
+#endif
+               { SIGBUS,  "SIGBUS"  },
+               { SIGFPE,  "SIGFPE"  },
+               { SIGILL,  "SIGILL"  },
+               { SIGINT,  "SIGINT"  },
+               { SIGSEGV, "SIGSEGV" },
+               { SIGTERM, "SIGTERM" },
+       };
+
+       void write_to_stderr(const char* data, size_t size)
+       {
+               auto result = write(STDERR_FILENO, data, size);
+               (void)result; // Ignore errors.
+       }
+
+       void write_to_stderr(const char* data)
+       {
+               write_to_stderr(data, strlen(data));
+       }
+
+       void call_default_signal_handler(int signal_number)
+       {
+               struct sigaction sig_action;
+               memset(&sig_action, 0, sizeof(sig_action));
+               sigemptyset(&sig_action.sa_mask);
+               sig_action.sa_handler = SIG_DFL;
+               sigaction(signal_number, &sig_action, NULL);
+               kill(getpid(), signal_number);
+       }
+
+       void signal_handler(int signal_number, siginfo_t*, void*)
+       {
+               const char* signal_name = "UNKNOWN SIGNAL";
+
+               for (const auto& s : ALL_SIGNALS) {
+                       if (s.number == signal_number) {
+                               signal_name = s.name;
+                               break;
+                       }
+               }
+
+               // --------------------------------------------------------------------
+               /* There are few things that are safe to do in a signal handler,
+                  but writing to stderr is one of them.
+                  So we first print out what happened to stderr so we're sure that gets out,
+                  then we do the unsafe things, like logging the stack trace.
+               */
+
+               if (g_colorlogtostderr && s_terminal_has_color) {
+                       write_to_stderr(terminal_reset());
+                       write_to_stderr(terminal_bold());
+                       write_to_stderr(terminal_light_red());
+               }
+               write_to_stderr("\n");
+               write_to_stderr("Loguru caught a signal: ");
+               write_to_stderr(signal_name);
+               write_to_stderr("\n");
+               if (g_colorlogtostderr && s_terminal_has_color) {
+                       write_to_stderr(terminal_reset());
+               }
+
+               // --------------------------------------------------------------------
+
+#if LOGURU_UNSAFE_SIGNAL_HANDLER
+               // --------------------------------------------------------------------
+               /* Now we do unsafe things. This can for example lead to deadlocks if
+                  the signal was triggered from the system's memory management functions
+                  and the code below tries to do allocations.
+               */
+
+               flush();
+               char preamble_buff[LOGURU_PREAMBLE_WIDTH];
+               print_preamble(preamble_buff, sizeof(preamble_buff), Verbosity_FATAL, "", 0);
+               auto message = Message{Verbosity_FATAL, "", 0, preamble_buff, "", "Signal: ", signal_name};
+               try {
+                       log_message(1, message, false, false);
+               } catch (...) {
+                       // This can happed due to s_fatal_handler.
+                       write_to_stderr("Exception caught and ignored by Loguru signal handler.\n");
+               }
+               flush();
+
+               // --------------------------------------------------------------------
+#endif // LOGURU_UNSAFE_SIGNAL_HANDLER
+
+               call_default_signal_handler(signal_number);
+       }
+
+       void install_signal_handlers()
+       {
+               struct sigaction sig_action;
+               memset(&sig_action, 0, sizeof(sig_action));
+               sigemptyset(&sig_action.sa_mask);
+               sig_action.sa_flags |= SA_SIGINFO;
+               sig_action.sa_sigaction = &signal_handler;
+               for (const auto& s : ALL_SIGNALS) {
+                       CHECK_F(sigaction(s.number, &sig_action, NULL) != -1,
+                               "Failed to install handler for %s", s.name);
+               }
+       }
+} // namespace loguru
+
+#endif // _WIN32
+
+#endif // LOGURU_IMPLEMENTATION
\ No newline at end of file
diff --git a/meson.build b/meson.build
new file mode 100644 (file)
index 0000000..748dd5e
--- /dev/null
@@ -0,0 +1,28 @@
+project('aurum', ['cpp'],
+  version: '0.0.1',
+  default_options : ['buildtype=debugoptimized', 'cpp_std=c++17'],
+  meson_version : '>=0.47'
+)
+
+config_h = configuration_data()
+if get_option('tizen') == true
+  config_h.set10('GBS_BUILD', true)
+  config_h.set10('GBSBUILD', true)
+  config_h.set10('TIZEN', true)
+else
+  message('')
+endif
+
+root_inc = include_directories('./')
+
+subdir('protocol')
+subdir('libloguru')
+subdir('libaurum')
+subdir('bootstrap')
+subdir('tests')
+
+configure_file(
+  output: 'config.h',
+  configuration: config_h
+)
+
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644 (file)
index 0000000..1bcc370
--- /dev/null
@@ -0,0 +1,7 @@
+option('tizen',
+  type: 'boolean',
+  value: false,
+  description: 'enable tizen specific dependancy'
+)
+
+
diff --git a/misc/down_grpc.sh b/misc/down_grpc.sh
new file mode 100755 (executable)
index 0000000..96dc0dc
--- /dev/null
@@ -0,0 +1,17 @@
+#!/bin/bash -xe
+
+repo_url=http://download.tizen.org/snapshots/tizen/unified/latest/repos/standard/packages/armv7l/
+repo_dbg_url=http://download.tizen.org/snapshots/tizen/unified/latest/repos/standard/debug/
+
+libgrpc_filter=" -A libgrpc* "
+grpc_filter=" -A grpc-* "
+grpc_dbg_filter=" -A grpc-debug*armv7l* -A libatspi-debug*armv7l&"
+libgrpc_dbg_filter=" -A libgrpc-debug*armv7l* "
+
+test -d rpm && rm -rf rpm
+
+wget -P rpm --recursive -nd -nH -np $repo_url $grpc_filter
+wget -P rpm --recursive -nd -nH -np $repo_url $libgrpc_filter
+
+wget -P rpm --recursive -nd -nH -np $repo_dbg_url $grpc_dbg_filter
+wget -P rpm --recursive -nd -nH -np $repo_dbg_url $libgrpc_dbg_filter
diff --git a/misc/env.sh b/misc/env.sh
new file mode 100644 (file)
index 0000000..c2c77e2
--- /dev/null
@@ -0,0 +1,69 @@
+export TIZEN_WAYLAND_SHM_DIR=/run/.efl
+export beam_delta=0.5
+export EVAS_FONT_DPI=72
+export HOSTNAME=
+export LISTEN_PID=774
+export beam=15
+export WAYLAND_DISPLAY=wayland-0
+export ECORE_INPUT_CANCEL=1
+export EINA_LOG_ABORT_LEVEL=0
+export SHELL=/bin/bash
+export left_context=0
+export HISTSIZE=1000
+export fbank_conf=/usr/apps/org.tizen.stt-engine-embedded/shared/res/model/conf/fbank.conf
+export ac_weight=0.75
+export ELM_ENGINE=wayland_egl
+export LC_ALL=POSIX
+export lattice_beam=8.0
+export USER=owner
+export GST_DEBUG=2
+export WRT_BLOCK_SINGLE_PROCESS=OFF
+export right_context=3
+export symtab=/usr/apps/org.tizen.stt-engine-embedded/shared/res/model/lm/words.txt
+export ECORE_IMF_MODULE=wayland
+export MAIL=/var/spool/mail/owner
+export PATH=/bin:/usr/bin:/sbin:/usr/sbin
+export ECTOR_BACKEND=default
+export LISTEN_FDNAMES=launchpad-process-pool.socket
+export max_active=7000
+export EINA_LOG_DLOG_ENABLE=1
+export ELM_ATSPI_MODE=1
+export online=true
+export PWD=/opt/usr/home/owner
+export result=/usr/apps/org.tizen.stt-engine-embedded/shared/res/result.txt
+export lattice_out=lat.kor
+export nnet_in=/usr/apps/org.tizen.stt-engine-embedded/shared/res/model/am/converted_2_Eigen_Mat.nnet
+export prior=/usr/apps/org.tizen.stt-engine-embedded/shared/res/model/am/label.counts
+export LANG=en_US.UTF-8
+export PYTHONSTARTUP=/etc/pythonstart
+export global_cmvn=/usr/apps/org.tizen.stt-engine-embedded/shared/res/model/am/global_cmvn0412.ark
+export EVAS_SHM_FLUSH=1
+export fst_in=/usr/apps/org.tizen.stt-engine-embedded/shared/res/model/lm/TLG.fst
+export HISTCONTROL=ignoredups
+export hyp_filtering_cmd=cat
+export EINA_LOG_LEVEL=2
+export SHLVL=1
+export HOME=/opt/usr/home/owner
+export use_gpu=no
+export ECORE_ANIMATOR_SKIP=1
+export big_lm=/usr/apps/org.tizen.stt-engine-embedded/shared/res/model/lm/Mix_Bixby_VD_64k.1e8.arpa.carpa
+export ECORE_IMF_INPUT_PANEL_ENABLED=1
+export LOGNAME=owner
+export splice_conf=/usr/apps/org.tizen.stt-engine-embedded/shared/res/model/conf/splice.conf
+export cmvn_conf=/usr/apps/org.tizen.stt-engine-embedded/shared/res/model/conf/online_cmvn.conf
+export EVAS_GL_NO_BLACKLIST=1
+export DBUS_SESSION_BUS_ADDRESS="kernel:path=/sys/fs/kdbus/5001-user/bus;unix:path=/run/user/5001/bus"
+export res_dir=/usr/apps/org.tizen.stt-engine-embedded/shared/res
+export word_ins_penalty=1.0
+export delta_conf=/usr/apps/org.tizen.stt-engine-embedded/shared/res/model/conf/delta.conf
+export MANAGERPID=610
+export JOURNAL_STREAM=7:22973
+export EINA_LOG_ABORT=1
+export XDG_RUNTIME_DIR=/run/user/5001
+export ELM_DISPLAY=wl
+export GTK_IM_MODULE=scim
+export LISTEN_FDS=1
+export G_BROKEN_FILENAMES=1
+export ELM_PROFILE=mobile
+export work_dir=/usr/apps/org.tizen.stt-engine-embedded/shared/res/model
+export _=/usr/bin/launchpad-process-pool
diff --git a/misc/setup_device.sh b/misc/setup_device.sh
new file mode 100755 (executable)
index 0000000..a102690
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+sdb forward --remove-all
+sdb forward tcp:50051 tcp:50051
+sdb shell "su - owner -c 'systemctl --user start aurum-bootstrap'"
+sdb shell "ps -ef | grep aurum-bootstrap"
diff --git a/packaging/aurum.manifest b/packaging/aurum.manifest
new file mode 100644 (file)
index 0000000..96a1325
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+    <request>
+        <domain name="_"/>
+    </request>
+</manifest>
\ No newline at end of file
diff --git a/packaging/aurum.spec b/packaging/aurum.spec
new file mode 100644 (file)
index 0000000..ad92cb1
--- /dev/null
@@ -0,0 +1,108 @@
+Name:           aurum
+Version:        0.1.0
+Release:        0
+License:        Apache-2.0
+Summary:        Automation framework for Ui testing
+Group:          UI Framework
+Source:         %{name}-%{version}.tar.gz
+Source1001:     %{name}.manifest
+
+BuildRequires:  meson
+BuildRequires:  pkgconfig(grpc)
+BuildRequires:  pkgconfig(grpc++)
+BuildRequires:  pkgconfig(atspi-2)
+BuildRequires:  pkgconfig(capi-system-info)
+BuildRequires:  pkgconfig(capi-ui-efl-util)
+BuildRequires:  pkgconfig(dlog)
+BuildRequires:  pkgconfig(elementary)
+BuildRequires:  gtest-devel
+
+BuildRequires: pkgconfig(aul)
+BuildRequires: pkgconfig(capi-appfw-package-manager)
+BuildRequires: pkgconfig(capi-appfw-app-control)
+BuildRequires: pkgconfig(capi-appfw-app-manager)
+
+Requires:  pkgconfig(atspi-2)
+Requires:  pkgconfig(dlog)
+
+%description
+aurum is a project for testing ui.
+it provides interfaces through gRPC protocol.
+
+%package devel
+Summary: devel package for libaurum
+
+%description devel
+devel package for libaurum
+
+%package bootstrap
+Summary: bootstrap
+License: Apache-2.0
+Requires: %{name} = %{version}-%{release}
+Requires: pkgconfig(grpc)
+Requires: pkgconfig(grpc++)
+
+%description bootstrap
+gRPC Server
+
+%prep
+%setup -q
+cp %{SOURCE1001} .
+
+meson \
+    --prefix /usr \
+    --libdir %{_libdir} \
+    -Dcpp_std=c++17 \
+    -Dtizen=true \
+    gbsbuild 2>&1
+
+%build
+
+ninja \
+    -C gbsbuild \
+    -j %(echo "`/usr/bin/getconf _NPROCESSORS_ONLN`") \
+    -v \
+    all
+
+ninja \
+    -C gbsbuild \
+    -j %(echo "`/usr/bin/getconf _NPROCESSORS_ONLN`") \
+    -v \
+    test
+
+%install
+
+export DESTDIR=%{buildroot}
+ninja -C gbsbuild install
+
+%post
+sbin/ldconfig
+
+%postun 
+sbin/ldconfig
+
+%post bootstrap
+#/sbin/ldconfig
+chsmack -e "User" %{_bindir}/bootstrap_server
+
+%postun bootstrap
+/sbin/ldconfig
+
+%files
+%manifest %{name}.manifest
+%defattr(-,root,root)
+%license COPYING
+%{_libdir}/libaurum.so.*
+
+%files devel
+%manifest %{name}.manifest
+%defattr(-,root,root)
+%license COPYING
+%{_libdir}/libaurum.so
+
+%files bootstrap
+%manifest %{name}.manifest
+%defattr(-,root,root)
+%license COPYING
+%{_bindir}/aurum_bootstrap
+%{_unitdir_user}/aurum-bootstrap.service
diff --git a/protocol/aurum.proto b/protocol/aurum.proto
new file mode 100644 (file)
index 0000000..2e63e8b
--- /dev/null
@@ -0,0 +1,301 @@
+syntax = "proto3";
+package aurum;
+
+service Bootstrap {
+   rpc sync(ReqEmpty) returns (RspEmpty) {}
+   rpc killServer(ReqEmpty) returns (RspEmpty) {}
+
+   rpc findElement(ReqFindElement) returns (RspFindElement) {}
+
+   rpc getValue(ReqGetValue) returns (RspGetValue) {}
+   rpc setValue(ReqSetValue) returns (RspSetValue) {}
+   rpc getSize(ReqGetSize) returns (RspGetSize) {}
+   rpc clear(ReqClear) returns (RspClear) {}
+   rpc getAttribute(ReqGetAttribute) returns (RspGetAttribute) {}
+
+   rpc click(ReqClick) returns (RspClick) {}
+   rpc longClick(ReqClick) returns (RspClick) {}
+   rpc flick(ReqFlick) returns (RspFlick) {}
+
+   rpc touchDown(ReqTouchDown) returns (RspTouchDown) {}
+   rpc touchMove(ReqTouchMove) returns (RspTouchMove) {}
+   rpc touchUp(ReqTouchUp) returns (RspTouchUp) {}
+
+   rpc installApp(stream ReqInstallApp) returns (RspInstallApp) {}
+   rpc removeApp(ReqRemoveApp) returns (RspRemoveApp) {}
+   rpc getAppInfo(ReqGetAppInfo) returns (RspGetAppInfo) {}
+   rpc launchApp(ReqLaunchApp) returns (RspLaunchApp) {}
+   rpc closeApp(ReqCloseApp) returns (RspCloseApp) {}
+
+   rpc getDeviceTime(ReqGetDeviceTime) returns (RspGetDeviceTime) {}
+   rpc getLocation(ReqGetLocation) returns (RspGetLocation) {}
+   rpc sendKey(ReqKey) returns (RspKey) {}
+}
+
+// ------------------------------------ //
+
+enum RspStatus {
+   OK = 0;
+   NA = 1;
+   ERROR = 2;
+}
+
+enum ParamType {
+   STRING = 0;
+   INT = 1;
+   DOUBLE = 2;
+   BOOL = 3;
+}
+
+message Element {
+   string elementId = 1;
+}
+
+message Point {
+   int32 x = 1;
+   int32 y = 2;
+}
+
+message Rect {
+   int32 x = 1;
+   int32 y = 2;
+   int32 width = 3;
+   int32 height = 4;
+}
+
+// ------------------------------------ //
+
+message ReqFindElement {
+   enum RequestType {
+      AUTOMATIONID = 0;
+      TEXT = 1;
+      TYPE = 2;
+      STYLE = 3;
+   }
+   string elementId = 1;
+   RequestType strategy = 2;
+   oneof params {
+         string automationId = 3;
+         string textField = 4;
+         string widgetType = 5;
+         string widgetStyle = 6;
+   }
+}
+message RspFindElement {
+   RspStatus status = 1;
+   repeated Element elements = 2;
+}
+
+// ------------------------------------ //
+
+message ReqGetValue {
+   string elementId = 1;
+}
+message RspGetValue {
+   RspStatus status = 1;
+   ParamType type  = 2;
+   oneof params {
+      string stringValue = 3;
+      int32 intValue = 4;
+      double doubleValue = 5;
+      double boolValue = 6;
+   }
+}
+
+message ReqSetValue {
+   string elementId = 1;
+   ParamType type  = 2;
+   oneof params {
+      string stringValue = 3;
+      int32 intValue = 4;
+      double doubleValue = 5;
+      double boolValue = 6;
+   }
+}
+message RspSetValue {
+   RspStatus status = 1;
+}
+
+message ReqGetSize{
+   string elementId = 1;
+}
+message RspGetSize{
+   RspStatus status = 1;
+   Rect size = 2;
+}
+
+message ReqClear{
+   string elementId = 1;
+}
+message RspClear{
+   RspStatus status = 1;
+}
+
+message ReqGetAttribute {
+   enum RequestType {
+      VISIBLE     = 0;
+      FOCUSABLE   = 1;
+      FOCUSED     = 2;
+      ENABLED     = 3;
+      CLICKABLE   = 4;
+      SCROLLABLE  = 5;
+      CHECKABLE   = 6;
+      CHECKED     = 7;
+      SELECTED    = 8;
+      SELECTABLE  = 9;
+   }
+   string elementId = 1;
+   RequestType attribute = 2;
+}
+message RspGetAttribute {
+   RspStatus status = 1;
+   ParamType type = 2;
+   oneof params {
+      string stringValue = 3;
+      int32 intValue = 4;
+      double doubleValue = 5;
+      double boolValue = 6;
+   }
+}
+
+// ------------------------------------ //
+
+message ReqClick{
+   enum RequestType {
+      ELEMENTID = 0;
+      COORD = 1;
+      ATSPI = 2;
+   }
+   RequestType type  = 1;
+   oneof params {
+      string elementId = 2;
+      Point coordination = 3;
+   }
+}
+message RspClick{
+   RspStatus status = 1;
+}
+
+message ReqFlick{
+   Point startPoint = 1;
+   Point endPoint = 2;
+   int32 durationMs = 3;
+}
+message RspFlick{
+   RspStatus status = 1;
+}
+
+message ReqTouchDown{
+   Point coordination = 1;
+}
+message RspTouchDown{
+   RspStatus status = 1;
+   int32 seqId = 2;
+}
+
+message ReqTouchMove{
+   int32 seqId = 1;
+   Point coordination = 2;
+}
+message RspTouchMove{
+   RspStatus status = 1;
+}
+
+message ReqTouchUp{
+   int32 seqId = 1;
+   Point coordination = 2;
+}
+message RspTouchUp{
+   RspStatus status = 1;
+}
+
+// ------------------------------------ //
+
+message ReqInstallApp{
+   bytes package = 1;
+}
+message RspInstallApp{
+   RspStatus status = 1;
+}
+
+message ReqRemoveApp{
+   string packageName = 1;
+}
+message RspRemoveApp{
+   RspStatus status = 1;
+}
+
+message ReqGetAppInfo{
+   string packageName = 1;
+}
+message RspGetAppInfo {
+   RspStatus status = 1;
+   bool isInstalled = 2;
+   bool isRunning = 3;
+   bool isFocused = 4;
+}
+
+message ReqLaunchApp{
+   string packageName = 1;
+}
+message RspLaunchApp{
+   RspStatus status = 1;
+}
+
+message ReqCloseApp{
+   string packageName = 1;
+}
+message RspCloseApp{
+   RspStatus status = 1;
+}
+
+// ------------------------------------ //
+
+message ReqGetDeviceTime{
+}
+message RspGetDeviceTime{
+   RspStatus status = 1;
+}
+
+message ReqGetLocation{
+}
+message RspGetLocation{
+   RspStatus status = 1;
+   string alt = 2;
+   string lat = 3;
+}
+
+message ReqKey{
+   enum KeyType{
+      BACK = 0;
+      MENU = 1;
+      HOME = 2;
+      VOLUP = 3;
+      VOLDOWN = 4;
+      POWER = 5;
+      KEY = 6;
+      XF86 = 7;
+   }
+   enum KeyActionType{
+      PRESS = 0;
+      RELEASE = 1;
+      STROKE = 2;
+      LONG_STROKE = 3;
+   }
+   KeyType type = 1;
+   KeyActionType actionType = 2;
+   oneof keys {
+      uint32 keyCode = 3;
+      string XF86keyCode = 4;
+   }
+}
+message RspKey{
+   RspStatus status = 1;
+}
+
+// ------------------------------------ //
+
+message ReqEmpty {
+}
+message RspEmpty {
+}
diff --git a/protocol/empty.cpp b/protocol/empty.cpp
new file mode 100644 (file)
index 0000000..ce8a40f
--- /dev/null
@@ -0,0 +1,3 @@
+int main(){
+    return 0;
+}
\ No newline at end of file
diff --git a/protocol/examples/node/README b/protocol/examples/node/README
new file mode 100644 (file)
index 0000000..529ca8c
--- /dev/null
@@ -0,0 +1,8 @@
+```sh
+npm install
+npm install -g grpc-tools
+
+grpc_tools_node_protoc --js_out=import_style=commonjs,binary:./ --grpc_out=./ --plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin` ../../aurum.proto
+
+grpc_tools_node_protoc --js_out=import_style=commonjs,binary:../node/static_codegen/route_guide/ --grpc_out=../node/static_codegen/route_guide/ --plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin` aurum.proto
+```
diff --git a/protocol/examples/node/client.js b/protocol/examples/node/client.js
new file mode 100644 (file)
index 0000000..b368488
--- /dev/null
@@ -0,0 +1,41 @@
+
+var messages = require('./aurum_pb');
+var services = require('./aurum_grpc_pb');
+
+var grpc = require('grpc');
+
+function main() {
+  var client = new services.BootstrapClient('localhost:50051',
+                                          grpc.credentials.createInsecure());
+
+  var request = new messages.ReqFindElement();
+
+  client.sync(new messages.ReqEmpty(), function(err, res){console.log(res)});
+
+  request.setStrategy(proto.aurum.ReqFindElement.RequestType.TEXT);
+  request.setTextfield("DONE");
+
+  client.findElement(request, function(err, response) {
+    if (err) {
+      console.log(err);
+      return;
+    }
+    var returnList = response.getElementsList();
+    console.log(returnList);
+    if (returnList.length > 0)
+    {
+      var request2 = new messages.ReqClick();
+      request2.setType(proto.aurum.ReqClick.RequestType.ELEMENTID)
+      request2.setElementid(returnList[0].getElementid());
+      client.click(request2, function(err, response2) {
+        if (err) {
+          console.log(err);
+          return;
+        }
+        console.log(response2);
+      });
+    }
+  });
+}
+
+main();
diff --git a/protocol/examples/node/gen.sh b/protocol/examples/node/gen.sh
new file mode 100755 (executable)
index 0000000..207f23c
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+grpc_tools_node_protoc --js_out=import_style=commonjs,binary:./ \
+  --grpc_out=./ --plugin=protoc-gen-grpc=`which grpc_tools_node_protoc_plugin`\
+  -I ./../../ ../../aurum.proto
+
diff --git a/protocol/examples/node/package-lock.json b/protocol/examples/node/package-lock.json
new file mode 100644 (file)
index 0000000..e2e4041
--- /dev/null
@@ -0,0 +1,3791 @@
+{
+  "name": "ua-node-example",
+  "version": "0.1.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@grpc/proto-loader": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.1.0.tgz",
+      "integrity": "sha512-GHoZBR5N+65AWazsCiJDxeMSbUpp2qNYbjeD3mP27L2PT25+GvIupIIJiCgULH87NAiAd9SmWMRBVz+uSiHpgA==",
+      "requires": {
+        "@types/lodash": "^4.14.104",
+        "@types/node": "^9.4.6",
+        "lodash": "^4.17.5",
+        "protobufjs": "^6.8.6"
+      }
+    },
+    "@protobufjs/aspromise": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+      "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78="
+    },
+    "@protobufjs/base64": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+      "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+    },
+    "@protobufjs/codegen": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+      "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+    },
+    "@protobufjs/eventemitter": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+      "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A="
+    },
+    "@protobufjs/fetch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+      "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
+      "requires": {
+        "@protobufjs/aspromise": "^1.1.1",
+        "@protobufjs/inquire": "^1.1.0"
+      }
+    },
+    "@protobufjs/float": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+      "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E="
+    },
+    "@protobufjs/inquire": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+      "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik="
+    },
+    "@protobufjs/path": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+      "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0="
+    },
+    "@protobufjs/pool": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+      "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q="
+    },
+    "@protobufjs/utf8": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+      "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
+    },
+    "@types/bytebuffer": {
+      "version": "5.0.40",
+      "resolved": "https://registry.npmjs.org/@types/bytebuffer/-/bytebuffer-5.0.40.tgz",
+      "integrity": "sha512-h48dyzZrPMz25K6Q4+NCwWaxwXany2FhQg/ErOcdZS1ZpsaDnDMZg8JYLMTGz7uvXKrcKGJUZJlZObyfgdaN9g==",
+      "requires": {
+        "@types/long": "*",
+        "@types/node": "*"
+      }
+    },
+    "@types/lodash": {
+      "version": "4.14.138",
+      "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.138.tgz",
+      "integrity": "sha512-A4uJgHz4hakwNBdHNPdxOTkYmXNgmUAKLbXZ7PKGslgeV0Mb8P3BlbYfPovExek1qnod4pDfRbxuzcVs3dlFLg=="
+    },
+    "@types/long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-1w52Nyx4Gq47uuu0EVcsHBxZFJgurQ+rTKS3qMHxR1GY2T8c2AJYd6vZoZ9q1rupaDjU0yT+Jc2XTyXkjeMA+Q=="
+    },
+    "@types/node": {
+      "version": "9.6.51",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-9.6.51.tgz",
+      "integrity": "sha512-5lhC7QM2J3b/+epdwaNfRuG2peN4c9EX+mkd27+SqLKhJSdswHTZvc4aZLBZChi+Wo32+E1DeMZs0fSpu/uBXQ=="
+    },
+    "ansi-regex": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
+      "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
+    },
+    "ascli": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/ascli/-/ascli-1.0.1.tgz",
+      "integrity": "sha1-vPpZdKYvGOgcq660lzKrSoj5Brw=",
+      "requires": {
+        "colour": "~0.7.1",
+        "optjs": "~3.2.2"
+      }
+    },
+    "async": {
+      "version": "1.5.2",
+      "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
+      "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
+    },
+    "bytebuffer": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/bytebuffer/-/bytebuffer-5.0.1.tgz",
+      "integrity": "sha1-WC7qSxqHO20CCkjVjfhfC7ps/d0=",
+      "requires": {
+        "long": "~3"
+      },
+      "dependencies": {
+        "long": {
+          "version": "3.2.0",
+          "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
+          "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s="
+        }
+      }
+    },
+    "camelcase": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
+      "integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
+    },
+    "cliui": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
+      "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
+      "requires": {
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1",
+        "wrap-ansi": "^2.0.0"
+      }
+    },
+    "code-point-at": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
+      "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
+    },
+    "colour": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/colour/-/colour-0.7.1.tgz",
+      "integrity": "sha1-nLFpkX7F0SwHNtPoaFdG3xyt93g="
+    },
+    "decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
+    },
+    "google-protobuf": {
+      "version": "3.9.1",
+      "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.9.1.tgz",
+      "integrity": "sha512-tkz7SVwBktFbqFK3teXFUY/VM57+mbUgV9bSD+sZH1ocHJ7uk7BfEWMRdU24dd0ciUDokreA7ghH2fYFIczQdw=="
+    },
+    "grpc": {
+      "version": "1.23.3",
+      "resolved": "https://registry.npmjs.org/grpc/-/grpc-1.23.3.tgz",
+      "integrity": "sha512-7vdzxPw9s5UYch4aUn4hyM5tMaouaxUUkwkgJlwbR4AXMxiYZJOv19N2ps2eKiuUbJovo5fnGF9hg/X91gWYjw==",
+      "requires": {
+        "@types/bytebuffer": "^5.0.40",
+        "lodash.camelcase": "^4.3.0",
+        "lodash.clone": "^4.5.0",
+        "nan": "^2.13.2",
+        "node-pre-gyp": "^0.13.0",
+        "protobufjs": "^5.0.3"
+      },
+      "dependencies": {
+        "abbrev": {
+          "version": "1.1.1",
+          "bundled": true
+        },
+        "ansi-regex": {
+          "version": "2.1.1",
+          "bundled": true
+        },
+        "aproba": {
+          "version": "1.2.0",
+          "bundled": true
+        },
+        "are-we-there-yet": {
+          "version": "1.1.5",
+          "bundled": true,
+          "requires": {
+            "delegates": "^1.0.0",
+            "readable-stream": "^2.0.6"
+          }
+        },
+        "balanced-match": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "brace-expansion": {
+          "version": "1.1.11",
+          "bundled": true,
+          "requires": {
+            "balanced-match": "^1.0.0",
+            "concat-map": "0.0.1"
+          }
+        },
+        "chownr": {
+          "version": "1.1.2",
+          "bundled": true
+        },
+        "code-point-at": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "concat-map": {
+          "version": "0.0.1",
+          "bundled": true
+        },
+        "console-control-strings": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "core-util-is": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "debug": {
+          "version": "3.2.6",
+          "bundled": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "deep-extend": {
+          "version": "0.6.0",
+          "bundled": true
+        },
+        "delegates": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "detect-libc": {
+          "version": "1.0.3",
+          "bundled": true
+        },
+        "fs-minipass": {
+          "version": "1.2.6",
+          "bundled": true,
+          "requires": {
+            "minipass": "^2.2.1"
+          }
+        },
+        "fs.realpath": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "gauge": {
+          "version": "2.7.4",
+          "bundled": true,
+          "requires": {
+            "aproba": "^1.0.3",
+            "console-control-strings": "^1.0.0",
+            "has-unicode": "^2.0.0",
+            "object-assign": "^4.1.0",
+            "signal-exit": "^3.0.0",
+            "string-width": "^1.0.1",
+            "strip-ansi": "^3.0.1",
+            "wide-align": "^1.1.0"
+          }
+        },
+        "glob": {
+          "version": "7.1.4",
+          "bundled": true,
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.0.4",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "has-unicode": {
+          "version": "2.0.1",
+          "bundled": true
+        },
+        "iconv-lite": {
+          "version": "0.4.24",
+          "bundled": true,
+          "requires": {
+            "safer-buffer": ">= 2.1.2 < 3"
+          }
+        },
+        "ignore-walk": {
+          "version": "3.0.1",
+          "bundled": true,
+          "requires": {
+            "minimatch": "^3.0.4"
+          }
+        },
+        "inflight": {
+          "version": "1.0.6",
+          "bundled": true,
+          "requires": {
+            "once": "^1.3.0",
+            "wrappy": "1"
+          }
+        },
+        "inherits": {
+          "version": "2.0.4",
+          "bundled": true
+        },
+        "ini": {
+          "version": "1.3.5",
+          "bundled": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "1.0.0",
+          "bundled": true,
+          "requires": {
+            "number-is-nan": "^1.0.0"
+          }
+        },
+        "isarray": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "minimatch": {
+          "version": "3.0.4",
+          "bundled": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        },
+        "minimist": {
+          "version": "1.2.0",
+          "bundled": true
+        },
+        "minipass": {
+          "version": "2.3.5",
+          "bundled": true,
+          "requires": {
+            "safe-buffer": "^5.1.2",
+            "yallist": "^3.0.0"
+          }
+        },
+        "minizlib": {
+          "version": "1.2.1",
+          "bundled": true,
+          "requires": {
+            "minipass": "^2.2.1"
+          }
+        },
+        "mkdirp": {
+          "version": "0.5.1",
+          "bundled": true,
+          "requires": {
+            "minimist": "0.0.8"
+          },
+          "dependencies": {
+            "minimist": {
+              "version": "0.0.8",
+              "bundled": true
+            }
+          }
+        },
+        "ms": {
+          "version": "2.1.2",
+          "bundled": true
+        },
+        "needle": {
+          "version": "2.4.0",
+          "bundled": true,
+          "requires": {
+            "debug": "^3.2.6",
+            "iconv-lite": "^0.4.4",
+            "sax": "^1.2.4"
+          }
+        },
+        "node-pre-gyp": {
+          "version": "0.13.0",
+          "bundled": true,
+          "requires": {
+            "detect-libc": "^1.0.2",
+            "mkdirp": "^0.5.1",
+            "needle": "^2.2.1",
+            "nopt": "^4.0.1",
+            "npm-packlist": "^1.1.6",
+            "npmlog": "^4.0.2",
+            "rc": "^1.2.7",
+            "rimraf": "^2.6.1",
+            "semver": "^5.3.0",
+            "tar": "^4"
+          }
+        },
+        "nopt": {
+          "version": "4.0.1",
+          "bundled": true,
+          "requires": {
+            "abbrev": "1",
+            "osenv": "^0.1.4"
+          }
+        },
+        "npm-bundled": {
+          "version": "1.0.6",
+          "bundled": true
+        },
+        "npm-packlist": {
+          "version": "1.4.4",
+          "bundled": true,
+          "requires": {
+            "ignore-walk": "^3.0.1",
+            "npm-bundled": "^1.0.1"
+          }
+        },
+        "npmlog": {
+          "version": "4.1.2",
+          "bundled": true,
+          "requires": {
+            "are-we-there-yet": "~1.1.2",
+            "console-control-strings": "~1.1.0",
+            "gauge": "~2.7.3",
+            "set-blocking": "~2.0.0"
+          }
+        },
+        "number-is-nan": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "object-assign": {
+          "version": "4.1.1",
+          "bundled": true
+        },
+        "once": {
+          "version": "1.4.0",
+          "bundled": true,
+          "requires": {
+            "wrappy": "1"
+          }
+        },
+        "os-homedir": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "os-tmpdir": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "osenv": {
+          "version": "0.1.5",
+          "bundled": true,
+          "requires": {
+            "os-homedir": "^1.0.0",
+            "os-tmpdir": "^1.0.0"
+          }
+        },
+        "path-is-absolute": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "process-nextick-args": {
+          "version": "2.0.1",
+          "bundled": true
+        },
+        "protobufjs": {
+          "version": "5.0.3",
+          "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-5.0.3.tgz",
+          "integrity": "sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA==",
+          "requires": {
+            "ascli": "~1",
+            "bytebuffer": "~5",
+            "glob": "^7.0.5",
+            "yargs": "^3.10.0"
+          }
+        },
+        "rc": {
+          "version": "1.2.8",
+          "bundled": true,
+          "requires": {
+            "deep-extend": "^0.6.0",
+            "ini": "~1.3.0",
+            "minimist": "^1.2.0",
+            "strip-json-comments": "~2.0.1"
+          }
+        },
+        "readable-stream": {
+          "version": "2.3.6",
+          "bundled": true,
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "rimraf": {
+          "version": "2.7.1",
+          "bundled": true,
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "bundled": true
+        },
+        "safer-buffer": {
+          "version": "2.1.2",
+          "bundled": true
+        },
+        "sax": {
+          "version": "1.2.4",
+          "bundled": true
+        },
+        "semver": {
+          "version": "5.7.1",
+          "bundled": true
+        },
+        "set-blocking": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "signal-exit": {
+          "version": "3.0.2",
+          "bundled": true
+        },
+        "string-width": {
+          "version": "1.0.2",
+          "bundled": true,
+          "requires": {
+            "code-point-at": "^1.0.0",
+            "is-fullwidth-code-point": "^1.0.0",
+            "strip-ansi": "^3.0.0"
+          }
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "bundled": true,
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        },
+        "strip-ansi": {
+          "version": "3.0.1",
+          "bundled": true,
+          "requires": {
+            "ansi-regex": "^2.0.0"
+          }
+        },
+        "strip-json-comments": {
+          "version": "2.0.1",
+          "bundled": true
+        },
+        "tar": {
+          "version": "4.4.10",
+          "bundled": true,
+          "requires": {
+            "chownr": "^1.1.1",
+            "fs-minipass": "^1.2.5",
+            "minipass": "^2.3.5",
+            "minizlib": "^1.2.1",
+            "mkdirp": "^0.5.0",
+            "safe-buffer": "^5.1.2",
+            "yallist": "^3.0.3"
+          }
+        },
+        "util-deprecate": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "wide-align": {
+          "version": "1.1.3",
+          "bundled": true,
+          "requires": {
+            "string-width": "^1.0.2 || 2"
+          }
+        },
+        "wrappy": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "yallist": {
+          "version": "3.0.3",
+          "bundled": true
+        }
+      }
+    },
+    "invert-kv": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
+      "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
+    },
+    "is-fullwidth-code-point": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
+      "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
+      "requires": {
+        "number-is-nan": "^1.0.0"
+      }
+    },
+    "lcid": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
+      "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
+      "requires": {
+        "invert-kv": "^1.0.0"
+      }
+    },
+    "lodash": {
+      "version": "4.17.15",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
+      "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
+    },
+    "lodash.camelcase": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+      "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
+    },
+    "lodash.clone": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.clone/-/lodash.clone-4.5.0.tgz",
+      "integrity": "sha1-GVhwRQ9aExkkeN9Lw9I9LeoZB7Y="
+    },
+    "long": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
+      "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
+    },
+    "minimist": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
+      "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
+    },
+    "nan": {
+      "version": "2.14.0",
+      "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz",
+      "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg=="
+    },
+    "npm": {
+      "version": "6.11.2",
+      "resolved": "https://registry.npmjs.org/npm/-/npm-6.11.2.tgz",
+      "integrity": "sha512-OAkXqI4bm5MUvqVvqe6rxCXmJqrln8VDlkdftpOoayHKazz8IOCJAiCuKmz0TchL224EAKeG86umuD6RYNpuEg==",
+      "requires": {
+        "JSONStream": "^1.3.5",
+        "abbrev": "~1.1.1",
+        "ansicolors": "~0.3.2",
+        "ansistyles": "~0.1.3",
+        "aproba": "^2.0.0",
+        "archy": "~1.0.0",
+        "bin-links": "^1.1.3",
+        "bluebird": "^3.5.5",
+        "byte-size": "^5.0.1",
+        "cacache": "^12.0.3",
+        "call-limit": "^1.1.1",
+        "chownr": "^1.1.2",
+        "ci-info": "^2.0.0",
+        "cli-columns": "^3.1.2",
+        "cli-table3": "^0.5.1",
+        "cmd-shim": "^3.0.3",
+        "columnify": "~1.5.4",
+        "config-chain": "^1.1.12",
+        "debuglog": "*",
+        "detect-indent": "~5.0.0",
+        "detect-newline": "^2.1.0",
+        "dezalgo": "~1.0.3",
+        "editor": "~1.0.0",
+        "figgy-pudding": "^3.5.1",
+        "find-npm-prefix": "^1.0.2",
+        "fs-vacuum": "~1.2.10",
+        "fs-write-stream-atomic": "~1.0.10",
+        "gentle-fs": "^2.2.1",
+        "glob": "^7.1.4",
+        "graceful-fs": "^4.2.2",
+        "has-unicode": "~2.0.1",
+        "hosted-git-info": "^2.8.2",
+        "iferr": "^1.0.2",
+        "imurmurhash": "*",
+        "infer-owner": "^1.0.4",
+        "inflight": "~1.0.6",
+        "inherits": "^2.0.4",
+        "ini": "^1.3.5",
+        "init-package-json": "^1.10.3",
+        "is-cidr": "^3.0.0",
+        "json-parse-better-errors": "^1.0.2",
+        "lazy-property": "~1.0.0",
+        "libcipm": "^4.0.3",
+        "libnpm": "^3.0.1",
+        "libnpmaccess": "^3.0.2",
+        "libnpmhook": "^5.0.3",
+        "libnpmorg": "^1.0.1",
+        "libnpmsearch": "^2.0.2",
+        "libnpmteam": "^1.0.2",
+        "libnpx": "^10.2.0",
+        "lock-verify": "^2.1.0",
+        "lockfile": "^1.0.4",
+        "lodash._baseindexof": "*",
+        "lodash._baseuniq": "~4.6.0",
+        "lodash._bindcallback": "*",
+        "lodash._cacheindexof": "*",
+        "lodash._createcache": "*",
+        "lodash._getnative": "*",
+        "lodash.clonedeep": "~4.5.0",
+        "lodash.restparam": "*",
+        "lodash.union": "~4.6.0",
+        "lodash.uniq": "~4.5.0",
+        "lodash.without": "~4.4.0",
+        "lru-cache": "^5.1.1",
+        "meant": "~1.0.1",
+        "mississippi": "^3.0.0",
+        "mkdirp": "~0.5.1",
+        "move-concurrently": "^1.0.1",
+        "node-gyp": "^5.0.3",
+        "nopt": "~4.0.1",
+        "normalize-package-data": "^2.5.0",
+        "npm-audit-report": "^1.3.2",
+        "npm-cache-filename": "~1.0.2",
+        "npm-install-checks": "~3.0.0",
+        "npm-lifecycle": "^3.1.3",
+        "npm-package-arg": "^6.1.1",
+        "npm-packlist": "^1.4.4",
+        "npm-pick-manifest": "^3.0.0",
+        "npm-profile": "^4.0.2",
+        "npm-registry-fetch": "^4.0.0",
+        "npm-user-validate": "~1.0.0",
+        "npmlog": "~4.1.2",
+        "once": "~1.4.0",
+        "opener": "^1.5.1",
+        "osenv": "^0.1.5",
+        "pacote": "^9.5.8",
+        "path-is-inside": "~1.0.2",
+        "promise-inflight": "~1.0.1",
+        "qrcode-terminal": "^0.12.0",
+        "query-string": "^6.8.2",
+        "qw": "~1.0.1",
+        "read": "~1.0.7",
+        "read-cmd-shim": "^1.0.3",
+        "read-installed": "~4.0.3",
+        "read-package-json": "^2.1.0",
+        "read-package-tree": "^5.3.1",
+        "readable-stream": "^3.4.0",
+        "readdir-scoped-modules": "^1.1.0",
+        "request": "^2.88.0",
+        "retry": "^0.12.0",
+        "rimraf": "^2.6.3",
+        "safe-buffer": "^5.1.2",
+        "semver": "^5.7.1",
+        "sha": "^3.0.0",
+        "slide": "~1.1.6",
+        "sorted-object": "~2.0.1",
+        "sorted-union-stream": "~2.1.3",
+        "ssri": "^6.0.1",
+        "stringify-package": "^1.0.0",
+        "tar": "^4.4.10",
+        "text-table": "~0.2.0",
+        "tiny-relative-date": "^1.3.0",
+        "uid-number": "0.0.6",
+        "umask": "~1.1.0",
+        "unique-filename": "^1.1.1",
+        "unpipe": "~1.0.0",
+        "update-notifier": "^2.5.0",
+        "uuid": "^3.3.2",
+        "validate-npm-package-license": "^3.0.4",
+        "validate-npm-package-name": "~3.0.0",
+        "which": "^1.3.1",
+        "worker-farm": "^1.7.0",
+        "write-file-atomic": "^2.4.3"
+      },
+      "dependencies": {
+        "JSONStream": {
+          "version": "1.3.5",
+          "bundled": true,
+          "requires": {
+            "jsonparse": "^1.2.0",
+            "through": ">=2.2.7 <3"
+          }
+        },
+        "abbrev": {
+          "version": "1.1.1",
+          "bundled": true
+        },
+        "agent-base": {
+          "version": "4.3.0",
+          "bundled": true,
+          "requires": {
+            "es6-promisify": "^5.0.0"
+          }
+        },
+        "agentkeepalive": {
+          "version": "3.5.2",
+          "bundled": true,
+          "requires": {
+            "humanize-ms": "^1.2.1"
+          }
+        },
+        "ajv": {
+          "version": "5.5.2",
+          "bundled": true,
+          "requires": {
+            "co": "^4.6.0",
+            "fast-deep-equal": "^1.0.0",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.3.0"
+          }
+        },
+        "ansi-align": {
+          "version": "2.0.0",
+          "bundled": true,
+          "requires": {
+            "string-width": "^2.0.0"
+          }
+        },
+        "ansi-regex": {
+          "version": "2.1.1",
+          "bundled": true
+        },
+        "ansi-styles": {
+          "version": "3.2.1",
+          "bundled": true,
+          "requires": {
+            "color-convert": "^1.9.0"
+          }
+        },
+        "ansicolors": {
+          "version": "0.3.2",
+          "bundled": true
+        },
+        "ansistyles": {
+          "version": "0.1.3",
+          "bundled": true
+        },
+        "aproba": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "archy": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "are-we-there-yet": {
+          "version": "1.1.4",
+          "bundled": true,
+          "requires": {
+            "delegates": "^1.0.0",
+            "readable-stream": "^2.0.6"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.3",
+                "isarray": "~1.0.0",
+                "process-nextick-args": "~2.0.0",
+                "safe-buffer": "~5.1.1",
+                "string_decoder": "~1.1.1",
+                "util-deprecate": "~1.0.1"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "~5.1.0"
+              }
+            }
+          }
+        },
+        "asap": {
+          "version": "2.0.6",
+          "bundled": true
+        },
+        "asn1": {
+          "version": "0.2.4",
+          "bundled": true,
+          "requires": {
+            "safer-buffer": "~2.1.0"
+          }
+        },
+        "assert-plus": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "asynckit": {
+          "version": "0.4.0",
+          "bundled": true
+        },
+        "aws-sign2": {
+          "version": "0.7.0",
+          "bundled": true
+        },
+        "aws4": {
+          "version": "1.8.0",
+          "bundled": true
+        },
+        "balanced-match": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "bcrypt-pbkdf": {
+          "version": "1.0.2",
+          "bundled": true,
+          "optional": true,
+          "requires": {
+            "tweetnacl": "^0.14.3"
+          }
+        },
+        "bin-links": {
+          "version": "1.1.3",
+          "bundled": true,
+          "requires": {
+            "bluebird": "^3.5.3",
+            "cmd-shim": "^3.0.0",
+            "gentle-fs": "^2.0.1",
+            "graceful-fs": "^4.1.15",
+            "write-file-atomic": "^2.3.0"
+          }
+        },
+        "bluebird": {
+          "version": "3.5.5",
+          "bundled": true
+        },
+        "boxen": {
+          "version": "1.3.0",
+          "bundled": true,
+          "requires": {
+            "ansi-align": "^2.0.0",
+            "camelcase": "^4.0.0",
+            "chalk": "^2.0.1",
+            "cli-boxes": "^1.0.0",
+            "string-width": "^2.0.0",
+            "term-size": "^1.2.0",
+            "widest-line": "^2.0.0"
+          }
+        },
+        "brace-expansion": {
+          "version": "1.1.11",
+          "bundled": true,
+          "requires": {
+            "balanced-match": "^1.0.0",
+            "concat-map": "0.0.1"
+          }
+        },
+        "buffer-from": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "builtins": {
+          "version": "1.0.3",
+          "bundled": true
+        },
+        "byline": {
+          "version": "5.0.0",
+          "bundled": true
+        },
+        "byte-size": {
+          "version": "5.0.1",
+          "bundled": true
+        },
+        "cacache": {
+          "version": "12.0.3",
+          "bundled": true,
+          "requires": {
+            "bluebird": "^3.5.5",
+            "chownr": "^1.1.1",
+            "figgy-pudding": "^3.5.1",
+            "glob": "^7.1.4",
+            "graceful-fs": "^4.1.15",
+            "infer-owner": "^1.0.3",
+            "lru-cache": "^5.1.1",
+            "mississippi": "^3.0.0",
+            "mkdirp": "^0.5.1",
+            "move-concurrently": "^1.0.1",
+            "promise-inflight": "^1.0.1",
+            "rimraf": "^2.6.3",
+            "ssri": "^6.0.1",
+            "unique-filename": "^1.1.1",
+            "y18n": "^4.0.0"
+          }
+        },
+        "call-limit": {
+          "version": "1.1.1",
+          "bundled": true
+        },
+        "camelcase": {
+          "version": "4.1.0",
+          "bundled": true
+        },
+        "capture-stack-trace": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "caseless": {
+          "version": "0.12.0",
+          "bundled": true
+        },
+        "chalk": {
+          "version": "2.4.1",
+          "bundled": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
+        "chownr": {
+          "version": "1.1.2",
+          "bundled": true
+        },
+        "ci-info": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "cidr-regex": {
+          "version": "2.0.10",
+          "bundled": true,
+          "requires": {
+            "ip-regex": "^2.1.0"
+          }
+        },
+        "cli-boxes": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "cli-columns": {
+          "version": "3.1.2",
+          "bundled": true,
+          "requires": {
+            "string-width": "^2.0.0",
+            "strip-ansi": "^3.0.1"
+          }
+        },
+        "cli-table3": {
+          "version": "0.5.1",
+          "bundled": true,
+          "requires": {
+            "colors": "^1.1.2",
+            "object-assign": "^4.1.0",
+            "string-width": "^2.1.1"
+          }
+        },
+        "cliui": {
+          "version": "4.1.0",
+          "bundled": true,
+          "requires": {
+            "string-width": "^2.1.1",
+            "strip-ansi": "^4.0.0",
+            "wrap-ansi": "^2.0.0"
+          },
+          "dependencies": {
+            "ansi-regex": {
+              "version": "3.0.0",
+              "bundled": true
+            },
+            "strip-ansi": {
+              "version": "4.0.0",
+              "bundled": true,
+              "requires": {
+                "ansi-regex": "^3.0.0"
+              }
+            }
+          }
+        },
+        "clone": {
+          "version": "1.0.4",
+          "bundled": true
+        },
+        "cmd-shim": {
+          "version": "3.0.3",
+          "bundled": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "mkdirp": "~0.5.0"
+          }
+        },
+        "co": {
+          "version": "4.6.0",
+          "bundled": true
+        },
+        "code-point-at": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "color-convert": {
+          "version": "1.9.1",
+          "bundled": true,
+          "requires": {
+            "color-name": "^1.1.1"
+          }
+        },
+        "color-name": {
+          "version": "1.1.3",
+          "bundled": true
+        },
+        "colors": {
+          "version": "1.3.3",
+          "bundled": true,
+          "optional": true
+        },
+        "columnify": {
+          "version": "1.5.4",
+          "bundled": true,
+          "requires": {
+            "strip-ansi": "^3.0.0",
+            "wcwidth": "^1.0.0"
+          }
+        },
+        "combined-stream": {
+          "version": "1.0.6",
+          "bundled": true,
+          "requires": {
+            "delayed-stream": "~1.0.0"
+          }
+        },
+        "concat-map": {
+          "version": "0.0.1",
+          "bundled": true
+        },
+        "concat-stream": {
+          "version": "1.6.2",
+          "bundled": true,
+          "requires": {
+            "buffer-from": "^1.0.0",
+            "inherits": "^2.0.3",
+            "readable-stream": "^2.2.2",
+            "typedarray": "^0.0.6"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.3",
+                "isarray": "~1.0.0",
+                "process-nextick-args": "~2.0.0",
+                "safe-buffer": "~5.1.1",
+                "string_decoder": "~1.1.1",
+                "util-deprecate": "~1.0.1"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "~5.1.0"
+              }
+            }
+          }
+        },
+        "config-chain": {
+          "version": "1.1.12",
+          "bundled": true,
+          "requires": {
+            "ini": "^1.3.4",
+            "proto-list": "~1.2.1"
+          }
+        },
+        "configstore": {
+          "version": "3.1.2",
+          "bundled": true,
+          "requires": {
+            "dot-prop": "^4.1.0",
+            "graceful-fs": "^4.1.2",
+            "make-dir": "^1.0.0",
+            "unique-string": "^1.0.0",
+            "write-file-atomic": "^2.0.0",
+            "xdg-basedir": "^3.0.0"
+          }
+        },
+        "console-control-strings": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "copy-concurrently": {
+          "version": "1.0.5",
+          "bundled": true,
+          "requires": {
+            "aproba": "^1.1.1",
+            "fs-write-stream-atomic": "^1.0.8",
+            "iferr": "^0.1.5",
+            "mkdirp": "^0.5.1",
+            "rimraf": "^2.5.4",
+            "run-queue": "^1.0.0"
+          },
+          "dependencies": {
+            "aproba": {
+              "version": "1.2.0",
+              "bundled": true
+            },
+            "iferr": {
+              "version": "0.1.5",
+              "bundled": true
+            }
+          }
+        },
+        "core-util-is": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "create-error-class": {
+          "version": "3.0.2",
+          "bundled": true,
+          "requires": {
+            "capture-stack-trace": "^1.0.0"
+          }
+        },
+        "cross-spawn": {
+          "version": "5.1.0",
+          "bundled": true,
+          "requires": {
+            "lru-cache": "^4.0.1",
+            "shebang-command": "^1.2.0",
+            "which": "^1.2.9"
+          },
+          "dependencies": {
+            "lru-cache": {
+              "version": "4.1.5",
+              "bundled": true,
+              "requires": {
+                "pseudomap": "^1.0.2",
+                "yallist": "^2.1.2"
+              }
+            },
+            "yallist": {
+              "version": "2.1.2",
+              "bundled": true
+            }
+          }
+        },
+        "crypto-random-string": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "cyclist": {
+          "version": "0.2.2",
+          "bundled": true
+        },
+        "dashdash": {
+          "version": "1.14.1",
+          "bundled": true,
+          "requires": {
+            "assert-plus": "^1.0.0"
+          }
+        },
+        "debug": {
+          "version": "3.1.0",
+          "bundled": true,
+          "requires": {
+            "ms": "2.0.0"
+          },
+          "dependencies": {
+            "ms": {
+              "version": "2.0.0",
+              "bundled": true
+            }
+          }
+        },
+        "debuglog": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "decamelize": {
+          "version": "1.2.0",
+          "bundled": true
+        },
+        "decode-uri-component": {
+          "version": "0.2.0",
+          "bundled": true
+        },
+        "deep-extend": {
+          "version": "0.5.1",
+          "bundled": true
+        },
+        "defaults": {
+          "version": "1.0.3",
+          "bundled": true,
+          "requires": {
+            "clone": "^1.0.2"
+          }
+        },
+        "define-properties": {
+          "version": "1.1.3",
+          "bundled": true,
+          "requires": {
+            "object-keys": "^1.0.12"
+          }
+        },
+        "delayed-stream": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "delegates": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "detect-indent": {
+          "version": "5.0.0",
+          "bundled": true
+        },
+        "detect-newline": {
+          "version": "2.1.0",
+          "bundled": true
+        },
+        "dezalgo": {
+          "version": "1.0.3",
+          "bundled": true,
+          "requires": {
+            "asap": "^2.0.0",
+            "wrappy": "1"
+          }
+        },
+        "dot-prop": {
+          "version": "4.2.0",
+          "bundled": true,
+          "requires": {
+            "is-obj": "^1.0.0"
+          }
+        },
+        "dotenv": {
+          "version": "5.0.1",
+          "bundled": true
+        },
+        "duplexer3": {
+          "version": "0.1.4",
+          "bundled": true
+        },
+        "duplexify": {
+          "version": "3.6.0",
+          "bundled": true,
+          "requires": {
+            "end-of-stream": "^1.0.0",
+            "inherits": "^2.0.1",
+            "readable-stream": "^2.0.0",
+            "stream-shift": "^1.0.0"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.3",
+                "isarray": "~1.0.0",
+                "process-nextick-args": "~2.0.0",
+                "safe-buffer": "~5.1.1",
+                "string_decoder": "~1.1.1",
+                "util-deprecate": "~1.0.1"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "~5.1.0"
+              }
+            }
+          }
+        },
+        "ecc-jsbn": {
+          "version": "0.1.2",
+          "bundled": true,
+          "optional": true,
+          "requires": {
+            "jsbn": "~0.1.0",
+            "safer-buffer": "^2.1.0"
+          }
+        },
+        "editor": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "encoding": {
+          "version": "0.1.12",
+          "bundled": true,
+          "requires": {
+            "iconv-lite": "~0.4.13"
+          }
+        },
+        "end-of-stream": {
+          "version": "1.4.1",
+          "bundled": true,
+          "requires": {
+            "once": "^1.4.0"
+          }
+        },
+        "env-paths": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "err-code": {
+          "version": "1.1.2",
+          "bundled": true
+        },
+        "errno": {
+          "version": "0.1.7",
+          "bundled": true,
+          "requires": {
+            "prr": "~1.0.1"
+          }
+        },
+        "es-abstract": {
+          "version": "1.12.0",
+          "bundled": true,
+          "requires": {
+            "es-to-primitive": "^1.1.1",
+            "function-bind": "^1.1.1",
+            "has": "^1.0.1",
+            "is-callable": "^1.1.3",
+            "is-regex": "^1.0.4"
+          }
+        },
+        "es-to-primitive": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "is-callable": "^1.1.4",
+            "is-date-object": "^1.0.1",
+            "is-symbol": "^1.0.2"
+          }
+        },
+        "es6-promise": {
+          "version": "4.2.8",
+          "bundled": true
+        },
+        "es6-promisify": {
+          "version": "5.0.0",
+          "bundled": true,
+          "requires": {
+            "es6-promise": "^4.0.3"
+          }
+        },
+        "escape-string-regexp": {
+          "version": "1.0.5",
+          "bundled": true
+        },
+        "execa": {
+          "version": "0.7.0",
+          "bundled": true,
+          "requires": {
+            "cross-spawn": "^5.0.1",
+            "get-stream": "^3.0.0",
+            "is-stream": "^1.1.0",
+            "npm-run-path": "^2.0.0",
+            "p-finally": "^1.0.0",
+            "signal-exit": "^3.0.0",
+            "strip-eof": "^1.0.0"
+          },
+          "dependencies": {
+            "get-stream": {
+              "version": "3.0.0",
+              "bundled": true
+            }
+          }
+        },
+        "extend": {
+          "version": "3.0.2",
+          "bundled": true
+        },
+        "extsprintf": {
+          "version": "1.3.0",
+          "bundled": true
+        },
+        "fast-deep-equal": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "fast-json-stable-stringify": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "figgy-pudding": {
+          "version": "3.5.1",
+          "bundled": true
+        },
+        "find-npm-prefix": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "find-up": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "locate-path": "^2.0.0"
+          }
+        },
+        "flush-write-stream": {
+          "version": "1.0.3",
+          "bundled": true,
+          "requires": {
+            "inherits": "^2.0.1",
+            "readable-stream": "^2.0.4"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.3",
+                "isarray": "~1.0.0",
+                "process-nextick-args": "~2.0.0",
+                "safe-buffer": "~5.1.1",
+                "string_decoder": "~1.1.1",
+                "util-deprecate": "~1.0.1"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "~5.1.0"
+              }
+            }
+          }
+        },
+        "forever-agent": {
+          "version": "0.6.1",
+          "bundled": true
+        },
+        "form-data": {
+          "version": "2.3.2",
+          "bundled": true,
+          "requires": {
+            "asynckit": "^0.4.0",
+            "combined-stream": "1.0.6",
+            "mime-types": "^2.1.12"
+          }
+        },
+        "from2": {
+          "version": "2.3.0",
+          "bundled": true,
+          "requires": {
+            "inherits": "^2.0.1",
+            "readable-stream": "^2.0.0"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.3",
+                "isarray": "~1.0.0",
+                "process-nextick-args": "~2.0.0",
+                "safe-buffer": "~5.1.1",
+                "string_decoder": "~1.1.1",
+                "util-deprecate": "~1.0.1"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "~5.1.0"
+              }
+            }
+          }
+        },
+        "fs-minipass": {
+          "version": "1.2.6",
+          "bundled": true,
+          "requires": {
+            "minipass": "^2.2.1"
+          }
+        },
+        "fs-vacuum": {
+          "version": "1.2.10",
+          "bundled": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "path-is-inside": "^1.0.1",
+            "rimraf": "^2.5.2"
+          }
+        },
+        "fs-write-stream-atomic": {
+          "version": "1.0.10",
+          "bundled": true,
+          "requires": {
+            "graceful-fs": "^4.1.2",
+            "iferr": "^0.1.5",
+            "imurmurhash": "^0.1.4",
+            "readable-stream": "1 || 2"
+          },
+          "dependencies": {
+            "iferr": {
+              "version": "0.1.5",
+              "bundled": true
+            },
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.3",
+                "isarray": "~1.0.0",
+                "process-nextick-args": "~2.0.0",
+                "safe-buffer": "~5.1.1",
+                "string_decoder": "~1.1.1",
+                "util-deprecate": "~1.0.1"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "~5.1.0"
+              }
+            }
+          }
+        },
+        "fs.realpath": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "function-bind": {
+          "version": "1.1.1",
+          "bundled": true
+        },
+        "gauge": {
+          "version": "2.7.4",
+          "bundled": true,
+          "requires": {
+            "aproba": "^1.0.3",
+            "console-control-strings": "^1.0.0",
+            "has-unicode": "^2.0.0",
+            "object-assign": "^4.1.0",
+            "signal-exit": "^3.0.0",
+            "string-width": "^1.0.1",
+            "strip-ansi": "^3.0.1",
+            "wide-align": "^1.1.0"
+          },
+          "dependencies": {
+            "aproba": {
+              "version": "1.2.0",
+              "bundled": true
+            },
+            "string-width": {
+              "version": "1.0.2",
+              "bundled": true,
+              "requires": {
+                "code-point-at": "^1.0.0",
+                "is-fullwidth-code-point": "^1.0.0",
+                "strip-ansi": "^3.0.0"
+              }
+            }
+          }
+        },
+        "genfun": {
+          "version": "5.0.0",
+          "bundled": true
+        },
+        "gentle-fs": {
+          "version": "2.2.1",
+          "bundled": true,
+          "requires": {
+            "aproba": "^1.1.2",
+            "chownr": "^1.1.2",
+            "fs-vacuum": "^1.2.10",
+            "graceful-fs": "^4.1.11",
+            "iferr": "^0.1.5",
+            "infer-owner": "^1.0.4",
+            "mkdirp": "^0.5.1",
+            "path-is-inside": "^1.0.2",
+            "read-cmd-shim": "^1.0.1",
+            "slide": "^1.1.6"
+          },
+          "dependencies": {
+            "aproba": {
+              "version": "1.2.0",
+              "bundled": true
+            },
+            "iferr": {
+              "version": "0.1.5",
+              "bundled": true
+            }
+          }
+        },
+        "get-caller-file": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "get-stream": {
+          "version": "4.1.0",
+          "bundled": true,
+          "requires": {
+            "pump": "^3.0.0"
+          }
+        },
+        "getpass": {
+          "version": "0.1.7",
+          "bundled": true,
+          "requires": {
+            "assert-plus": "^1.0.0"
+          }
+        },
+        "glob": {
+          "version": "7.1.4",
+          "bundled": true,
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.0.4",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "global-dirs": {
+          "version": "0.1.1",
+          "bundled": true,
+          "requires": {
+            "ini": "^1.3.4"
+          }
+        },
+        "got": {
+          "version": "6.7.1",
+          "bundled": true,
+          "requires": {
+            "create-error-class": "^3.0.0",
+            "duplexer3": "^0.1.4",
+            "get-stream": "^3.0.0",
+            "is-redirect": "^1.0.0",
+            "is-retry-allowed": "^1.0.0",
+            "is-stream": "^1.0.0",
+            "lowercase-keys": "^1.0.0",
+            "safe-buffer": "^5.0.1",
+            "timed-out": "^4.0.0",
+            "unzip-response": "^2.0.1",
+            "url-parse-lax": "^1.0.0"
+          },
+          "dependencies": {
+            "get-stream": {
+              "version": "3.0.0",
+              "bundled": true
+            }
+          }
+        },
+        "graceful-fs": {
+          "version": "4.2.2",
+          "bundled": true
+        },
+        "har-schema": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "har-validator": {
+          "version": "5.1.0",
+          "bundled": true,
+          "requires": {
+            "ajv": "^5.3.0",
+            "har-schema": "^2.0.0"
+          }
+        },
+        "has": {
+          "version": "1.0.3",
+          "bundled": true,
+          "requires": {
+            "function-bind": "^1.1.1"
+          }
+        },
+        "has-flag": {
+          "version": "3.0.0",
+          "bundled": true
+        },
+        "has-symbols": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "has-unicode": {
+          "version": "2.0.1",
+          "bundled": true
+        },
+        "hosted-git-info": {
+          "version": "2.8.2",
+          "bundled": true,
+          "requires": {
+            "lru-cache": "^5.1.1"
+          }
+        },
+        "http-cache-semantics": {
+          "version": "3.8.1",
+          "bundled": true
+        },
+        "http-proxy-agent": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "agent-base": "4",
+            "debug": "3.1.0"
+          }
+        },
+        "http-signature": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "assert-plus": "^1.0.0",
+            "jsprim": "^1.2.2",
+            "sshpk": "^1.7.0"
+          }
+        },
+        "https-proxy-agent": {
+          "version": "2.2.2",
+          "bundled": true,
+          "requires": {
+            "agent-base": "^4.3.0",
+            "debug": "^3.1.0"
+          }
+        },
+        "humanize-ms": {
+          "version": "1.2.1",
+          "bundled": true,
+          "requires": {
+            "ms": "^2.0.0"
+          }
+        },
+        "iconv-lite": {
+          "version": "0.4.23",
+          "bundled": true,
+          "requires": {
+            "safer-buffer": ">= 2.1.2 < 3"
+          }
+        },
+        "iferr": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "ignore-walk": {
+          "version": "3.0.1",
+          "bundled": true,
+          "requires": {
+            "minimatch": "^3.0.4"
+          }
+        },
+        "import-lazy": {
+          "version": "2.1.0",
+          "bundled": true
+        },
+        "imurmurhash": {
+          "version": "0.1.4",
+          "bundled": true
+        },
+        "infer-owner": {
+          "version": "1.0.4",
+          "bundled": true
+        },
+        "inflight": {
+          "version": "1.0.6",
+          "bundled": true,
+          "requires": {
+            "once": "^1.3.0",
+            "wrappy": "1"
+          }
+        },
+        "inherits": {
+          "version": "2.0.4",
+          "bundled": true
+        },
+        "ini": {
+          "version": "1.3.5",
+          "bundled": true
+        },
+        "init-package-json": {
+          "version": "1.10.3",
+          "bundled": true,
+          "requires": {
+            "glob": "^7.1.1",
+            "npm-package-arg": "^4.0.0 || ^5.0.0 || ^6.0.0",
+            "promzard": "^0.3.0",
+            "read": "~1.0.1",
+            "read-package-json": "1 || 2",
+            "semver": "2.x || 3.x || 4 || 5",
+            "validate-npm-package-license": "^3.0.1",
+            "validate-npm-package-name": "^3.0.0"
+          }
+        },
+        "invert-kv": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "ip": {
+          "version": "1.1.5",
+          "bundled": true
+        },
+        "ip-regex": {
+          "version": "2.1.0",
+          "bundled": true
+        },
+        "is-callable": {
+          "version": "1.1.4",
+          "bundled": true
+        },
+        "is-ci": {
+          "version": "1.1.0",
+          "bundled": true,
+          "requires": {
+            "ci-info": "^1.0.0"
+          },
+          "dependencies": {
+            "ci-info": {
+              "version": "1.6.0",
+              "bundled": true
+            }
+          }
+        },
+        "is-cidr": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "cidr-regex": "^2.0.10"
+          }
+        },
+        "is-date-object": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "1.0.0",
+          "bundled": true,
+          "requires": {
+            "number-is-nan": "^1.0.0"
+          }
+        },
+        "is-installed-globally": {
+          "version": "0.1.0",
+          "bundled": true,
+          "requires": {
+            "global-dirs": "^0.1.0",
+            "is-path-inside": "^1.0.0"
+          }
+        },
+        "is-npm": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "is-obj": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "is-path-inside": {
+          "version": "1.0.1",
+          "bundled": true,
+          "requires": {
+            "path-is-inside": "^1.0.1"
+          }
+        },
+        "is-redirect": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "is-regex": {
+          "version": "1.0.4",
+          "bundled": true,
+          "requires": {
+            "has": "^1.0.1"
+          }
+        },
+        "is-retry-allowed": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "is-stream": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "is-symbol": {
+          "version": "1.0.2",
+          "bundled": true,
+          "requires": {
+            "has-symbols": "^1.0.0"
+          }
+        },
+        "is-typedarray": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "isarray": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "isexe": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "isstream": {
+          "version": "0.1.2",
+          "bundled": true
+        },
+        "jsbn": {
+          "version": "0.1.1",
+          "bundled": true,
+          "optional": true
+        },
+        "json-parse-better-errors": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "json-schema": {
+          "version": "0.2.3",
+          "bundled": true
+        },
+        "json-schema-traverse": {
+          "version": "0.3.1",
+          "bundled": true
+        },
+        "json-stringify-safe": {
+          "version": "5.0.1",
+          "bundled": true
+        },
+        "jsonparse": {
+          "version": "1.3.1",
+          "bundled": true
+        },
+        "jsprim": {
+          "version": "1.4.1",
+          "bundled": true,
+          "requires": {
+            "assert-plus": "1.0.0",
+            "extsprintf": "1.3.0",
+            "json-schema": "0.2.3",
+            "verror": "1.10.0"
+          }
+        },
+        "latest-version": {
+          "version": "3.1.0",
+          "bundled": true,
+          "requires": {
+            "package-json": "^4.0.0"
+          }
+        },
+        "lazy-property": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "lcid": {
+          "version": "1.0.0",
+          "bundled": true,
+          "requires": {
+            "invert-kv": "^1.0.0"
+          }
+        },
+        "libcipm": {
+          "version": "4.0.3",
+          "bundled": true,
+          "requires": {
+            "bin-links": "^1.1.2",
+            "bluebird": "^3.5.1",
+            "figgy-pudding": "^3.5.1",
+            "find-npm-prefix": "^1.0.2",
+            "graceful-fs": "^4.1.11",
+            "ini": "^1.3.5",
+            "lock-verify": "^2.0.2",
+            "mkdirp": "^0.5.1",
+            "npm-lifecycle": "^3.0.0",
+            "npm-logical-tree": "^1.2.1",
+            "npm-package-arg": "^6.1.0",
+            "pacote": "^9.1.0",
+            "read-package-json": "^2.0.13",
+            "rimraf": "^2.6.2",
+            "worker-farm": "^1.6.0"
+          }
+        },
+        "libnpm": {
+          "version": "3.0.1",
+          "bundled": true,
+          "requires": {
+            "bin-links": "^1.1.2",
+            "bluebird": "^3.5.3",
+            "find-npm-prefix": "^1.0.2",
+            "libnpmaccess": "^3.0.2",
+            "libnpmconfig": "^1.2.1",
+            "libnpmhook": "^5.0.3",
+            "libnpmorg": "^1.0.1",
+            "libnpmpublish": "^1.1.2",
+            "libnpmsearch": "^2.0.2",
+            "libnpmteam": "^1.0.2",
+            "lock-verify": "^2.0.2",
+            "npm-lifecycle": "^3.0.0",
+            "npm-logical-tree": "^1.2.1",
+            "npm-package-arg": "^6.1.0",
+            "npm-profile": "^4.0.2",
+            "npm-registry-fetch": "^4.0.0",
+            "npmlog": "^4.1.2",
+            "pacote": "^9.5.3",
+            "read-package-json": "^2.0.13",
+            "stringify-package": "^1.0.0"
+          }
+        },
+        "libnpmaccess": {
+          "version": "3.0.2",
+          "bundled": true,
+          "requires": {
+            "aproba": "^2.0.0",
+            "get-stream": "^4.0.0",
+            "npm-package-arg": "^6.1.0",
+            "npm-registry-fetch": "^4.0.0"
+          }
+        },
+        "libnpmconfig": {
+          "version": "1.2.1",
+          "bundled": true,
+          "requires": {
+            "figgy-pudding": "^3.5.1",
+            "find-up": "^3.0.0",
+            "ini": "^1.3.5"
+          },
+          "dependencies": {
+            "find-up": {
+              "version": "3.0.0",
+              "bundled": true,
+              "requires": {
+                "locate-path": "^3.0.0"
+              }
+            },
+            "locate-path": {
+              "version": "3.0.0",
+              "bundled": true,
+              "requires": {
+                "p-locate": "^3.0.0",
+                "path-exists": "^3.0.0"
+              }
+            },
+            "p-limit": {
+              "version": "2.2.0",
+              "bundled": true,
+              "requires": {
+                "p-try": "^2.0.0"
+              }
+            },
+            "p-locate": {
+              "version": "3.0.0",
+              "bundled": true,
+              "requires": {
+                "p-limit": "^2.0.0"
+              }
+            },
+            "p-try": {
+              "version": "2.2.0",
+              "bundled": true
+            }
+          }
+        },
+        "libnpmhook": {
+          "version": "5.0.3",
+          "bundled": true,
+          "requires": {
+            "aproba": "^2.0.0",
+            "figgy-pudding": "^3.4.1",
+            "get-stream": "^4.0.0",
+            "npm-registry-fetch": "^4.0.0"
+          }
+        },
+        "libnpmorg": {
+          "version": "1.0.1",
+          "bundled": true,
+          "requires": {
+            "aproba": "^2.0.0",
+            "figgy-pudding": "^3.4.1",
+            "get-stream": "^4.0.0",
+            "npm-registry-fetch": "^4.0.0"
+          }
+        },
+        "libnpmpublish": {
+          "version": "1.1.2",
+          "bundled": true,
+          "requires": {
+            "aproba": "^2.0.0",
+            "figgy-pudding": "^3.5.1",
+            "get-stream": "^4.0.0",
+            "lodash.clonedeep": "^4.5.0",
+            "normalize-package-data": "^2.4.0",
+            "npm-package-arg": "^6.1.0",
+            "npm-registry-fetch": "^4.0.0",
+            "semver": "^5.5.1",
+            "ssri": "^6.0.1"
+          }
+        },
+        "libnpmsearch": {
+          "version": "2.0.2",
+          "bundled": true,
+          "requires": {
+            "figgy-pudding": "^3.5.1",
+            "get-stream": "^4.0.0",
+            "npm-registry-fetch": "^4.0.0"
+          }
+        },
+        "libnpmteam": {
+          "version": "1.0.2",
+          "bundled": true,
+          "requires": {
+            "aproba": "^2.0.0",
+            "figgy-pudding": "^3.4.1",
+            "get-stream": "^4.0.0",
+            "npm-registry-fetch": "^4.0.0"
+          }
+        },
+        "libnpx": {
+          "version": "10.2.0",
+          "bundled": true,
+          "requires": {
+            "dotenv": "^5.0.1",
+            "npm-package-arg": "^6.0.0",
+            "rimraf": "^2.6.2",
+            "safe-buffer": "^5.1.0",
+            "update-notifier": "^2.3.0",
+            "which": "^1.3.0",
+            "y18n": "^4.0.0",
+            "yargs": "^11.0.0"
+          }
+        },
+        "locate-path": {
+          "version": "2.0.0",
+          "bundled": true,
+          "requires": {
+            "p-locate": "^2.0.0",
+            "path-exists": "^3.0.0"
+          }
+        },
+        "lock-verify": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "npm-package-arg": "^6.1.0",
+            "semver": "^5.4.1"
+          }
+        },
+        "lockfile": {
+          "version": "1.0.4",
+          "bundled": true,
+          "requires": {
+            "signal-exit": "^3.0.2"
+          }
+        },
+        "lodash._baseindexof": {
+          "version": "3.1.0",
+          "bundled": true
+        },
+        "lodash._baseuniq": {
+          "version": "4.6.0",
+          "bundled": true,
+          "requires": {
+            "lodash._createset": "~4.0.0",
+            "lodash._root": "~3.0.0"
+          }
+        },
+        "lodash._bindcallback": {
+          "version": "3.0.1",
+          "bundled": true
+        },
+        "lodash._cacheindexof": {
+          "version": "3.0.2",
+          "bundled": true
+        },
+        "lodash._createcache": {
+          "version": "3.1.2",
+          "bundled": true,
+          "requires": {
+            "lodash._getnative": "^3.0.0"
+          }
+        },
+        "lodash._createset": {
+          "version": "4.0.3",
+          "bundled": true
+        },
+        "lodash._getnative": {
+          "version": "3.9.1",
+          "bundled": true
+        },
+        "lodash._root": {
+          "version": "3.0.1",
+          "bundled": true
+        },
+        "lodash.clonedeep": {
+          "version": "4.5.0",
+          "bundled": true
+        },
+        "lodash.restparam": {
+          "version": "3.6.1",
+          "bundled": true
+        },
+        "lodash.union": {
+          "version": "4.6.0",
+          "bundled": true
+        },
+        "lodash.uniq": {
+          "version": "4.5.0",
+          "bundled": true
+        },
+        "lodash.without": {
+          "version": "4.4.0",
+          "bundled": true
+        },
+        "lowercase-keys": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "lru-cache": {
+          "version": "5.1.1",
+          "bundled": true,
+          "requires": {
+            "yallist": "^3.0.2"
+          }
+        },
+        "make-dir": {
+          "version": "1.3.0",
+          "bundled": true,
+          "requires": {
+            "pify": "^3.0.0"
+          }
+        },
+        "make-fetch-happen": {
+          "version": "5.0.0",
+          "bundled": true,
+          "requires": {
+            "agentkeepalive": "^3.4.1",
+            "cacache": "^12.0.0",
+            "http-cache-semantics": "^3.8.1",
+            "http-proxy-agent": "^2.1.0",
+            "https-proxy-agent": "^2.2.1",
+            "lru-cache": "^5.1.1",
+            "mississippi": "^3.0.0",
+            "node-fetch-npm": "^2.0.2",
+            "promise-retry": "^1.1.1",
+            "socks-proxy-agent": "^4.0.0",
+            "ssri": "^6.0.0"
+          }
+        },
+        "meant": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "mem": {
+          "version": "1.1.0",
+          "bundled": true,
+          "requires": {
+            "mimic-fn": "^1.0.0"
+          }
+        },
+        "mime-db": {
+          "version": "1.35.0",
+          "bundled": true
+        },
+        "mime-types": {
+          "version": "2.1.19",
+          "bundled": true,
+          "requires": {
+            "mime-db": "~1.35.0"
+          }
+        },
+        "mimic-fn": {
+          "version": "1.2.0",
+          "bundled": true
+        },
+        "minimatch": {
+          "version": "3.0.4",
+          "bundled": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        },
+        "minimist": {
+          "version": "0.0.8",
+          "bundled": true
+        },
+        "minipass": {
+          "version": "2.3.3",
+          "bundled": true,
+          "requires": {
+            "safe-buffer": "^5.1.2",
+            "yallist": "^3.0.0"
+          },
+          "dependencies": {
+            "yallist": {
+              "version": "3.0.2",
+              "bundled": true
+            }
+          }
+        },
+        "minizlib": {
+          "version": "1.2.1",
+          "bundled": true,
+          "requires": {
+            "minipass": "^2.2.1"
+          }
+        },
+        "mississippi": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "concat-stream": "^1.5.0",
+            "duplexify": "^3.4.2",
+            "end-of-stream": "^1.1.0",
+            "flush-write-stream": "^1.0.0",
+            "from2": "^2.1.0",
+            "parallel-transform": "^1.1.0",
+            "pump": "^3.0.0",
+            "pumpify": "^1.3.3",
+            "stream-each": "^1.1.0",
+            "through2": "^2.0.0"
+          }
+        },
+        "mkdirp": {
+          "version": "0.5.1",
+          "bundled": true,
+          "requires": {
+            "minimist": "0.0.8"
+          }
+        },
+        "move-concurrently": {
+          "version": "1.0.1",
+          "bundled": true,
+          "requires": {
+            "aproba": "^1.1.1",
+            "copy-concurrently": "^1.0.0",
+            "fs-write-stream-atomic": "^1.0.8",
+            "mkdirp": "^0.5.1",
+            "rimraf": "^2.5.4",
+            "run-queue": "^1.0.3"
+          },
+          "dependencies": {
+            "aproba": {
+              "version": "1.2.0",
+              "bundled": true
+            }
+          }
+        },
+        "ms": {
+          "version": "2.1.1",
+          "bundled": true
+        },
+        "mute-stream": {
+          "version": "0.0.7",
+          "bundled": true
+        },
+        "node-fetch-npm": {
+          "version": "2.0.2",
+          "bundled": true,
+          "requires": {
+            "encoding": "^0.1.11",
+            "json-parse-better-errors": "^1.0.0",
+            "safe-buffer": "^5.1.1"
+          }
+        },
+        "node-gyp": {
+          "version": "5.0.3",
+          "bundled": true,
+          "requires": {
+            "env-paths": "^1.0.0",
+            "glob": "^7.0.3",
+            "graceful-fs": "^4.1.2",
+            "mkdirp": "^0.5.0",
+            "nopt": "2 || 3",
+            "npmlog": "0 || 1 || 2 || 3 || 4",
+            "request": "^2.87.0",
+            "rimraf": "2",
+            "semver": "~5.3.0",
+            "tar": "^4.4.8",
+            "which": "1"
+          },
+          "dependencies": {
+            "nopt": {
+              "version": "3.0.6",
+              "bundled": true,
+              "requires": {
+                "abbrev": "1"
+              }
+            },
+            "semver": {
+              "version": "5.3.0",
+              "bundled": true
+            }
+          }
+        },
+        "nopt": {
+          "version": "4.0.1",
+          "bundled": true,
+          "requires": {
+            "abbrev": "1",
+            "osenv": "^0.1.4"
+          }
+        },
+        "normalize-package-data": {
+          "version": "2.5.0",
+          "bundled": true,
+          "requires": {
+            "hosted-git-info": "^2.1.4",
+            "resolve": "^1.10.0",
+            "semver": "2 || 3 || 4 || 5",
+            "validate-npm-package-license": "^3.0.1"
+          },
+          "dependencies": {
+            "resolve": {
+              "version": "1.10.0",
+              "bundled": true,
+              "requires": {
+                "path-parse": "^1.0.6"
+              }
+            }
+          }
+        },
+        "npm-audit-report": {
+          "version": "1.3.2",
+          "bundled": true,
+          "requires": {
+            "cli-table3": "^0.5.0",
+            "console-control-strings": "^1.1.0"
+          }
+        },
+        "npm-bundled": {
+          "version": "1.0.6",
+          "bundled": true
+        },
+        "npm-cache-filename": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "npm-install-checks": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "semver": "^2.3.0 || 3.x || 4 || 5"
+          }
+        },
+        "npm-lifecycle": {
+          "version": "3.1.3",
+          "bundled": true,
+          "requires": {
+            "byline": "^5.0.0",
+            "graceful-fs": "^4.1.15",
+            "node-gyp": "^5.0.2",
+            "resolve-from": "^4.0.0",
+            "slide": "^1.1.6",
+            "uid-number": "0.0.6",
+            "umask": "^1.1.0",
+            "which": "^1.3.1"
+          }
+        },
+        "npm-logical-tree": {
+          "version": "1.2.1",
+          "bundled": true
+        },
+        "npm-package-arg": {
+          "version": "6.1.1",
+          "bundled": true,
+          "requires": {
+            "hosted-git-info": "^2.7.1",
+            "osenv": "^0.1.5",
+            "semver": "^5.6.0",
+            "validate-npm-package-name": "^3.0.0"
+          }
+        },
+        "npm-packlist": {
+          "version": "1.4.4",
+          "bundled": true,
+          "requires": {
+            "ignore-walk": "^3.0.1",
+            "npm-bundled": "^1.0.1"
+          }
+        },
+        "npm-pick-manifest": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "figgy-pudding": "^3.5.1",
+            "npm-package-arg": "^6.0.0",
+            "semver": "^5.4.1"
+          }
+        },
+        "npm-profile": {
+          "version": "4.0.2",
+          "bundled": true,
+          "requires": {
+            "aproba": "^1.1.2 || 2",
+            "figgy-pudding": "^3.4.1",
+            "npm-registry-fetch": "^4.0.0"
+          }
+        },
+        "npm-registry-fetch": {
+          "version": "4.0.0",
+          "bundled": true,
+          "requires": {
+            "JSONStream": "^1.3.4",
+            "bluebird": "^3.5.1",
+            "figgy-pudding": "^3.4.1",
+            "lru-cache": "^5.1.1",
+            "make-fetch-happen": "^5.0.0",
+            "npm-package-arg": "^6.1.0"
+          }
+        },
+        "npm-run-path": {
+          "version": "2.0.2",
+          "bundled": true,
+          "requires": {
+            "path-key": "^2.0.0"
+          }
+        },
+        "npm-user-validate": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "npmlog": {
+          "version": "4.1.2",
+          "bundled": true,
+          "requires": {
+            "are-we-there-yet": "~1.1.2",
+            "console-control-strings": "~1.1.0",
+            "gauge": "~2.7.3",
+            "set-blocking": "~2.0.0"
+          }
+        },
+        "number-is-nan": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "oauth-sign": {
+          "version": "0.9.0",
+          "bundled": true
+        },
+        "object-assign": {
+          "version": "4.1.1",
+          "bundled": true
+        },
+        "object-keys": {
+          "version": "1.0.12",
+          "bundled": true
+        },
+        "object.getownpropertydescriptors": {
+          "version": "2.0.3",
+          "bundled": true,
+          "requires": {
+            "define-properties": "^1.1.2",
+            "es-abstract": "^1.5.1"
+          }
+        },
+        "once": {
+          "version": "1.4.0",
+          "bundled": true,
+          "requires": {
+            "wrappy": "1"
+          }
+        },
+        "opener": {
+          "version": "1.5.1",
+          "bundled": true
+        },
+        "os-homedir": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "os-locale": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "execa": "^0.7.0",
+            "lcid": "^1.0.0",
+            "mem": "^1.1.0"
+          }
+        },
+        "os-tmpdir": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "osenv": {
+          "version": "0.1.5",
+          "bundled": true,
+          "requires": {
+            "os-homedir": "^1.0.0",
+            "os-tmpdir": "^1.0.0"
+          }
+        },
+        "p-finally": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "p-limit": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "p-try": "^1.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "2.0.0",
+          "bundled": true,
+          "requires": {
+            "p-limit": "^1.1.0"
+          }
+        },
+        "p-try": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "package-json": {
+          "version": "4.0.1",
+          "bundled": true,
+          "requires": {
+            "got": "^6.7.1",
+            "registry-auth-token": "^3.0.1",
+            "registry-url": "^3.0.3",
+            "semver": "^5.1.0"
+          }
+        },
+        "pacote": {
+          "version": "9.5.8",
+          "bundled": true,
+          "requires": {
+            "bluebird": "^3.5.3",
+            "cacache": "^12.0.2",
+            "chownr": "^1.1.2",
+            "figgy-pudding": "^3.5.1",
+            "get-stream": "^4.1.0",
+            "glob": "^7.1.3",
+            "infer-owner": "^1.0.4",
+            "lru-cache": "^5.1.1",
+            "make-fetch-happen": "^5.0.0",
+            "minimatch": "^3.0.4",
+            "minipass": "^2.3.5",
+            "mississippi": "^3.0.0",
+            "mkdirp": "^0.5.1",
+            "normalize-package-data": "^2.4.0",
+            "npm-package-arg": "^6.1.0",
+            "npm-packlist": "^1.1.12",
+            "npm-pick-manifest": "^3.0.0",
+            "npm-registry-fetch": "^4.0.0",
+            "osenv": "^0.1.5",
+            "promise-inflight": "^1.0.1",
+            "promise-retry": "^1.1.1",
+            "protoduck": "^5.0.1",
+            "rimraf": "^2.6.2",
+            "safe-buffer": "^5.1.2",
+            "semver": "^5.6.0",
+            "ssri": "^6.0.1",
+            "tar": "^4.4.10",
+            "unique-filename": "^1.1.1",
+            "which": "^1.3.1"
+          },
+          "dependencies": {
+            "minipass": {
+              "version": "2.3.5",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "^5.1.2",
+                "yallist": "^3.0.0"
+              }
+            }
+          }
+        },
+        "parallel-transform": {
+          "version": "1.1.0",
+          "bundled": true,
+          "requires": {
+            "cyclist": "~0.2.2",
+            "inherits": "^2.0.3",
+            "readable-stream": "^2.1.5"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.3",
+                "isarray": "~1.0.0",
+                "process-nextick-args": "~2.0.0",
+                "safe-buffer": "~5.1.1",
+                "string_decoder": "~1.1.1",
+                "util-deprecate": "~1.0.1"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "~5.1.0"
+              }
+            }
+          }
+        },
+        "path-exists": {
+          "version": "3.0.0",
+          "bundled": true
+        },
+        "path-is-absolute": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "path-is-inside": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "path-key": {
+          "version": "2.0.1",
+          "bundled": true
+        },
+        "path-parse": {
+          "version": "1.0.6",
+          "bundled": true
+        },
+        "performance-now": {
+          "version": "2.1.0",
+          "bundled": true
+        },
+        "pify": {
+          "version": "3.0.0",
+          "bundled": true
+        },
+        "prepend-http": {
+          "version": "1.0.4",
+          "bundled": true
+        },
+        "process-nextick-args": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "promise-inflight": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "promise-retry": {
+          "version": "1.1.1",
+          "bundled": true,
+          "requires": {
+            "err-code": "^1.0.0",
+            "retry": "^0.10.0"
+          },
+          "dependencies": {
+            "retry": {
+              "version": "0.10.1",
+              "bundled": true
+            }
+          }
+        },
+        "promzard": {
+          "version": "0.3.0",
+          "bundled": true,
+          "requires": {
+            "read": "1"
+          }
+        },
+        "proto-list": {
+          "version": "1.2.4",
+          "bundled": true
+        },
+        "protoduck": {
+          "version": "5.0.1",
+          "bundled": true,
+          "requires": {
+            "genfun": "^5.0.0"
+          }
+        },
+        "prr": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "pseudomap": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "psl": {
+          "version": "1.1.29",
+          "bundled": true
+        },
+        "pump": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "end-of-stream": "^1.1.0",
+            "once": "^1.3.1"
+          }
+        },
+        "pumpify": {
+          "version": "1.5.1",
+          "bundled": true,
+          "requires": {
+            "duplexify": "^3.6.0",
+            "inherits": "^2.0.3",
+            "pump": "^2.0.0"
+          },
+          "dependencies": {
+            "pump": {
+              "version": "2.0.1",
+              "bundled": true,
+              "requires": {
+                "end-of-stream": "^1.1.0",
+                "once": "^1.3.1"
+              }
+            }
+          }
+        },
+        "punycode": {
+          "version": "1.4.1",
+          "bundled": true
+        },
+        "qrcode-terminal": {
+          "version": "0.12.0",
+          "bundled": true
+        },
+        "qs": {
+          "version": "6.5.2",
+          "bundled": true
+        },
+        "query-string": {
+          "version": "6.8.2",
+          "bundled": true,
+          "requires": {
+            "decode-uri-component": "^0.2.0",
+            "split-on-first": "^1.0.0",
+            "strict-uri-encode": "^2.0.0"
+          }
+        },
+        "qw": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "rc": {
+          "version": "1.2.7",
+          "bundled": true,
+          "requires": {
+            "deep-extend": "^0.5.1",
+            "ini": "~1.3.0",
+            "minimist": "^1.2.0",
+            "strip-json-comments": "~2.0.1"
+          },
+          "dependencies": {
+            "minimist": {
+              "version": "1.2.0",
+              "bundled": true
+            }
+          }
+        },
+        "read": {
+          "version": "1.0.7",
+          "bundled": true,
+          "requires": {
+            "mute-stream": "~0.0.4"
+          }
+        },
+        "read-cmd-shim": {
+          "version": "1.0.3",
+          "bundled": true,
+          "requires": {
+            "graceful-fs": "^4.1.2"
+          }
+        },
+        "read-installed": {
+          "version": "4.0.3",
+          "bundled": true,
+          "requires": {
+            "debuglog": "^1.0.1",
+            "graceful-fs": "^4.1.2",
+            "read-package-json": "^2.0.0",
+            "readdir-scoped-modules": "^1.0.0",
+            "semver": "2 || 3 || 4 || 5",
+            "slide": "~1.1.3",
+            "util-extend": "^1.0.1"
+          }
+        },
+        "read-package-json": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "glob": "^7.1.1",
+            "graceful-fs": "^4.1.2",
+            "json-parse-better-errors": "^1.0.1",
+            "normalize-package-data": "^2.0.0",
+            "slash": "^1.0.0"
+          }
+        },
+        "read-package-tree": {
+          "version": "5.3.1",
+          "bundled": true,
+          "requires": {
+            "read-package-json": "^2.0.0",
+            "readdir-scoped-modules": "^1.0.0",
+            "util-promisify": "^2.1.0"
+          }
+        },
+        "readable-stream": {
+          "version": "3.4.0",
+          "bundled": true,
+          "requires": {
+            "inherits": "^2.0.3",
+            "string_decoder": "^1.1.1",
+            "util-deprecate": "^1.0.1"
+          }
+        },
+        "readdir-scoped-modules": {
+          "version": "1.1.0",
+          "bundled": true,
+          "requires": {
+            "debuglog": "^1.0.1",
+            "dezalgo": "^1.0.0",
+            "graceful-fs": "^4.1.2",
+            "once": "^1.3.0"
+          }
+        },
+        "registry-auth-token": {
+          "version": "3.3.2",
+          "bundled": true,
+          "requires": {
+            "rc": "^1.1.6",
+            "safe-buffer": "^5.0.1"
+          }
+        },
+        "registry-url": {
+          "version": "3.1.0",
+          "bundled": true,
+          "requires": {
+            "rc": "^1.0.1"
+          }
+        },
+        "request": {
+          "version": "2.88.0",
+          "bundled": true,
+          "requires": {
+            "aws-sign2": "~0.7.0",
+            "aws4": "^1.8.0",
+            "caseless": "~0.12.0",
+            "combined-stream": "~1.0.6",
+            "extend": "~3.0.2",
+            "forever-agent": "~0.6.1",
+            "form-data": "~2.3.2",
+            "har-validator": "~5.1.0",
+            "http-signature": "~1.2.0",
+            "is-typedarray": "~1.0.0",
+            "isstream": "~0.1.2",
+            "json-stringify-safe": "~5.0.1",
+            "mime-types": "~2.1.19",
+            "oauth-sign": "~0.9.0",
+            "performance-now": "^2.1.0",
+            "qs": "~6.5.2",
+            "safe-buffer": "^5.1.2",
+            "tough-cookie": "~2.4.3",
+            "tunnel-agent": "^0.6.0",
+            "uuid": "^3.3.2"
+          }
+        },
+        "require-directory": {
+          "version": "2.1.1",
+          "bundled": true
+        },
+        "require-main-filename": {
+          "version": "1.0.1",
+          "bundled": true
+        },
+        "resolve-from": {
+          "version": "4.0.0",
+          "bundled": true
+        },
+        "retry": {
+          "version": "0.12.0",
+          "bundled": true
+        },
+        "rimraf": {
+          "version": "2.6.3",
+          "bundled": true,
+          "requires": {
+            "glob": "^7.1.3"
+          }
+        },
+        "run-queue": {
+          "version": "1.0.3",
+          "bundled": true,
+          "requires": {
+            "aproba": "^1.1.1"
+          },
+          "dependencies": {
+            "aproba": {
+              "version": "1.2.0",
+              "bundled": true
+            }
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "bundled": true
+        },
+        "safer-buffer": {
+          "version": "2.1.2",
+          "bundled": true
+        },
+        "semver": {
+          "version": "5.7.1",
+          "bundled": true
+        },
+        "semver-diff": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "semver": "^5.0.3"
+          }
+        },
+        "set-blocking": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "sha": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "graceful-fs": "^4.1.2"
+          }
+        },
+        "shebang-command": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "shebang-regex": "^1.0.0"
+          }
+        },
+        "shebang-regex": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "signal-exit": {
+          "version": "3.0.2",
+          "bundled": true
+        },
+        "slash": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "slide": {
+          "version": "1.1.6",
+          "bundled": true
+        },
+        "smart-buffer": {
+          "version": "4.0.2",
+          "bundled": true
+        },
+        "socks": {
+          "version": "2.3.2",
+          "bundled": true,
+          "requires": {
+            "ip": "^1.1.5",
+            "smart-buffer": "4.0.2"
+          }
+        },
+        "socks-proxy-agent": {
+          "version": "4.0.2",
+          "bundled": true,
+          "requires": {
+            "agent-base": "~4.2.1",
+            "socks": "~2.3.2"
+          },
+          "dependencies": {
+            "agent-base": {
+              "version": "4.2.1",
+              "bundled": true,
+              "requires": {
+                "es6-promisify": "^5.0.0"
+              }
+            }
+          }
+        },
+        "sorted-object": {
+          "version": "2.0.1",
+          "bundled": true
+        },
+        "sorted-union-stream": {
+          "version": "2.1.3",
+          "bundled": true,
+          "requires": {
+            "from2": "^1.3.0",
+            "stream-iterate": "^1.1.0"
+          },
+          "dependencies": {
+            "from2": {
+              "version": "1.3.0",
+              "bundled": true,
+              "requires": {
+                "inherits": "~2.0.1",
+                "readable-stream": "~1.1.10"
+              }
+            },
+            "isarray": {
+              "version": "0.0.1",
+              "bundled": true
+            },
+            "readable-stream": {
+              "version": "1.1.14",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.1",
+                "isarray": "0.0.1",
+                "string_decoder": "~0.10.x"
+              }
+            },
+            "string_decoder": {
+              "version": "0.10.31",
+              "bundled": true
+            }
+          }
+        },
+        "spdx-correct": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "spdx-expression-parse": "^3.0.0",
+            "spdx-license-ids": "^3.0.0"
+          }
+        },
+        "spdx-exceptions": {
+          "version": "2.1.0",
+          "bundled": true
+        },
+        "spdx-expression-parse": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "spdx-exceptions": "^2.1.0",
+            "spdx-license-ids": "^3.0.0"
+          }
+        },
+        "spdx-license-ids": {
+          "version": "3.0.3",
+          "bundled": true
+        },
+        "split-on-first": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "sshpk": {
+          "version": "1.14.2",
+          "bundled": true,
+          "requires": {
+            "asn1": "~0.2.3",
+            "assert-plus": "^1.0.0",
+            "bcrypt-pbkdf": "^1.0.0",
+            "dashdash": "^1.12.0",
+            "ecc-jsbn": "~0.1.1",
+            "getpass": "^0.1.1",
+            "jsbn": "~0.1.0",
+            "safer-buffer": "^2.0.2",
+            "tweetnacl": "~0.14.0"
+          }
+        },
+        "ssri": {
+          "version": "6.0.1",
+          "bundled": true,
+          "requires": {
+            "figgy-pudding": "^3.5.1"
+          }
+        },
+        "stream-each": {
+          "version": "1.2.2",
+          "bundled": true,
+          "requires": {
+            "end-of-stream": "^1.1.0",
+            "stream-shift": "^1.0.0"
+          }
+        },
+        "stream-iterate": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "readable-stream": "^2.1.5",
+            "stream-shift": "^1.0.0"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.3",
+                "isarray": "~1.0.0",
+                "process-nextick-args": "~2.0.0",
+                "safe-buffer": "~5.1.1",
+                "string_decoder": "~1.1.1",
+                "util-deprecate": "~1.0.1"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "~5.1.0"
+              }
+            }
+          }
+        },
+        "stream-shift": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "strict-uri-encode": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "string-width": {
+          "version": "2.1.1",
+          "bundled": true,
+          "requires": {
+            "is-fullwidth-code-point": "^2.0.0",
+            "strip-ansi": "^4.0.0"
+          },
+          "dependencies": {
+            "ansi-regex": {
+              "version": "3.0.0",
+              "bundled": true
+            },
+            "is-fullwidth-code-point": {
+              "version": "2.0.0",
+              "bundled": true
+            },
+            "strip-ansi": {
+              "version": "4.0.0",
+              "bundled": true,
+              "requires": {
+                "ansi-regex": "^3.0.0"
+              }
+            }
+          }
+        },
+        "string_decoder": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        },
+        "stringify-package": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "strip-ansi": {
+          "version": "3.0.1",
+          "bundled": true,
+          "requires": {
+            "ansi-regex": "^2.0.0"
+          }
+        },
+        "strip-eof": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "strip-json-comments": {
+          "version": "2.0.1",
+          "bundled": true
+        },
+        "supports-color": {
+          "version": "5.4.0",
+          "bundled": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        },
+        "tar": {
+          "version": "4.4.10",
+          "bundled": true,
+          "requires": {
+            "chownr": "^1.1.1",
+            "fs-minipass": "^1.2.5",
+            "minipass": "^2.3.5",
+            "minizlib": "^1.2.1",
+            "mkdirp": "^0.5.0",
+            "safe-buffer": "^5.1.2",
+            "yallist": "^3.0.3"
+          },
+          "dependencies": {
+            "minipass": {
+              "version": "2.3.5",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "^5.1.2",
+                "yallist": "^3.0.0"
+              }
+            },
+            "yallist": {
+              "version": "3.0.3",
+              "bundled": true
+            }
+          }
+        },
+        "term-size": {
+          "version": "1.2.0",
+          "bundled": true,
+          "requires": {
+            "execa": "^0.7.0"
+          }
+        },
+        "text-table": {
+          "version": "0.2.0",
+          "bundled": true
+        },
+        "through": {
+          "version": "2.3.8",
+          "bundled": true
+        },
+        "through2": {
+          "version": "2.0.3",
+          "bundled": true,
+          "requires": {
+            "readable-stream": "^2.1.5",
+            "xtend": "~4.0.1"
+          },
+          "dependencies": {
+            "readable-stream": {
+              "version": "2.3.6",
+              "bundled": true,
+              "requires": {
+                "core-util-is": "~1.0.0",
+                "inherits": "~2.0.3",
+                "isarray": "~1.0.0",
+                "process-nextick-args": "~2.0.0",
+                "safe-buffer": "~5.1.1",
+                "string_decoder": "~1.1.1",
+                "util-deprecate": "~1.0.1"
+              }
+            },
+            "string_decoder": {
+              "version": "1.1.1",
+              "bundled": true,
+              "requires": {
+                "safe-buffer": "~5.1.0"
+              }
+            }
+          }
+        },
+        "timed-out": {
+          "version": "4.0.1",
+          "bundled": true
+        },
+        "tiny-relative-date": {
+          "version": "1.3.0",
+          "bundled": true
+        },
+        "tough-cookie": {
+          "version": "2.4.3",
+          "bundled": true,
+          "requires": {
+            "psl": "^1.1.24",
+            "punycode": "^1.4.1"
+          }
+        },
+        "tunnel-agent": {
+          "version": "0.6.0",
+          "bundled": true,
+          "requires": {
+            "safe-buffer": "^5.0.1"
+          }
+        },
+        "tweetnacl": {
+          "version": "0.14.5",
+          "bundled": true,
+          "optional": true
+        },
+        "typedarray": {
+          "version": "0.0.6",
+          "bundled": true
+        },
+        "uid-number": {
+          "version": "0.0.6",
+          "bundled": true
+        },
+        "umask": {
+          "version": "1.1.0",
+          "bundled": true
+        },
+        "unique-filename": {
+          "version": "1.1.1",
+          "bundled": true,
+          "requires": {
+            "unique-slug": "^2.0.0"
+          }
+        },
+        "unique-slug": {
+          "version": "2.0.0",
+          "bundled": true,
+          "requires": {
+            "imurmurhash": "^0.1.4"
+          }
+        },
+        "unique-string": {
+          "version": "1.0.0",
+          "bundled": true,
+          "requires": {
+            "crypto-random-string": "^1.0.0"
+          }
+        },
+        "unpipe": {
+          "version": "1.0.0",
+          "bundled": true
+        },
+        "unzip-response": {
+          "version": "2.0.1",
+          "bundled": true
+        },
+        "update-notifier": {
+          "version": "2.5.0",
+          "bundled": true,
+          "requires": {
+            "boxen": "^1.2.1",
+            "chalk": "^2.0.1",
+            "configstore": "^3.0.0",
+            "import-lazy": "^2.1.0",
+            "is-ci": "^1.0.10",
+            "is-installed-globally": "^0.1.0",
+            "is-npm": "^1.0.0",
+            "latest-version": "^3.0.0",
+            "semver-diff": "^2.0.0",
+            "xdg-basedir": "^3.0.0"
+          }
+        },
+        "url-parse-lax": {
+          "version": "1.0.0",
+          "bundled": true,
+          "requires": {
+            "prepend-http": "^1.0.1"
+          }
+        },
+        "util-deprecate": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "util-extend": {
+          "version": "1.0.3",
+          "bundled": true
+        },
+        "util-promisify": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "object.getownpropertydescriptors": "^2.0.3"
+          }
+        },
+        "uuid": {
+          "version": "3.3.2",
+          "bundled": true
+        },
+        "validate-npm-package-license": {
+          "version": "3.0.4",
+          "bundled": true,
+          "requires": {
+            "spdx-correct": "^3.0.0",
+            "spdx-expression-parse": "^3.0.0"
+          }
+        },
+        "validate-npm-package-name": {
+          "version": "3.0.0",
+          "bundled": true,
+          "requires": {
+            "builtins": "^1.0.3"
+          }
+        },
+        "verror": {
+          "version": "1.10.0",
+          "bundled": true,
+          "requires": {
+            "assert-plus": "^1.0.0",
+            "core-util-is": "1.0.2",
+            "extsprintf": "^1.2.0"
+          }
+        },
+        "wcwidth": {
+          "version": "1.0.1",
+          "bundled": true,
+          "requires": {
+            "defaults": "^1.0.3"
+          }
+        },
+        "which": {
+          "version": "1.3.1",
+          "bundled": true,
+          "requires": {
+            "isexe": "^2.0.0"
+          }
+        },
+        "which-module": {
+          "version": "2.0.0",
+          "bundled": true
+        },
+        "wide-align": {
+          "version": "1.1.2",
+          "bundled": true,
+          "requires": {
+            "string-width": "^1.0.2"
+          },
+          "dependencies": {
+            "string-width": {
+              "version": "1.0.2",
+              "bundled": true,
+              "requires": {
+                "code-point-at": "^1.0.0",
+                "is-fullwidth-code-point": "^1.0.0",
+                "strip-ansi": "^3.0.0"
+              }
+            }
+          }
+        },
+        "widest-line": {
+          "version": "2.0.0",
+          "bundled": true,
+          "requires": {
+            "string-width": "^2.1.1"
+          }
+        },
+        "worker-farm": {
+          "version": "1.7.0",
+          "bundled": true,
+          "requires": {
+            "errno": "~0.1.7"
+          }
+        },
+        "wrap-ansi": {
+          "version": "2.1.0",
+          "bundled": true,
+          "requires": {
+            "string-width": "^1.0.1",
+            "strip-ansi": "^3.0.1"
+          },
+          "dependencies": {
+            "string-width": {
+              "version": "1.0.2",
+              "bundled": true,
+              "requires": {
+                "code-point-at": "^1.0.0",
+                "is-fullwidth-code-point": "^1.0.0",
+                "strip-ansi": "^3.0.0"
+              }
+            }
+          }
+        },
+        "wrappy": {
+          "version": "1.0.2",
+          "bundled": true
+        },
+        "write-file-atomic": {
+          "version": "2.4.3",
+          "bundled": true,
+          "requires": {
+            "graceful-fs": "^4.1.11",
+            "imurmurhash": "^0.1.4",
+            "signal-exit": "^3.0.2"
+          }
+        },
+        "xdg-basedir": {
+          "version": "3.0.0",
+          "bundled": true
+        },
+        "xtend": {
+          "version": "4.0.1",
+          "bundled": true
+        },
+        "y18n": {
+          "version": "4.0.0",
+          "bundled": true
+        },
+        "yallist": {
+          "version": "3.0.3",
+          "bundled": true
+        },
+        "yargs": {
+          "version": "11.0.0",
+          "bundled": true,
+          "requires": {
+            "cliui": "^4.0.0",
+            "decamelize": "^1.1.1",
+            "find-up": "^2.1.0",
+            "get-caller-file": "^1.0.1",
+            "os-locale": "^2.0.0",
+            "require-directory": "^2.1.1",
+            "require-main-filename": "^1.0.1",
+            "set-blocking": "^2.0.0",
+            "string-width": "^2.0.0",
+            "which-module": "^2.0.0",
+            "y18n": "^3.2.1",
+            "yargs-parser": "^9.0.2"
+          },
+          "dependencies": {
+            "y18n": {
+              "version": "3.2.1",
+              "bundled": true
+            }
+          }
+        },
+        "yargs-parser": {
+          "version": "9.0.2",
+          "bundled": true,
+          "requires": {
+            "camelcase": "^4.1.0"
+          }
+        }
+      }
+    },
+    "number-is-nan": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
+      "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
+    },
+    "optjs": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/optjs/-/optjs-3.2.2.tgz",
+      "integrity": "sha1-aabOicRCpEQDFBrS+bNwvVu29O4="
+    },
+    "os-locale": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
+      "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
+      "requires": {
+        "lcid": "^1.0.0"
+      }
+    },
+    "protobufjs": {
+      "version": "6.8.8",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz",
+      "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==",
+      "requires": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/long": "^4.0.0",
+        "@types/node": "^10.1.0",
+        "long": "^4.0.0"
+      },
+      "dependencies": {
+        "@types/node": {
+          "version": "10.14.16",
+          "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.16.tgz",
+          "integrity": "sha512-/opXIbfn0P+VLt+N8DE4l8Mn8rbhiJgabU96ZJ0p9mxOkIks5gh6RUnpHak7Yh0SFkyjO/ODbxsQQPV2bpMmyA=="
+        }
+      }
+    },
+    "string-width": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
+      "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
+      "requires": {
+        "code-point-at": "^1.0.0",
+        "is-fullwidth-code-point": "^1.0.0",
+        "strip-ansi": "^3.0.0"
+      }
+    },
+    "strip-ansi": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
+      "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
+      "requires": {
+        "ansi-regex": "^2.0.0"
+      }
+    },
+    "window-size": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz",
+      "integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY="
+    },
+    "wrap-ansi": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
+      "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
+      "requires": {
+        "string-width": "^1.0.1",
+        "strip-ansi": "^3.0.1"
+      }
+    },
+    "y18n": {
+      "version": "3.2.1",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
+      "integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
+    },
+    "yargs": {
+      "version": "3.32.0",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz",
+      "integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=",
+      "requires": {
+        "camelcase": "^2.0.1",
+        "cliui": "^3.0.3",
+        "decamelize": "^1.1.1",
+        "os-locale": "^1.4.0",
+        "string-width": "^1.0.1",
+        "window-size": "^0.1.4",
+        "y18n": "^3.2.0"
+      }
+    }
+  }
+}
diff --git a/protocol/examples/node/package.json b/protocol/examples/node/package.json
new file mode 100644 (file)
index 0000000..45eeab4
--- /dev/null
@@ -0,0 +1,13 @@
+{
+  "name": "ua-node-example",
+  "version": "0.1.0",
+  "dependencies": {
+    "@grpc/proto-loader": "^0.1.0",
+    "async": "^1.5.2",
+    "google-protobuf": "^3.0.0",
+    "grpc": "^1.11.0",
+    "lodash": "^4.6.1",
+    "minimist": "^1.2.0",
+    "npm": "^6.11.2"
+  }
+}
diff --git a/protocol/examples/python/README b/protocol/examples/python/README
new file mode 100644 (file)
index 0000000..03199a7
--- /dev/null
@@ -0,0 +1,15 @@
+```sh
+
+python3 -m venv v
+
+source v/bin/active
+
+pip3 install -r requiements.txt
+
+./gen.sh
+
+python3 sample01.py
+
+./deactivate
+
+```
diff --git a/protocol/examples/python/gen.sh b/protocol/examples/python/gen.sh
new file mode 100755 (executable)
index 0000000..6fbe764
--- /dev/null
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+python3 -m grpc_tools.protoc --python_out=./ --grpc_python_out=./ -I ./../../ ../../aurum.proto
+
diff --git a/protocol/examples/python/killServer.py b/protocol/examples/python/killServer.py
new file mode 100644 (file)
index 0000000..d0f9977
--- /dev/null
@@ -0,0 +1,14 @@
+from __future__ import print_function
+import aurum_pb2
+import aurum_pb2_grpc
+import logging
+import grpc
+
+def run():
+    with grpc.insecure_channel('127.0.0.1:50051') as channel:
+        stub = aurum_pb2_grpc.BootstrapStub(channel)
+        stub.killServer(aurum_pb2.ReqEmpty())
+
+if __name__ == '__main__':
+    logging.basicConfig()
+    run()
\ No newline at end of file
diff --git a/protocol/examples/python/org.tizen.uicomponents.arm.tpk b/protocol/examples/python/org.tizen.uicomponents.arm.tpk
new file mode 100644 (file)
index 0000000..e6a8b01
Binary files /dev/null and b/protocol/examples/python/org.tizen.uicomponents.arm.tpk differ
diff --git a/protocol/examples/python/requirements.txt b/protocol/examples/python/requirements.txt
new file mode 100644 (file)
index 0000000..095b31b
--- /dev/null
@@ -0,0 +1,5 @@
+grpcio==1.23.0
+grpcio-tools==1.23.0
+pkg-resources==0.0.0
+protobuf==3.9.1
+six==1.12.0
diff --git a/protocol/examples/python/sample01.py b/protocol/examples/python/sample01.py
new file mode 100644 (file)
index 0000000..a143c15
--- /dev/null
@@ -0,0 +1,126 @@
+from __future__ import print_function
+import aurum_pb2
+import aurum_pb2_grpc
+import logging
+import grpc
+import time
+
+def back(stub):
+    rsp_key = stub.sendKey(aurum_pb2.ReqKey(
+                    type='BACK',
+                    actionType='STROKE',
+                )
+            )
+
+    rsp_find = stub.findElement(aurum_pb2.ReqFindElement(
+                    strategy='TEXT',
+                    textField='TestMemo'
+                )
+            )
+
+    for item in rsp_find.elements:
+        print(item)
+        stub.click(aurum_pb2.ReqClick(
+                type='ELEMENTID',
+                elementId=item.elementId
+            )
+        )
+
+def findNclick(stub, text):
+    rsp_find = stub.findElement(aurum_pb2.ReqFindElement(
+                    strategy='TEXT',
+                    textField=text
+                )
+            )
+
+    for item in rsp_find.elements:
+        print(item)
+        stub.click(aurum_pb2.ReqClick(
+                type='ELEMENTID',
+                elementId=item.elementId
+            )
+        )
+
+def flick(stub):
+    rsp_flick = stub.flick(aurum_pb2.ReqFlick(
+        startPoint=aurum_pb2.Point(x=100, y=100),
+        endPoint=aurum_pb2.Point(x=400, y=400),
+        durationMs=1
+    ))
+
+def launchApp(stub):
+    rsp_launch = stub.launchApp(aurum_pb2.ReqLaunchApp(
+           packageName='org.example.uicomponents'
+    ))
+
+def closeApp(stub):
+    rsp_launch = stub.closeApp(aurum_pb2.ReqCloseApp(
+           packageName='org.example.uicomponents'
+    ))
+
+CHUNK_SIZE = 1024 * 1024
+def get_file_chunks(filename):
+   with open(filename, 'rb') as f:
+       while True:
+           piece = f.read(CHUNK_SIZE)
+           if len(piece) == 0:
+               return
+           yield aurum_pb2.ReqInstallApp(package=piece)
+
+def installApp(stub):
+   in_file_name = './org.tizen.uicomponents.arm.tpk'
+   chunks_generator = get_file_chunks(in_file_name)
+   rsp_install = stub.installApp(chunks_generator)
+
+def removeApp(stub):
+   rsp_install = stub.removeApp(aurum_pb2.ReqRemoveApp(
+                            packageName='org.example.uicomponents'
+                        )
+                    )
+
+def getAppInfo(stub):
+   rsp_info = stub.getAppInfo(aurum_pb2.ReqGetAppInfo(packageName='org.example.uicomponents'))
+   print(rsp_info)
+
+def touchdown(stub, xx, yy):
+   rsp = stub.touchDown(aurum_pb2.ReqTouchDown(coordination=aurum_pb2.Point(x=xx,y=yy)))
+   print(rsp)
+
+def touchmove(stub, xx, yy):
+   rsp = stub.touchMove(aurum_pb2.ReqTouchMove(coordination=aurum_pb2.Point(x=xx,y=yy)))
+   print(rsp)
+
+def touchup(stub, xx, yy):
+   rsp = stub.touchUp(aurum_pb2.ReqTouchUp(coordination=aurum_pb2.Point(x=xx,y=yy)))
+   print(rsp)
+
+def sync(stub):
+   rsp = stub.sync(aurum_pb2.ReqEmpty())
+   print(rsp)
+
+def run():
+    with grpc.insecure_channel('127.0.0.1:50051') as channel:
+        stub = aurum_pb2_grpc.BootstrapStub(channel)
+
+        findNclick(stub, 'Testmemo')
+        back(stub)
+        flick(stub)
+        installApp(stub)
+        time.sleep(1)
+        launchApp(stub)
+        time.sleep(1)
+        getAppInfo(stub)
+        time.sleep(1)
+        closeApp(stub)
+        time.sleep(1)
+#        removeApp(stub)
+        flick(stub)
+        touchdown(stub, 300, 300)
+        touchmove(stub, 250, 250)
+        touchmove(stub, 200, 200)
+        touchmove(stub, 110, 110)
+        touchup(stub, 100, 100)
+
+if __name__ == '__main__':
+    logging.basicConfig()
+    run()
diff --git a/protocol/examples/python/sample02.py b/protocol/examples/python/sample02.py
new file mode 100644 (file)
index 0000000..e49b583
--- /dev/null
@@ -0,0 +1,130 @@
+from __future__ import print_function
+import aurum_pb2
+import aurum_pb2_grpc
+import logging
+import grpc
+import time
+
+def sendKey(stub, key):
+    rsp_key = stub.sendKey(aurum_pb2.ReqKey(
+                    type=key,
+                    actionType='STROKE',
+                )
+            )
+    time.sleep(1)
+
+def launchApp(stub, pkgname):
+    rsp_launch = stub.launchApp(aurum_pb2.ReqLaunchApp(
+           packageName=pkgname
+    ))
+
+def closeApp(stub, pkgname):
+    rsp_launch = stub.closeApp(aurum_pb2.ReqCloseApp(
+           packageName=pkgname
+    ))
+
+def getAppInfo(stub, pkgname):
+   rsp_info = stub.getAppInfo(aurum_pb2.ReqGetAppInfo(packageName=pkgname))
+   print(rsp_info)
+
+def touchdown(stub, xx, yy):
+   rsp = stub.touchDown(aurum_pb2.ReqTouchDown(coordination=aurum_pb2.Point(x=xx,y=yy)))
+   print(rsp)
+
+def touchmove(stub, xx, yy):
+   rsp = stub.touchMove(aurum_pb2.ReqTouchMove(coordination=aurum_pb2.Point(x=xx,y=yy)))
+   print(rsp)
+
+def touchup(stub, xx, yy):
+   rsp = stub.touchUp(aurum_pb2.ReqTouchUp(coordination=aurum_pb2.Point(x=xx,y=yy)))
+   print(rsp)
+
+def sync(stub):
+   rsp = stub.sync(aurum_pb2.ReqEmpty())
+   print(rsp)
+
+
+def new_memo(stub):
+    touchdown(stub, 630,1140)
+    time.sleep(0.1)
+    touchup(stub, 630,1140)
+    time.sleep(0.5)
+
+def findElementByText(stub, text):
+    rsp_find = stub.findElement(aurum_pb2.ReqFindElement(
+                strategy='TEXT',
+                textField=text
+            )
+        )
+    for item in rsp_find.elements:
+        return item.elementId
+    return None
+
+def findElementsByText(stub, text):
+    rsp_find = stub.findElement(aurum_pb2.ReqFindElement(
+                strategy='TEXT',
+                textField=text
+            )
+        )
+    return rsp_find.elements
+
+def clickById(stub, id):
+    stub.click(aurum_pb2.ReqClick(
+                type='ELEMENTID',
+                elementId=id
+            )
+        )
+
+def run_memo(stub):
+    foundId = findElementByText(stub, 'All apps')
+    time.sleep(1)
+    if foundId != None:
+        clickById(stub, foundId)
+        time.sleep(1)
+
+        foundId = findElementByText(stub, 'Memo')
+        time.sleep(1)
+        if foundId != None:
+            clickById(stub, foundId)
+            time.sleep(2)
+
+def set_text(stub, text):
+    foundId = findElementByText(stub, 'Title')
+    if foundId != None:
+      clickById(stub, foundId)
+      time.sleep(1.2)
+      stub.setValue(aurum_pb2.ReqSetValue(
+               elementId=foundId,
+               stringValue=text))
+    foundIds = findElementsByText(stub, "Memo")
+    if len(foundIds) >= 2:
+        stub.setValue(aurum_pb2.ReqSetValue(
+                   elementId=foundIds[1].elementId,
+                   stringValue=text))
+    time.sleep(0.2)
+    foundId = findElementByText(stub, 'DONE')
+    if foundId != None:
+      clickById(stub, foundId)
+
+
+def run():
+    with grpc.insecure_channel('127.0.0.1:50051') as channel:
+        stub = aurum_pb2_grpc.BootstrapStub(channel)
+
+        getAppInfo(stub, 'org.tizen.memo')
+        time.sleep(1)
+        sendKey(stub, 'HOME')
+        time.sleep(1)
+        closeApp(stub, 'org.tizen.memo')
+        time.sleep(1)
+        getAppInfo(stub, 'org.tizen.memo')
+        time.sleep(1)
+        run_memo(stub)
+        time.sleep(1)
+        new_memo(stub)
+        time.sleep(1)
+        set_text(stub, 'hello')
+
+if __name__ == '__main__':
+    logging.basicConfig()
+    run()
diff --git a/protocol/examples/python/sample03.py b/protocol/examples/python/sample03.py
new file mode 100644 (file)
index 0000000..65e4420
--- /dev/null
@@ -0,0 +1,116 @@
+from __future__ import print_function
+import aurum_pb2
+import aurum_pb2_grpc
+import logging
+import grpc
+import time
+
+def sendKey(stub, key):
+    rsp_key = stub.sendKey(aurum_pb2.ReqKey(
+                    type=key,
+                    actionType='STROKE',
+                )
+            )
+    time.sleep(1.5)
+
+def launchApp(stub, pkgname):
+    rsp_launch = stub.launchApp(aurum_pb2.ReqLaunchApp(
+           packageName=pkgname
+    ))
+
+def closeApp(stub, pkgname):
+    rsp_launch = stub.closeApp(aurum_pb2.ReqCloseApp(
+           packageName=pkgname
+    ))
+
+def getAppInfo(stub, pkgname):
+   rsp_info = stub.getAppInfo(aurum_pb2.ReqGetAppInfo(packageName=pkgname))
+   print(rsp_info)
+
+def touchdown(stub, xx, yy):
+   rsp = stub.touchDown(aurum_pb2.ReqTouchDown(coordination=aurum_pb2.Point(x=xx,y=yy)))
+   print(rsp)
+
+def touchmove(stub, xx, yy):
+   rsp = stub.touchMove(aurum_pb2.ReqTouchMove(coordination=aurum_pb2.Point(x=xx,y=yy)))
+   print(rsp)
+
+def touchup(stub, xx, yy):
+   rsp = stub.touchUp(aurum_pb2.ReqTouchUp(coordination=aurum_pb2.Point(x=xx,y=yy)))
+   print(rsp)
+
+def sync(stub):
+   rsp = stub.sync(aurum_pb2.ReqEmpty())
+   print(rsp)
+
+
+def new_memo(stub):
+    touchdown(stub, 630,1140)
+    time.sleep(0.1)
+    touchup(stub, 630,1140)
+    time.sleep(0.3)
+
+def findElementByText(stub, text):
+    rsp_find = stub.findElement(aurum_pb2.ReqFindElement(
+                strategy='TEXT',
+                textField=text
+            )
+        )
+    for item in rsp_find.elements:
+        return item.elementId
+    return None
+
+def findElementsByText(stub, text):
+    rsp_find = stub.findElement(aurum_pb2.ReqFindElement(
+                strategy='TEXT',
+                textField=text
+            )
+        )
+    els = []
+    for el in rsp_find.elements:
+        els.append(el.elementId)
+    return els
+
+def clickById(stub, id):
+    stub.click(aurum_pb2.ReqClick(
+                type='ELEMENTID',
+                elementId=id
+            )
+        )
+
+def run_memo(stub):
+    foundId = findElementByText(stub, 'All apps')
+    time.sleep(1.5)
+    if foundId != None:
+        clickById(stub, foundId)
+        time.sleep(1.5)
+
+        foundId = findElementByText(stub, 'Memo')
+        time.sleep(1.5)
+        if foundId != None:
+            clickById(stub, foundId)
+            time.sleep(2.5)
+
+def set_text(stub, text):
+    foundIds = findElementsByText(stub, "Memo")
+    foundIds += (findElementsByText(stub, "Title"))
+    print(foundIds)
+    for el in foundIds:
+        print(el)
+        stub.setValue(aurum_pb2.ReqSetValue(
+                   elementId=el,
+                   stringValue=text))
+
+    #foundId = findElementByText(stub, 'DONE')
+    #if foundId != None:
+    #  clickById(stub, foundId)
+
+
+def run():
+    with grpc.insecure_channel('127.0.0.1:50051') as channel:
+        stub = aurum_pb2_grpc.BootstrapStub(channel)
+        set_text(stub, 'hello')
+
+if __name__ == '__main__':
+    logging.basicConfig()
+    run()
diff --git a/protocol/meson.build b/protocol/meson.build
new file mode 100644 (file)
index 0000000..048bcdf
--- /dev/null
@@ -0,0 +1,27 @@
+grpc_deps = []
+grpc_deps += dependency('protobuf')
+grpc_deps += dependency('grpc')
+grpc_deps += dependency('grpc++')
+
+grpc_protoc_prog = find_program('protoc')
+grpc_cpp_plug_prog = find_program('grpc_cpp_plugin')
+
+grpc_pb_gen = generator(
+    grpc_protoc_prog,
+    output : ['@BASENAME@.pb.cc', '@BASENAME@.pb.h'],
+    arguments : ['--proto_path=@CURRENT_SOURCE_DIR@', '--cpp_out=@BUILD_DIR@', '@INPUT@']
+)
+
+grpc_grpc_gen = generator(
+    grpc_protoc_prog,
+    output : ['@BASENAME@.grpc.pb.cc', '@BASENAME@.grpc.pb.h'],
+    arguments : ['--proto_path=@CURRENT_SOURCE_DIR@', '--grpc_out=@BUILD_DIR@', '--plugin=protoc-gen-grpc='+grpc_cpp_plug_prog.path(), '@INPUT@']
+)
+
+grpc_pb_src = grpc_pb_gen.process(files('aurum.proto'))
+grpc_src = grpc_grpc_gen.process(files('aurum.proto'))
+
+ui_protocol_bin = executable('ui_protocol_bin',
+    files('empty.cpp'), grpc_pb_src, grpc_src,
+    dependencies: [grpc_deps]
+)
diff --git a/tests/meson.build b/tests/meson.build
new file mode 100644 (file)
index 0000000..9d39472
--- /dev/null
@@ -0,0 +1,19 @@
+test_src = [
+    files('ua_test.cpp'),
+]
+
+test_inc = [
+    include_directories('./')
+]
+
+test_deps = [
+    dependency('gtest', main:true),
+    libaurum,
+]
+
+test_bin = executable('ua_test', test_src, 
+                    include_directories: test_inc,
+                    dependencies: test_deps,
+            )
+
+test('ua test', test_bin)
\ No newline at end of file
diff --git a/tests/ua_test.cpp b/tests/ua_test.cpp
new file mode 100644 (file)
index 0000000..2587e72
--- /dev/null
@@ -0,0 +1,32 @@
+#include <gtest/gtest.h>
+
+#include <UiDevice.h>
+#include <UiObject.h>
+#include <UiSelector.h>
+#include <Sel.h>
+
+class UaTest : public ::testing::Test {
+    protected:
+        void SetUp() override {
+        }
+
+        void TearDown() override {
+        }
+};
+
+TEST_F(UaTest, EmptyTest)
+{
+    ASSERT_EQ(true, true);
+}
+
+TEST_F(UaTest, DeviceInit)
+{
+    const UiDevice *mDevice = UiDevice::getInstance(DeviceType::DEFAULT);
+    ASSERT_NE(mDevice, nullptr);
+}
+
+TEST_F(UaTest, TextSelector)
+{
+    std::unique_ptr<UiSelector> sel = Sel::text("test");
+    ASSERT_NE(sel.get(), nullptr);
+}
\ No newline at end of file