Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8237cd62da | |||
| 181759ff26 | |||
| 7b79636681 | |||
| 4dee8c62c9 | |||
| 9a9f5058ed | |||
| ff56bfce04 | |||
| 098b79b331 | |||
| 20154d1e95 | |||
| 64baa7906b | |||
| d31530e0bd | |||
| a80db04dac | |||
| 0a6f7feeca | |||
| 091d7ceeba | |||
| cfdeb17639 | |||
| aaae3dcbb2 | |||
| 4eb0ef75bc | |||
| 09caf314f1 | |||
| a4f96c8dfa | |||
| 660f0f0e73 | |||
| edf51a4441 | |||
| 8e94e829d0 | |||
| 3f9b406fb2 | |||
| 0a36f7d370 | |||
| b85d6f76a6 | |||
| c8a7a6678f | |||
| c6577b1155 | |||
| 8aa28a877e | |||
| bffc32519b | |||
| 876526364b | |||
| ce2d47e6b9 | |||
| 870b8de249 | |||
| 4a9d2cb546 | |||
| c696db9e45 | |||
| d2d5b8960f | |||
| 49d03df73b | |||
| 9e35748d9a |
Vendored
+11
-1
@@ -63,7 +63,17 @@
|
||||
"typeinfo": "cpp",
|
||||
"variant": "cpp",
|
||||
"cstring": "cpp",
|
||||
"cinttypes": "cpp"
|
||||
"cinttypes": "cpp",
|
||||
"any": "cpp",
|
||||
"codecvt": "cpp",
|
||||
"complex": "cpp",
|
||||
"coroutine": "cpp",
|
||||
"csignal": "cpp",
|
||||
"list": "cpp",
|
||||
"source_location": "cpp",
|
||||
"future": "cpp",
|
||||
"shared_mutex": "cpp",
|
||||
"typeindex": "cpp"
|
||||
},
|
||||
"editor.rulers": [80, 120]
|
||||
}
|
||||
+18
-7
@@ -1,7 +1,7 @@
|
||||
AC_INIT([Harriman-Peikoff Project], [0.00.002],
|
||||
[latentprion@gmail.com],
|
||||
[harikoff],
|
||||
[github.com/latentprion/harikoff])
|
||||
[http://github.com/latentprion/harikoff])
|
||||
|
||||
AC_CONFIG_SRCDIR([hcore/mind.cpp])
|
||||
AC_CONFIG_AUX_DIR([autotools-aux])
|
||||
@@ -31,29 +31,40 @@ AC_DEFINE_UNQUOTED([CONFIG_MIND_VOSCILLATOR_FREQ_MS],
|
||||
[${MIND_VOSCILLATOR_FREQ_MS}],
|
||||
[Frequency of the mind virtual oscillator in milliseconds])
|
||||
|
||||
m4_include([m4/ax_boost_base.m4])
|
||||
m4_include([m4/ax_boost_asio_1.69.0.m4])
|
||||
m4_include([m4/ac_prog_flex.m4])
|
||||
m4_include([m4/ac_prog_bison.m4])
|
||||
AC_PROG_CC
|
||||
AC_PROG_CXX
|
||||
AM_PROG_AR
|
||||
LT_INIT([shared])
|
||||
AC_PROG_LEX(noyywrap)
|
||||
AC_PROG_LEX([noyywrap])
|
||||
AC_PROG_YACC
|
||||
AS_IF([test -z "${LEX}" || test -z "${YACC}"], [
|
||||
AC_MSG_ERROR([LEX and YACC must both be available in PATH.])
|
||||
])
|
||||
|
||||
AC_SEARCH_LIBS([dlopen], [dl ldl], [], [
|
||||
AC_MSG_ERROR([dlopen() not found in libdl or libldl.])
|
||||
AX_BOOST_BASE([1.69.0], [], [AC_MSG_ERROR(m4_normalize([Boost v1.69.0 or higher
|
||||
is required, because Boost.System is header-only from 1.69.0 onwards.]))])
|
||||
AX_BOOST_ASIO_gte_1_69_0
|
||||
AS_IF([test "x${HAVE_BOOST_ASIO}" == "x"], [
|
||||
AC_MSG_ERROR(m4_normalize([Boost.Asio must be available in headers.
|
||||
Try --with-boost-asio if need be.]))
|
||||
])
|
||||
|
||||
AM_CPPFLAGS=m4_normalize(["-I\"\$(top_srcdir)/include\""])
|
||||
AC_SEARCH_LIBS([dlopen], [dl ldl], [], [
|
||||
AC_MSG_ERROR([dlopen() not found in libdl or libldl.])])
|
||||
AC_SEARCH_LIBS([dlsym], [dl ldl], [], [
|
||||
AC_MSG_ERROR([dlsym() not found in libdl or libldl.])])
|
||||
|
||||
AM_CPPFLAGS=m4_normalize(["-I\"\$(top_srcdir)/include\"
|
||||
-Wall -Wextra -pedantic"])
|
||||
|
||||
AC_SUBST([AM_CPPFLAGS])
|
||||
AC_SUBST([YACC])
|
||||
AC_SUBST([LEX])
|
||||
|
||||
m4_include([m4/senseApis.m4])
|
||||
m4_include([m4/sense_api_opts.m4])
|
||||
AC_SUBST([SENSEAPIS_ENABLED])
|
||||
|
||||
AC_CONFIG_HEADERS([include/config.h])
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# DeviceSpec: API `x11`, server `xorg`:
|
||||
|
||||
|
||||
|
||||
* `xwininfo` is relevant.
|
||||
@@ -0,0 +1,121 @@
|
||||
# DeviceSpec: API `xcb`, provider `xorg`
|
||||
|
||||
## Overview
|
||||
|
||||
The `xcb` API with the `xorg` provider allows Harikoff to interact with Xorg
|
||||
server windows. This can be used to capture visual data from specific windows
|
||||
or entire screens managed by the Xorg server.
|
||||
|
||||
## DeviceSpec Format
|
||||
|
||||
The general format of a device-spec for the `xcb` API with the `xorg` provider
|
||||
is:
|
||||
```
|
||||
sensor-type|implexor|xcb(api-params)|xorg(provider-params)|deviceSelector
|
||||
```
|
||||
|
||||
* `sensor-type` is always either '`+idev`' (interoceptor), '`+edev`'
|
||||
(extrospector), or '`+adev`' (actuator).
|
||||
* `implexor` is the name of the implexor algorithm that should be used with
|
||||
the data that is provided by the `provider` via the `api`.
|
||||
* `api` is `xcb` in this case, and the `api-params` in parentheses may be
|
||||
omitted, in which case the parentheses will be empty, but the parentheses
|
||||
must always be written out.
|
||||
* `provider` is `xorg` in this case, and the `provider-params` in parentheses
|
||||
may be omitted, in which case the parentheses will be empty, but the
|
||||
parentheses must always be written out.
|
||||
* `deviceSelector` is the idiosyncratic label/name used by the `provider` to
|
||||
identify the specific window or screen you want to access via that
|
||||
`provider`.
|
||||
|
||||
## `api-params` and `provider-params`
|
||||
|
||||
### `api-params`
|
||||
|
||||
The `api-params` for the `xcb` API can include the following:
|
||||
|
||||
* `dev-id` or `devid`: Specifies that the `deviceSelector` is a numeric window
|
||||
ID. The ID can be specified in decimal or hexadecimal format.
|
||||
* `dev-string`, `dev-str`, `devstr`, or `devstring`: Specifies that the
|
||||
`deviceSelector` is an exact window name.
|
||||
* `dev-substring`, `dev-substr`, `devsubstr`, or `devsubstring`: Specifies
|
||||
that the `deviceSelector` is a substring match (default).
|
||||
|
||||
Example:
|
||||
```
|
||||
xcb(dev-id)|123456
|
||||
xcb(dev-id)|0x1e240
|
||||
xcb(dev-string)|exact-window-name
|
||||
xcb(dev-substring)|window-name
|
||||
```
|
||||
|
||||
### `provider-params`
|
||||
|
||||
The `provider-params` for the `xorg` provider can include the following:
|
||||
|
||||
* `display`: The display number (e.g., `0` for `:0`).
|
||||
* `screen`: The screen number within the display (e.g., `0` for the first
|
||||
screen).
|
||||
|
||||
Example:
|
||||
```
|
||||
xorg(display=0|screen=0)
|
||||
```
|
||||
|
||||
## `deviceSelector`
|
||||
|
||||
The `deviceSelector` can be one of the following:
|
||||
|
||||
* A numeric window ID.
|
||||
* A window name (exact match or substring match).
|
||||
|
||||
### Matching Types
|
||||
|
||||
* By default, the `deviceSelector` is treated as a substring match.
|
||||
* To specify an exact match, use the `dev-string` parameter.
|
||||
* To specify a numeric window ID, use the `dev-id` parameter.
|
||||
|
||||
Example:
|
||||
```
|
||||
xcb(dev-substring)|window-name
|
||||
xcb(dev-string)|exact-window-name
|
||||
xcb(dev-id)|123456
|
||||
```
|
||||
|
||||
## Escaping Whitespace
|
||||
|
||||
In `deviceSelector` and other string tokens, whitespace can be included by
|
||||
prefixing it with a backslash (`\`). The backslash will be discarded during
|
||||
parsing.
|
||||
|
||||
Example:
|
||||
```
|
||||
xcb(dev-string)|My\ Exact\ Window\ Name
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### To attach a specific window by name (substring match):
|
||||
```
|
||||
+edev|visual-implexor|xcb(dev-substring)|xorg(display=0|screen=0)|my-window
|
||||
```
|
||||
This will attach to a window whose name contains "my-window" as a substring.
|
||||
|
||||
### To attach a specific window by exact name:
|
||||
```
|
||||
+edev|visual-implexor|xcb(dev-string)|xorg(display=0|screen=0)|My\ Exact\ Window\ Name
|
||||
```
|
||||
This will attach to a window whose name exactly matches "My Exact Window Name".
|
||||
|
||||
### To attach a specific window by numeric ID:
|
||||
```
|
||||
+edev|visual-implexor|xcb(dev-id)|xorg(display=0|screen=0)|123456
|
||||
```
|
||||
This will attach to a window with the numeric ID `123456`.
|
||||
|
||||
### To attach the entire screen:
|
||||
```
|
||||
+edev|visual-implexor|xcb()|xorg(display=0|screen=0)|all
|
||||
```
|
||||
This will attach to the entire screen `0` of display `0`.
|
||||
|
||||
@@ -46,6 +46,11 @@ If there's more than one parameter item in a list of `api-params` or
|
||||
+edev|audio-implexor|alsa(shmem|param2|param3)|alsa()|cardname
|
||||
```
|
||||
|
||||
Each parameter must be in one of these forms:
|
||||
* key=value
|
||||
* key=
|
||||
* key
|
||||
|
||||
Some examples follow:
|
||||
|
||||
### To attach a particular window from a window manager:
|
||||
|
||||
+2
-1
@@ -2,4 +2,5 @@ SUBDIRS = deviceManager senseApis
|
||||
AM_CPPFLAGS+= -I"$(top_srcdir)/hcore/include"
|
||||
|
||||
noinst_LIBRARIES = libhcore.a
|
||||
libhcore_a_SOURCES = mind.cpp opts.cpp
|
||||
libhcore_a_SOURCES = mind.cpp opts.cpp componentThread.cpp
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
#include <iostream>
|
||||
#include <componentThread.h>
|
||||
|
||||
namespace hk {
|
||||
|
||||
namespace director {
|
||||
ComponentThread director;
|
||||
}
|
||||
namespace simulator {
|
||||
ComponentThread canvas;
|
||||
}
|
||||
namespace subconscious {
|
||||
ComponentThread subconscious;
|
||||
}
|
||||
|
||||
std::unordered_map<std::thread::id, ComponentThread&>
|
||||
ComponentThread::componentThreads =
|
||||
{
|
||||
{director::director.thread.get_id(), director::director},
|
||||
{simulator::canvas.thread.get_id(), simulator::canvas},
|
||||
{subconscious::subconscious.thread.get_id(), subconscious::subconscious}
|
||||
};
|
||||
|
||||
void ComponentThread::signalThread(std::thread::id id)
|
||||
{
|
||||
auto it = componentThreads.find(id);
|
||||
if (it == componentThreads.end())
|
||||
{
|
||||
throw std::runtime_error(std::string(__func__)
|
||||
+ ": Thread ID not found in componentThreads map");
|
||||
}
|
||||
|
||||
ComponentThread& componentThread = it->second;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(componentThread.startupSync.mutex);
|
||||
componentThread.startupSync.ready = true;
|
||||
}
|
||||
componentThread.startupSync.cv.notify_one();
|
||||
}
|
||||
|
||||
void ComponentThread::main(ComponentThread& self)
|
||||
{
|
||||
// We sleep on spawn until the main thread tells us to continue.
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(self.startupSync.mutex);
|
||||
self.startupSync.cv.wait(lock, [&self]() {
|
||||
return self.startupSync.ready;
|
||||
});
|
||||
}
|
||||
|
||||
std::cout << __func__ << ": Starting event loop." << std::endl;
|
||||
self.getIoService().run();
|
||||
std::cout << __func__ << ": Exiting." << std::endl;
|
||||
}
|
||||
|
||||
void ComponentThread::validateThreadIds(void)
|
||||
{
|
||||
for (const auto& [id, componentThread] : componentThreads)
|
||||
{
|
||||
// std::thread::id() is usable as an invalid ID.
|
||||
if (id == std::thread::id())
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": Invalid Thread ID.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace hk
|
||||
@@ -8,41 +8,30 @@
|
||||
#include <opts.h>
|
||||
#include <deviceManager/deviceManager.h>
|
||||
|
||||
std::vector<std::unique_ptr<DeviceManager::InteroceptorDeviceSpec>>
|
||||
namespace hk {
|
||||
namespace device {
|
||||
|
||||
std::vector<std::shared_ptr<InteroceptorDeviceSpec>>
|
||||
DeviceManager::interoceptorDeviceSpecs;
|
||||
std::vector<std::unique_ptr<DeviceManager::ExtrospectorDeviceSpec>>
|
||||
std::vector<std::shared_ptr<ExtrospectorDeviceSpec>>
|
||||
DeviceManager::extrospectorDeviceSpecs;
|
||||
std::vector<std::reference_wrapper<DeviceManager::SensorDeviceSpec>>
|
||||
DeviceManager::sensorDeviceSpecs;
|
||||
std::vector<std::shared_ptr<SenseDeviceSpec>>
|
||||
DeviceManager::senseDeviceSpecs;
|
||||
|
||||
std::ostream& operator<<(
|
||||
std::ostream& os, const DeviceManager::SensorDeviceSpec& spec)
|
||||
{
|
||||
os << "Device: " << spec.sensorType << ", Implexor: "
|
||||
<< spec.implexor << ", API: " << spec.api
|
||||
<< ", API Params: (";
|
||||
for (auto it = spec.apiParams.begin(); it != spec.apiParams.end(); ++it) {
|
||||
os << *it << (it + 1 == spec.apiParams.end() ? "" : " ");
|
||||
}
|
||||
os << "), Provider: " << spec.provider << ", Provider Params: (";
|
||||
for (auto it = spec.providerParams.begin(); it != spec.providerParams.end(); ++it) {
|
||||
os << *it << (it + 1 == spec.providerParams.end() ? "" : " ");
|
||||
}
|
||||
os << "), Device Selector: " << spec.deviceSelector << std::endl;
|
||||
return os;
|
||||
}
|
||||
|
||||
const std::string DeviceManager::printDeviceSpecs(void)
|
||||
const std::string DeviceManager::stringifyDeviceSpecs(void)
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
for (const auto& spec : DeviceManager::interoceptorDeviceSpecs) {
|
||||
oss << "Interoceptor " << *spec;
|
||||
oss << "Interoceptor " << spec->stringify();
|
||||
}
|
||||
|
||||
for (const auto& spec : DeviceManager::extrospectorDeviceSpecs) {
|
||||
oss << "Extrospector " << *spec;
|
||||
oss << "Extrospector " << spec->stringify();
|
||||
}
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
} // namespace device
|
||||
} // namespace hk
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
#include "deviceSpecp.hh"
|
||||
#include "deviceSpecl.hh"
|
||||
|
||||
namespace hk {
|
||||
namespace device {
|
||||
|
||||
std::string DeviceManager::readDeviceFile(const std::string& filename)
|
||||
{
|
||||
std::ifstream file(filename);
|
||||
@@ -23,11 +26,12 @@ std::string DeviceManager::readDeviceFile(const std::string& filename)
|
||||
(std::istreambuf_iterator<char>(file)),
|
||||
std::istreambuf_iterator<char>());
|
||||
|
||||
return std::move(content);
|
||||
return content;
|
||||
}
|
||||
|
||||
void DeviceManager::collateAllDeviceSpecs(const OptionParser& options)
|
||||
void DeviceManager::collateAllDeviceSpecs(void)
|
||||
{
|
||||
OptionParser &options = OptionParser::getOptions();
|
||||
allDeviceSpecs = options.deviceSpecs;
|
||||
|
||||
for (const auto& file : options.deviceSpecFiles)
|
||||
@@ -60,3 +64,6 @@ void DeviceManager::parseAllDeviceSpecs(void)
|
||||
"Check specs for errors");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace device
|
||||
} // namespace hk
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
%option prefix="deviceSpecl"
|
||||
%option nounput
|
||||
%{
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <deviceManager/deviceManager.h>
|
||||
#include "deviceSpecp.hh"
|
||||
%}
|
||||
@@ -22,7 +25,13 @@
|
||||
"|" { return PIPE; }
|
||||
"(" { return LPAREN; }
|
||||
")" { return RPAREN; }
|
||||
[^\|\(\) \t\r\n]+ { deviceSpecplval.str = strdup(yytext); return STRING; }
|
||||
[ \t]*"="[ \t]* { return EQUALS; } // Allow optional whitespace around equals
|
||||
([^=\|\(\) \t\r\n]|\\[ \t])+ {
|
||||
std::string token(yytext);
|
||||
token.erase(std::remove(token.begin(), token.end(), '\\'), token.end());
|
||||
deviceSpecplval.str = strdup(token.c_str());
|
||||
return STRING;
|
||||
}
|
||||
\r?\n { /* ignore newlines */ }
|
||||
[ \t]+ { /* ignore whitespace */ }
|
||||
. { return yytext[0]; }
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <user/senseDeviceSpec.h>
|
||||
#include <deviceManager/deviceManager.h>
|
||||
|
||||
#ifndef yylex
|
||||
@@ -24,13 +25,20 @@
|
||||
#undef yylex
|
||||
#define yylex deviceSpecllex
|
||||
|
||||
#ifdef yytext
|
||||
#undef yytext
|
||||
#endif
|
||||
#define yytext deviceSpecltext
|
||||
|
||||
// Declare the symbols that our lexer will export.
|
||||
int yylex(void);
|
||||
extern char* yytext; // Declare yytext to access the current token text
|
||||
void yyerror(const char *message)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
throw std::runtime_error(
|
||||
std::string("deviceSpec parser error: ")
|
||||
+ std::string(message));
|
||||
+ std::string(message)
|
||||
+ " at token: " + std::string(yytext));
|
||||
}
|
||||
|
||||
%}
|
||||
@@ -38,18 +46,21 @@ void yyerror(const char *message)
|
||||
%union {
|
||||
char* str;
|
||||
char chr;
|
||||
DeviceManager::SensorDeviceSpec* sensorSpec;
|
||||
DeviceManager::InteroceptorDeviceSpec* interoceptorSpec;
|
||||
DeviceManager::ExtrospectorDeviceSpec* extrospectorSpec;
|
||||
std::vector<std::string>* stringVector;
|
||||
hk::device::SenseDeviceSpec* sensorSpec;
|
||||
hk::device::InteroceptorDeviceSpec* interoceptorSpec;
|
||||
hk::device::ExtrospectorDeviceSpec* extrospectorSpec;
|
||||
std::vector<std::pair<std::string,std::string>>* paramVector;
|
||||
std::pair<std::string,std::string>* param;
|
||||
}
|
||||
|
||||
%token <str> STRING
|
||||
%token PIPE DOUBLE_PIPE LPAREN RPAREN
|
||||
%token <chr> KEYWORD_SPECTYPE_ACTUATOR
|
||||
%token <chr> KEYWORD_SPECTYPE_EXTROSPECTOR KEYWORD_SPECTYPE_INTEROSPECTOR
|
||||
%token EQUALS // Add new token for '='
|
||||
|
||||
%type <stringVector> params opt_params
|
||||
%type <paramVector> params opt_params
|
||||
%type <param> param
|
||||
%type <sensorSpec> spec_body
|
||||
%type <interoceptorSpec> interoceptor_spec
|
||||
%type <extrospectorSpec> extrospector_spec
|
||||
@@ -72,31 +83,33 @@ sensor_spec:
|
||||
|
||||
interoceptor_spec:
|
||||
KEYWORD_SPECTYPE_INTEROSPECTOR PIPE spec_body {
|
||||
auto spec = std::make_unique<DeviceManager::InteroceptorDeviceSpec>(
|
||||
*static_cast<DeviceManager::InteroceptorDeviceSpec *>($3));
|
||||
auto spec = std::make_shared<hk::device::InteroceptorDeviceSpec>(
|
||||
*static_cast<hk::device::InteroceptorDeviceSpec *>($3));
|
||||
|
||||
spec->sensorType = $1;
|
||||
DeviceManager::interoceptorDeviceSpecs.push_back(std::move(spec));
|
||||
DeviceManager::sensorDeviceSpecs.push_back(*spec);
|
||||
hk::device::DeviceManager::interoceptorDeviceSpecs.push_back(spec);
|
||||
hk::device::DeviceManager::senseDeviceSpecs.push_back(spec);
|
||||
|
||||
delete $3;
|
||||
}
|
||||
;
|
||||
|
||||
extrospector_spec:
|
||||
KEYWORD_SPECTYPE_EXTROSPECTOR PIPE spec_body {
|
||||
auto spec = std::make_unique<DeviceManager::ExtrospectorDeviceSpec>(
|
||||
*static_cast<DeviceManager::ExtrospectorDeviceSpec *>($3));
|
||||
auto spec = std::make_shared<hk::device::ExtrospectorDeviceSpec>(
|
||||
*static_cast<hk::device::ExtrospectorDeviceSpec *>($3));
|
||||
|
||||
spec->sensorType = $1;
|
||||
DeviceManager::extrospectorDeviceSpecs.push_back(std::move(spec));
|
||||
DeviceManager::sensorDeviceSpecs.push_back(*spec);
|
||||
hk::device::DeviceManager::extrospectorDeviceSpecs.push_back(spec);
|
||||
hk::device::DeviceManager::senseDeviceSpecs.push_back(spec);
|
||||
|
||||
delete $3;
|
||||
}
|
||||
;
|
||||
|
||||
spec_body:
|
||||
STRING PIPE STRING LPAREN opt_params RPAREN PIPE STRING LPAREN opt_params RPAREN PIPE STRING {
|
||||
$$ = new DeviceManager::SensorDeviceSpec();
|
||||
$$ = new hk::device::SenseDeviceSpec();
|
||||
$$->sensorType = '\0';
|
||||
$$->implexor = std::string($1);
|
||||
$$->api = std::string($3);
|
||||
@@ -111,12 +124,32 @@ spec_body:
|
||||
|
||||
opt_params:
|
||||
params
|
||||
| /* empty */ { $$ = new std::vector<std::string>(); }
|
||||
| /* empty */ { $$ = new std::vector<std::pair<std::string,std::string>>(); }
|
||||
;
|
||||
|
||||
params:
|
||||
STRING { $$ = new std::vector<std::string>{ $1 }; }
|
||||
| params PIPE STRING { $$ = $1; $$->push_back($3); }
|
||||
param {
|
||||
$$ = new std::vector<std::pair<std::string,std::string>>();
|
||||
$$->push_back(*$1);
|
||||
delete $1;
|
||||
}
|
||||
| params PIPE param {
|
||||
$$ = $1;
|
||||
$$->push_back(*$3);
|
||||
delete $3;
|
||||
}
|
||||
;
|
||||
|
||||
param:
|
||||
STRING {
|
||||
$$ = new std::pair<std::string,std::string>($1, "");
|
||||
}
|
||||
| STRING EQUALS {
|
||||
$$ = new std::pair<std::string,std::string>($1, "");
|
||||
}
|
||||
| STRING EQUALS STRING {
|
||||
$$ = new std::pair<std::string,std::string>($1, $3);
|
||||
}
|
||||
;
|
||||
|
||||
%%
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
#ifndef COMPONENT_THREAD_H
|
||||
#define COMPONENT_THREAD_H
|
||||
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <condition_variable>
|
||||
#include <boost/asio.hpp>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace hk {
|
||||
|
||||
class ComponentThread
|
||||
{
|
||||
public:
|
||||
ComponentThread()
|
||||
: work(io_service), startupSync(),
|
||||
thread(ComponentThread::main, std::ref(*this))
|
||||
{}
|
||||
|
||||
boost::asio::io_service& getIoService(void) { return io_service; }
|
||||
|
||||
static boost::asio::io_service& getEventLoop(
|
||||
std::thread::id id = std::this_thread::get_id())
|
||||
{
|
||||
auto it = componentThreads.find(id);
|
||||
if (it == componentThreads.end())
|
||||
{
|
||||
throw std::runtime_error(std::string(__func__)
|
||||
+ ": Thread ID not found in componentThreads map");
|
||||
}
|
||||
|
||||
return it->second.getIoService();
|
||||
}
|
||||
|
||||
static void main(ComponentThread &self);
|
||||
static void signalThread(std::thread::id id);
|
||||
static void validateThreadIds(void);
|
||||
|
||||
public:
|
||||
boost::asio::io_service io_service;
|
||||
boost::asio::io_service::work work;
|
||||
struct StartupSync {
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
bool ready;
|
||||
|
||||
StartupSync() : ready(false) {}
|
||||
} startupSync;
|
||||
|
||||
/* Always ensure that this is last so that the thread is spawned after
|
||||
* everything else.
|
||||
*/
|
||||
std::thread thread;
|
||||
static std::unordered_map<std::thread::id, ComponentThread&> componentThreads;
|
||||
};
|
||||
|
||||
namespace director {
|
||||
extern ComponentThread director;
|
||||
}
|
||||
namespace simulator {
|
||||
extern ComponentThread canvas;
|
||||
}
|
||||
namespace subconscious {
|
||||
extern ComponentThread subconscious;
|
||||
}
|
||||
|
||||
} // namespace hk
|
||||
|
||||
#endif // COMPONENT_THREAD_H
|
||||
@@ -7,32 +7,14 @@
|
||||
#include <opts.h>
|
||||
#include <utility>
|
||||
#include <iostream>
|
||||
#include <user/senseDeviceSpec.h>
|
||||
|
||||
namespace hk {
|
||||
namespace device {
|
||||
|
||||
class DeviceManager
|
||||
{
|
||||
public:
|
||||
struct SensorDeviceSpec
|
||||
{
|
||||
char sensorType;
|
||||
std::string implexor;
|
||||
std::string api;
|
||||
std::vector<std::string> apiParams;
|
||||
std::string provider;
|
||||
std::vector<std::string> providerParams;
|
||||
std::string deviceSelector;
|
||||
|
||||
friend std::ostream& operator<<(
|
||||
std::ostream& os, const SensorDeviceSpec& spec);
|
||||
};
|
||||
|
||||
struct InteroceptorDeviceSpec : public SensorDeviceSpec
|
||||
{
|
||||
};
|
||||
|
||||
struct ExtrospectorDeviceSpec : public SensorDeviceSpec
|
||||
{
|
||||
};
|
||||
|
||||
static DeviceManager& getInstance()
|
||||
{
|
||||
static DeviceManager instance;
|
||||
@@ -40,9 +22,9 @@ public:
|
||||
}
|
||||
|
||||
std::string readDeviceFile(const std::string& filename);
|
||||
void collateAllDeviceSpecs(const OptionParser& options);
|
||||
void collateAllDeviceSpecs(void);
|
||||
void parseAllDeviceSpecs(void);
|
||||
static const std::string printDeviceSpecs();
|
||||
static const std::string stringifyDeviceSpecs(void);
|
||||
|
||||
private:
|
||||
DeviceManager() = default;
|
||||
@@ -52,12 +34,15 @@ private:
|
||||
|
||||
public:
|
||||
std::string allDeviceSpecs;
|
||||
static std::vector<std::unique_ptr<InteroceptorDeviceSpec>>
|
||||
static std::vector<std::shared_ptr<InteroceptorDeviceSpec>>
|
||||
interoceptorDeviceSpecs;
|
||||
static std::vector<std::unique_ptr<ExtrospectorDeviceSpec>>
|
||||
static std::vector<std::shared_ptr<ExtrospectorDeviceSpec>>
|
||||
extrospectorDeviceSpecs;
|
||||
static std::vector<std::reference_wrapper<SensorDeviceSpec>>
|
||||
sensorDeviceSpecs;
|
||||
static std::vector<std::shared_ptr<SenseDeviceSpec>>
|
||||
senseDeviceSpecs;
|
||||
};
|
||||
|
||||
} // namespace device
|
||||
} // namespace hk
|
||||
|
||||
#endif // DEVICEMANAGER_H
|
||||
|
||||
@@ -12,8 +12,8 @@ public:
|
||||
OptionParser() : verbose(false), printUsage(false) {}
|
||||
~OptionParser() = default;
|
||||
|
||||
void parseArguments(int argc, char *argv[]);
|
||||
void dumpOptions() const;
|
||||
void parseArguments(int argc, char *argv[], char **envp);
|
||||
std::string stringifyOptions(void) const;
|
||||
std::string getUsage() const;
|
||||
|
||||
static OptionParser &getOptions(void)
|
||||
|
||||
@@ -11,85 +11,43 @@
|
||||
namespace hk {
|
||||
namespace sense_api {
|
||||
|
||||
/* C++ version of the C struct above, which Harikoff uses to manage the
|
||||
* lib and connect implexors to it.
|
||||
*/
|
||||
class SenseApiDesc
|
||||
{
|
||||
public:
|
||||
class ExportedImplexorApiDesc
|
||||
{
|
||||
public:
|
||||
ExportedImplexorApiDesc(const CExportedImplexorApiDesc& cDesc)
|
||||
// The caller should sanity check before calling this constructor.
|
||||
: name(cDesc.name)
|
||||
{}
|
||||
|
||||
const std::string name;
|
||||
};
|
||||
|
||||
public:
|
||||
SenseApiDesc() = default;
|
||||
SenseApiDesc(const CSenseApiDesc& cDesc)
|
||||
// The caller should sanity check before calling this constructor.
|
||||
: name(cDesc.name)
|
||||
{
|
||||
for (uint32_t i = 0; i < cDesc.numExportedImplexorApis; ++i)
|
||||
{
|
||||
if (!CExportedImplexorApiDesc_sanityCheck(
|
||||
&cDesc.exportedImplexorApis[i]))
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Sanity check failed for exported implexor API descriptor");
|
||||
}
|
||||
|
||||
exportedImplexorApis.push_back(
|
||||
ExportedImplexorApiDesc(cDesc.exportedImplexorApis[i]));
|
||||
}
|
||||
}
|
||||
|
||||
std::string name;
|
||||
// These are the implexors whose APIs this lib exports.
|
||||
std::vector<ExportedImplexorApiDesc> exportedImplexorApis;
|
||||
};
|
||||
|
||||
class SenseApiLib
|
||||
{
|
||||
private:
|
||||
friend class SenseApiManager;
|
||||
struct DlCloser
|
||||
{
|
||||
void operator()(void* handle) const
|
||||
{
|
||||
if (handle) {
|
||||
dlclose(handle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
SenseApiLib(
|
||||
const std::string& path, void *_dlopen_handle, getSenseApiDescFn *descFn)
|
||||
: libraryPath(path),
|
||||
dlopen_handle(
|
||||
_dlopen_handle, reinterpret_cast<void(*)(void*)>(&dlclose)),
|
||||
dlopen_handle(_dlopen_handle, DlCloser()),
|
||||
HK_GET_SENSE_API_DESC_FN_NAME(descFn)
|
||||
{}
|
||||
|
||||
void setSenseApiDesc(const CSenseApiDesc* desc)
|
||||
void setSenseApiDesc(const SenseApiDesc &desc)
|
||||
{
|
||||
if (!CSenseApiDesc_sanityCheck(desc) ||
|
||||
!Csal_libMgmtOps_sanityCheck(desc->sal_libMgmtOps))
|
||||
if (!SenseApiDesc::sanityCheck(desc))
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": Sanity check failed for sense API "
|
||||
"descriptor in library '" + libraryPath + "'");
|
||||
}
|
||||
for (uint32_t i = 0; i < desc->numExportedImplexorApis; ++i)
|
||||
{
|
||||
if (!CExportedImplexorApiDesc_sanityCheck(
|
||||
&desc->exportedImplexorApis[i]))
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": Sanity check failed for "
|
||||
"exported implexor API descriptor in library '"
|
||||
+ libraryPath + "'");
|
||||
}
|
||||
}
|
||||
new (&senseApiDesc) SenseApiDesc(*desc); // Placement new
|
||||
|
||||
senseApiDesc = desc;
|
||||
}
|
||||
|
||||
public:
|
||||
std::string libraryPath;
|
||||
std::unique_ptr<void, void(*)(void*)> dlopen_handle;
|
||||
std::unique_ptr<void, DlCloser> dlopen_handle;
|
||||
/* UNIMPLEMENTED: API-specific cmdline options. These affect this specific
|
||||
* sense api lib's behaviour globally.
|
||||
*/
|
||||
@@ -97,7 +55,7 @@ public:
|
||||
|
||||
/**
|
||||
* @brief Every sense API lib is required to provide a function that returns
|
||||
* a CSenseApiDesc struct. This struct states which API the lib uses to
|
||||
* a SenseApiDesc struct. This struct states which API the lib uses to
|
||||
* connect Harikoff to the sense provider it supports.
|
||||
*
|
||||
* This getter function should be visible to dlsym() so that Harikoff can
|
||||
@@ -111,6 +69,12 @@ public:
|
||||
* descriptor.
|
||||
*/
|
||||
SenseApiDesc senseApiDesc;
|
||||
|
||||
std::string stringify() const {
|
||||
std::string result = "Library Path: " + libraryPath + "\n";
|
||||
result += "Sense API Descriptor: " + senseApiDesc.stringify() + "\n";
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace sense_api
|
||||
|
||||
@@ -8,11 +8,13 @@
|
||||
#include <optional>
|
||||
#include <functional>
|
||||
#include <senseApis/senseApiLib.h>
|
||||
#include <user/senseDeviceSpec.h>
|
||||
|
||||
namespace hk {
|
||||
namespace sense_api {
|
||||
|
||||
class SenseApiManager {
|
||||
class SenseApiManager
|
||||
{
|
||||
public:
|
||||
static SenseApiManager& getInstance()
|
||||
{
|
||||
@@ -23,8 +25,24 @@ public:
|
||||
SenseApiLib& loadSenseApiLib(const std::string& libraryPath);
|
||||
std::optional<std::reference_wrapper<SenseApiLib>> getSenseApiLib(
|
||||
const std::string& libraryPath);
|
||||
std::optional<std::reference_wrapper<SenseApiLib>> getSenseApiLibByApiName(
|
||||
const std::string& apiName);
|
||||
void unloadSenseApiLib(const std::string& libraryPath);
|
||||
|
||||
void initializeSenseApiLib(SenseApiLib& lib);
|
||||
void finalizeSenseApiLib(SenseApiLib& lib);
|
||||
|
||||
void loadAllSenseApiLibsFromOptions(void);
|
||||
void unloadAllSenseApiLibs(void);
|
||||
void initializeAllSenseApiLibs(void);
|
||||
void finalizeAllSenseApiLibs(void);
|
||||
|
||||
void attachAllSenseDevicesFromSpecs(void);
|
||||
void attachSenseDevice(const device::SenseDeviceSpec& spec);
|
||||
void detachSenseDevice(const device::SenseDeviceSpec& spec);
|
||||
void detachAllSenseDevices(void);
|
||||
|
||||
std::string stringifyLibs() const;
|
||||
|
||||
private:
|
||||
SenseApiManager() = default;
|
||||
|
||||
+17
-11
@@ -5,6 +5,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sys/stat.h>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
struct option OptionParser::longOptions[] = {
|
||||
@@ -21,8 +22,9 @@ struct option OptionParser::longOptions[] = {
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
|
||||
void OptionParser::parseArguments(int argc, char *argv[])
|
||||
void OptionParser::parseArguments(int argc, char *argv[], char **envp)
|
||||
{
|
||||
(void)envp;
|
||||
int opt;
|
||||
int optionIndex = 0;
|
||||
|
||||
@@ -91,24 +93,28 @@ std::string OptionParser::getUsage() const
|
||||
"[-?|--help]";
|
||||
}
|
||||
|
||||
void OptionParser::dumpOptions() const
|
||||
std::string OptionParser::stringifyOptions(void) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
if (verbose) {
|
||||
std::cout << "Verbose mode is on" << std::endl;
|
||||
oss << "Verbose mode is on" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "Device Specs: " << deviceSpecs << std::endl;
|
||||
oss << "Device Specs: " << deviceSpecs << std::endl;
|
||||
|
||||
std::cout << "Device Spec Files: ";
|
||||
oss << "Device Spec Files: ";
|
||||
for (const auto& file : deviceSpecFiles) {
|
||||
std::cout << file << " ";
|
||||
oss << file << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
oss << std::endl;
|
||||
|
||||
std::cout << "Sense API Library Path: " << senseApiLibPath << std::endl;
|
||||
std::cout << "Sense API Libraries: ";
|
||||
oss << "Sense API Library Path: " << senseApiLibPath << std::endl;
|
||||
oss << "Sense API Libraries: ";
|
||||
for (const auto& lib : senseApiLibs) {
|
||||
std::cout << lib << " ";
|
||||
oss << lib << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
oss << std::endl;
|
||||
|
||||
return oss.str();
|
||||
}
|
||||
|
||||
@@ -6,20 +6,27 @@
|
||||
#include <senseApis/senseApiLib.h>
|
||||
#include <opts.h>
|
||||
#include <user/senseApiDesc.h>
|
||||
#include <deviceManager/deviceManager.h>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace hk {
|
||||
namespace sense_api {
|
||||
|
||||
struct DlCloser {
|
||||
void operator()(void* handle) const {
|
||||
if (handle) {
|
||||
dlclose(handle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Searches for a library in predefined locations
|
||||
* @param libraryPath The name or path of the library to find
|
||||
* @return Optional containing the full path if found in search paths, nullopt
|
||||
* if not
|
||||
*
|
||||
* Searches for the library in the following locations in order:
|
||||
* 1. Custom path specified by --sense-api-lib-path option (if provided)
|
||||
* 2. Current working directory
|
||||
* 3. Directory containing the executable
|
||||
*
|
||||
* If the library is not found in any of these locations, returns nullopt and
|
||||
* falls back to system default library search paths (LD_LIBRARY_PATH, etc.)
|
||||
*/
|
||||
static std::optional<std::string> findLibraryPath(
|
||||
const std::string& libraryPath)
|
||||
{
|
||||
@@ -45,26 +52,24 @@ static std::optional<std::string> findLibraryPath(
|
||||
|
||||
std::cerr << std::string(__func__) + ": library '"
|
||||
+ libraryPath + "' isn't in search bespoke search paths: ";
|
||||
for (const auto& path : searchPaths)
|
||||
{
|
||||
for (const auto& path : searchPaths) {
|
||||
std::cerr << path << " ";
|
||||
}
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "Trying to load " + libraryPath + " from system default search "
|
||||
"paths\n";
|
||||
std::cerr << "Trying to load " + libraryPath + " from system default "
|
||||
"search paths\n";
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
SenseApiLib& SenseApiManager::loadSenseApiLib(const std::string& libraryPath)
|
||||
{
|
||||
const CSenseApiDesc *libApiDesc;
|
||||
std::optional<std::string> fullPath = findLibraryPath(libraryPath);
|
||||
std::string resolvedPath = fullPath.value_or(libraryPath);
|
||||
|
||||
// Clear any existing error
|
||||
dlerror();
|
||||
auto dlopen_handle = std::unique_ptr<void, DlCloser>(
|
||||
auto dlopen_handle = std::unique_ptr<void, SenseApiLib::DlCloser>(
|
||||
dlopen(resolvedPath.c_str(), RTLD_LAZY));
|
||||
if (!dlopen_handle && fullPath.has_value())
|
||||
{
|
||||
@@ -97,14 +102,7 @@ SenseApiLib& SenseApiManager::loadSenseApiLib(const std::string& libraryPath)
|
||||
+ libraryPath + "'");
|
||||
}
|
||||
|
||||
libApiDesc = func();
|
||||
if (!libApiDesc)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": getSenseApiDesc() returned NULL for "
|
||||
"library '" + libraryPath + "'");
|
||||
}
|
||||
|
||||
const SenseApiDesc &libApiDesc = func();
|
||||
auto lib = std::make_unique<SenseApiLib>(
|
||||
libraryPath, dlopen_handle.release(), func);
|
||||
lib->setSenseApiDesc(libApiDesc);
|
||||
@@ -125,6 +123,19 @@ SenseApiManager::getSenseApiLib(const std::string& libraryPath)
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<std::reference_wrapper<SenseApiLib>>
|
||||
SenseApiManager::getSenseApiLibByApiName(const std::string& apiName)
|
||||
{
|
||||
auto it = std::find_if(senseApiLibs.begin(), senseApiLibs.end(),
|
||||
[&apiName](const std::unique_ptr<SenseApiLib>& lib) {
|
||||
return lib->senseApiDesc.name == apiName;
|
||||
}
|
||||
);
|
||||
|
||||
if (it != senseApiLibs.end()) { return **it; }
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void SenseApiManager::unloadSenseApiLib(const std::string& libraryPath)
|
||||
{
|
||||
auto it = std::find_if(senseApiLibs.begin(), senseApiLibs.end(),
|
||||
@@ -143,6 +154,11 @@ void SenseApiManager::unloadSenseApiLib(const std::string& libraryPath)
|
||||
<< libraryPath << '\n';
|
||||
}
|
||||
|
||||
void SenseApiManager::unloadAllSenseApiLibs(void)
|
||||
{
|
||||
senseApiLibs.clear();
|
||||
}
|
||||
|
||||
void SenseApiManager::loadAllSenseApiLibsFromOptions()
|
||||
{
|
||||
const auto& options = OptionParser::getOptions();
|
||||
@@ -151,5 +167,102 @@ void SenseApiManager::loadAllSenseApiLibsFromOptions()
|
||||
}
|
||||
}
|
||||
|
||||
std::string SenseApiManager::stringifyLibs() const
|
||||
{
|
||||
std::string result;
|
||||
for (const auto& lib : senseApiLibs) {
|
||||
result += lib->stringify() + "\n";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void SenseApiManager::initializeSenseApiLib(SenseApiLib& lib)
|
||||
{
|
||||
if (!lib.senseApiDesc.sal_mgmt_libOps.initializeInd)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": initializeInd() is NULL for library '"
|
||||
+ lib.libraryPath + "'");
|
||||
}
|
||||
lib.senseApiDesc.sal_mgmt_libOps.initializeInd();
|
||||
}
|
||||
|
||||
void SenseApiManager::finalizeSenseApiLib(SenseApiLib& lib)
|
||||
{
|
||||
if (!lib.senseApiDesc.sal_mgmt_libOps.finalizeInd)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": finalizeInd() is NULL for library '"
|
||||
+ lib.libraryPath + "'");
|
||||
}
|
||||
lib.senseApiDesc.sal_mgmt_libOps.finalizeInd();
|
||||
}
|
||||
|
||||
void SenseApiManager::initializeAllSenseApiLibs(void)
|
||||
{
|
||||
for (auto& lib : senseApiLibs) {
|
||||
initializeSenseApiLib(*lib);
|
||||
}
|
||||
}
|
||||
|
||||
void SenseApiManager::finalizeAllSenseApiLibs(void)
|
||||
{
|
||||
for (auto& lib : senseApiLibs) {
|
||||
finalizeSenseApiLib(*lib);
|
||||
}
|
||||
}
|
||||
|
||||
void SenseApiManager::attachSenseDevice(const device::SenseDeviceSpec& spec)
|
||||
{
|
||||
auto libOpt = getSenseApiLibByApiName(spec.api);
|
||||
if (!libOpt)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": No library found for API '"
|
||||
+ spec.api + "'");
|
||||
}
|
||||
auto& lib = libOpt.value().get();
|
||||
if (!lib.senseApiDesc.sal_mgmt_libOps.attachDeviceReq)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": attachDeviceReq() is NULL for library '"
|
||||
+ lib.libraryPath + "'");
|
||||
}
|
||||
lib.senseApiDesc.sal_mgmt_libOps.attachDeviceReq(spec);
|
||||
}
|
||||
|
||||
void SenseApiManager::detachSenseDevice(const device::SenseDeviceSpec& spec)
|
||||
{
|
||||
auto libOpt = getSenseApiLibByApiName(spec.api);
|
||||
if (!libOpt)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": No library found for API '"
|
||||
+ spec.api + "'");
|
||||
}
|
||||
auto& lib = libOpt.value().get();
|
||||
if (!lib.senseApiDesc.sal_mgmt_libOps.detachDeviceReq)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": detachDeviceReq() is NULL for library '"
|
||||
+ lib.libraryPath + "'");
|
||||
}
|
||||
lib.senseApiDesc.sal_mgmt_libOps.detachDeviceReq(spec);
|
||||
}
|
||||
|
||||
void SenseApiManager::attachAllSenseDevicesFromSpecs(void)
|
||||
{
|
||||
for (const auto& spec : device::DeviceManager::senseDeviceSpecs) {
|
||||
attachSenseDevice(*spec);
|
||||
}
|
||||
}
|
||||
|
||||
void SenseApiManager::detachAllSenseDevices(void)
|
||||
{
|
||||
for (const auto& spec : device::DeviceManager::senseDeviceSpecs) {
|
||||
detachSenseDevice(*spec);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sense_api
|
||||
} // namespace hk
|
||||
|
||||
+93
-63
@@ -3,98 +3,128 @@
|
||||
|
||||
#include <preprocessor.h>
|
||||
#include <stdbool.h>
|
||||
#include <user/senseDeviceSpec.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
namespace hk {
|
||||
namespace sense_api {
|
||||
|
||||
/* Exported by all sense API Libraries to tell Harikoff what API the lib uses
|
||||
* to connect to providers; and also to state which implexor APIs it exports.
|
||||
*/
|
||||
struct CExportedImplexorApiDesc
|
||||
typedef int (sal_mho_initializeRdyFn)(void);
|
||||
typedef int (sal_mho_finalizeRdyFn)(void);
|
||||
typedef int (sal_mho_attachDeviceAckFn)(const device::SenseDeviceSpec &desc);
|
||||
typedef int (sal_mho_detachDeviceAckFn)(const device::SenseDeviceSpec &desc);
|
||||
|
||||
struct Sal_Mgmt_HkOps
|
||||
{
|
||||
const char *name;
|
||||
// Lib calls this function to notify Harikoff that it's done initializing.
|
||||
sal_mho_initializeRdyFn *initializeRdy;
|
||||
// Lib calls this function to notify Harikoff that it's done finalizing.
|
||||
sal_mho_finalizeRdyFn *finalizeRdy;
|
||||
// Lib calls this to notify Harikoff that it's done attaching a device.
|
||||
sal_mho_attachDeviceAckFn *attachDeviceAck;
|
||||
// Lib calls this to notify Harikoff that it's done detaching a device.
|
||||
sal_mho_detachDeviceAckFn *detachDeviceAck;
|
||||
};
|
||||
|
||||
typedef int (sal_lmo_initializeIndFn)(void);
|
||||
typedef int (sal_lmo_finalizeIndFn)(void);
|
||||
typedef int (sal_lmo_attachDeviceReqFn)(void);
|
||||
typedef int (sal_lmo_detachDeviceReqFn)(void);
|
||||
typedef int (sal_mlo_initializeIndFn)(void);
|
||||
typedef int (sal_mlo_finalizeIndFn)(void);
|
||||
typedef int (sal_mlo_attachDeviceReqFn)(const device::SenseDeviceSpec &desc);
|
||||
typedef int (sal_mlo_detachDeviceReqFn)(const device::SenseDeviceSpec &desc);
|
||||
|
||||
struct Csal_libMgmtOps
|
||||
struct Sal_Mgmt_LibOps
|
||||
{
|
||||
/* When Harikoff loads a sense API lib, it calls this function to initialize
|
||||
* the lib. When this returns, the lib should be ready to attach devices.
|
||||
*/
|
||||
sal_lmo_initializeIndFn *initializeInd;
|
||||
sal_mlo_initializeIndFn *initializeInd;
|
||||
/* Harikoff calls this to finalize the lib and free its internal
|
||||
* resources. When this returns, the lib should be ready to be unloaded.
|
||||
*/
|
||||
sal_lmo_finalizeIndFn *finalizeInd;
|
||||
sal_mlo_finalizeIndFn *finalizeInd;
|
||||
/* Harikoff calls this to attach a device to the lib. When it returns, the
|
||||
* device should be attached and ready to be implexed.
|
||||
*/
|
||||
sal_lmo_attachDeviceReqFn *attachDeviceReq;
|
||||
sal_mlo_attachDeviceReqFn *attachDeviceReq;
|
||||
// When this returns, the device should be detached.
|
||||
sal_lmo_detachDeviceReqFn *detachDeviceReq;
|
||||
sal_mlo_detachDeviceReqFn *detachDeviceReq;
|
||||
|
||||
static bool sanityCheck(const Sal_Mgmt_LibOps &ops)
|
||||
{
|
||||
if (!ops.initializeInd || !ops.finalizeInd
|
||||
|| !ops.attachDeviceReq || !ops.detachDeviceReq)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct CSenseApiDesc
|
||||
/* C++ version of the C struct above, which Harikoff uses to manage the
|
||||
* lib and connect implexors to it.
|
||||
*/
|
||||
class SenseApiDesc
|
||||
{
|
||||
/* Shortname that identifies the API used by this lib to talk to the sense
|
||||
* provider.
|
||||
*/
|
||||
const char *name;
|
||||
/* These are the implexors whose APIs this lib exports.
|
||||
*/
|
||||
uint32_t numExportedImplexorApis;
|
||||
CExportedImplexorApiDesc *exportedImplexorApis;
|
||||
/* Sub-API for managing the lib. Library role within the API.
|
||||
*/
|
||||
Csal_libMgmtOps *sal_libMgmtOps;
|
||||
public:
|
||||
class ExportedImplexorApiDesc
|
||||
{
|
||||
public:
|
||||
ExportedImplexorApiDesc(const std::string name)
|
||||
// The caller should sanity check before calling this constructor.
|
||||
: name(name)
|
||||
{}
|
||||
|
||||
static bool sanityCheck(const ExportedImplexorApiDesc &desc)
|
||||
{
|
||||
if (desc.name.empty()) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
std::string name;
|
||||
};
|
||||
|
||||
public:
|
||||
SenseApiDesc() = default;
|
||||
|
||||
std::string stringify() const
|
||||
{
|
||||
std::string result = "Name: " + name + "\n";
|
||||
result += "Exported Implexor APIs:\n";
|
||||
for (const auto& api : exportedImplexorApis) {
|
||||
result += " - " + api.name + "\n";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool sanityCheck(const SenseApiDesc &desc)
|
||||
{
|
||||
if (desc.name.empty() || desc.exportedImplexorApis.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const auto& api : desc.exportedImplexorApis) {
|
||||
if (!ExportedImplexorApiDesc::sanityCheck(api)) { return false; }
|
||||
}
|
||||
|
||||
return Sal_Mgmt_LibOps::sanityCheck(desc.sal_mgmt_libOps);
|
||||
}
|
||||
|
||||
std::string name;
|
||||
// These are the implexors whose APIs this lib exports.
|
||||
std::vector<ExportedImplexorApiDesc> exportedImplexorApis;
|
||||
Sal_Mgmt_LibOps sal_mgmt_libOps;
|
||||
};
|
||||
|
||||
static bool CSenseApiDesc_sanityCheck(const CSenseApiDesc *desc)
|
||||
{
|
||||
if (!desc || !desc->name || desc->numExportedImplexorApis < 1
|
||||
||!desc->exportedImplexorApis || !desc->sal_libMgmtOps)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool CExportedImplexorApiDesc_sanityCheck(
|
||||
const CExportedImplexorApiDesc *desc)
|
||||
{
|
||||
if (!desc || !desc->name)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool Csal_libMgmtOps_sanityCheck(const Csal_libMgmtOps *ops)
|
||||
{
|
||||
if (!ops || !ops->initializeInd || !ops->finalizeInd
|
||||
|| !ops->attachDeviceReq || !ops->detachDeviceReq)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#define HK_GET_SENSE_API_DESC_FN_NAME getSenseApiDesc
|
||||
#define HK_GET_SENSE_API_DESC_FN_NAME_STR \
|
||||
HK_QUOTE(HK_GET_SENSE_API_DESC_FN_NAME)
|
||||
|
||||
typedef const CSenseApiDesc *(getSenseApiDescFn)(void);
|
||||
typedef const SenseApiDesc &(getSenseApiDescFn)(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
} // namespace sense_api
|
||||
} // namespace hk
|
||||
|
||||
#endif // __USER_SENSE_API_LIB_H__
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
#ifndef SENSORDEVICESPEC_H
|
||||
#define SENSORDEVICESPEC_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace hk {
|
||||
namespace device {
|
||||
|
||||
class SenseDeviceSpec
|
||||
{
|
||||
public:
|
||||
friend std::ostream& operator<<(
|
||||
std::ostream& os, const SenseDeviceSpec& spec)
|
||||
{
|
||||
os << spec.stringify();
|
||||
return os;
|
||||
}
|
||||
|
||||
bool operator==(const SenseDeviceSpec& other) const
|
||||
{
|
||||
return sensorType == other.sensorType &&
|
||||
provider == other.provider &&
|
||||
deviceSelector == other.deviceSelector;
|
||||
}
|
||||
|
||||
public:
|
||||
char sensorType;
|
||||
std::string implexor;
|
||||
std::string api;
|
||||
std::vector<std::pair<std::string,std::string>> apiParams;
|
||||
std::string provider;
|
||||
std::vector<std::pair<std::string,std::string>> providerParams;
|
||||
std::string deviceSelector;
|
||||
|
||||
std::string stringify() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Device: " << sensorType << ", Implexor: "
|
||||
<< implexor << ", API: " << api << ", API Params: (";
|
||||
for (const auto& param : apiParams)
|
||||
{
|
||||
os << param.first;
|
||||
if (!param.second.empty()) {
|
||||
os << "=" << param.second;
|
||||
}
|
||||
os << " ";
|
||||
}
|
||||
os << "), Provider: " << provider << ", Provider Params: (";
|
||||
for (const auto& param : providerParams)
|
||||
{
|
||||
os << param.first;
|
||||
if (!param.second.empty()) {
|
||||
os << "=" << param.second;
|
||||
}
|
||||
os << " ";
|
||||
}
|
||||
os << "), Device Selector: " << deviceSelector << std::endl;
|
||||
|
||||
return os.str();
|
||||
}
|
||||
};
|
||||
|
||||
class InteroceptorDeviceSpec : public SenseDeviceSpec
|
||||
{
|
||||
};
|
||||
|
||||
class ExtrospectorDeviceSpec : public SenseDeviceSpec
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace device
|
||||
} // namespace hk
|
||||
|
||||
#endif // SENSORDEVICESPEC_H
|
||||
@@ -0,0 +1,95 @@
|
||||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_boost_asio.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_BOOST_ASIO
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Test for Asio library from the Boost C++ libraries. The macro requires a
|
||||
# preceding call to AX_BOOST_BASE. Further documentation is available at
|
||||
# <http://randspringer.de/boost/index.html>.
|
||||
#
|
||||
# This macro calls:
|
||||
#
|
||||
# AC_SUBST(BOOST_ASIO_LIB)
|
||||
#
|
||||
# And sets:
|
||||
#
|
||||
# HAVE_BOOST_ASIO
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
|
||||
# Copyright (c) 2008 Pete Greenwell <pete@mu.org>
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 18
|
||||
|
||||
AC_DEFUN([AX_BOOST_ASIO_gte_1_69_0],
|
||||
[
|
||||
AC_ARG_WITH([boost-asio],
|
||||
AS_HELP_STRING([--with-boost-asio@<:@=special-lib@:>@],
|
||||
[use the ASIO library from boost - it is possible to specify a certain library for the linker
|
||||
e.g. --with-boost-asio=boost_system-gcc41-mt-1_34 ]),
|
||||
[
|
||||
if test "$withval" = "no"; then
|
||||
want_boost="no"
|
||||
elif test "$withval" = "yes"; then
|
||||
want_boost="yes"
|
||||
ax_boost_user_asio_lib=""
|
||||
else
|
||||
want_boost="yes"
|
||||
ax_boost_user_asio_lib="$withval"
|
||||
fi
|
||||
],
|
||||
[want_boost="yes"]
|
||||
)
|
||||
|
||||
if test "x$want_boost" = "xyes"; then
|
||||
AC_REQUIRE([AC_PROG_CC])
|
||||
CPPFLAGS_SAVED="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
|
||||
export CPPFLAGS
|
||||
|
||||
LDFLAGS_SAVED="$LDFLAGS"
|
||||
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
|
||||
export LDFLAGS
|
||||
|
||||
AC_CACHE_CHECK(whether the Boost::ASIO library is available,
|
||||
ax_cv_boost_asio,
|
||||
[AC_LANG_PUSH([C++])
|
||||
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ @%:@include <boost/asio.hpp>
|
||||
]],
|
||||
[[
|
||||
|
||||
boost::asio::io_service io;
|
||||
boost::system::error_code timer_result;
|
||||
boost::asio::deadline_timer t(io);
|
||||
t.cancel();
|
||||
io.run_one();
|
||||
return 0;
|
||||
]])],
|
||||
ax_cv_boost_asio=yes, ax_cv_boost_asio=no)
|
||||
AC_LANG_POP([C++])
|
||||
])
|
||||
if test "x$ax_cv_boost_asio" = "xyes"; then
|
||||
AC_DEFINE(HAVE_BOOST_ASIO,,
|
||||
[define if the Boost::ASIO library is available])
|
||||
BOOST_ASIO_LIB=""
|
||||
AC_SUBST(BOOST_ASIO_LIB)
|
||||
HAVE_BOOST_ASIO="yes"
|
||||
else
|
||||
HAVE_BOOST_ASIO="no"
|
||||
fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS_SAVED"
|
||||
LDFLAGS="$LDFLAGS_SAVED"
|
||||
fi
|
||||
])
|
||||
@@ -0,0 +1,305 @@
|
||||
# ===========================================================================
|
||||
# https://www.gnu.org/software/autoconf-archive/ax_boost_base.html
|
||||
# ===========================================================================
|
||||
#
|
||||
# SYNOPSIS
|
||||
#
|
||||
# AX_BOOST_BASE([MINIMUM-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
#
|
||||
# DESCRIPTION
|
||||
#
|
||||
# Test for the Boost C++ libraries of a particular version (or newer)
|
||||
#
|
||||
# If no path to the installed boost library is given the macro searches
|
||||
# under /usr, /usr/local, /opt, /opt/local and /opt/homebrew and evaluates
|
||||
# the $BOOST_ROOT environment variable. Further documentation is available
|
||||
# at <http://randspringer.de/boost/index.html>.
|
||||
#
|
||||
# This macro calls:
|
||||
#
|
||||
# AC_SUBST(BOOST_CPPFLAGS) / AC_SUBST(BOOST_LDFLAGS)
|
||||
#
|
||||
# And sets:
|
||||
#
|
||||
# HAVE_BOOST
|
||||
#
|
||||
# LICENSE
|
||||
#
|
||||
# Copyright (c) 2008 Thomas Porschberg <thomas@randspringer.de>
|
||||
# Copyright (c) 2009 Peter Adolphs
|
||||
#
|
||||
# Copying and distribution of this file, with or without modification, are
|
||||
# permitted in any medium without royalty provided the copyright notice
|
||||
# and this notice are preserved. This file is offered as-is, without any
|
||||
# warranty.
|
||||
|
||||
#serial 55
|
||||
|
||||
# example boost program (need to pass version)
|
||||
m4_define([_AX_BOOST_BASE_PROGRAM],
|
||||
[AC_LANG_PROGRAM([[
|
||||
#include <boost/version.hpp>
|
||||
]],[[
|
||||
(void) ((void)sizeof(char[1 - 2*!!((BOOST_VERSION) < ($1))]));
|
||||
]])])
|
||||
|
||||
AC_DEFUN([AX_BOOST_BASE],
|
||||
[
|
||||
AC_ARG_WITH([boost],
|
||||
[AS_HELP_STRING([--with-boost@<:@=ARG@:>@],
|
||||
[use Boost library from a standard location (ARG=yes),
|
||||
from the specified location (ARG=<path>),
|
||||
or disable it (ARG=no)
|
||||
@<:@ARG=yes@:>@ ])],
|
||||
[
|
||||
AS_CASE([$withval],
|
||||
[no],[want_boost="no";_AX_BOOST_BASE_boost_path=""],
|
||||
[yes],[want_boost="yes";_AX_BOOST_BASE_boost_path=""],
|
||||
[want_boost="yes";_AX_BOOST_BASE_boost_path="$withval"])
|
||||
],
|
||||
[want_boost="yes"])
|
||||
|
||||
|
||||
AC_ARG_WITH([boost-libdir],
|
||||
[AS_HELP_STRING([--with-boost-libdir=LIB_DIR],
|
||||
[Force given directory for boost libraries.
|
||||
Note that this will override library path detection,
|
||||
so use this parameter only if default library detection fails
|
||||
and you know exactly where your boost libraries are located.])],
|
||||
[
|
||||
AS_IF([test -d "$withval"],
|
||||
[_AX_BOOST_BASE_boost_lib_path="$withval"],
|
||||
[AC_MSG_ERROR([--with-boost-libdir expected directory name])])
|
||||
],
|
||||
[_AX_BOOST_BASE_boost_lib_path=""])
|
||||
|
||||
BOOST_LDFLAGS=""
|
||||
BOOST_CPPFLAGS=""
|
||||
AS_IF([test "x$want_boost" = "xyes"],
|
||||
[_AX_BOOST_BASE_RUNDETECT([$1],[$2],[$3])])
|
||||
AC_SUBST(BOOST_CPPFLAGS)
|
||||
AC_SUBST(BOOST_LDFLAGS)
|
||||
])
|
||||
|
||||
|
||||
# convert a version string in $2 to numeric and affect to polymorphic var $1
|
||||
AC_DEFUN([_AX_BOOST_BASE_TONUMERICVERSION],[
|
||||
AS_IF([test "x$2" = "x"],[_AX_BOOST_BASE_TONUMERICVERSION_req="1.20.0"],[_AX_BOOST_BASE_TONUMERICVERSION_req="$2"])
|
||||
_AX_BOOST_BASE_TONUMERICVERSION_req_shorten=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '\([[0-9]]*\.[[0-9]]*\)'`
|
||||
_AX_BOOST_BASE_TONUMERICVERSION_req_major=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '\([[0-9]]*\)'`
|
||||
AS_IF([test "x$_AX_BOOST_BASE_TONUMERICVERSION_req_major" = "x"],
|
||||
[AC_MSG_ERROR([You should at least specify libboost major version])])
|
||||
_AX_BOOST_BASE_TONUMERICVERSION_req_minor=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '[[0-9]]*\.\([[0-9]]*\)'`
|
||||
AS_IF([test "x$_AX_BOOST_BASE_TONUMERICVERSION_req_minor" = "x"],
|
||||
[_AX_BOOST_BASE_TONUMERICVERSION_req_minor="0"])
|
||||
_AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req : '[[0-9]]*\.[[0-9]]*\.\([[0-9]]*\)'`
|
||||
AS_IF([test "X$_AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor" = "X"],
|
||||
[_AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor="0"])
|
||||
_AX_BOOST_BASE_TONUMERICVERSION_RET=`expr $_AX_BOOST_BASE_TONUMERICVERSION_req_major \* 100000 \+ $_AX_BOOST_BASE_TONUMERICVERSION_req_minor \* 100 \+ $_AX_BOOST_BASE_TONUMERICVERSION_req_sub_minor`
|
||||
AS_VAR_SET($1,$_AX_BOOST_BASE_TONUMERICVERSION_RET)
|
||||
])
|
||||
|
||||
dnl Run the detection of boost should be run only if $want_boost
|
||||
AC_DEFUN([_AX_BOOST_BASE_RUNDETECT],[
|
||||
_AX_BOOST_BASE_TONUMERICVERSION(WANT_BOOST_VERSION,[$1])
|
||||
succeeded=no
|
||||
|
||||
|
||||
AC_REQUIRE([AC_CANONICAL_HOST])
|
||||
dnl On 64-bit systems check for system libraries in both lib64 and lib.
|
||||
dnl The former is specified by FHS, but e.g. Debian does not adhere to
|
||||
dnl this (as it rises problems for generic multi-arch support).
|
||||
dnl The last entry in the list is chosen by default when no libraries
|
||||
dnl are found, e.g. when only header-only libraries are installed!
|
||||
AS_CASE([${host_cpu}],
|
||||
[x86_64],[libsubdirs="lib64 libx32 lib lib64"],
|
||||
[mips*64*],[libsubdirs="lib64 lib32 lib lib64"],
|
||||
[ppc64|powerpc64|s390x|sparc64|aarch64|ppc64le|powerpc64le|riscv64|e2k|loongarch64],[libsubdirs="lib64 lib lib64"],
|
||||
[libsubdirs="lib"]
|
||||
)
|
||||
|
||||
dnl allow for real multi-arch paths e.g. /usr/lib/x86_64-linux-gnu. Give
|
||||
dnl them priority over the other paths since, if libs are found there, they
|
||||
dnl are almost assuredly the ones desired.
|
||||
AS_CASE([${host_cpu}],
|
||||
[i?86],[multiarch_libsubdir="lib/i386-${host_os}"],
|
||||
[armv7l],[multiarch_libsubdir="lib/arm-${host_os}"],
|
||||
[multiarch_libsubdir="lib/${host_cpu}-${host_os}"]
|
||||
)
|
||||
|
||||
dnl first we check the system location for boost libraries
|
||||
dnl this location is chosen if boost libraries are installed with the --layout=system option
|
||||
dnl or if you install boost with RPM
|
||||
AS_IF([test "x$_AX_BOOST_BASE_boost_path" != "x"],[
|
||||
AC_MSG_CHECKING([for boostlib >= $1 ($WANT_BOOST_VERSION) includes in "$_AX_BOOST_BASE_boost_path/include"])
|
||||
AS_IF([test -d "$_AX_BOOST_BASE_boost_path/include" && test -r "$_AX_BOOST_BASE_boost_path/include"],[
|
||||
AC_MSG_RESULT([yes])
|
||||
BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path/include"
|
||||
for _AX_BOOST_BASE_boost_path_tmp in $multiarch_libsubdir $libsubdirs; do
|
||||
AC_MSG_CHECKING([for boostlib >= $1 ($WANT_BOOST_VERSION) lib path in "$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp"])
|
||||
AS_IF([test -d "$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp" && test -r "$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp" ],[
|
||||
AC_MSG_RESULT([yes])
|
||||
BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_path/$_AX_BOOST_BASE_boost_path_tmp";
|
||||
break;
|
||||
],
|
||||
[AC_MSG_RESULT([no])])
|
||||
done],[
|
||||
AC_MSG_RESULT([no])])
|
||||
],[
|
||||
if test X"$cross_compiling" = Xyes; then
|
||||
search_libsubdirs=$multiarch_libsubdir
|
||||
else
|
||||
search_libsubdirs="$multiarch_libsubdir $libsubdirs"
|
||||
fi
|
||||
for _AX_BOOST_BASE_boost_path_tmp in /usr /usr/local /opt /opt/local /opt/homebrew ; do
|
||||
if test -d "$_AX_BOOST_BASE_boost_path_tmp/include/boost" && test -r "$_AX_BOOST_BASE_boost_path_tmp/include/boost" ; then
|
||||
for libsubdir in $search_libsubdirs ; do
|
||||
if ls "$_AX_BOOST_BASE_boost_path_tmp/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
|
||||
done
|
||||
BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_path_tmp/$libsubdir"
|
||||
BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path_tmp/include"
|
||||
break;
|
||||
fi
|
||||
done
|
||||
])
|
||||
|
||||
dnl overwrite ld flags if we have required special directory with
|
||||
dnl --with-boost-libdir parameter
|
||||
AS_IF([test "x$_AX_BOOST_BASE_boost_lib_path" != "x"],
|
||||
[BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_lib_path"])
|
||||
|
||||
AC_MSG_CHECKING([for boostlib >= $1 ($WANT_BOOST_VERSION)])
|
||||
CPPFLAGS_SAVED="$CPPFLAGS"
|
||||
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
|
||||
export CPPFLAGS
|
||||
|
||||
LDFLAGS_SAVED="$LDFLAGS"
|
||||
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
|
||||
export LDFLAGS
|
||||
|
||||
AC_REQUIRE([AC_PROG_CXX])
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_COMPILE_IFELSE([_AX_BOOST_BASE_PROGRAM($WANT_BOOST_VERSION)],[
|
||||
AC_MSG_RESULT(yes)
|
||||
succeeded=yes
|
||||
found_system=yes
|
||||
],[
|
||||
])
|
||||
AC_LANG_POP([C++])
|
||||
|
||||
|
||||
|
||||
dnl if we found no boost with system layout we search for boost libraries
|
||||
dnl built and installed without the --layout=system option or for a staged(not installed) version
|
||||
if test "x$succeeded" != "xyes" ; then
|
||||
CPPFLAGS="$CPPFLAGS_SAVED"
|
||||
LDFLAGS="$LDFLAGS_SAVED"
|
||||
BOOST_CPPFLAGS=
|
||||
if test -z "$_AX_BOOST_BASE_boost_lib_path" ; then
|
||||
BOOST_LDFLAGS=
|
||||
fi
|
||||
_version=0
|
||||
if test -n "$_AX_BOOST_BASE_boost_path" ; then
|
||||
if test -d "$_AX_BOOST_BASE_boost_path" && test -r "$_AX_BOOST_BASE_boost_path"; then
|
||||
for i in `ls -d $_AX_BOOST_BASE_boost_path/include/boost-* 2>/dev/null`; do
|
||||
_version_tmp=`echo $i | sed "s#$_AX_BOOST_BASE_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
|
||||
V_CHECK=`expr $_version_tmp \> $_version`
|
||||
if test "x$V_CHECK" = "x1" ; then
|
||||
_version=$_version_tmp
|
||||
fi
|
||||
VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
|
||||
BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path/include/boost-$VERSION_UNDERSCORE"
|
||||
done
|
||||
dnl if nothing found search for layout used in Windows distributions
|
||||
if test -z "$BOOST_CPPFLAGS"; then
|
||||
if test -d "$_AX_BOOST_BASE_boost_path/boost" && test -r "$_AX_BOOST_BASE_boost_path/boost"; then
|
||||
BOOST_CPPFLAGS="-I$_AX_BOOST_BASE_boost_path"
|
||||
fi
|
||||
fi
|
||||
dnl if we found something and BOOST_LDFLAGS was unset before
|
||||
dnl (because "$_AX_BOOST_BASE_boost_lib_path" = ""), set it here.
|
||||
if test -n "$BOOST_CPPFLAGS" && test -z "$BOOST_LDFLAGS"; then
|
||||
for libsubdir in $libsubdirs ; do
|
||||
if ls "$_AX_BOOST_BASE_boost_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
|
||||
done
|
||||
BOOST_LDFLAGS="-L$_AX_BOOST_BASE_boost_path/$libsubdir"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if test "x$cross_compiling" != "xyes" ; then
|
||||
for _AX_BOOST_BASE_boost_path in /usr /usr/local /opt /opt/local /opt/homebrew ; do
|
||||
if test -d "$_AX_BOOST_BASE_boost_path" && test -r "$_AX_BOOST_BASE_boost_path" ; then
|
||||
for i in `ls -d $_AX_BOOST_BASE_boost_path/include/boost-* 2>/dev/null`; do
|
||||
_version_tmp=`echo $i | sed "s#$_AX_BOOST_BASE_boost_path##" | sed 's/\/include\/boost-//' | sed 's/_/./'`
|
||||
V_CHECK=`expr $_version_tmp \> $_version`
|
||||
if test "x$V_CHECK" = "x1" ; then
|
||||
_version=$_version_tmp
|
||||
best_path=$_AX_BOOST_BASE_boost_path
|
||||
fi
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
VERSION_UNDERSCORE=`echo $_version | sed 's/\./_/'`
|
||||
BOOST_CPPFLAGS="-I$best_path/include/boost-$VERSION_UNDERSCORE"
|
||||
if test -z "$_AX_BOOST_BASE_boost_lib_path" ; then
|
||||
for libsubdir in $libsubdirs ; do
|
||||
if ls "$best_path/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
|
||||
done
|
||||
BOOST_LDFLAGS="-L$best_path/$libsubdir"
|
||||
fi
|
||||
fi
|
||||
|
||||
if test -n "$BOOST_ROOT" ; then
|
||||
for libsubdir in $libsubdirs ; do
|
||||
if ls "$BOOST_ROOT/stage/$libsubdir/libboost_"* >/dev/null 2>&1 ; then break; fi
|
||||
done
|
||||
if test -d "$BOOST_ROOT" && test -r "$BOOST_ROOT" && test -d "$BOOST_ROOT/stage/$libsubdir" && test -r "$BOOST_ROOT/stage/$libsubdir"; then
|
||||
version_dir=`expr //$BOOST_ROOT : '.*/\(.*\)'`
|
||||
stage_version=`echo $version_dir | sed 's/boost_//' | sed 's/_/./g'`
|
||||
stage_version_shorten=`expr $stage_version : '\([[0-9]]*\.[[0-9]]*\)'`
|
||||
V_CHECK=`expr $stage_version_shorten \>\= $_version`
|
||||
if test "x$V_CHECK" = "x1" && test -z "$_AX_BOOST_BASE_boost_lib_path" ; then
|
||||
AC_MSG_NOTICE(We will use a staged boost library from $BOOST_ROOT)
|
||||
BOOST_CPPFLAGS="-I$BOOST_ROOT"
|
||||
BOOST_LDFLAGS="-L$BOOST_ROOT/stage/$libsubdir"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS $BOOST_CPPFLAGS"
|
||||
export CPPFLAGS
|
||||
LDFLAGS="$LDFLAGS $BOOST_LDFLAGS"
|
||||
export LDFLAGS
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_COMPILE_IFELSE([_AX_BOOST_BASE_PROGRAM($WANT_BOOST_VERSION)],[
|
||||
AC_MSG_RESULT(yes)
|
||||
succeeded=yes
|
||||
found_system=yes
|
||||
],[
|
||||
])
|
||||
AC_LANG_POP([C++])
|
||||
fi
|
||||
|
||||
if test "x$succeeded" != "xyes" ; then
|
||||
if test "x$_version" = "x0" ; then
|
||||
AC_MSG_NOTICE([[We could not detect the boost libraries (version $1 or higher). If you have a staged boost library (still not installed) please specify \$BOOST_ROOT in your environment and do not give a PATH to --with-boost option. If you are sure you have boost installed, then check your version number looking in <boost/version.hpp>. See http://randspringer.de/boost for more documentation.]])
|
||||
else
|
||||
AC_MSG_NOTICE([Your boost libraries seems to old (version $_version).])
|
||||
fi
|
||||
BOOST_LDFLAGS=""
|
||||
BOOST_CPPFLAGS=""
|
||||
# execute ACTION-IF-NOT-FOUND (if present):
|
||||
ifelse([$3], , :, [$3])
|
||||
else
|
||||
AC_DEFINE(HAVE_BOOST,,[define if the Boost library is available])
|
||||
# execute ACTION-IF-FOUND (if present):
|
||||
ifelse([$2], , :, [$2])
|
||||
fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS_SAVED"
|
||||
LDFLAGS="$LDFLAGS_SAVED"
|
||||
|
||||
])
|
||||
@@ -5,7 +5,7 @@ AC_ARG_ENABLE([senseapi-xcbxorg],
|
||||
[Enable XCB/Xorg SenseAPI backend])],
|
||||
[AS_CASE([$enableval],
|
||||
[no], [enable_senseapi_xcbxorg=no],
|
||||
[yes|*], [
|
||||
[yes|""|*], [
|
||||
enable_senseapi_xcbxorg=yes
|
||||
AC_DEFINE([XCBXORG_ENABLED], [1],
|
||||
[Define to 1 if you have XCB/Xorg SenseAPI backend])
|
||||
@@ -14,6 +14,8 @@ AC_ARG_ENABLE([senseapi-xcbxorg],
|
||||
AC_MSG_ERROR(m4_normalize([XCB library not found. Sense API
|
||||
XCB/Xorg requires the XCB dev headers and shlib.]))
|
||||
])
|
||||
AC_SUBST([XCB_CFLAGS])
|
||||
AC_SUBST([XCB_LIBS])
|
||||
]
|
||||
)],
|
||||
[enable_senseapi_xcbxorg=no]
|
||||
@@ -1,49 +1,108 @@
|
||||
#include <iostream>
|
||||
#include <exception>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <condition_variable>
|
||||
#include <boost/asio.hpp>
|
||||
#include <opts.h>
|
||||
#include <mind.h>
|
||||
#include <deviceManager/deviceManager.h>
|
||||
#include <senseApis/senseApiManager.h>
|
||||
#include "componentThread.h"
|
||||
|
||||
namespace hk {
|
||||
|
||||
int main(int argc, char **argv)
|
||||
static int initializeHarikoff(int argc, char **argv, char **envp);
|
||||
|
||||
} // namespace hk
|
||||
|
||||
int main(int argc, char **argv, char **envp)
|
||||
{
|
||||
using namespace hk;
|
||||
try {
|
||||
std::cout << __func__ << ": Entering main()" << std::endl;
|
||||
boost::asio::io_service mrntLoop;
|
||||
boost::asio::io_service::work work(mrntLoop);
|
||||
|
||||
try {
|
||||
// Validate thread IDs
|
||||
hk::ComponentThread::validateThreadIds();
|
||||
|
||||
// Post initializeHarikoff to mrntLoop
|
||||
mrntLoop.post([&]()
|
||||
{
|
||||
int ret = hk::initializeHarikoff(argc, argv, envp);
|
||||
if (ret != 0)
|
||||
{
|
||||
std::cerr << "Initialization failed with code: "
|
||||
<< ret << std::endl;
|
||||
std::exit(ret);
|
||||
}
|
||||
});
|
||||
|
||||
mrntLoop.run();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
std::cerr << __func__ << ": Exception occurred: " << e.what()
|
||||
<< std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cerr << __func__ << ": Unknown exception occurred" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::cout << __func__ << ": Exiting normally" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace hk {
|
||||
|
||||
static int initializeHarikoff(int argc, char **argv, char **envp)
|
||||
{
|
||||
std::cout << __func__ << ": Entering" << std::endl;
|
||||
|
||||
using namespace hk;
|
||||
OptionParser &options = OptionParser::getOptions();
|
||||
hk::Mind mind;
|
||||
|
||||
std::cout << PACKAGE_NAME << " " << PACKAGE_VERSION << std::endl;
|
||||
|
||||
try {
|
||||
options.parseArguments(argc, argv);
|
||||
options.parseArguments(argc, argv, envp);
|
||||
std::cout << options.stringifyOptions() << std::endl;
|
||||
}
|
||||
catch (const std::invalid_argument& e) {
|
||||
std::cerr << e.what() << '\n' << options.getUsage() << '\n';
|
||||
catch (const std::invalid_argument& e)
|
||||
{
|
||||
std::cerr << __func__ << ": Exception occurred: " << e.what() << '\n'
|
||||
<< options.getUsage() << '\n';
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (options.printUsage) {
|
||||
if (options.printUsage)
|
||||
{
|
||||
std::cout << options.getUsage() << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
options.dumpOptions();
|
||||
DeviceManager::getInstance().collateAllDeviceSpecs(options);
|
||||
DeviceManager::getInstance().parseAllDeviceSpecs();
|
||||
std::cout << DeviceManager::printDeviceSpecs() << std::endl;
|
||||
device::DeviceManager::getInstance().collateAllDeviceSpecs();
|
||||
device::DeviceManager::getInstance().parseAllDeviceSpecs();
|
||||
std::cout << device::DeviceManager::stringifyDeviceSpecs() << std::endl;
|
||||
sense_api::SenseApiManager::getInstance().loadAllSenseApiLibsFromOptions();
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "Exception occurred: " << e.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
catch (...) {
|
||||
std::cerr << "Unknown exception occurred" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
std::cout << sense_api::SenseApiManager::getInstance().stringifyLibs()
|
||||
<< std::endl;
|
||||
sense_api::SenseApiManager::getInstance().initializeAllSenseApiLibs();
|
||||
sense_api::SenseApiManager::getInstance().attachAllSenseDevicesFromSpecs();
|
||||
|
||||
/* Start the threads */
|
||||
for (const auto& [id, componentThread]
|
||||
: hk::ComponentThread::componentThreads) {
|
||||
hk::ComponentThread::signalThread(id);
|
||||
}
|
||||
|
||||
std::cout << __func__ << ": Exiting" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::cout << "Exiting normally" << std::endl;
|
||||
return 0;
|
||||
}
|
||||
} // namespace hk
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
pkglib_LTLIBRARIES=libxcbXorg.la
|
||||
libxcbXorg_la_SOURCES=xcbXorg.cpp
|
||||
libxcbXorg_la_LDFLAGS=$(XCB_LIBS)
|
||||
|
||||
xcbXorg.$(OBJEXT): CPPFLAGS+=$(XCB_CFLAGS) -Wno-c++20-extensions
|
||||
xcbXorg.l$(OBJEXT): CPPFLAGS+=$(XCB_CFLAGS) -Wno-c++20-extensions
|
||||
|
||||
+494
-36
@@ -1,69 +1,527 @@
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <stdexcept>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <user/senseDeviceSpec.h>
|
||||
#include <user/senseApiDesc.h>
|
||||
#include <xcb/xcb.h>
|
||||
#include "xcbXorg.h"
|
||||
|
||||
|
||||
static CExportedImplexorApiDesc xcbXorgExportedImplexorApis[] =
|
||||
// Key for identifying unique X server connections
|
||||
struct XcbConnection
|
||||
{
|
||||
struct XConnectionIdentifier
|
||||
{
|
||||
.name = "video-implexor"
|
||||
int display;
|
||||
int screen;
|
||||
|
||||
bool operator<(const XConnectionIdentifier& other) const
|
||||
{
|
||||
if (display != other.display) return display < other.display;
|
||||
return screen < other.screen;
|
||||
}
|
||||
|
||||
std::string stringify() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "display=" << display << ", screen=" << screen;
|
||||
return os.str();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief RAII guard for managing X server connection lifetime
|
||||
*
|
||||
* This guard ensures proper cleanup of X server connections in case of
|
||||
* errors during attachDeviceReq. If a connection is created but the device
|
||||
* attachment fails, and the connection's refcount is 0, this guard will
|
||||
* automatically remove the connection from the connections map.
|
||||
*
|
||||
* The guard can be "committed" using commit() to indicate successful
|
||||
* device attachment, in which case it will not perform cleanup on destruction.
|
||||
*/
|
||||
class ConnectionGuard
|
||||
{
|
||||
std::shared_ptr<XcbConnection> conn;
|
||||
bool committed = false;
|
||||
|
||||
public:
|
||||
explicit ConnectionGuard(std::shared_ptr<XcbConnection> c)
|
||||
: conn(std::move(c))
|
||||
{}
|
||||
|
||||
void commit(void) { committed = true; }
|
||||
|
||||
~ConnectionGuard()
|
||||
{
|
||||
if (!committed && conn && conn->refCount == 0) {
|
||||
XcbConnection::connections.erase(conn->connectionIdentifier);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
XcbConnection(const XConnectionIdentifier& id)
|
||||
: connection(nullptr, &xcb_disconnect),
|
||||
connectionIdentifier(id), refCount(0)
|
||||
{
|
||||
// Convert to X display string format (e.g., ":0.1")
|
||||
std::string displayString = ":" + std::to_string(id.display)
|
||||
+ "." + std::to_string(id.screen);
|
||||
|
||||
int screenNum;
|
||||
connection.reset(xcb_connect(displayString.c_str(), &screenNum));
|
||||
if (xcb_connection_has_error(connection.get()))
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": Failed to connect to X server "
|
||||
+ connectionIdentifier.stringify());
|
||||
}
|
||||
|
||||
// Verify we got the screen we asked for
|
||||
if (screenNum != id.screen)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string(__func__) + ": Connected to wrong screen. "
|
||||
"Requested " + connectionIdentifier.stringify()
|
||||
+ " but got screen " + std::to_string(screenNum));
|
||||
}
|
||||
}
|
||||
|
||||
// Delete copy/move operations - we'll manage instances through pointers
|
||||
XcbConnection(const XcbConnection&) = delete;
|
||||
XcbConnection& operator=(const XcbConnection&) = delete;
|
||||
XcbConnection(XcbConnection&&) = delete;
|
||||
XcbConnection& operator=(XcbConnection&&) = delete;
|
||||
|
||||
std::unique_ptr<xcb_connection_t, decltype(&xcb_disconnect)> connection;
|
||||
XConnectionIdentifier connectionIdentifier;
|
||||
std::atomic<int> refCount;
|
||||
|
||||
public:
|
||||
static std::map<XConnectionIdentifier, std::shared_ptr<XcbConnection>>
|
||||
connections;
|
||||
|
||||
static std::shared_ptr<XcbConnection> getOrCreateConnection(
|
||||
const XConnectionIdentifier& id)
|
||||
{
|
||||
auto it = connections.find(id);
|
||||
if (it != connections.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
auto conn = std::make_shared<XcbConnection>(id);
|
||||
connections.emplace(id, conn);
|
||||
return conn;
|
||||
}
|
||||
};
|
||||
|
||||
static sal_lmo_initializeIndFn xcbXorg_initializeInd;
|
||||
static sal_lmo_finalizeIndFn xcbXorg_finalizeInd;
|
||||
static sal_lmo_attachDeviceReqFn xcbXorg_attachDeviceReq;
|
||||
static sal_lmo_detachDeviceReqFn xcbXorg_detachDeviceReq;
|
||||
namespace xcb_window_search {
|
||||
|
||||
static Csal_libMgmtOps xcbXorgSalLibMgmtOps =
|
||||
{
|
||||
.initializeInd = xcbXorg_initializeInd,
|
||||
.finalizeInd = xcbXorg_finalizeInd,
|
||||
.attachDeviceReq = xcbXorg_attachDeviceReq,
|
||||
.detachDeviceReq = xcbXorg_detachDeviceReq
|
||||
};
|
||||
// Custom deleters for XCB reply types
|
||||
struct XcbReplyDeleter {
|
||||
void operator()(xcb_query_tree_reply_t* p) { free(p); }
|
||||
void operator()(xcb_get_property_reply_t* p) { free(p); }
|
||||
};
|
||||
|
||||
static CSenseApiDesc xcbXorgApiDesc =
|
||||
{
|
||||
.name = "XcbXorg",
|
||||
.numExportedImplexorApis = sizeof(xcbXorgExportedImplexorApis) /
|
||||
sizeof(*xcbXorgExportedImplexorApis),
|
||||
.exportedImplexorApis = xcbXorgExportedImplexorApis,
|
||||
.sal_libMgmtOps = &xcbXorgSalLibMgmtOps
|
||||
};
|
||||
enum class MatchType { SUBSTRING, EXACT, ID };
|
||||
|
||||
extern HK_UNMANGLED getSenseApiDescFn HK_GET_SENSE_API_DESC_FN_NAME;
|
||||
static xcb_window_t findById(
|
||||
xcb_connection_t* conn, xcb_window_t root, uint32_t targetId);
|
||||
|
||||
const CSenseApiDesc *HK_GET_SENSE_API_DESC_FN_NAME(void)
|
||||
{
|
||||
return &xcbXorgApiDesc;
|
||||
static xcb_window_t findByName(
|
||||
xcb_connection_t* conn, xcb_window_t root,
|
||||
const std::string& targetName, std::string& outWindowName,
|
||||
MatchType matchType);
|
||||
}
|
||||
|
||||
class AttachedDevice
|
||||
{
|
||||
public:
|
||||
struct WindowSelector
|
||||
{
|
||||
xcb_window_search::MatchType matchType;
|
||||
|
||||
XcbConnection::XConnectionIdentifier xconn;
|
||||
struct
|
||||
{
|
||||
uint32_t id;
|
||||
std::string name;
|
||||
} window;
|
||||
|
||||
std::string stringify() const
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << "Display: " << xconn.display
|
||||
<< ", Screen: " << xconn.screen << ", Window: ";
|
||||
if (matchType == xcb_window_search::MatchType::ID) {
|
||||
os << window.id;
|
||||
} else {
|
||||
os << "\"" << window.name << "\"";
|
||||
}
|
||||
os << " (matchType="
|
||||
<< (matchType == xcb_window_search::MatchType::EXACT
|
||||
? "exact" :
|
||||
(matchType == xcb_window_search::MatchType::SUBSTRING)
|
||||
? "substring" : "id")
|
||||
<< ")";
|
||||
return os.str();
|
||||
}
|
||||
};
|
||||
|
||||
AttachedDevice(const hk::device::SenseDeviceSpec &spec,
|
||||
std::shared_ptr<XcbConnection> conn)
|
||||
: deviceSpec(spec)
|
||||
// This std::move is moving ownership from a shared_ptr to a shared_ptr
|
||||
, connection(std::move(conn))
|
||||
{
|
||||
windowSelector.xconn.display = getRequiredParamAsInt(spec, "display");
|
||||
windowSelector.xconn.screen = getRequiredParamAsInt(spec, "screen");
|
||||
parseWindowSelector(spec, windowSelector);
|
||||
|
||||
// Get the root window
|
||||
const xcb_setup_t* setup = xcb_get_setup(connection->connection.get());
|
||||
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(setup);
|
||||
for (int i = 0; i < windowSelector.xconn.screen; ++i) {
|
||||
xcb_screen_next(&iter);
|
||||
}
|
||||
xcb_window_t root = iter.data->root;
|
||||
|
||||
// Search for window
|
||||
xcb_window_t targetWindow = 0;
|
||||
if (windowSelector.matchType == xcb_window_search::MatchType::ID)
|
||||
{
|
||||
targetWindow = xcb_window_search::findById(
|
||||
connection->connection.get(), root, windowSelector.window.id);
|
||||
}
|
||||
else
|
||||
{
|
||||
targetWindow = xcb_window_search::findByName(
|
||||
connection->connection.get(), root,
|
||||
windowSelector.window.name, actualWindowName,
|
||||
windowSelector.matchType);
|
||||
}
|
||||
|
||||
if (!targetWindow)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Failed to find window "
|
||||
+ (windowSelector.matchType == xcb_window_search::MatchType::ID
|
||||
? std::to_string(windowSelector.window.id)
|
||||
: "\"" + windowSelector.window.name + "\"")
|
||||
+ " on display " + std::to_string(windowSelector.xconn.display)
|
||||
+ ", screen " + std::to_string(windowSelector.xconn.screen));
|
||||
}
|
||||
}
|
||||
|
||||
hk::device::SenseDeviceSpec deviceSpec;
|
||||
WindowSelector windowSelector;
|
||||
std::shared_ptr<XcbConnection> connection;
|
||||
std::string actualWindowName;
|
||||
|
||||
public:
|
||||
static int getRequiredParamAsInt(
|
||||
const hk::device::SenseDeviceSpec& spec,
|
||||
const std::string& paramName)
|
||||
{
|
||||
auto it = std::find_if(
|
||||
spec.providerParams.begin(),
|
||||
spec.providerParams.end(),
|
||||
[¶mName](const auto& param) {
|
||||
return param.first == paramName;
|
||||
}
|
||||
);
|
||||
|
||||
if (it == spec.providerParams.end())
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"No " + paramName + " specified in provider params");
|
||||
}
|
||||
|
||||
try {
|
||||
return std::stoi(it->second);
|
||||
} catch (const std::exception& e) {
|
||||
throw std::runtime_error(
|
||||
"Failed to parse '" + paramName + "' param value '"
|
||||
+ it->second + "' as integer: " + e.what());
|
||||
}
|
||||
}
|
||||
|
||||
static void parseWindowSelector(
|
||||
const hk::device::SenseDeviceSpec& spec,
|
||||
WindowSelector& windowSelector)
|
||||
{
|
||||
// Default match type
|
||||
windowSelector.matchType = xcb_window_search::MatchType::SUBSTRING;
|
||||
|
||||
// Check if 'dev-id', 'dev-string', or 'dev-substring' is specified
|
||||
for (const auto& param : spec.apiParams)
|
||||
{
|
||||
if (param.first == "dev-id" || param.first == "devid")
|
||||
{
|
||||
windowSelector.matchType = xcb_window_search::MatchType::ID;
|
||||
break;
|
||||
}
|
||||
if (param.first == "dev-string" || param.first == "dev-str"
|
||||
|| param.first == "devstr" || param.first == "devstring")
|
||||
{
|
||||
windowSelector.matchType = xcb_window_search::MatchType::EXACT;
|
||||
}
|
||||
if (param.first == "dev-substring" || param.first == "dev-substr"
|
||||
|| param.first == "devsubstr" || param.first == "devsubstring")
|
||||
{
|
||||
windowSelector.matchType = xcb_window_search::
|
||||
MatchType::SUBSTRING;
|
||||
}
|
||||
}
|
||||
|
||||
if (windowSelector.matchType == xcb_window_search::MatchType::ID)
|
||||
{
|
||||
try {
|
||||
windowSelector.window.id = std::stoul(
|
||||
spec.deviceSelector, nullptr, 0);
|
||||
} catch (const std::exception&) {
|
||||
throw std::runtime_error(
|
||||
"Window selector: 'dev-id' present, but selector is not "
|
||||
"numeric");
|
||||
}
|
||||
}
|
||||
else {
|
||||
windowSelector.window.name = spec.deviceSelector;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Define the static member
|
||||
std::map<XcbConnection::XConnectionIdentifier, std::shared_ptr<XcbConnection>>
|
||||
XcbConnection::connections;
|
||||
|
||||
static std::vector<AttachedDevice> attachedDevices;
|
||||
|
||||
namespace xcb_window_search {
|
||||
|
||||
static xcb_window_t findById(
|
||||
xcb_connection_t* conn, xcb_window_t root, uint32_t targetId)
|
||||
{
|
||||
xcb_query_tree_cookie_t cookie = xcb_query_tree(conn, root);
|
||||
std::unique_ptr<xcb_query_tree_reply_t, XcbReplyDeleter> reply(
|
||||
xcb_query_tree_reply(conn, cookie, nullptr));
|
||||
if (!reply) return 0;
|
||||
|
||||
// First check if current window is the target
|
||||
if (root == targetId) {
|
||||
return root;
|
||||
}
|
||||
|
||||
// Then check all children
|
||||
xcb_window_t* children = xcb_query_tree_children(reply.get());
|
||||
int num_children = xcb_query_tree_children_length(reply.get());
|
||||
|
||||
for (int i = 0; i < num_children; ++i)
|
||||
{
|
||||
if (children[i] == targetId) {
|
||||
return children[i];
|
||||
}
|
||||
|
||||
// Recursively search this child's subtree
|
||||
if (xcb_window_t result = findById(
|
||||
conn, children[i], targetId))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static xcb_window_t findByName(
|
||||
xcb_connection_t* conn, xcb_window_t root,
|
||||
const std::string& targetName, std::string& outWindowName,
|
||||
MatchType matchType)
|
||||
{
|
||||
xcb_query_tree_cookie_t cookie = xcb_query_tree(conn, root);
|
||||
std::unique_ptr<xcb_query_tree_reply_t, XcbReplyDeleter> reply(
|
||||
xcb_query_tree_reply(conn, cookie, nullptr));
|
||||
if (!reply) return 0;
|
||||
|
||||
// First check current window
|
||||
xcb_get_property_cookie_t prop_cookie = xcb_get_property(
|
||||
conn, 0, root, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 1024);
|
||||
|
||||
std::unique_ptr<xcb_get_property_reply_t, XcbReplyDeleter> prop_reply(
|
||||
xcb_get_property_reply(conn, prop_cookie, nullptr));
|
||||
|
||||
if (prop_reply)
|
||||
{
|
||||
int len = xcb_get_property_value_length(prop_reply.get());
|
||||
char* name = static_cast<char*>(
|
||||
xcb_get_property_value(prop_reply.get()));
|
||||
if (len > 0)
|
||||
{
|
||||
std::string windowName(name, len);
|
||||
if ((matchType == MatchType::EXACT
|
||||
&& windowName == targetName)
|
||||
|| (matchType == MatchType::SUBSTRING
|
||||
&& windowName.find(targetName) != std::string::npos))
|
||||
{
|
||||
outWindowName = windowName;
|
||||
return root;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Then check all children
|
||||
xcb_window_t* children = xcb_query_tree_children(reply.get());
|
||||
int num_children = xcb_query_tree_children_length(reply.get());
|
||||
|
||||
for (int i = 0; i < num_children; ++i)
|
||||
{
|
||||
prop_cookie = xcb_get_property(
|
||||
conn, 0, children[i],
|
||||
XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 0, 1024);
|
||||
|
||||
prop_reply.reset(xcb_get_property_reply(conn, prop_cookie, nullptr));
|
||||
if (prop_reply)
|
||||
{
|
||||
int len = xcb_get_property_value_length(prop_reply.get());
|
||||
char* name = static_cast<char*>(xcb_get_property_value(
|
||||
prop_reply.get()));
|
||||
if (len > 0)
|
||||
{
|
||||
std::string windowName(name, len);
|
||||
if ((matchType == MatchType::EXACT
|
||||
&& windowName == targetName)
|
||||
|| (matchType == MatchType::SUBSTRING
|
||||
&& windowName.find(targetName) != std::string::npos))
|
||||
{
|
||||
outWindowName = windowName;
|
||||
return children[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively search this child's subtree
|
||||
if (xcb_window_t result = findByName(
|
||||
conn, children[i], targetName, outWindowName, matchType))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace xcb_window_search
|
||||
|
||||
static hk::sense_api::sal_mlo_initializeIndFn xcbXorg_initializeInd;
|
||||
static hk::sense_api::sal_mlo_finalizeIndFn xcbXorg_finalizeInd;
|
||||
static hk::sense_api::sal_mlo_attachDeviceReqFn xcbXorg_attachDeviceReq;
|
||||
static hk::sense_api::sal_mlo_detachDeviceReqFn xcbXorg_detachDeviceReq;
|
||||
|
||||
static hk::sense_api::SenseApiDesc xcbXorgApiDesc =
|
||||
{
|
||||
.name = "xcb",
|
||||
.exportedImplexorApis = { { "video-implexor" } },
|
||||
.sal_mgmt_libOps = {
|
||||
.initializeInd = xcbXorg_initializeInd,
|
||||
.finalizeInd = xcbXorg_finalizeInd,
|
||||
.attachDeviceReq = xcbXorg_attachDeviceReq,
|
||||
.detachDeviceReq = xcbXorg_detachDeviceReq
|
||||
}
|
||||
};
|
||||
|
||||
extern HK_UNMANGLED hk::sense_api::getSenseApiDescFn
|
||||
HK_GET_SENSE_API_DESC_FN_NAME;
|
||||
|
||||
const hk::sense_api::SenseApiDesc &HK_GET_SENSE_API_DESC_FN_NAME(void)
|
||||
{
|
||||
return xcbXorgApiDesc;
|
||||
}
|
||||
|
||||
static sal_lmo_initializeIndFn xcbXorg_initializeInd;
|
||||
int xcbXorg_initializeInd(void)
|
||||
{
|
||||
std::cerr << "XcbXorg::sal_lmo_initializeInd\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sal_lmo_finalizeIndFn xcbXorg_finalizeInd;
|
||||
int xcbXorg_finalizeInd(void)
|
||||
{
|
||||
std::cerr << "XcbXorg::sal_lmo_finalizeInd\n";
|
||||
XcbConnection::connections.clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sal_lmo_attachDeviceReqFn xcbXorg_attachDeviceReq;
|
||||
int xcbXorg_attachDeviceReq(void)
|
||||
int xcbXorg_attachDeviceReq(const hk::device::SenseDeviceSpec &desc)
|
||||
{
|
||||
std::cerr << "XcbXorg::sal_lmo_attachDeviceReq\n";
|
||||
// Ensure connection exists before creating device. Create conn'tion if not.
|
||||
XcbConnection::XConnectionIdentifier id{
|
||||
AttachedDevice::getRequiredParamAsInt(desc, "display"),
|
||||
AttachedDevice::getRequiredParamAsInt(desc, "screen")
|
||||
};
|
||||
auto conn = XcbConnection::getOrCreateConnection(id);
|
||||
// RAII protection in case AttachDevice construction below fails
|
||||
XcbConnection::ConnectionGuard guard(conn);
|
||||
|
||||
// Create device and increment connection refcount
|
||||
attachedDevices.emplace_back(desc, conn);
|
||||
// Successfully attached device, so decouple guard from RAII cleanup
|
||||
conn->refCount++;
|
||||
guard.commit();
|
||||
|
||||
std::cout << "Attaching X11 window:\n "
|
||||
<< attachedDevices.back().windowSelector.stringify() << "\n"
|
||||
<< " Actual window name: \""
|
||||
<< attachedDevices.back().actualWindowName << "\"\n"
|
||||
<< " Using " << (conn->refCount > 1 ? "existing" : "new")
|
||||
<< " connection to X server\n";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static sal_lmo_detachDeviceReqFn xcbXorg_detachDeviceReq;
|
||||
int xcbXorg_detachDeviceReq(void)
|
||||
int xcbXorg_detachDeviceReq(const hk::device::SenseDeviceSpec &spec)
|
||||
{
|
||||
std::cerr << "XcbXorg::sal_lmo_detachDeviceReq\n";
|
||||
auto it = std::find_if(attachedDevices.begin(), attachedDevices.end(),
|
||||
[&spec](const AttachedDevice &device) {
|
||||
return device.deviceSpec == spec;
|
||||
}
|
||||
);
|
||||
|
||||
if (it == attachedDevices.end())
|
||||
{
|
||||
auto displayIt = std::find_if(
|
||||
spec.providerParams.begin(), spec.providerParams.end(),
|
||||
[](const auto& param) { return param.first == "display"; });
|
||||
auto screenIt = std::find_if(
|
||||
spec.providerParams.begin(), spec.providerParams.end(),
|
||||
[](const auto& param) { return param.first == "screen"; });
|
||||
|
||||
std::cerr << __func__ << ": Device not attached: "
|
||||
<< "display=" << (displayIt != spec.providerParams.end()
|
||||
? displayIt->second : "not specified")
|
||||
<< ", screen=" << (screenIt != spec.providerParams.end()
|
||||
? screenIt->second : "not specified")
|
||||
<< ", selector=" << spec.deviceSelector << "\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
XcbConnection::XConnectionIdentifier id{
|
||||
it->windowSelector.xconn.display,
|
||||
it->windowSelector.xconn.screen
|
||||
};
|
||||
|
||||
// Atomic decrement refcount
|
||||
int newCount = --it->connection->refCount;
|
||||
|
||||
// If no more references, close the connection
|
||||
if (newCount == 0)
|
||||
{
|
||||
XcbConnection::connections.erase(id);
|
||||
std::cout << "Closed X server connection (display="
|
||||
<< id.display << ", screen=" << id.screen << ")\n";
|
||||
}
|
||||
|
||||
attachedDevices.erase(it);
|
||||
std::cout << __func__ << ": Detached device\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user