Compare commits
164 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0559d9ea42 | |||
| 1c78fc5c31 | |||
| 3b224501c7 | |||
| 0dc8abaa28 | |||
| eb069c4a96 | |||
| 32e76c2ca5 | |||
| 4827177703 | |||
| d36d03dcc3 | |||
| 6362298016 | |||
| 6314b0182a | |||
| 0fefa5be7f | |||
| 7ea31cdb8d | |||
| d217354689 | |||
| bb7be7fb3c | |||
| ee8fca3008 | |||
| 525af45fd4 | |||
| 3ff329a553 | |||
| 66257bcd0e | |||
| 6ef86eea05 | |||
| 8f41e164a2 | |||
| b9ca38bff1 | |||
| e024ccdf95 | |||
| f3f2384f9b | |||
| cd63593ae5 | |||
| 58dbc7e320 | |||
| e3795c4233 | |||
| edc198dd00 | |||
| 2bf7390f97 | |||
| 1ea1b4b9f6 | |||
| c14208f6ce | |||
| f4ff8f0e40 | |||
| ba3841c30b | |||
| 4a8cb12294 | |||
| b6b2ce7ada | |||
| 7bee9b07ae | |||
| d26b791dd2 | |||
| 7579446388 | |||
| 099d60bcc4 | |||
| 42b32f27e6 | |||
| c457ee7aca | |||
| e2e589dc17 | |||
| d4898bbca1 | |||
| e836b2bf32 | |||
| fef73692f7 | |||
| 285b63b618 | |||
| 1deb92a416 | |||
| 6f6fa77498 | |||
| 79825e4da3 | |||
| 6114a2648d | |||
| 4a3daaf403 | |||
| 49c8b5bca1 | |||
| a2598e80fd | |||
| 4fde28dad8 | |||
| e276fcbdce | |||
| 36c79f3a2e | |||
| 513405a831 | |||
| 7b962a75d3 | |||
| 270437fdd4 | |||
| a17c940377 | |||
| b28239550e | |||
| 1e17b83061 | |||
| 1bf5f46404 | |||
| 064dc43fbc | |||
| 020a4968e5 | |||
| d0aa8e2306 | |||
| 79f3e84ff8 | |||
| 756571b9b4 | |||
| 9c16aeeb55 | |||
| d6a0b0301e | |||
| e87656fd12 | |||
| f06aeb6c9b | |||
| c58d422158 | |||
| 8dfb1e5b2f | |||
| 50b8aaf34d | |||
| 0f5e499d7c | |||
| 710749c399 | |||
| 3503cce0db | |||
| 90a0eebdd8 | |||
| 1540af1e74 | |||
| 065b2593f4 | |||
| e7974db324 | |||
| 336094ef90 | |||
| d43a8af6bd | |||
| 37ad6995c3 | |||
| c9e8a9f1fb | |||
| dacc050bf9 | |||
| f8825942b1 | |||
| e201b5e695 | |||
| 29a1e1ecf2 | |||
| a5c2f47e9f | |||
| 42f55bb324 | |||
| 1450d745ab | |||
| 76141e3a92 | |||
| 36592293dd | |||
| f1696f8272 | |||
| 99c126a08c | |||
| 293c1054d1 | |||
| 38298a8ef8 | |||
| 2b8b176038 | |||
| 76e465bd1d | |||
| ead7d8ff5f | |||
| 9cc7a6685c | |||
| 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 | |||
| 53583e5735 | |||
| 2a397ae064 | |||
| 88df316013 | |||
| 396bcefbf4 | |||
| 62db724246 | |||
| bbbd6c36cd | |||
| d14cef5328 | |||
| 988e84a545 | |||
| 4f2fbaa255 | |||
| 01ddb6d842 | |||
| 1178970728 | |||
| 090f0d3b02 | |||
| 04db7bf76c | |||
| 2dc3083cdb | |||
| bffa2b837c | |||
| b40790ee4a | |||
| c864bcfdd2 | |||
| f5e3986644 | |||
| 5a5e2fa25f | |||
| b9aa53822f | |||
| 376b29871b | |||
| fe3f911db4 | |||
| f594d29a2d | |||
| 6a494f7ff7 | |||
| 36acbdfc36 | |||
| dd7a75d9b5 |
+5
-1
@@ -1,10 +1,14 @@
|
|||||||
|
/build
|
||||||
|
/b-*
|
||||||
*~
|
*~
|
||||||
**/.deps/
|
**/.deps/
|
||||||
**/Makefile.in
|
**/Makefile.in
|
||||||
aclocal.m4
|
aclocal.m4
|
||||||
/b
|
/b
|
||||||
|
/bautotools
|
||||||
/autotools-aux
|
/autotools-aux
|
||||||
autom4te.cache/
|
autom4te.cache/
|
||||||
config.h.in
|
config.h.in
|
||||||
configure
|
configure
|
||||||
|
*.swp
|
||||||
|
cscope.out
|
||||||
|
|||||||
Vendored
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
"includePath": [
|
"includePath": [
|
||||||
"${workspaceFolder}/**",
|
"${workspaceFolder}/**",
|
||||||
"${workspaceFolder}/include",
|
"${workspaceFolder}/include",
|
||||||
"${workspaceFolder}/hcore/include",
|
"${workspaceFolder}/smocore/include",
|
||||||
"/usr/include",
|
"/usr/include",
|
||||||
"/usr/local/include",
|
"/usr/local/include",
|
||||||
"${workspaceFolder}/b/include"
|
"${workspaceFolder}/b/include"
|
||||||
|
|||||||
Vendored
+145
-11
@@ -5,13 +5,39 @@
|
|||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
{
|
||||||
"name": "C++ Launch",
|
"name": "Debug salmanoff (Basic)",
|
||||||
"type": "cppdbg",
|
"type": "cppdbg",
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"program": "${workspaceFolder}/b/harikoff",
|
"program": "${workspaceFolder}/b/salmanoff",
|
||||||
"args": [],
|
"args": [],
|
||||||
"stopAtEntry": false,
|
"stopAtEntry": false,
|
||||||
"cwd": "${workspaceFolder}",
|
"cwd": "${workspaceFolder}/b",
|
||||||
|
"environment": [],
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Set disassembly flavor to intel",
|
||||||
|
"text": "set disassembly-flavor intel",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "build-salmanoff",
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug salmanoff (Help)",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/b/salmanoff",
|
||||||
|
"args": ["--help"],
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "${workspaceFolder}/b",
|
||||||
"environment": [],
|
"environment": [],
|
||||||
"externalConsole": false,
|
"externalConsole": false,
|
||||||
"MIMode": "gdb",
|
"MIMode": "gdb",
|
||||||
@@ -22,15 +48,123 @@
|
|||||||
"ignoreFailures": true
|
"ignoreFailures": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"preLaunchTask": "Build",
|
"preLaunchTask": "build-salmanoff",
|
||||||
"miDebuggerPath": "/usr/bin/gdb",
|
"miDebuggerPath": "/usr/bin/gdb"
|
||||||
"logging": {
|
},
|
||||||
"trace": true,
|
{
|
||||||
"traceResponse": true,
|
"name": "Debug salmanoff (Verbose)",
|
||||||
"engineLogging": true,
|
"type": "cppdbg",
|
||||||
"programOutput": true,
|
"request": "launch",
|
||||||
"exceptions": true
|
"program": "${workspaceFolder}/b/salmanoff",
|
||||||
|
"args": ["--verbose"],
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "${workspaceFolder}/b",
|
||||||
|
"environment": [],
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "build-salmanoff",
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug salmanoff (Custom Args)",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/b/salmanoff",
|
||||||
|
"args": ["--devicespec", "test_device", "--verbose"],
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "${workspaceFolder}/b",
|
||||||
|
"environment": [],
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "build-salmanoff",
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Attach to salmanoff (Remote Debug)",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "attach",
|
||||||
|
"program": "${workspaceFolder}/b/salmanoff",
|
||||||
|
"processId": "${command:pickProcess}",
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug salmanoff (Break on Main)",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/b/salmanoff",
|
||||||
|
"args": ["--help"],
|
||||||
|
"stopAtEntry": true,
|
||||||
|
"cwd": "${workspaceFolder}/b",
|
||||||
|
"environment": [],
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Set breakpoint on main",
|
||||||
|
"text": "break main",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "build-salmanoff",
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Debug salmanoff (ComponentThread Focus)",
|
||||||
|
"type": "cppdbg",
|
||||||
|
"request": "launch",
|
||||||
|
"program": "${workspaceFolder}/b/salmanoff",
|
||||||
|
"args": ["--help"],
|
||||||
|
"stopAtEntry": false,
|
||||||
|
"cwd": "${workspaceFolder}/b",
|
||||||
|
"environment": [],
|
||||||
|
"externalConsole": false,
|
||||||
|
"MIMode": "gdb",
|
||||||
|
"setupCommands": [
|
||||||
|
{
|
||||||
|
"description": "Enable pretty-printing for gdb",
|
||||||
|
"text": "-enable-pretty-printing",
|
||||||
|
"ignoreFailures": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Set breakpoint on ComponentThread constructor",
|
||||||
|
"text": "break ComponentThread::ComponentThread",
|
||||||
|
"ignoreFailures": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Set breakpoint on Mind constructor",
|
||||||
|
"text": "break Mind::Mind",
|
||||||
|
"ignoreFailures": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"preLaunchTask": "build-salmanoff",
|
||||||
|
"miDebuggerPath": "/usr/bin/gdb"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+16
-2
@@ -63,7 +63,21 @@
|
|||||||
"typeinfo": "cpp",
|
"typeinfo": "cpp",
|
||||||
"variant": "cpp",
|
"variant": "cpp",
|
||||||
"cstring": "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",
|
||||||
|
"bitset": "cpp"
|
||||||
},
|
},
|
||||||
"editor.rulers": [80, 120]
|
"editor.rulers": [80, 120],
|
||||||
|
"editor.tabSize": 4,
|
||||||
|
"editor.insertSpaces": false,
|
||||||
|
"editor.detectIndentation": false
|
||||||
}
|
}
|
||||||
Vendored
+53
-43
@@ -2,68 +2,78 @@
|
|||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"label": "Create Build Directory",
|
"label": "build-salmanoff",
|
||||||
"type": "shell",
|
|
||||||
"command": "mkdir -p b",
|
|
||||||
"problemMatcher": [],
|
|
||||||
"detail": "Creates the build directory."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Configure",
|
|
||||||
"type": "shell",
|
|
||||||
"command": "${workspaceFolder}/configure",
|
|
||||||
"options": {
|
|
||||||
"cwd": "${workspaceFolder}/b"
|
|
||||||
},
|
|
||||||
"group": {
|
|
||||||
"kind": "build",
|
|
||||||
"isDefault": true
|
|
||||||
},
|
|
||||||
"problemMatcher": ["$gcc"],
|
|
||||||
"dependsOn": ["Create Build Directory"],
|
|
||||||
"detail": "Runs the configure script to prepare the build environment."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"label": "Build",
|
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "make",
|
"command": "make",
|
||||||
"options": {
|
|
||||||
"cwd": "${workspaceFolder}/b"
|
|
||||||
},
|
|
||||||
"group": {
|
"group": {
|
||||||
"kind": "build",
|
"kind": "build",
|
||||||
"isDefault": true
|
"isDefault": true
|
||||||
},
|
},
|
||||||
"problemMatcher": ["$gcc"],
|
"presentation": {
|
||||||
"dependsOn": ["Configure"],
|
"echo": true,
|
||||||
"detail": "Builds the project using make."
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared",
|
||||||
|
"showReuseMessage": true,
|
||||||
|
"clear": false
|
||||||
|
},
|
||||||
|
"problemMatcher": [
|
||||||
|
"$gcc"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${workspaceFolder}/b"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "clean",
|
"label": "clean-salmanoff",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "make clean",
|
"command": "make clean",
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared",
|
||||||
|
"showReuseMessage": true,
|
||||||
|
"clear": false
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}/b"
|
"cwd": "${workspaceFolder}/b"
|
||||||
},
|
}
|
||||||
"group": {
|
|
||||||
"kind": "none"
|
|
||||||
},
|
|
||||||
"problemMatcher": [],
|
|
||||||
"detail": "Cleans the build artifacts."
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "test",
|
"label": "rebuild-salmanoff",
|
||||||
"type": "shell",
|
"type": "shell",
|
||||||
"command": "make test",
|
"command": "make clean && make",
|
||||||
|
"group": "build",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared",
|
||||||
|
"showReuseMessage": true,
|
||||||
|
"clear": false
|
||||||
|
},
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}/b"
|
"cwd": "${workspaceFolder}/b"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"group": {
|
{
|
||||||
"kind": "test",
|
"label": "run-salmanoff-help",
|
||||||
"isDefault": true
|
"type": "shell",
|
||||||
|
"command": "./salmanoff --help",
|
||||||
|
"group": "test",
|
||||||
|
"presentation": {
|
||||||
|
"echo": true,
|
||||||
|
"reveal": "always",
|
||||||
|
"focus": false,
|
||||||
|
"panel": "shared",
|
||||||
|
"showReuseMessage": true,
|
||||||
|
"clear": false
|
||||||
},
|
},
|
||||||
"problemMatcher": ["$gcc"],
|
"options": {
|
||||||
"detail": "Runs the tests."
|
"cwd": "${workspaceFolder}/b"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(salmanoff VERSION 0.00.004 LANGUAGES CXX)
|
||||||
|
|
||||||
|
include(CMakeDependentOption)
|
||||||
|
|
||||||
|
# Set C++ standard
|
||||||
|
set(CMAKE_CXX_STANDARD 20)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
# Build type
|
||||||
|
if(NOT CMAKE_BUILD_TYPE)
|
||||||
|
set(CMAKE_BUILD_TYPE Debug FORCE)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Compiler flags
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic")
|
||||||
|
|
||||||
|
# Mind oscillator configuration
|
||||||
|
set(MIND_VOSCILLATOR_PERIOD_MS 33 CACHE STRING "Mind's virtual osc clock rate (ms)")
|
||||||
|
if(NOT MIND_VOSCILLATOR_PERIOD_MS GREATER 0)
|
||||||
|
message(FATAL_ERROR "MIND_VOSCILLATOR_PERIOD_MS must be a positive integer > 0")
|
||||||
|
endif()
|
||||||
|
math(EXPR MIND_VOSCILLATOR_FREQ_MS "1000 / ${MIND_VOSCILLATOR_PERIOD_MS}")
|
||||||
|
|
||||||
|
# Configure config.h
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include/config.h.in
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/include/config.h
|
||||||
|
)
|
||||||
|
|
||||||
|
# Include directories
|
||||||
|
include_directories(
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/smocore/include
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/include
|
||||||
|
)
|
||||||
|
|
||||||
|
# Find core dependencies
|
||||||
|
find_package(Boost 1.69.0 REQUIRED COMPONENTS system)
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
find_package(FLEX REQUIRED)
|
||||||
|
find_package(BISON REQUIRED)
|
||||||
|
|
||||||
|
# Need dlopen() and dlsym()
|
||||||
|
find_library(DL_LIBRARY NAMES dl ldl)
|
||||||
|
if(NOT DL_LIBRARY)
|
||||||
|
message(FATAL_ERROR "Dynamic linking library (libdl/libldl) not found")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Add core components
|
||||||
|
add_subdirectory(smocore)
|
||||||
|
add_subdirectory(commonLibs)
|
||||||
|
add_subdirectory(senseApis)
|
||||||
|
add_subdirectory(wilzorApis)
|
||||||
|
|
||||||
|
# Main executable
|
||||||
|
add_executable(salmanoff main.cpp)
|
||||||
|
target_link_libraries(salmanoff
|
||||||
|
smocore
|
||||||
|
marionette
|
||||||
|
deviceManager
|
||||||
|
senseApis
|
||||||
|
${Boost_LIBRARIES}
|
||||||
|
${DL_LIBRARY}
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS salmanoff DESTINATION bin)
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
SUBDIRS = hcore
|
|
||||||
|
|
||||||
AM_CPPFLAGS+= -I"$(top_srcdir)/hcore/include"
|
|
||||||
|
|
||||||
bin_PROGRAMS = harikoff
|
|
||||||
harikoff_SOURCES = main.cpp
|
|
||||||
harikoff_LDADD = hcore/libhcore.a hcore/deviceManager/libdeviceManager.a
|
|
||||||
@@ -1,5 +1,10 @@
|
|||||||
# The Harriman-Peikoff Project
|
# The Salmanoff Project:
|
||||||
|
|
||||||
Wouldn't you like to know what this project is and does? Well, it's a secret!
|

|
||||||
But you can find out by reading the code. Or you could just ask me. Or you
|
|
||||||
could wait until I release it. But that's no fun.
|
This project, Salmanoff (pronounced: Sal-man-off), is an ROS rewrite of the Harikoff project. The name is more reflective of the people whose ideas sparked the solutions in my mind. These people are:
|
||||||
|
* Gregory `SAL`mieri.
|
||||||
|
* David Harri`MAN`.
|
||||||
|
* Leonard Peik`OFF`.
|
||||||
|
|
||||||
|
Would you like to know what this project is and does? Well, it's a secret! But you can find out by reading the code. Or you could just ask me. Or you could wait until I release it. But that's no fun.
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# Cross-compilation toolchain file for aarch64-linux-gnu
|
||||||
|
# This file should be used with cmake -DCMAKE_TOOLCHAIN_FILE=cmake/aarch64-linux-gnu.cmake
|
||||||
|
|
||||||
|
# Disable some features that might not be available in cross-compilation
|
||||||
|
set(CMAKE_CROSSCOMPILING TRUE)
|
||||||
|
|
||||||
|
# Target OS.
|
||||||
|
set(CMAKE_SYSTEM_NAME Linux)
|
||||||
|
# Use whatever the host system version is.
|
||||||
|
# set(CMAKE_SYSTEM_VERSION ${CMAKE_HOST_SYSTEM_VERSION})
|
||||||
|
set(CMAKE_SYSTEM_PROCESSOR aarch64)
|
||||||
|
|
||||||
|
# Specify the cross compilers.
|
||||||
|
# We could do some more advanced stuff here to search for the correct
|
||||||
|
# cross-compiler based on CMAKE_SYSTEM_NAME & CMAKE_SYSTEM_PROCESSOR.
|
||||||
|
set(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
|
||||||
|
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
|
||||||
|
# These are necessary for CLang.
|
||||||
|
# set(CMAKE_C_COMPILER_TARGET aarch64-linux-gnu)
|
||||||
|
# set(CMAKE_CXX_COMPILER_TARGET aarch64-linux-gnu)
|
||||||
|
|
||||||
|
# Set architecture-specific flags
|
||||||
|
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=armv8-a -mtune=cortex-a72")
|
||||||
|
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=armv8-a -mtune=cortex-a72")
|
||||||
|
|
||||||
|
# Set the target environment
|
||||||
|
set(CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu)
|
||||||
|
|
||||||
|
# Search for programs in the build host directories
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
|
||||||
|
# Search for libraries and headers in the target directories
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
|
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE BOTH)
|
||||||
|
|
||||||
|
# Set pkg-config to use the cross-compiled libraries
|
||||||
|
set(ENV{PKG_CONFIG_PATH} "/usr/aarch64-linux-gnu/lib/pkgconfig:/usr/lib/aarch64-linux-gnu/pkgconfig")
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
add_subdirectory(xcbXorg)
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
option(ENABLE_LIB_xcbXorg "Enable XCB/Xorg Connection Manager backend lib" ON)
|
||||||
|
|
||||||
|
if(ENABLE_LIB_xcbXorg)
|
||||||
|
pkg_check_modules(XCB REQUIRED xcb)
|
||||||
|
if(NOT XCB_FOUND)
|
||||||
|
message(FATAL_ERROR "XCB library not found. XCB/Xorg requires the XCB dev headers and shlib.")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_library(xcbXorg SHARED
|
||||||
|
xcbXorg.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Set config define for header generation
|
||||||
|
add_compile_definitions(CONFIG_LIB_XCBXORG_ENABLED)
|
||||||
|
target_include_directories(xcbXorg PUBLIC ${XCB_INCLUDE_DIRS})
|
||||||
|
target_link_libraries(xcbXorg ${XCB_LIBRARIES})
|
||||||
|
|
||||||
|
# Install rules
|
||||||
|
install(TARGETS xcbXorg DESTINATION lib)
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,292 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
|
#include <map>
|
||||||
|
#include <atomic>
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
#include "xcbXorg.h"
|
||||||
|
|
||||||
|
namespace xcb_xorg {
|
||||||
|
|
||||||
|
// Static member initialization
|
||||||
|
std::map<ConnectionIdentifier, std::shared_ptr<XcbConnection>>
|
||||||
|
ConnectionManager::connections;
|
||||||
|
|
||||||
|
std::string ConnectionIdentifier::stringify() const
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
os << "display=" << display << ", screen=" << screen;
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
XcbConnection::XcbConnection(const ConnectionIdentifier& 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<XcbConnection> ConnectionManager::getOrCreateConnection(
|
||||||
|
const ConnectionIdentifier& 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConnectionManager::cleanupAllConnections()
|
||||||
|
{
|
||||||
|
connections.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ConnectionManager::getConnectionCount()
|
||||||
|
{
|
||||||
|
return connections.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove a specific connection from the manager
|
||||||
|
* @param id Connection identifier to remove
|
||||||
|
*/
|
||||||
|
void ConnectionManager::removeConnection(const ConnectionIdentifier& id)
|
||||||
|
{
|
||||||
|
auto it = connections.find(id);
|
||||||
|
if (it != connections.end()) {
|
||||||
|
connections.erase(it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace window_search {
|
||||||
|
|
||||||
|
struct XcbReplyDeleter {
|
||||||
|
void operator()(xcb_query_tree_reply_t* p) { free(p); }
|
||||||
|
void operator()(xcb_get_property_reply_t* p) { free(p); }
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (root == targetId) return root;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 window_search
|
||||||
|
|
||||||
|
} // namespace xcb_xorg
|
||||||
|
|
||||||
|
// Export functions for external libraries
|
||||||
|
/**
|
||||||
|
* @brief Get or create a connection to the X server
|
||||||
|
* @param display Display number
|
||||||
|
* @param screen Screen number
|
||||||
|
* @return Shared pointer to connection (as void* for C compatibility)
|
||||||
|
*/
|
||||||
|
extern "C" get_or_create_connection_fn xcb_xorg_get_or_create_connection;
|
||||||
|
std::shared_ptr<xcb_xorg::XcbConnection> xcb_xorg_get_or_create_connection(
|
||||||
|
int display, int screen)
|
||||||
|
{
|
||||||
|
xcb_xorg::ConnectionIdentifier id{display, screen};
|
||||||
|
auto conn = xcb_xorg::ConnectionManager::getOrCreateConnection(id);
|
||||||
|
conn->incrementRefCount();
|
||||||
|
return conn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clean up all connections
|
||||||
|
*/
|
||||||
|
extern "C" cleanup_connections_fn xcb_xorg_cleanup_connections;
|
||||||
|
void xcb_xorg_cleanup_connections()
|
||||||
|
{
|
||||||
|
xcb_xorg::ConnectionManager::cleanupAllConnections();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of active connections
|
||||||
|
* @return Number of active connections
|
||||||
|
*/
|
||||||
|
size_t xcb_xorg_get_connection_count()
|
||||||
|
{
|
||||||
|
return xcb_xorg::ConnectionManager::getConnectionCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find window by ID
|
||||||
|
* @param conn Connection pointer (from xcb_xorg_get_connection)
|
||||||
|
* @param root Root window
|
||||||
|
* @param targetId Target window ID
|
||||||
|
* @return Window ID if found, 0 if not found
|
||||||
|
*/
|
||||||
|
extern "C" find_window_by_id_fn xcb_xorg_find_window_by_id;
|
||||||
|
xcb_window_t xcb_xorg_find_window_by_id(void* conn, xcb_window_t root, uint32_t targetId)
|
||||||
|
{
|
||||||
|
if (!conn) return 0;
|
||||||
|
auto connection = static_cast<xcb_xorg::XcbConnection*>(conn);
|
||||||
|
return xcb_xorg::window_search::findById(
|
||||||
|
connection->getConnection(), root, targetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find window by name
|
||||||
|
* @param conn Connection pointer (from xcb_xorg_get_connection)
|
||||||
|
* @param root Root window
|
||||||
|
* @param targetName Target window name
|
||||||
|
* @param outWindowName Output parameter for actual window name
|
||||||
|
* @param matchType Type of name matching
|
||||||
|
* @return Window ID if found, 0 if not found
|
||||||
|
*/
|
||||||
|
extern "C" find_window_by_name_fn xcb_xorg_find_window_by_name;
|
||||||
|
xcb_window_t xcb_xorg_find_window_by_name(void* conn, xcb_window_t root,
|
||||||
|
const std::string& targetName, std::string& outWindowName,
|
||||||
|
xcb_xorg::window_search::MatchType matchType)
|
||||||
|
{
|
||||||
|
if (!conn) return 0;
|
||||||
|
auto connection = static_cast<xcb_xorg::XcbConnection*>(conn);
|
||||||
|
return xcb_xorg::window_search::findByName(
|
||||||
|
connection->getConnection(), root, targetName, outWindowName,
|
||||||
|
matchType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Dereference a connection (decrements ref count and closes if zero)
|
||||||
|
* @param conn Shared pointer to the connection to dereference
|
||||||
|
*/
|
||||||
|
extern "C" dereference_connection_fn xcb_xorg_dereference_connection;
|
||||||
|
void xcb_xorg_dereference_connection(std::shared_ptr<xcb_xorg::XcbConnection> conn)
|
||||||
|
{
|
||||||
|
if (!conn) return;
|
||||||
|
|
||||||
|
int newRefCount = conn->decrementRefCount();
|
||||||
|
// Remove from connection manager if ref count reaches zero
|
||||||
|
if (newRefCount <= 0) {
|
||||||
|
xcb_xorg::ConnectionManager::removeConnection(conn->getIdentifier());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
#ifndef XCB_XORG_API_H
|
||||||
|
#define XCB_XORG_API_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <atomic>
|
||||||
|
#include <map>
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
|
||||||
|
namespace xcb_xorg {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Connection identifier for X server connections
|
||||||
|
*/
|
||||||
|
struct ConnectionIdentifier
|
||||||
|
{
|
||||||
|
int display;
|
||||||
|
int screen;
|
||||||
|
|
||||||
|
bool operator<(const ConnectionIdentifier& other) const
|
||||||
|
{
|
||||||
|
if (display != other.display) return display < other.display;
|
||||||
|
return screen < other.screen;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string stringify() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents a single X server connection using XCB
|
||||||
|
*
|
||||||
|
* This class manages a single connection to the X server. It provides
|
||||||
|
* RAII management for the connection and maintains a reference count
|
||||||
|
* for shared usage.
|
||||||
|
*/
|
||||||
|
class XcbConnection
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Constructor for creating a new connection
|
||||||
|
* @param id Connection identifier specifying display and screen
|
||||||
|
* @throws std::runtime_error if connection fails
|
||||||
|
*/
|
||||||
|
explicit XcbConnection(const ConnectionIdentifier& id);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Destructor - automatically closes the connection
|
||||||
|
*/
|
||||||
|
~XcbConnection() = default;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the underlying XCB connection
|
||||||
|
* @return Raw XCB connection pointer
|
||||||
|
*/
|
||||||
|
xcb_connection_t* getConnection() const { return connection.get(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the connection identifier
|
||||||
|
* @return Connection identifier
|
||||||
|
*/
|
||||||
|
const ConnectionIdentifier& getIdentifier() const { return connectionIdentifier; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Increment reference count
|
||||||
|
*/
|
||||||
|
void incrementRefCount() { refCount++; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Decrement reference count
|
||||||
|
* @return New reference count
|
||||||
|
*/
|
||||||
|
int decrementRefCount() { return --refCount; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get current reference count
|
||||||
|
* @return Current reference count
|
||||||
|
*/
|
||||||
|
int getRefCount() const { return refCount; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check if connection is valid
|
||||||
|
* @return true if connection is valid, false otherwise
|
||||||
|
*/
|
||||||
|
bool isValid() const { return connection && !xcb_connection_has_error(connection.get()); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<xcb_connection_t, decltype(&xcb_disconnect)> connection;
|
||||||
|
ConnectionIdentifier connectionIdentifier;
|
||||||
|
std::atomic<int> refCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Manages multiple X server connections
|
||||||
|
*
|
||||||
|
* This class manages a collection of XcbConnection instances. It ensures
|
||||||
|
* that each unique display and screen combination has a single connection,
|
||||||
|
* and provides RAII management for these connections.
|
||||||
|
*/
|
||||||
|
class ConnectionManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Get or create a connection to the X server
|
||||||
|
* @param id Connection identifier specifying display and screen
|
||||||
|
* @return Shared pointer to the connection
|
||||||
|
* @throws std::runtime_error if connection fails
|
||||||
|
*/
|
||||||
|
static std::shared_ptr<XcbConnection> getOrCreateConnection(
|
||||||
|
const ConnectionIdentifier& id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clean up all connections
|
||||||
|
*/
|
||||||
|
static void cleanupAllConnections();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Get the number of active connections
|
||||||
|
* @return Number of active connections
|
||||||
|
*/
|
||||||
|
static size_t getConnectionCount();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Remove a specific connection from the manager
|
||||||
|
* @param id Connection identifier to remove
|
||||||
|
*/
|
||||||
|
static void removeConnection(const ConnectionIdentifier& id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::map<ConnectionIdentifier, std::shared_ptr<XcbConnection>> connections;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Window search functionality
|
||||||
|
*/
|
||||||
|
namespace window_search {
|
||||||
|
|
||||||
|
enum class MatchType { SUBSTRING, EXACT, ID };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find window by ID
|
||||||
|
* @param conn XCB connection
|
||||||
|
* @param root Root window
|
||||||
|
* @param targetId Target window ID
|
||||||
|
* @return Window ID if found, 0 if not found
|
||||||
|
*/
|
||||||
|
xcb_window_t findById(xcb_connection_t* conn, xcb_window_t root, uint32_t targetId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Find window by name
|
||||||
|
* @param conn XCB connection
|
||||||
|
* @param root Root window
|
||||||
|
* @param targetName Target window name
|
||||||
|
* @param outWindowName Output parameter for actual window name
|
||||||
|
* @param matchType Type of name matching to perform
|
||||||
|
* @return Window ID if found, 0 if not found
|
||||||
|
*/
|
||||||
|
xcb_window_t findByName(xcb_connection_t* conn, xcb_window_t root,
|
||||||
|
const std::string& targetName, std::string& outWindowName,
|
||||||
|
MatchType matchType);
|
||||||
|
|
||||||
|
} // namespace window_search
|
||||||
|
|
||||||
|
} // namespace xcb_xorg
|
||||||
|
|
||||||
|
// Function signature types for dynamic loading of libxcbXorg
|
||||||
|
typedef std::shared_ptr<xcb_xorg::XcbConnection> get_or_create_connection_fn(
|
||||||
|
int display, int screen);
|
||||||
|
typedef void cleanup_connections_fn();
|
||||||
|
typedef void dereference_connection_fn(
|
||||||
|
std::shared_ptr<xcb_xorg::XcbConnection> conn);
|
||||||
|
typedef xcb_window_t find_window_by_id_fn(
|
||||||
|
void* conn, xcb_window_t root, uint32_t targetId);
|
||||||
|
typedef xcb_window_t find_window_by_name_fn(void* conn, xcb_window_t root,
|
||||||
|
const std::string& targetName, std::string& outWindowName,
|
||||||
|
xcb_xorg::window_search::MatchType matchType);
|
||||||
|
|
||||||
|
#endif // XCB_XORG_API_H
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
AC_INIT([Harriman-Peikoff Project], [0.00.000],
|
|
||||||
[latentprion@gmail.com],
|
|
||||||
[harikoff],
|
|
||||||
[github.com/latentprion/harikoff])
|
|
||||||
|
|
||||||
AC_CONFIG_SRCDIR([hcore/mind.cpp])
|
|
||||||
AC_CONFIG_AUX_DIR([autotools-aux])
|
|
||||||
AC_CONFIG_MACRO_DIR([m4])
|
|
||||||
|
|
||||||
AM_INIT_AUTOMAKE([foreign -Wall -Werror])
|
|
||||||
|
|
||||||
# Set the mind's quantized virtual oscillator period in milliseconds.
|
|
||||||
# Default value is 33 ms, user override via MIND_VOSCILLATOR_PERIOD_MS.
|
|
||||||
# Check if MIND_VOSCILLATOR_PERIOD_MS is a valid positive integer
|
|
||||||
AC_ARG_VAR([MIND_VOSCILLATOR_PERIOD_MS], m4_normalize([
|
|
||||||
Mind's virtual osc clock rate. Must be a positive integer, default value 33
|
|
||||||
]))
|
|
||||||
AS_IF([test -z "${MIND_VOSCILLATOR_PERIOD_MS}"], [MIND_VOSCILLATOR_PERIOD_MS=33])
|
|
||||||
AS_IF([! test "${MIND_VOSCILLATOR_PERIOD_MS}" -eq "${MIND_VOSCILLATOR_PERIOD_MS}" 2>/dev/null ||
|
|
||||||
test "${MIND_VOSCILLATOR_PERIOD_MS}" -le 0 2>/dev/null], [
|
|
||||||
AC_MSG_ERROR([MIND_VOSCILLATOR_PERIOD_MS must be a positive integer > 0.])
|
|
||||||
])
|
|
||||||
|
|
||||||
AC_DEFINE_UNQUOTED([CONFIG_MIND_VOSCILLATOR_PERIOD_MS],
|
|
||||||
[${MIND_VOSCILLATOR_PERIOD_MS}],
|
|
||||||
[Period of the mind virtual oscillator in milliseconds])
|
|
||||||
AC_COMPUTE_INT(
|
|
||||||
[MIND_VOSCILLATOR_FREQ_MS], [1000 / ${MIND_VOSCILLATOR_PERIOD_MS}], [],
|
|
||||||
[AC_MSG_ERROR([Failed to compute the mind's virtual oscillator frequency.])])
|
|
||||||
AC_DEFINE_UNQUOTED([CONFIG_MIND_VOSCILLATOR_FREQ_MS],
|
|
||||||
[${MIND_VOSCILLATOR_FREQ_MS}],
|
|
||||||
[Frequency of the mind virtual oscillator in milliseconds])
|
|
||||||
|
|
||||||
m4_include([m4/ac_prog_flex.m4])
|
|
||||||
m4_include([m4/ac_prog_bison.m4])
|
|
||||||
AC_PROG_CC
|
|
||||||
AC_PROG_CXX
|
|
||||||
AC_PROG_RANLIB
|
|
||||||
AM_PROG_AR
|
|
||||||
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.])
|
|
||||||
])
|
|
||||||
|
|
||||||
AM_CPPFLAGS=m4_normalize(["-I\"\$(top_srcdir)/include\""])
|
|
||||||
|
|
||||||
AC_SUBST([AM_CPPFLAGS])
|
|
||||||
AC_SUBST([YACC])
|
|
||||||
AC_SUBST([LEX])
|
|
||||||
|
|
||||||
AC_CONFIG_HEADERS([include/config.h])
|
|
||||||
AC_CONFIG_FILES([
|
|
||||||
Makefile hcore/Makefile
|
|
||||||
hcore/deviceManager/Makefile
|
|
||||||
])
|
|
||||||
|
|
||||||
AC_CONFIG_COMMANDS_POST([
|
|
||||||
AC_MSG_NOTICE([${PACKAGE_NAME} ${PACKAGE_VERSION} configuration:])
|
|
||||||
AC_MSG_NOTICE(m4_normalize([* MIND_VOSCILLATOR_PERIOD_MS:
|
|
||||||
${MIND_VOSCILLATOR_PERIOD_MS} ms
|
|
||||||
(freq: ${MIND_VOSCILLATOR_FREQ_MS} Hz)]))
|
|
||||||
])
|
|
||||||
|
|
||||||
AC_OUTPUT
|
|
||||||
@@ -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 Salmanoff 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`.
|
||||||
|
|
||||||
@@ -0,0 +1,282 @@
|
|||||||
|
# Adaptive Resource Acquisition with Re-queuing
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
This document describes a novel synchronization pattern that combines the benefits of spinlocks, mutexes, and queuing systems while avoiding their respective drawbacks. The pattern is designed for high-throughput async systems where multiple threads need to coordinate access to shared resources without blocking or wasting CPU cycles.
|
||||||
|
|
||||||
|
## Problem Statement
|
||||||
|
|
||||||
|
Traditional synchronization mechanisms have significant trade-offs that limit system performance:
|
||||||
|
|
||||||
|
- **Mutexes**: Block threads, causing context switches and reduced throughput
|
||||||
|
- **Spinlocks**: Waste CPU cycles while waiting, preventing other work from proceeding
|
||||||
|
- **Pure Queuing**: Serializes all operations, reducing parallelism unnecessarily
|
||||||
|
|
||||||
|
The challenge is to maintain data consistency across multi-segment async operations while maximizing system throughput. In high-performance systems, the overhead of context switching can be substantial, and CPU cycles are precious resources that should not be wasted on busy-waiting.
|
||||||
|
|
||||||
|
## Core Concept
|
||||||
|
|
||||||
|
The Adaptive Resource Acquisition pattern uses **atomic flags on shared objects** combined with **immediate re-queuing** to achieve optimal performance characteristics:
|
||||||
|
|
||||||
|
1. **No thread blocking** - Threads never sleep or context switch, maintaining maximum responsiveness
|
||||||
|
2. **No CPU waste** - No busy-waiting when other work could proceed, ensuring efficient resource utilization
|
||||||
|
3. **Maximum throughput** - Threads always process available work, maximizing system productivity
|
||||||
|
4. **Data consistency** - Atomic resource acquisition preserves integrity without traditional locking overhead
|
||||||
|
|
||||||
|
This approach fundamentally changes how we think about resource coordination, treating it as a flow management problem rather than a blocking synchronization problem.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Resource Objects
|
||||||
|
|
||||||
|
Each shared object that requires synchronization carries an atomic flag that indicates its availability. This flag serves as the primary coordination mechanism, allowing threads to atomically claim ownership without the overhead of traditional locks.
|
||||||
|
|
||||||
|
The resource object structure is intentionally simple, containing only the essential coordination mechanism and the resource-specific data. This minimalism reduces memory overhead and improves cache locality.
|
||||||
|
|
||||||
|
### Request Structure
|
||||||
|
|
||||||
|
Async operations are encapsulated as requests that specify their resource requirements and the operation to be performed. This encapsulation allows the system to reason about resource dependencies before attempting execution, enabling intelligent scheduling decisions.
|
||||||
|
|
||||||
|
The request structure includes metadata such as priority levels, which can be used for advanced scheduling policies. This flexibility allows the system to adapt to different workload characteristics and business requirements.
|
||||||
|
|
||||||
|
### Resource Manager
|
||||||
|
|
||||||
|
The core component orchestrates resource acquisition and request processing through a sophisticated coordination mechanism. It maintains a registry of all available resources and manages the flow of requests through the system.
|
||||||
|
|
||||||
|
The resource manager operates on a simple principle: attempt to acquire all required resources atomically, and if successful, execute the operation immediately. If any resource is unavailable, the request is immediately re-queued for later processing without any blocking or waiting.
|
||||||
|
|
||||||
|
## Algorithm
|
||||||
|
|
||||||
|
### Resource Acquisition Process
|
||||||
|
|
||||||
|
The resource acquisition process follows a simple but effective strategy. For each request, the system attempts to atomically acquire all required resources in a single pass. This atomicity is crucial for maintaining data consistency and preventing race conditions.
|
||||||
|
|
||||||
|
If all resources can be acquired atomically, the operation proceeds immediately. This represents the optimal case where no coordination overhead is incurred beyond the atomic operations themselves. The system achieves maximum throughput in this scenario.
|
||||||
|
|
||||||
|
If any resource cannot be acquired, the system immediately releases any resources that were successfully acquired and re-queues the request. This approach ensures that resources are never held unnecessarily and that the system can continue processing other requests without delay.
|
||||||
|
|
||||||
|
The key insight is that failed acquisition attempts are not failures in the traditional sense, but rather normal flow control mechanisms. The system treats resource contention as a scheduling opportunity rather than a blocking condition.
|
||||||
|
|
||||||
|
#### Atomic Resource Acquisition Pseudocode
|
||||||
|
|
||||||
|
```
|
||||||
|
TRY_ACQUIRE_RESOURCES(resource_names):
|
||||||
|
acquired_resources = []
|
||||||
|
|
||||||
|
FOR EACH resource_name IN resource_names:
|
||||||
|
resource = GET_RESOURCE(resource_name)
|
||||||
|
expected_value = false
|
||||||
|
desired_value = true
|
||||||
|
|
||||||
|
// Atomic compare-and-swap operation
|
||||||
|
IF ATOMIC_COMPARE_EXCHANGE_STRONG(resource.flag, expected_value, desired_value):
|
||||||
|
// Successfully acquired this resource
|
||||||
|
acquired_resources.ADD(resource)
|
||||||
|
ELSE:
|
||||||
|
// Failed to acquire this resource
|
||||||
|
// Release all previously acquired resources
|
||||||
|
FOR EACH acquired_resource IN acquired_resources:
|
||||||
|
ATOMIC_STORE(acquired_resource.flag, false)
|
||||||
|
RETURN false
|
||||||
|
|
||||||
|
// Successfully acquired all resources
|
||||||
|
RETURN true
|
||||||
|
```
|
||||||
|
|
||||||
|
### Request Processing Workflow
|
||||||
|
|
||||||
|
The request processing workflow is designed for maximum efficiency. Each request is processed exactly once per cycle, either by successful execution or by re-queuing for later processing.
|
||||||
|
|
||||||
|
When a request is successfully processed, the system immediately releases all acquired resources, making them available for other requests. This rapid resource turnover maximizes system throughput and minimizes resource contention.
|
||||||
|
|
||||||
|
The re-queuing mechanism ensures that no request is lost, while the immediate nature of the re-queuing prevents any blocking or waiting. Requests that cannot be processed immediately simply wait their turn in the queue, allowing other requests to proceed without interference.
|
||||||
|
|
||||||
|
#### Basic Processing Algorithm
|
||||||
|
|
||||||
|
```
|
||||||
|
PROCESS_REQUEST(request):
|
||||||
|
// Step 1: Dequeue the request
|
||||||
|
request = DEQUEUE_FROM_QUEUE()
|
||||||
|
|
||||||
|
// Step 2: Attempt atomic resource acquisition
|
||||||
|
resources_acquired = []
|
||||||
|
acquisition_successful = true
|
||||||
|
|
||||||
|
FOR EACH resource_name IN request.required_resources:
|
||||||
|
resource = GET_RESOURCE(resource_name)
|
||||||
|
IF ATOMIC_COMPARE_EXCHANGE(resource.flag, false, true):
|
||||||
|
resources_acquired.ADD(resource)
|
||||||
|
ELSE:
|
||||||
|
acquisition_successful = false
|
||||||
|
BREAK
|
||||||
|
|
||||||
|
// Step 3: Handle acquisition result
|
||||||
|
IF acquisition_successful:
|
||||||
|
// Execute the operation
|
||||||
|
EXECUTE_OPERATION(request.operation)
|
||||||
|
|
||||||
|
// Release all acquired resources
|
||||||
|
FOR EACH resource IN resources_acquired:
|
||||||
|
ATOMIC_STORE(resource.flag, false)
|
||||||
|
ELSE:
|
||||||
|
// Release any partially acquired resources
|
||||||
|
FOR EACH resource IN resources_acquired:
|
||||||
|
ATOMIC_STORE(resource.flag, false)
|
||||||
|
|
||||||
|
// Re-queue the request for later processing
|
||||||
|
ENQUEUE_REQUEST(request)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Event Loop Management
|
||||||
|
|
||||||
|
The event loop continuously processes requests from the queue until no more requests are available. This simple loop structure ensures that the system is always making progress on available work.
|
||||||
|
|
||||||
|
The loop processes requests in the order they were queued, providing a natural fairness mechanism. However, the system can be extended with priority queuing or other scheduling policies to meet specific requirements.
|
||||||
|
|
||||||
|
The event loop is designed to be efficient and non-blocking, ensuring that the system remains responsive even under high load conditions.
|
||||||
|
|
||||||
|
#### Main Event Loop Pseudocode
|
||||||
|
|
||||||
|
```
|
||||||
|
MAIN_EVENT_LOOP():
|
||||||
|
WHILE true:
|
||||||
|
// Check if there are requests to process
|
||||||
|
IF QUEUE_IS_EMPTY():
|
||||||
|
BREAK
|
||||||
|
|
||||||
|
// Dequeue the next request
|
||||||
|
request = DEQUEUE_FROM_QUEUE()
|
||||||
|
|
||||||
|
// Process the request (this includes re-queuing if needed)
|
||||||
|
PROCESS_REQUEST(request)
|
||||||
|
|
||||||
|
// Continue with next request
|
||||||
|
CONTINUE
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Multi-threaded Worker Loop
|
||||||
|
|
||||||
|
```
|
||||||
|
WORKER_THREAD():
|
||||||
|
WHILE true:
|
||||||
|
// Wait for work to become available
|
||||||
|
request = WAIT_FOR_REQUEST()
|
||||||
|
|
||||||
|
// Process the request
|
||||||
|
PROCESS_REQUEST(request)
|
||||||
|
|
||||||
|
// Return to waiting state
|
||||||
|
CONTINUE
|
||||||
|
```
|
||||||
|
|
||||||
|
## Multi-Threaded Implementation
|
||||||
|
|
||||||
|
### Thread-Safe Coordination
|
||||||
|
|
||||||
|
In a multi-threaded environment, the resource manager must coordinate access to its internal data structures while maintaining the non-blocking characteristics of the pattern. This coordination is achieved through careful use of atomic operations and minimal locking.
|
||||||
|
|
||||||
|
The queue management uses traditional mutex-based synchronization, but only for the queue operations themselves. The critical resource acquisition path remains lock-free, ensuring that the performance benefits of the pattern are preserved.
|
||||||
|
|
||||||
|
Worker threads continuously process requests from the shared queue, attempting to acquire resources and execute operations. The coordination between threads is handled implicitly through the atomic resource flags, eliminating the need for explicit thread synchronization in the critical path.
|
||||||
|
|
||||||
|
### Worker Thread Behavior
|
||||||
|
|
||||||
|
Worker threads operate in a continuous loop, processing requests as they become available. Each thread independently attempts to acquire resources and execute operations, creating natural parallelism without explicit coordination.
|
||||||
|
|
||||||
|
The worker threads are designed to be lightweight and efficient, with minimal overhead beyond the actual resource acquisition and operation execution. This design allows the system to scale effectively with the number of available CPU cores.
|
||||||
|
|
||||||
|
The thread coordination is handled through the shared queue and atomic resource flags, creating a self-balancing system that naturally distributes work across available threads.
|
||||||
|
|
||||||
|
## Use Cases
|
||||||
|
|
||||||
|
### Device Management Systems
|
||||||
|
|
||||||
|
In device management systems, multiple operations may need to coordinate access to physical or logical devices. The adaptive resource acquisition pattern provides an elegant solution for managing these complex coordination requirements.
|
||||||
|
|
||||||
|
For example, when attaching a device, the system may need to coordinate access to the device itself, the device registry, and various system resources. The pattern allows these operations to proceed atomically when resources are available, while gracefully handling contention through re-queuing.
|
||||||
|
|
||||||
|
The device management system can handle complex multi-step operations that require coordination across multiple resources, all while maintaining high throughput and responsiveness.
|
||||||
|
|
||||||
|
### Database Connection Pools
|
||||||
|
|
||||||
|
Database connection pools are a natural fit for the adaptive resource acquisition pattern. Each database operation requires access to a connection from the pool, and the pattern provides efficient coordination without the overhead of traditional locking.
|
||||||
|
|
||||||
|
The pattern allows the system to process multiple database operations concurrently when connections are available, while gracefully handling periods of high contention. The re-queuing mechanism ensures that no operations are lost, even during peak load periods.
|
||||||
|
|
||||||
|
The connection pool can implement sophisticated scheduling policies, such as priority queuing for different types of operations, while maintaining the performance benefits of the pattern.
|
||||||
|
|
||||||
|
## Performance Characteristics
|
||||||
|
|
||||||
|
### Throughput Analysis
|
||||||
|
|
||||||
|
The performance characteristics of the adaptive resource acquisition pattern are determined by the resource contention patterns in the system. In the best case, when resources are readily available, the system achieves maximum throughput with minimal overhead.
|
||||||
|
|
||||||
|
In the worst case, when resources are heavily contended, the system gracefully degrades to a queuing behavior, ensuring that all operations eventually complete. The system maintains fairness and prevents starvation through the natural ordering of the queue.
|
||||||
|
|
||||||
|
The average case performance represents the typical operating conditions, where the system achieves optimal parallelism while handling occasional resource contention through re-queuing.
|
||||||
|
|
||||||
|
### Comparison with Traditional Methods
|
||||||
|
|
||||||
|
The adaptive resource acquisition pattern provides a unique combination of performance characteristics that are not achievable with traditional synchronization mechanisms:
|
||||||
|
|
||||||
|
- **Mutexes** provide data consistency but at the cost of thread blocking and context switching overhead
|
||||||
|
- **Spinlocks** avoid context switching but waste CPU cycles during contention
|
||||||
|
- **Pure queuing** avoids both blocking and CPU waste but serializes operations unnecessarily
|
||||||
|
|
||||||
|
The adaptive pattern combines the best aspects of these approaches while avoiding their drawbacks, creating a solution that is both efficient and practical.
|
||||||
|
|
||||||
|
## Advanced Features
|
||||||
|
|
||||||
|
### Priority Queuing
|
||||||
|
|
||||||
|
The system can be extended with priority queuing to handle different types of operations with varying importance. High-priority operations can be processed before lower-priority operations, ensuring that critical operations receive timely attention.
|
||||||
|
|
||||||
|
The priority queuing mechanism integrates seamlessly with the existing re-queuing behavior, allowing the system to maintain its performance characteristics while providing sophisticated scheduling capabilities.
|
||||||
|
|
||||||
|
### Resource Groups
|
||||||
|
|
||||||
|
Complex operations may require coordination across multiple related resources. Resource groups allow the system to treat related resources as a single unit for acquisition purposes, simplifying the coordination logic for complex operations.
|
||||||
|
|
||||||
|
Resource groups can be used to implement sophisticated resource management policies, such as ensuring that related resources are always acquired together or implementing resource reservation mechanisms.
|
||||||
|
|
||||||
|
### Fairness Mechanisms
|
||||||
|
|
||||||
|
The system can implement various fairness mechanisms to ensure that all requests receive fair treatment over time. Round-robin processing, aging mechanisms, and other fairness policies can be implemented while maintaining the performance benefits of the pattern.
|
||||||
|
|
||||||
|
Fairness mechanisms are particularly important in systems where different types of operations have different resource requirements, ensuring that no operation type dominates the system resources.
|
||||||
|
|
||||||
|
## Implementation Considerations
|
||||||
|
|
||||||
|
### Memory Management
|
||||||
|
|
||||||
|
The pattern requires careful attention to memory management, particularly for the request objects and resource metadata. Smart pointers and object pooling can be used to minimize memory allocation overhead and improve performance.
|
||||||
|
|
||||||
|
The system should implement proper cleanup mechanisms for failed operations and ensure that resources are always released, even in error conditions.
|
||||||
|
|
||||||
|
### Error Handling
|
||||||
|
|
||||||
|
Robust error handling is essential for maintaining system reliability. The system should gracefully handle operation failures, resource unavailability, and other error conditions without affecting the overall system performance.
|
||||||
|
|
||||||
|
Retry mechanisms with exponential backoff can be implemented for transient failures, while deadlock detection and resolution mechanisms can handle more complex failure scenarios.
|
||||||
|
|
||||||
|
### Monitoring and Debugging
|
||||||
|
|
||||||
|
The system should provide comprehensive monitoring capabilities to track performance metrics, resource utilization, and queue behavior. These metrics are essential for tuning the system and identifying performance bottlenecks.
|
||||||
|
|
||||||
|
Debugging support should include detailed logging of resource acquisition attempts, queue operations, and operation execution, allowing developers to understand and optimize system behavior.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
The Adaptive Resource Acquisition pattern provides a novel solution to the classic synchronization dilemma. By combining atomic operations with intelligent re-queuing, it achieves maximum throughput while maintaining data consistency and avoiding the overhead of traditional synchronization mechanisms.
|
||||||
|
|
||||||
|
This pattern is particularly well-suited for high-performance async systems where traditional synchronization mechanisms would create unacceptable overhead. The pattern's simplicity and effectiveness make it a valuable addition to the toolkit of concurrent programming patterns.
|
||||||
|
|
||||||
|
The pattern represents a fundamental shift in how we think about resource coordination, treating it as a flow management problem rather than a blocking synchronization problem. This shift enables new levels of performance and scalability in concurrent systems.
|
||||||
|
|
||||||
|
The adaptive resource acquisition pattern is particularly valuable in:
|
||||||
|
- High-performance async systems where throughput is critical
|
||||||
|
- Resource-constrained environments where CPU cycles are precious
|
||||||
|
- Systems requiring predictable latency and responsiveness
|
||||||
|
- Multi-threaded applications with complex shared state requirements
|
||||||
|
|
||||||
|
By providing a practical solution to the synchronization dilemma, this pattern enables developers to build high-performance concurrent systems without sacrificing simplicity or reliability.
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
# Negtrin path reasoning:
|
||||||
|
|
||||||
|
This file is intended to enable me to trace the path of negtrin through a mind. This path of reasoning is important because it allows me to link my way from the understanding of the genesis of implexing, to understanding an implementation path for implexing.
|
||||||
|
|
||||||
|
## Motivation:
|
||||||
|
|
||||||
|
We already understand that implexing begins with an intrin. The intrin prompts a search for the body surface which is experiencing the intrin. That surface is first implexed and stored away. Then subsequently, the cause of the intrin is sought out by implexing things that are relating/interacting with that body surface.
|
||||||
|
|
||||||
|
Since it turns out that intrins are what motivate tabula rasa implexing, we can't move forward in designing our implexor code until we understand how the intrin path will work, since the implexor code will be invoked only as a consequence of, and in service to, the intrin processing code. We could implement something on the implexing side, but it would be a blind implementation.
|
||||||
|
|
||||||
|
## What exactly are we trying to figure out?
|
||||||
|
|
||||||
|
When intrins occur they occur at some device. Smo doesn't need a software-level representation of body parts, surfaces, or spots. The director can implex these and then build a proximity map for each intrin location by checking the causal relationships between the intrins when they occur. Intrins that co-occur in time and have a through-line of connecting co-occuring intrins are close enough to be proximally related.
|
||||||
|
|
||||||
|
This is how the director can build damage map volitionally without requiring us to force it to.
|
||||||
|
|
||||||
|
The design question we're pursuing here isn't the top-level notion of how to design a body map, but rather how to implement, at the software level, intrin implexing and delivery to the director.
|
||||||
|
* What information precisely do we have to deliver to the director for it to successfully implex body surfaces?
|
||||||
|
* At what point in the interoceptor data IRQ handling have we done enough work to enable the director to take over from that point?
|
||||||
|
|
||||||
|
The answers to these questions will, as a side effect, also tell us how to design the implexor model for interoceptors, at least for intrin interoceptors anyway.
|
||||||
|
* Should they be designed as NMI IRQs that the director can't ignore?
|
||||||
|
* Should they be prioritized normal IRQs that the director can ignore?
|
||||||
|
* Should they be IRQs at all? Maybe they should just be added to prioQ that the director polls.
|
||||||
|
* Should they be asychronously processed at all? Maybe the director should have to poll the device service routines manually.
|
||||||
|
|
||||||
|
Which of these implexor event interface designs is correct for the behavioural model?
|
||||||
|
Also, can this model that we're designing for interoceptors also work for extrospectors? Do intrin percept events work the same as nontrin percept events?
|
||||||
|
|
||||||
|
We still haven't fully crystallized the exact essential question we're trying to answer, but we have a list of questions. It appears that the fundamental question is what the correct behavioural model is for intrins and intrin processing. As a side-effect, we're also trying to figure out what the correct interface model should be between the interoceptor devices and the director. Finally, we're trying to figure out whether the design we come up with for interoceptors will be reusable for extrospectors.
|
||||||
|
|
||||||
|
## Suspicions:
|
||||||
|
|
||||||
|
### A hidden, implicit new subsystem:
|
||||||
|
|
||||||
|
I suspect that part of the reason why I can't easily figure out the operations that need to be performed to properly process intrins is that the current set of subsystems I'm using to model the processing is insufficient. There may be additional, new functions that need to be performed by a new logically distinct subsystem altogether, or a new role to be assigned to a current subsystem.
|
||||||
|
|
||||||
|
### Alternatively: my understanding of negtrins is simply inadequate:
|
||||||
|
|
||||||
|
It could also be that the reason I can't understand how to implement the negtrin path is because I just don't fully understand what negtrins are, implementation-wise. I.e: I still don't understand how to make negtrins be experienced by the director as intrinsically undesirable.
|
||||||
|
|
||||||
|
We do fully understand how to make postrins intrinsically desirable from an implementation perspective. But we don't understand fully how to make negtrins undesirable. Perhaps when we understand fully how to make the director __volitionally__ consider negtrins undesirable, the model will reveal itself.
|
||||||
|
|
||||||
|
## Intrinsically undesirable negtrin model design:
|
||||||
|
|
||||||
|
Postrins are represented as intrinsically desirable because the director will experience them as stupefactors. When a postrin occurs, the director will experience that as a stupifying experience, and will enter a HLT state, broken only by the suspicion that a greater state of stupefaction is possible.
|
||||||
|
|
||||||
|
### Our implicit assumption: negtrins have an implementation meaning on their own terms:
|
||||||
|
|
||||||
|
When the director dequeues a negtrin, what does it do? Perhaps our entire thought patterns around the default state of the director is simply wrong. We currently model the mind as having an idle loop, and then it can choose what to contemplate and pursue. Because of this thought model, we consider the question of how to treat negtrins to be a valid question: we treat negtrins as having their own intrinsic meaning.
|
||||||
|
|
||||||
|
### What if negtrins only have meaning as frustrators relative to postrins?
|
||||||
|
|
||||||
|
Consider what follows if we bias the entire implementation positively toward the direct pursuit of postrins. We treat the entire consciousness as a program whose fundamental goal is to enter a stupefaction loop on best postrin known to it. What would negtrins represent, in implementation terms? They would represent a frustrator. They would represent a dysvalue, automatically -- they would represent something that forces the mind to stop pursuing its highest known postrin and to attend to something cumbersome.
|
||||||
|
|
||||||
|
In other words: negtrins don't represent a distinct fact from postrins. The damage map that negtrins help us to build isn't meaningful on its own terms. It's only meaningful in relation to the stupefaction loop pursuit that it forces us to turn away from.
|
||||||
|
|
||||||
|
In this model, the stupefaction loop has intrinsic implementation meaning while the negtrin event has no intrinsic meaning: its meaning is merely that it diverts attention away from the pursuit and enjoyment of stupefaction.
|
||||||
|
|
||||||
|
#### Supporting evidence:
|
||||||
|
|
||||||
|
* A tabula rasa mind that has never experienced postrins literally has no desire to live. It has no positive goals.
|
||||||
|
|
||||||
|
#### Detracting evidence:
|
||||||
|
|
||||||
|
* A tabula rasa mind that has only experienced negtrins won't pursue any self-directed goals or desire to live, but it also will respond to negtrins. So even though this mind has never experienced a postrin, it will have a response (implying meaning and interpretation) to negtrins. If negtrins only have meaning relative to postrins, then this shouldn't be the case.
|
||||||
|
* Rejoinder: The tabula rasa mind which has never experienced a postrin will sit apoplectic in complete stasis, essentially in a "powered off" state. Is this not the same as a stupefied state? It seems that stupefaction is the default pursued state. To support the "default stupefaction pursuit" orientation, you'd only need to update your model of stupefaction to include net neutral states within your definition of postrins.
|
||||||
|
|
||||||
|
### Stupefactor-Frustrator model:
|
||||||
|
|
||||||
|
In this model, the mind doesn't receive nontrins as IRQs. Rather, it polls for them actively by "paying attention". Intrins are qualitatively different because they trigger IRQs. The mind is by default a procedural, polling system that volitionally polls its sensors. IRQs are intrins being injected.
|
||||||
|
|
||||||
|
Intrins are fundamentally different because they force attention, and a neurological response. When a sufficiently intense postrin happens, you automatically stupefy. I have never been forcibly postrin-injected while trying to conduct some other activity, so I can't confirm this model. I've never had the indignity of being forcibly made to experience a postrin when I didn't volitionally choose to participate in allowing it. An ideal experiment would be to somehow have say, a coregasm while working out and not have chosen it, and see whether I can ignore it. This is the closest I can conceive of as a barriered-off experiment.
|
||||||
|
|
||||||
|
For negtrins, I already know that a negtrin forcibly hijacks attention.
|
||||||
|
|
||||||
|
## The fundamental problem is the canvas-director evaluation function:
|
||||||
|
|
||||||
|
The fundamental unsolved problem is the contemplative evaluation whereby the canvas and director work together to produce a value judgment that considers intrins to be intrinsically [un]desirable.
|
||||||
|
|
||||||
|
### Who does comparison? Canvas or Director?
|
||||||
|
|
||||||
|
How does evaluation occur? Who does the comparison? We previously thought that the canvas does the comparisons but today we noticed that Director may be able to do them. In a sense, it makes more sense for Director to be the comparator because:
|
||||||
|
|
||||||
|
#### Director as comparator:
|
||||||
|
|
||||||
|
* If canvas merely helps director to transform mentents (we've finally found a good name), then director has more of an understanding of what specific instructions it's supposed to be giving to canvas.
|
||||||
|
* Our previous model of having canvas hold both goals and mentents made it difficult to really understand how director could ever "know" what instructions to send to canvas.
|
||||||
|
* With director holding goals, it can choose the types of scenes that need to be rendered in order to make comparisons and causal evaluations.
|
||||||
|
* This makes the canvas implexing make more sense: the comparison is being done within director, but the canvas is making the comparison possible by rendering the mentents in orientations that enable canvas to compare them.
|
||||||
|
* In a sense, this also makes it make more sense how compartmentalization occurs. A lot of the compartmentalization isn't within canvas, but rather it's between the director and canvas.
|
||||||
|
|
||||||
|
#### Canvas as comparator:
|
||||||
|
|
||||||
|
In a sense, we can think of it as director partitioning the canvas, and finding some way to refer to the mentents within different partitions. Director can then implex the rendered output from canvas and compare that way. Perhaps the director only compares, and the canvas holds both the mentents and the goals that the mentents are being compared against.
|
||||||
|
|
||||||
|
But how can the director know whether the goal currently loaded into the "goal partition" of the canvas is indeed the goal that it desired to compare against? The canvas is a distinct mentent space from the director's comparison stage. The director-comparator model has the advantage of ensuring that the director can be certain that its current goal matches whatever is being rendered by the canvas for it to implex from.
|
||||||
|
|
||||||
|
What we can do is synthesize a bit:
|
||||||
|
* Have a special shared region between the canvas and director called the "goalspace". This is where the director stores its current goal mentents and the canvas can read from this space directly. Some synchronization will be necessary between the two, but overall, it should be fine. Canvas will only ever need to read from it so we can prolly synchronize it with an RW spinlock, avoiding the overhead of sleeplocks (mutex, sem, etc).
|
||||||
|
|
||||||
|
I think we settle on the director as comparator, canvas as orienter model, with a shared goalspace between them.
|
||||||
|
|
||||||
|
### Where are introspective/contemplative qualia experienced?
|
||||||
|
|
||||||
|
In introspection/contemplation, canvas renders the scene into the buffers. Director implexes from the buffers. So in that sense, contemplative qualia are experienced when director implexes them from the buffers.
|
||||||
|
|
||||||
|
For intrins, canvas renders them into the sense buffers and then director implexes them and "experiences" them. I don't know whether canvas' rendering act triggers IRQs -- probably not. Since director is paying attention to what canvas is rendering, there's no need to forcibly direct director's attention toward canvas' rendered output.
|
||||||
|
|
||||||
|
### How does director's comparison yield the notion of intrinsic [un]desirability?
|
||||||
|
|
||||||
|
Imagine director asking canvas to simulate the effects of some interaction that would bring about direct pleasure.
|
||||||
|
|
||||||
|
Could it simply be that the director holds the goal of postrins as an innate comparison goal, and so when a comparison yields an intrin, it ends the need to compare, and the director just commits to action from then on?
|
||||||
|
* Prolly not because we don't only figure out that an interaction will yield an intrin. With postrins for example, we figure out that they'll yield a postrin and then we spend a long time elaborately contemplating that postrin in self-indulgent scenarios.
|
||||||
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# Device manager: attaching sensors and actuators.
|
# Device Attachment Specification DSL: attaching sensors and actuators to SMO.
|
||||||
|
|
||||||
## Attaching sensors:
|
## Attaching sensors:
|
||||||
|
|
||||||
Sensors are input devices to Harikoff. Harikoff will perceive them as
|
Sensors are input devices to Salmanoff. Salmanoff will perceive them as
|
||||||
perceptual inputs -- like your own sense organs. For example, if you attach a
|
perceptual inputs -- like your own sense organs. For example, if you attach a
|
||||||
camera as a sensor, harikoff will experience it in the same way that you
|
camera as a sensor, salmanoff will experience it in the same way that you
|
||||||
experience the visual sense data from your eyes.
|
experience the visual sense data from your eyes.
|
||||||
|
|
||||||
## Implexors:
|
## Implexors:
|
||||||
@@ -14,19 +14,21 @@ basically what conventional ML/LLM/ANN developers call an ROI ("Region of
|
|||||||
Interest") extraction algorithm. An Implex algorithm is used to scan a frame
|
Interest") extraction algorithm. An Implex algorithm is used to scan a frame
|
||||||
of input sensor data and detect objects and patterns within it.
|
of input sensor data and detect objects and patterns within it.
|
||||||
|
|
||||||
## Sensor device spec:
|
## Sensor device attachment specification:
|
||||||
|
|
||||||
The general format of a device-spec for a sensor is:
|
The general format of a device attachment specification for a sensor is:
|
||||||
```
|
```
|
||||||
sensor-type|implexor|api(api-params)|provider(provider-params)|deviceselector
|
sensor-type|dev-identifier
|
||||||
|
|implexor|api(api-params)|provider(provider-params)|deviceselector
|
||||||
```
|
```
|
||||||
|
|
||||||
* `sensor-type` is always either '`+idev`' (interoceptor), '`+edev`'
|
* `sensor-type` is always either '`+idev`' (interoceptor), '`+edev`'
|
||||||
(extrospector), or '`+adev`' (actuator).
|
(extrospector), or '`+adev`' (actuator).
|
||||||
|
* `dev-identifier` is a user-defined name for this specific device instance.
|
||||||
* `implexor` is the name of the implexor algorithm that should be used with
|
* `implexor` is the name of the implexor algorithm that should be used with
|
||||||
the data that is provided by the `provider` via the `api`.
|
the data that is provided by the `provider` via the `api`.
|
||||||
* `api` is the interface that the provider uses to export perceptual data for
|
* `api` is the interface that the provider uses to export perceptual data for
|
||||||
harikoff to read. Harikoff will run the `implexor` algorithm on the data
|
salmanoff to read. Salmanoff will run the `implexor` algorithm on the data
|
||||||
from this `api`. The `api-param` in parentheses may be omitted, in which
|
from this `api`. The `api-param` in parentheses may be omitted, in which
|
||||||
case the parentheses will be empty, but the parentheses must always be
|
case the parentheses will be empty, but the parentheses must always be
|
||||||
written out.
|
written out.
|
||||||
@@ -35,7 +37,7 @@ sensor-type|implexor|api(api-params)|provider(provider-params)|deviceselector
|
|||||||
which case the parenthesis will be empty, but the parentheses must always be
|
which case the parenthesis will be empty, but the parentheses must always be
|
||||||
written out.
|
written out.
|
||||||
* `device selector` is the idiosyncratic label/name used by the `provider` to
|
* `device selector` is the idiosyncratic label/name used by the `provider` to
|
||||||
identify the specific device you want to access via that `provider`.
|
identify the specific device you want to attach via that `provider`.
|
||||||
|
|
||||||
## `API-params` and `provider-params`:
|
## `API-params` and `provider-params`:
|
||||||
|
|
||||||
@@ -46,37 +48,42 @@ If there's more than one parameter item in a list of `api-params` or
|
|||||||
+edev|audio-implexor|alsa(shmem|param2|param3)|alsa()|cardname
|
+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:
|
Some examples follow:
|
||||||
|
|
||||||
### To attach a particular window from a window manager:
|
### To attach a particular window from a window manager:
|
||||||
```
|
```
|
||||||
+edev|visual-implexor|wayland()|wayland(server-socket)|window0
|
+edev|my-window|visual-implexor|wayland()|wayland(server-socket)|window0
|
||||||
```
|
```
|
||||||
Connect to the Wayland server that's listening on `server-socket`, using the
|
Connect to the Wayland server that's listening on `server-socket`, using the
|
||||||
`wayland` api. Ask that Wayland server to give harikoff read-access to all of
|
`wayland` api. Ask that Wayland server to give salmanoff read-access to all of
|
||||||
the frames composited into the window buffer for `window0`. Use harikoff's
|
the frames composited into the window buffer for `window0`. Use salmanoff's
|
||||||
`visual-implexor` to implex from that `window0`'s compositor data.
|
`visual-implexor` to implex from that `window0`'s compositor data.
|
||||||
|
|
||||||
### To attach a window manager's entire rendered desktop:
|
### To attach a window manager's entire rendered desktop:
|
||||||
```
|
```
|
||||||
+edev|visual-implexor|wayland()|wayland(listen-socket)|all
|
+edev|my-desktop|visual-implexor|wayland()|wayland(listen-socket)|all
|
||||||
```
|
```
|
||||||
In most cases, this is basically the same as attempting to attach all of the
|
In most cases, this is basically the same as attempting to attach all of the
|
||||||
underlying GFX server's output.
|
underlying GFX server's output.
|
||||||
|
|
||||||
Connect to the Wayland server that's listening on `listen-socket`, using the
|
Connect to the Wayland server that's listening on `listen-socket`, using the
|
||||||
`wayland` api. Ask that Wayland server to give harikoff read-access to the
|
`wayland` api. Ask that Wayland server to give salmanoff read-access to the
|
||||||
entire compositor framebuffer. Use harikoff's `visual-implexor` to implex from
|
entire compositor framebuffer. Use salmanoff's `visual-implexor` to implex from
|
||||||
that Wayland server's compositor data.
|
that Wayland server's compositor data.
|
||||||
|
|
||||||
### To attach all of an Xorg server's gfx output to all screens:
|
### To attach all of an Xorg server's gfx output to all screens:
|
||||||
```
|
```
|
||||||
+edev|visual-implexor|x11()|xorg(listen-socket)|all
|
+edev|my-xorg-display|visual-implexor|x11()|xorg(listen-socket)|all
|
||||||
```
|
```
|
||||||
|
|
||||||
Connect to the Xorg server that's listening on `listen-socket`, using the `x11`
|
Connect to the Xorg server that's listening on `listen-socket`, using the `x11`
|
||||||
api. Ask that Xorg server to let Harikoff read out all of the frames written
|
api. Ask that Xorg server to let Salmanoff read out all of the frames written
|
||||||
out to all screens. Use harikoff's `visual-implexor` to implex from the
|
out to all screens. Use salmanoff's `visual-implexor` to implex from the
|
||||||
server's gfx framebuffer data.
|
server's gfx framebuffer data.
|
||||||
|
|
||||||
In most cases, this is basically the same as attempting to attach all of the
|
In most cases, this is basically the same as attempting to attach all of the
|
||||||
@@ -88,11 +95,11 @@ WM's output.
|
|||||||
|
|
||||||
### To attach all of an Xorg server's gfx output to a particular screen:
|
### To attach all of an Xorg server's gfx output to a particular screen:
|
||||||
```
|
```
|
||||||
+edev|visual-implexor|x11()|xorg(listen-socket)|:0
|
+edev|my-screen|visual-implexor|x11()|xorg(listen-socket)|:0
|
||||||
```
|
```
|
||||||
Connect to the Xorg server that's listening on `listen-socket`, using the `x11`
|
Connect to the Xorg server that's listening on `listen-socket`, using the `x11`
|
||||||
api. Ask that Xorg server to let Harikoff read out all of the frames written
|
api. Ask that Xorg server to let Salmanoff read out all of the frames written
|
||||||
out to display `:0`. Use harikoff's `visual-implexor` to implex from display
|
out to display `:0`. Use salmanoff's `visual-implexor` to implex from display
|
||||||
`:0`'s framebuffer data.
|
`:0`'s framebuffer data.
|
||||||
|
|
||||||
* Implementation note:
|
* Implementation note:
|
||||||
@@ -101,17 +108,17 @@ out to display `:0`. Use harikoff's `visual-implexor` to implex from display
|
|||||||
|
|
||||||
### To attach a camera device by connecting directly to its Linux driver:
|
### To attach a camera device by connecting directly to its Linux driver:
|
||||||
```
|
```
|
||||||
+edev|visual-implexor|v4l()|linux()|/dev/video0
|
+edev|my-camera|visual-implexor|v4l()|linux()|/dev/video0
|
||||||
```
|
```
|
||||||
We specify that we want to use the `linux` kernel's loaded driver to connect
|
We specify that we want to use the `linux` kernel's loaded driver to connect
|
||||||
to communicate with `/dev/video0`, via the `Video4Linux` API. We want harikoff
|
to communicate with `/dev/video0`, via the `Video4Linux` API. We want salmanoff
|
||||||
to use the `visual-implexor` algorithm to implex from `/dev/video0`'s data.
|
to use the `visual-implexor` algorithm to implex from `/dev/video0`'s data.
|
||||||
|
|
||||||
If `/dev/video0` is already consumed by another process, this may likely fail.
|
If `/dev/video0` is already consumed by another process, this may likely fail.
|
||||||
|
|
||||||
### To attach a microphone that's managed by ALSA server:
|
### To attach a microphone that's managed by ALSA server:
|
||||||
```
|
```
|
||||||
+edev|audio-implexor|alsa(shmem)|alsa()|cardname
|
+edev|my-microphone|audio-implexor|alsa(shmem)|alsa()|cardname
|
||||||
```
|
```
|
||||||
|
|
||||||
Connect to the ALSA server via `shmem`, using the `alsa` API. Request access to
|
Connect to the ALSA server via `shmem`, using the `alsa` API. Request access to
|
||||||
@@ -120,7 +127,7 @@ the microphone function of the sound card with the name `cardname`. Use the
|
|||||||
|
|
||||||
### To attach a thermal sensor managed by Linux:
|
### To attach a thermal sensor managed by Linux:
|
||||||
```
|
```
|
||||||
+idev|thermal-implexor|thermal-zone()|linux()|/sys/class/thermal_zone0
|
+idev|my-thermal|thermal-implexor|thermal-zone()|linux()|/sys/class/thermal_zone0
|
||||||
```
|
```
|
||||||
|
|
||||||
Use the `thermal-zone` SysFS API provided by `linux` to connect to the sensor
|
Use the `thermal-zone` SysFS API provided by `linux` to connect to the sensor
|
||||||
@@ -129,31 +136,33 @@ Use the `thermal-zone` SysFS API provided by `linux` to connect to the sensor
|
|||||||
|
|
||||||
## Attaching actuators:
|
## Attaching actuators:
|
||||||
|
|
||||||
Actuators are Harikoff's way of enacting changes in the external world.
|
Actuators are Salmanoff's way of enacting changes in the external world.
|
||||||
They're like your libs, or your mouth. Actuators enable harikoff to write
|
They're like your libs, or your mouth. Actuators enable salmanoff to write
|
||||||
outputs to the world outside.
|
outputs to the world outside.
|
||||||
|
|
||||||
### Wilzors:
|
### Wilzors:
|
||||||
|
|
||||||
Actuator devices are analogous to your body's limbs. Harikoff controls these
|
Actuator devices are analogous to your body's limbs. Salmanoff controls these
|
||||||
by using `wilzor` algorithms. Wilzor is a contraction of **Wil**lpower
|
by using `wilzor` algorithms. Wilzor is a contraction of **Wil**lpower
|
||||||
Actuat**Or** but with a 'Z' in the middle to make it sound cooler. Different
|
Actuat**Or** but with a 'Z' in the middle to make it sound cooler. Different
|
||||||
types of devices will require different wilzor algorithms. You need to know
|
types of devices will require different wilzor algorithms. You need to know
|
||||||
what type of wilzor algorithm needs to be used to enable harikoff to control
|
what type of wilzor algorithm needs to be used to enable salmanoff to control
|
||||||
your actuator device.
|
your actuator device.
|
||||||
|
|
||||||
The general format for an actuator's device spec is:
|
The general format for an actuator's device attachment specification is:
|
||||||
```
|
```
|
||||||
WIP: TBD.
|
WIP: TBD.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Device specification files:
|
## Device attachment specification files:
|
||||||
|
|
||||||
Inside of a device spec file, you can list any number of device specs.
|
Inside of a device attachment specification file, you can list any number of
|
||||||
Separate individual device specs with two consecutive h-bar characters (`||`),
|
device attachment specifications.
|
||||||
|
Separate individual device attachment specifications with two consecutive h-bar
|
||||||
|
characters (`||`),
|
||||||
like this:
|
like this:
|
||||||
```
|
```
|
||||||
+edev|visual-implexor|wayland()|wayland(server-socket)|window0
|
+edev|my-window|visual-implexor|wayland()|wayland(server-socket)|window0
|
||||||
|| +edev|visual-implexor|x11()|xorg(listen-socket)|all
|
|| +edev|my-xorg-display|visual-implexor|x11()|xorg(listen-socket)|all
|
||||||
|| +idev|thermal-implexor|thermal-zone()|linux()|/sys/class/thermal_zone0
|
|| +idev|my-thermal|thermal-implexor|thermal-zone()|linux()|/sys/class/thermal_zone0
|
||||||
```
|
```
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 74 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 280 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
@@ -1,5 +0,0 @@
|
|||||||
SUBDIRS = deviceManager
|
|
||||||
AM_CPPFLAGS+= -I"$(top_srcdir)/hcore/include"
|
|
||||||
|
|
||||||
noinst_LIBRARIES = libhcore.a
|
|
||||||
libhcore_a_SOURCES = mind.cpp opts.cpp
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
AM_CPPFLAGS+= -I"$(top_srcdir)/hcore/include"
|
|
||||||
AM_YFLAGS = -d
|
|
||||||
|
|
||||||
noinst_LIBRARIES = libdeviceManager.a
|
|
||||||
libdeviceManager_a_SOURCES = deviceSpecp.yy deviceSpecl.ll \
|
|
||||||
deviceManager.cpp deviceSpecParser.cpp
|
|
||||||
|
|
||||||
deviceSpecl.cc: deviceSpecl.ll
|
|
||||||
deviceSpecl.o: AM_LFLAGS += --header-file=deviceSpecl.hh \
|
|
||||||
-o deviceSpecl.cc
|
|
||||||
deviceSpecp.cc deviceSpecp.hh: deviceSpecp.yy
|
|
||||||
deviceSpecp.o: AM_YFLAGS += -p deviceSpecp \
|
|
||||||
--header=deviceSpecp.hh -o deviceSpecp.cc
|
|
||||||
|
|
||||||
deviceSpecParser.o: AM_CXXFLAGS+=-Wno-ignored-attributes
|
|
||||||
|
|
||||||
CLEANFILES=deviceSpecp.cc deviceSpecp.hh \
|
|
||||||
deviceSpecl.cc deviceSpecl.hh
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <sstream>
|
|
||||||
#include <opts.h>
|
|
||||||
#include <deviceManager/deviceManager.h>
|
|
||||||
|
|
||||||
|
|
||||||
std::vector<DeviceManager::InteroceptorDeviceSpec>
|
|
||||||
DeviceManager::interoceptorDeviceSpecs;
|
|
||||||
std::vector<DeviceManager::ExtrospectorDeviceSpec>
|
|
||||||
DeviceManager::extrospectorDeviceSpecs;
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
std::ostringstream oss;
|
|
||||||
|
|
||||||
for (const auto& spec : DeviceManager::interoceptorDeviceSpecs) {
|
|
||||||
oss << "Interoceptor " << spec;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& spec : DeviceManager::extrospectorDeviceSpecs) {
|
|
||||||
oss << "Extrospector " << spec;
|
|
||||||
}
|
|
||||||
|
|
||||||
return oss.str();
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include <sstream>
|
|
||||||
#include <memory>
|
|
||||||
#include <cstdio>
|
|
||||||
#include <deviceManager/deviceManager.h>
|
|
||||||
#include "deviceSpecp.hh"
|
|
||||||
#include "deviceSpecl.hh"
|
|
||||||
|
|
||||||
std::string DeviceManager::readDeviceFile(const std::string& filename)
|
|
||||||
{
|
|
||||||
std::ifstream file(filename);
|
|
||||||
if (!file.is_open()) {
|
|
||||||
throw std::runtime_error("Could not open file: " + filename);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string content(
|
|
||||||
(std::istreambuf_iterator<char>(file)),
|
|
||||||
std::istreambuf_iterator<char>());
|
|
||||||
|
|
||||||
return std::move(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeviceManager::collateAllDeviceSpecs(const OptionParser& options)
|
|
||||||
{
|
|
||||||
allDeviceSpecs = options.deviceSpecs;
|
|
||||||
|
|
||||||
for (const auto& file : options.deviceSpecFiles)
|
|
||||||
{
|
|
||||||
std::string fileContent = readDeviceFile(file);
|
|
||||||
if (!allDeviceSpecs.empty()) {
|
|
||||||
allDeviceSpecs += "||";
|
|
||||||
}
|
|
||||||
allDeviceSpecs += fileContent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DeviceManager::parseAllDeviceSpecs(void)
|
|
||||||
{
|
|
||||||
std::unique_ptr<FILE, decltype(&fclose)> input(
|
|
||||||
fmemopen((void*)allDeviceSpecs.c_str(),
|
|
||||||
allDeviceSpecs.size(), "r"),
|
|
||||||
&fclose);
|
|
||||||
|
|
||||||
if (!input) {
|
|
||||||
throw std::runtime_error("Failed to open memory as file");
|
|
||||||
}
|
|
||||||
|
|
||||||
deviceSpeclin = input.get();
|
|
||||||
if (deviceSpecpparse()) {
|
|
||||||
throw std::runtime_error("Failed to parse device specs");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
%option prefix="deviceSpecl"
|
|
||||||
%{
|
|
||||||
#include <vector>
|
|
||||||
#include <deviceManager/deviceManager.h>
|
|
||||||
#include "deviceSpecp.hh"
|
|
||||||
%}
|
|
||||||
|
|
||||||
%%
|
|
||||||
"+adev" {
|
|
||||||
deviceSpecplval.chr = yytext[1];
|
|
||||||
return KEYWORD_SPECTYPE_ACTUATOR;
|
|
||||||
}
|
|
||||||
"+edev" {
|
|
||||||
deviceSpecplval.chr = yytext[1];
|
|
||||||
return KEYWORD_SPECTYPE_EXTROSPECTOR;
|
|
||||||
}
|
|
||||||
"+idev" {
|
|
||||||
deviceSpecplval.chr = yytext[1];
|
|
||||||
return KEYWORD_SPECTYPE_INTEROSPECTOR;
|
|
||||||
}
|
|
||||||
"||" { return DOUBLE_PIPE; }
|
|
||||||
"|" { return PIPE; }
|
|
||||||
"(" { return LPAREN; }
|
|
||||||
")" { return RPAREN; }
|
|
||||||
[^\|\(\) \t\r\n]+ { deviceSpecplval.str = strdup(yytext); return STRING; }
|
|
||||||
\r?\n { /* ignore newlines */ }
|
|
||||||
[ \t]+ { /* ignore whitespace */ }
|
|
||||||
. { return yytext[0]; }
|
|
||||||
%%
|
|
||||||
|
|
||||||
int deviceSpeclwrap(void)
|
|
||||||
{
|
|
||||||
return 1; // Indicate end of input
|
|
||||||
}
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
%{
|
|
||||||
#include <vector>
|
|
||||||
#include <utility>
|
|
||||||
#include <string>
|
|
||||||
#include <cstring>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <deviceManager/deviceManager.h>
|
|
||||||
|
|
||||||
#ifndef yylex
|
|
||||||
/* We use different prefixes for the lexer and parser.
|
|
||||||
* * Our lexer's prefix is deviceSpecl.
|
|
||||||
* * Our parser's prefix is deviceSpecp.
|
|
||||||
*
|
|
||||||
* Yacc and Bison don't have a way to handle the scenario where the lexer has
|
|
||||||
* a different prefix from the parser that they generate. They assume that the
|
|
||||||
* lexer must have the same prefix as the parser they generate. So we just use
|
|
||||||
* this #define below to override yacc/bison's presumed prefix for the lexer.
|
|
||||||
*/
|
|
||||||
#error "Yacc should have defined yylex, and we need to override it to tell yacc the name of our lex function."
|
|
||||||
#endif
|
|
||||||
#undef yylex
|
|
||||||
#define yylex deviceSpecllex
|
|
||||||
|
|
||||||
// Declare the symbols that our lexer will export.
|
|
||||||
int yylex(void);
|
|
||||||
void yyerror(const char *message)
|
|
||||||
{
|
|
||||||
throw std::runtime_error(
|
|
||||||
std::string("deviceSpec parser error: ")
|
|
||||||
+ std::string(message));
|
|
||||||
}
|
|
||||||
|
|
||||||
%}
|
|
||||||
|
|
||||||
%union {
|
|
||||||
char* str;
|
|
||||||
char chr;
|
|
||||||
DeviceManager::SensorDeviceSpec* sensorSpec;
|
|
||||||
DeviceManager::InteroceptorDeviceSpec* interoceptorSpec;
|
|
||||||
DeviceManager::ExtrospectorDeviceSpec* extrospectorSpec;
|
|
||||||
std::vector<std::string>* stringVector;
|
|
||||||
}
|
|
||||||
|
|
||||||
%token <str> STRING
|
|
||||||
%token PIPE DOUBLE_PIPE LPAREN RPAREN
|
|
||||||
%token <chr> KEYWORD_SPECTYPE_ACTUATOR
|
|
||||||
%token <chr> KEYWORD_SPECTYPE_EXTROSPECTOR KEYWORD_SPECTYPE_INTEROSPECTOR
|
|
||||||
|
|
||||||
%type <stringVector> params opt_params
|
|
||||||
%type <sensorSpec> spec_body
|
|
||||||
%type <interoceptorSpec> interoceptor_spec
|
|
||||||
%type <extrospectorSpec> extrospector_spec
|
|
||||||
|
|
||||||
%%
|
|
||||||
|
|
||||||
file: /* NOTHING */
|
|
||||||
| sensor_specs
|
|
||||||
;
|
|
||||||
|
|
||||||
sensor_specs:
|
|
||||||
sensor_spec
|
|
||||||
| sensor_specs DOUBLE_PIPE sensor_spec
|
|
||||||
;
|
|
||||||
|
|
||||||
sensor_spec:
|
|
||||||
interoceptor_spec
|
|
||||||
| extrospector_spec
|
|
||||||
;
|
|
||||||
|
|
||||||
interoceptor_spec:
|
|
||||||
KEYWORD_SPECTYPE_INTEROSPECTOR PIPE spec_body {
|
|
||||||
DeviceManager::InteroceptorDeviceSpec *spec =
|
|
||||||
static_cast<DeviceManager::InteroceptorDeviceSpec *>($3);
|
|
||||||
|
|
||||||
spec->sensorType = $1;
|
|
||||||
DeviceManager::interoceptorDeviceSpecs.push_back(*spec);
|
|
||||||
delete spec;
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
extrospector_spec:
|
|
||||||
KEYWORD_SPECTYPE_EXTROSPECTOR PIPE spec_body {
|
|
||||||
DeviceManager::ExtrospectorDeviceSpec *spec =
|
|
||||||
static_cast<DeviceManager::ExtrospectorDeviceSpec *>($3);
|
|
||||||
|
|
||||||
spec->sensorType = $1;
|
|
||||||
DeviceManager::extrospectorDeviceSpecs.push_back(*spec);
|
|
||||||
delete spec;
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
spec_body:
|
|
||||||
STRING PIPE STRING LPAREN opt_params RPAREN PIPE STRING LPAREN opt_params RPAREN PIPE STRING {
|
|
||||||
$$ = new DeviceManager::SensorDeviceSpec();
|
|
||||||
$$->sensorType = '\0';
|
|
||||||
$$->implexor = std::string($1);
|
|
||||||
$$->api = std::string($3);
|
|
||||||
$$->apiParams = std::move(*$5);
|
|
||||||
$$->provider = std::string($8);
|
|
||||||
$$->providerParams = std::move(*$10);
|
|
||||||
$$->deviceSelector = std::string($13);
|
|
||||||
delete $5;
|
|
||||||
delete $10;
|
|
||||||
}
|
|
||||||
;
|
|
||||||
|
|
||||||
opt_params:
|
|
||||||
params
|
|
||||||
| /* empty */ { $$ = new std::vector<std::string>(); }
|
|
||||||
;
|
|
||||||
|
|
||||||
params:
|
|
||||||
STRING { $$ = new std::vector<std::string>{ $1 }; }
|
|
||||||
| params PIPE STRING { $$ = $1; $$->push_back($3); }
|
|
||||||
;
|
|
||||||
|
|
||||||
%%
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
#ifndef BODY_MESSAGE_H
|
|
||||||
#define BODY_MESSAGE_H
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include <body/limb.h>
|
|
||||||
#include <body/bodyPart.h>
|
|
||||||
|
|
||||||
class BodyMessage
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
BodyMessage() = default;
|
|
||||||
~BodyMessage() = default;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BodySpotImpactEntry
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum class ReportType
|
|
||||||
{
|
|
||||||
PRESSURE,
|
|
||||||
PAIN,
|
|
||||||
PLEASURE,
|
|
||||||
HEAT,
|
|
||||||
COLD
|
|
||||||
};
|
|
||||||
|
|
||||||
BodySpotImpactEntry(uint32_t _spot, ReportType _type, uint32_t _value)
|
|
||||||
: spot(_spot), type(_type), value(_value)
|
|
||||||
{}
|
|
||||||
~BodySpotImpactEntry() = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
uint32_t spot;
|
|
||||||
ReportType type;
|
|
||||||
uint32_t value;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BodySpotImpactInd
|
|
||||||
: public BodyMessage
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
BodySpotImpactInd(BodyPart &_part) : part(_part) {}
|
|
||||||
~BodySpotImpactInd() = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
BodyPart ∂
|
|
||||||
std::vector<BodySpotImpactEntry> entries;
|
|
||||||
};
|
|
||||||
|
|
||||||
class BodyPartMsg
|
|
||||||
: public BodyMessage
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
BodyPartMsg(const BodyPart& _part)
|
|
||||||
:part(_part)
|
|
||||||
{}
|
|
||||||
|
|
||||||
public:
|
|
||||||
const BodyPart& part;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // BODY_MESSAGE_H
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#ifndef BODY_LIMB_H
|
|
||||||
#define BODY_LIMB_H
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <set>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
#include <body/bodyPart.h>
|
|
||||||
|
|
||||||
class BodyLimb
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
BodyLimb(uint32_t _id) : id(_id) {}
|
|
||||||
BodyLimb(uint32_t _id,
|
|
||||||
const std::string& _name, const std::string& _desc,
|
|
||||||
const std::string& _loc)
|
|
||||||
: id(_id), name(_name), description(_desc), location(_loc)
|
|
||||||
{}
|
|
||||||
|
|
||||||
~BodyLimb() = default;
|
|
||||||
|
|
||||||
public:
|
|
||||||
uint32_t id;
|
|
||||||
std::string name, description, location;
|
|
||||||
std::set<uint32_t, BodyPart> parts;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // BODY_LIMB_H
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
#ifndef _CONCEPT_H
|
|
||||||
#define _CONCEPT_H
|
|
||||||
|
|
||||||
#include <mentalEntity.h>
|
|
||||||
|
|
||||||
class Concept
|
|
||||||
: public MentalEntity
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
#ifndef DEVICEMANAGER_H
|
|
||||||
#define DEVICEMANAGER_H
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <opts.h>
|
|
||||||
#include <utility>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
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;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string readDeviceFile(const std::string& filename);
|
|
||||||
void collateAllDeviceSpecs(const OptionParser& options);
|
|
||||||
void parseAllDeviceSpecs(void);
|
|
||||||
static const std::string printDeviceSpecs();
|
|
||||||
|
|
||||||
private:
|
|
||||||
DeviceManager() = default;
|
|
||||||
~DeviceManager() = default;
|
|
||||||
DeviceManager(const DeviceManager&) = delete;
|
|
||||||
DeviceManager& operator=(const DeviceManager&) = delete;
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::string allDeviceSpecs;
|
|
||||||
static std::vector<InteroceptorDeviceSpec> interoceptorDeviceSpecs;
|
|
||||||
static std::vector<ExtrospectorDeviceSpec> extrospectorDeviceSpecs;
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // DEVICEMANAGER_H
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
#ifndef DIRECTOR_H
|
|
||||||
#define DIRECTOR_H
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <goal.h>
|
|
||||||
|
|
||||||
namespace hk {
|
|
||||||
namespace director {
|
|
||||||
|
|
||||||
class Director {
|
|
||||||
public:
|
|
||||||
Director() = default;
|
|
||||||
~Director() = default;
|
|
||||||
|
|
||||||
Goal purpose;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace director
|
|
||||||
} // namespace hk
|
|
||||||
|
|
||||||
#endif // DIRECTOR_H
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#ifndef _MENTAL_ENTITY_H
|
|
||||||
#define _MENTAL_ENTITY_H
|
|
||||||
|
|
||||||
namespace hk {
|
|
||||||
|
|
||||||
class MentalEntity
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
using Id = uint32_t;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace hk
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
#ifndef _MIND_H
|
|
||||||
#define _MIND_H
|
|
||||||
|
|
||||||
#include <config.h>
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#include <director/director.h>
|
|
||||||
#include <simulator/simulator.h>
|
|
||||||
|
|
||||||
namespace hk {
|
|
||||||
|
|
||||||
class Mind
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void execute(void);
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::thread directorThread;
|
|
||||||
std::thread simulatorThread;
|
|
||||||
std::thread subconsciousThread;
|
|
||||||
|
|
||||||
director::Director director;
|
|
||||||
simulator::Simulator canvas;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace hk
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
#ifndef OPTS_H
|
|
||||||
#define OPTS_H
|
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <getopt.h>
|
|
||||||
|
|
||||||
// Define a class to hold the options and parse arguments
|
|
||||||
class OptionParser
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
OptionParser() : verbose(false), printUsage(false) {}
|
|
||||||
~OptionParser() = default;
|
|
||||||
|
|
||||||
void parseArguments(int argc, char *argv[]);
|
|
||||||
void dumpOptions() const;
|
|
||||||
std::string getUsage() const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::string deviceSpecs;
|
|
||||||
std::vector<std::string> deviceSpecFiles;
|
|
||||||
bool verbose, printUsage;
|
|
||||||
|
|
||||||
static struct option longOptions[];
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // OPTS_H
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
#ifndef _QUALE_H
|
|
||||||
#define _QUALE_H
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <attentionTrigger.h>
|
|
||||||
|
|
||||||
class Quale
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
enum class Type
|
|
||||||
{
|
|
||||||
NEUTRAL,
|
|
||||||
/* Bounding refers to qualia such as tactile pressure which
|
|
||||||
* are mostly neutral but disclose information about the limits
|
|
||||||
* of the body.
|
|
||||||
**/
|
|
||||||
BOUNDING,
|
|
||||||
PAINFUL,
|
|
||||||
PLEASURABLE
|
|
||||||
} type;
|
|
||||||
|
|
||||||
int32_t intensity;
|
|
||||||
};
|
|
||||||
|
|
||||||
class NeutralQuale
|
|
||||||
: public Quale
|
|
||||||
{
|
|
||||||
};
|
|
||||||
|
|
||||||
class NonNeutralQuale
|
|
||||||
: public Quale, public AttentionTrigger
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual void eventInd(void);
|
|
||||||
|
|
||||||
public:
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
|
|
||||||
#include <mind.h>
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
#include <opts.h>
|
|
||||||
#include <iostream>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <getopt.h>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
struct option OptionParser::longOptions[] = {
|
|
||||||
{"devicespec", required_argument, 0, 's'},
|
|
||||||
{"spec", required_argument, 0, 's'},
|
|
||||||
{"devspec", required_argument, 0, 's'},
|
|
||||||
{"devfile", required_argument, 0, 'd'},
|
|
||||||
{"devicefile", required_argument, 0, 'd'},
|
|
||||||
{"verbose", no_argument, 0, 'v'},
|
|
||||||
{"help", no_argument, 0, '?'},
|
|
||||||
{0, 0, 0, 0}
|
|
||||||
};
|
|
||||||
|
|
||||||
void OptionParser::parseArguments(int argc, char *argv[])
|
|
||||||
{
|
|
||||||
int opt;
|
|
||||||
int optionIndex = 0;
|
|
||||||
optind = 1; // Reset optind to 1 before parsing
|
|
||||||
while ((opt = getopt_long(
|
|
||||||
argc, argv, "s:d:v?", longOptions, &optionIndex)) != -1)
|
|
||||||
{
|
|
||||||
switch (opt)
|
|
||||||
{
|
|
||||||
case 's':
|
|
||||||
if (!deviceSpecs.empty()) {
|
|
||||||
deviceSpecs += "||";
|
|
||||||
}
|
|
||||||
deviceSpecs += std::string(optarg);
|
|
||||||
break;
|
|
||||||
case 'd':
|
|
||||||
deviceSpecFiles.push_back(optarg);
|
|
||||||
break;
|
|
||||||
case 'v':
|
|
||||||
verbose = true;
|
|
||||||
break;
|
|
||||||
case '?':
|
|
||||||
printUsage = true;
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
throw std::invalid_argument("Invalid argument encountered: " + std::string(argv[optind - 1]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string OptionParser::getUsage() const
|
|
||||||
{
|
|
||||||
return "Usage: program [-s|--devicespec|--spec|--devspec <device_spec>] "
|
|
||||||
"[-d|--devfile|--devicefile <filename>] "
|
|
||||||
"[-v|--verbose] "
|
|
||||||
"[-?|--help]";
|
|
||||||
}
|
|
||||||
|
|
||||||
void OptionParser::dumpOptions() const
|
|
||||||
{
|
|
||||||
if (verbose) {
|
|
||||||
std::cout << "Verbose mode is on" << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::cout << "Device Specs: " << deviceSpecs << std::endl;
|
|
||||||
|
|
||||||
std::cout << "Device Spec Files: ";
|
|
||||||
for (const auto& file : deviceSpecFiles) {
|
|
||||||
std::cout << file << " ";
|
|
||||||
}
|
|
||||||
std::cout << std::endl;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
#ifndef _CONFIG_H
|
||||||
|
#define _CONFIG_H
|
||||||
|
|
||||||
|
/* Package information */
|
||||||
|
#define PACKAGE_NAME "@PROJECT_NAME@"
|
||||||
|
#define PACKAGE_VERSION "@PROJECT_VERSION@"
|
||||||
|
|
||||||
|
/* Mind oscillator configuration */
|
||||||
|
#define CONFIG_MIND_VOSCILLATOR_PERIOD_MS @MIND_VOSCILLATOR_PERIOD_MS@
|
||||||
|
#define CONFIG_MIND_VOSCILLATOR_FREQ_MS @MIND_VOSCILLATOR_FREQ_MS@
|
||||||
|
|
||||||
|
/* Cross-compilation configuration */
|
||||||
|
#cmakedefine CMAKE_CROSSCOMPILING
|
||||||
|
|
||||||
|
/* Common Libraries */
|
||||||
|
#cmakedefine CONFIG_LIB_XCBXORG_ENABLED
|
||||||
|
#cmakedefine CONFIG_LIB_ALSA_ENABLED
|
||||||
|
|
||||||
|
/* Sense APIs */
|
||||||
|
#cmakedefine CONFIG_SENSEAPI_XCBWINDOW_ENABLED
|
||||||
|
#cmakedefine CONFIG_SENSEAPI_V4L_ENABLED
|
||||||
|
#cmakedefine CONFIG_SENSEAPI_ALSAMIC_ENABLED
|
||||||
|
#cmakedefine CONFIG_SENSEAPI_LIVOX_ENABLED
|
||||||
|
#cmakedefine CONFIG_SENSEAPI_R3LIVE_ENABLED
|
||||||
|
#cmakedefine CONFIG_SENSEAPI_FASTLIO2_ENABLED
|
||||||
|
#cmakedefine CONFIG_SENSEAPI_ADALIO2_ENABLED
|
||||||
|
#cmakedefine CONFIG_SENSEAPI_DEEPLIO2_ENABLED
|
||||||
|
|
||||||
|
/* Wilzor APIs */
|
||||||
|
#cmakedefine CONFIG_WILZORAPI_XCBMOUSE_ENABLED
|
||||||
|
#cmakedefine CONFIG_WILZORAPI_XCBKEYBOARD_ENABLED
|
||||||
|
#cmakedefine CONFIG_WILZORAPI_ALSAVOICE_ENABLED
|
||||||
|
|
||||||
|
/* Legacy defines for backward compatibility */
|
||||||
|
#cmakedefine CONFIG_XCBWINDOW_ENABLED
|
||||||
|
#cmakedefine CONFIG_V4L_ENABLED
|
||||||
|
#cmakedefine CONFIG_ALSAMIC_ENABLED
|
||||||
|
#cmakedefine CONFIG_LIVOX_ENABLED
|
||||||
|
#cmakedefine CONFIG_R3LIVE_ENABLED
|
||||||
|
#cmakedefine CONFIG_FASTLIO2_ENABLED
|
||||||
|
#cmakedefine CONFIG_ADALIO2_ENABLED
|
||||||
|
#cmakedefine CONFIG_DEEPLIO2_ENABLED
|
||||||
|
#cmakedefine CONFIG_XCBMOUSE_ENABLED
|
||||||
|
#cmakedefine CONFIG_XCBKEYBOARD_ENABLED
|
||||||
|
#cmakedefine CONFIG_ALSAVOICE_ENABLED
|
||||||
|
|
||||||
|
#endif /* _CONFIG_H */
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef SMO_PREPROCESSOR_H
|
||||||
|
#define SMO_PREPROCESSOR_H
|
||||||
|
|
||||||
|
#define SMO_Q(x) #x
|
||||||
|
#define SMO_QUOTE(x) SMO_Q(x)
|
||||||
|
|
||||||
|
#define SMO_CONCAT(a, b) a ## b
|
||||||
|
|
||||||
|
#define SMO_UNMANGLED "C"
|
||||||
|
|
||||||
|
#endif // SMO_PREPROCESSOR_H
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
#ifndef SENSORDEVICESPEC_H
|
||||||
|
#define SENSORDEVICESPEC_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
namespace device {
|
||||||
|
|
||||||
|
class DeviceAttachmentSpec
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
friend std::ostream& operator<<(
|
||||||
|
std::ostream& os, const DeviceAttachmentSpec& spec)
|
||||||
|
{
|
||||||
|
os << spec.stringify();
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const DeviceAttachmentSpec& other) const
|
||||||
|
{
|
||||||
|
return deviceIdentifier == other.deviceIdentifier &&
|
||||||
|
sensorType == other.sensorType &&
|
||||||
|
provider == other.provider &&
|
||||||
|
deviceSelector == other.deviceSelector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string deviceIdentifier;
|
||||||
|
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 Identifier: " << deviceIdentifier
|
||||||
|
<< ", Sensor Type: " << 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 InteroceptorDevAttachmentSpec : public DeviceAttachmentSpec
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
class ExtrospectorDevAttachmentSpec : public DeviceAttachmentSpec
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace device
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif // SENSORDEVICESPEC_H
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
#ifndef __USER_SENSE_API_LIB_H__
|
||||||
|
#define __USER_SENSE_API_LIB_H__
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <preprocessor.h>
|
||||||
|
#include <user/deviceAttachmentSpec.h>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
namespace sense_api {
|
||||||
|
|
||||||
|
typedef int (sal_mlo_initializeIndFn)(void);
|
||||||
|
typedef int (sal_mlo_finalizeIndFn)(void);
|
||||||
|
typedef int (sal_mlo_attachDeviceReqFn)(
|
||||||
|
const std::shared_ptr<device::DeviceAttachmentSpec>& desc);
|
||||||
|
typedef int (sal_mlo_detachDeviceReqFn)(
|
||||||
|
const std::shared_ptr<device::DeviceAttachmentSpec>& desc);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Hooks provided by Salmanoff to senseApi libraries.
|
||||||
|
|
||||||
|
* This structure contains function pointers that senseApi libraries can use
|
||||||
|
* to interact with Salmanoff's functionality, such as searching for commonLibs.
|
||||||
|
*/
|
||||||
|
struct SalmanoffCallbacks
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @brief Search for a library in Salmanoff's search paths
|
||||||
|
* @param libraryPath The relative filename of the library to search for
|
||||||
|
* @return Optional containing the full path if found, nullopt if not found
|
||||||
|
*
|
||||||
|
* This function searches for the given library in the same search paths
|
||||||
|
* that Salmanoff uses when loading senseApi libraries (user-specified
|
||||||
|
* paths via -p option, current directory, and executable directory).
|
||||||
|
*/
|
||||||
|
std::optional<std::string> (*searchForLibInSmoSearchPaths)(
|
||||||
|
const std::string& libraryPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Sal_Mgmt_LibOps
|
||||||
|
{
|
||||||
|
/* When Salmanoff 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_mlo_initializeIndFn *initializeInd;
|
||||||
|
/* Salmanoff calls this to finalize the lib and free its internal
|
||||||
|
* resources. When this returns, the lib should be ready to be unloaded.
|
||||||
|
*/
|
||||||
|
sal_mlo_finalizeIndFn *finalizeInd;
|
||||||
|
/* Salmanoff calls this to attach a device to the lib. When it returns, the
|
||||||
|
* device should be attached and ready to be implexed.
|
||||||
|
*/
|
||||||
|
sal_mlo_attachDeviceReqFn *attachDeviceReq;
|
||||||
|
// When this returns, the device should be detached.
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Exported by all sense API Libraries to tell Salmanoff what API the lib uses
|
||||||
|
* to connect to providers; and also to state which implexor APIs it exports.
|
||||||
|
*/
|
||||||
|
class SenseApiDesc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class ExportedImplexorApiDesc
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static bool sanityCheck(const ExportedImplexorApiDesc &desc)
|
||||||
|
{
|
||||||
|
if (desc.name.empty()) { return false; }
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define SMO_GET_SENSE_API_DESC_FN_NAME getSenseApiDesc
|
||||||
|
#define SMO_GET_SENSE_API_DESC_FN_NAME_STR \
|
||||||
|
SMO_QUOTE(SMO_GET_SENSE_API_DESC_FN_NAME)
|
||||||
|
#define SMO_GET_SENSE_API_DESC_FN_TYPEDEF \
|
||||||
|
SMO_CONCAT(SMO_GET_SENSE_API_DESC_FN_NAME, Fn)
|
||||||
|
|
||||||
|
/* Every Sense API library must define a global instance of this
|
||||||
|
* function. Salmanoff will search for it and invoke it via dlsym().
|
||||||
|
*
|
||||||
|
* The function must return a SenseApiDesc struct that Smo will tell
|
||||||
|
* Smo what implexors can be used with it & what APIs it exports.
|
||||||
|
* The SenseApiDesc struct also gives Smo pointers to API functions
|
||||||
|
* to invoke for communication between Smo and the library.
|
||||||
|
*
|
||||||
|
* The SalmanoffCallbacks parameter provides the library with access to
|
||||||
|
* Salmanoff's hooks.
|
||||||
|
*/
|
||||||
|
typedef const SenseApiDesc &(SMO_GET_SENSE_API_DESC_FN_TYPEDEF)(
|
||||||
|
const SalmanoffCallbacks& callbacks);
|
||||||
|
|
||||||
|
} // namespace sense_api
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif // __USER_SENSE_API_LIB_H__
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
# This script was snagged from the MESA project, which uses an MIT license.
|
|
||||||
|
|
||||||
#
|
|
||||||
# Check for Bison.
|
|
||||||
#
|
|
||||||
# This macro verifies that Bison is installed. If successful, then
|
|
||||||
# 1) YACC is set to bison -y (to emulate YACC calls)
|
|
||||||
# 2) BISON is set to bison
|
|
||||||
#
|
|
||||||
AC_DEFUN([AC_PROG_BISON],
|
|
||||||
[AC_PROG_YACC()
|
|
||||||
if test "$YACC" != "bison -y"; then
|
|
||||||
AC_SUBST(BISON,[])
|
|
||||||
AC_MSG_WARN([bison not found])
|
|
||||||
else
|
|
||||||
AC_SUBST(BISON,[bison])
|
|
||||||
fi
|
|
||||||
])
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
# This script was snagged from the MESA project, which uses an MIT license.
|
|
||||||
|
|
||||||
#
|
|
||||||
# Check for FLEX.
|
|
||||||
#
|
|
||||||
# This macro verifies that flex is installed. If successful, then
|
|
||||||
# 1) LEX is set to flex (to emulate lex calls)
|
|
||||||
# 2) FLEX is set to flex
|
|
||||||
#
|
|
||||||
AC_DEFUN([AC_PROG_FLEX], [
|
|
||||||
AC_PROG_LEX(noyywrap)
|
|
||||||
if test "$LEX" != "flex"; then
|
|
||||||
AC_SUBST(FLEX,[])
|
|
||||||
AC_MSG_ERROR([flex not found])
|
|
||||||
else
|
|
||||||
AC_SUBST(FLEX,[flex])
|
|
||||||
fi
|
|
||||||
])
|
|
||||||
@@ -1,44 +1,27 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <exception>
|
#include <componentThread.h>
|
||||||
#include <opts.h>
|
#include <marionette/marionette.h>
|
||||||
#include <mind.h>
|
|
||||||
#include <deviceManager/deviceManager.h>
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
|
int main(int argc, char *argv[], char *envp[])
|
||||||
{
|
{
|
||||||
try {
|
/* We don't do anything inside of main()
|
||||||
OptionParser options;
|
* Main merely waits for the marionette thread to exit.
|
||||||
hk::Mind mind;
|
*/
|
||||||
|
std::cout << "CRT:" << __func__ << ": about to JOLT Mrntt with cmdline args"
|
||||||
|
<< '\n';
|
||||||
|
smo::mrntt::mrntt->getIoService().post(
|
||||||
|
[argc, argv, envp]()
|
||||||
|
{
|
||||||
|
std::cout << "Mrntt:" << __func__ << ":JOLTED: setting cmdline args"
|
||||||
|
<< '\n';
|
||||||
|
smo::CrtCommandLineArgs::set(argc, argv, envp);
|
||||||
|
smo::mrntt::mrntt->getIoService().stop();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
std::cout << PACKAGE_NAME << " " << PACKAGE_VERSION << std::endl;
|
smo::mrntt::mrntt->thread.join();
|
||||||
|
std::cout << "CRT:" << __func__ << ": Mrntt exited with code '"
|
||||||
try {
|
<< smo::mrntt::exitCode << "'\n";
|
||||||
options.parseArguments(argc, argv);
|
return smo::mrntt::exitCode;
|
||||||
}
|
|
||||||
catch (const std::invalid_argument& e) {
|
|
||||||
std::cerr << e.what() << '\n' << options.getUsage() << '\n';
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
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 << "Exiting normally" << std::endl;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+54
@@ -0,0 +1,54 @@
|
|||||||
|
#! /usr/bin/bash
|
||||||
|
|
||||||
|
winNameToFind=${1}
|
||||||
|
|
||||||
|
printUsage()
|
||||||
|
{
|
||||||
|
echo "${0}: <STRING:windowNameToFind>"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "${winNameToFind}x" = "x" ]; then
|
||||||
|
printUsage
|
||||||
|
fi
|
||||||
|
|
||||||
|
extractAllChildIdsFrom()
|
||||||
|
{
|
||||||
|
echo $(echo "${1}" \
|
||||||
|
| grep '^[[:space:]]*0x[0-9a-zA-Z]' \
|
||||||
|
| awk '{print $1}')
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
searchOneParentByName()
|
||||||
|
{
|
||||||
|
local thisWinId=$1
|
||||||
|
local winNameToFind=$2
|
||||||
|
|
||||||
|
local thisWininfoStdout=$(xwininfo -id "${thisWinId}")
|
||||||
|
local thisWinName=$(echo "${thisWininfoStdout}" \
|
||||||
|
| grep '[Ww]indow[[:space:]]*[iI][dD]' \
|
||||||
|
| grep -oP '[0-9a-zA-Z]*[[:space:]]*\".*\"$' \
|
||||||
|
| sed 's/[0-9a-zA-Z]*[[:space:]]*//')
|
||||||
|
echo "Searching win [ID=${thisWinId}, name=\"${thisWinName}\"] and its children..."
|
||||||
|
if echo "${thisWinName}" | grep -q "${winNameToFind}"; then
|
||||||
|
echo "${thisWininfoStdout}"
|
||||||
|
# return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local allChildrenUnparsedOutput=$(xwininfo -id "${thisWinId}" -children)
|
||||||
|
for i in $(extractAllChildIdsFrom "${allChildrenUnparsedOutput}");
|
||||||
|
do
|
||||||
|
searchOneParentByName "${i}" "${winNameToFind}"
|
||||||
|
done
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rootChildWindowOutput=$(xwininfo -root -children)
|
||||||
|
for i in $(extractAllChildIdsFrom "${rootChildWindowOutput}");
|
||||||
|
do
|
||||||
|
searchOneParentByName "${i}" "${winNameToFind}"
|
||||||
|
done
|
||||||
|
|
||||||
|
exit 1
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
add_subdirectory(xcbWindow)
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# XCB/Xorg Window Attaching SenseAPI backend
|
||||||
|
cmake_dependent_option(ENABLE_SENSEAPI_xcbWindow
|
||||||
|
"Enable XCB/Xorg Window Attaching SenseAPI backend" ON
|
||||||
|
"ENABLE_LIB_xcbXorg" ON)
|
||||||
|
|
||||||
|
if(ENABLE_SENSEAPI_xcbWindow)
|
||||||
|
add_library(xcbWindow SHARED
|
||||||
|
xcbWindow.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(xcbWindow PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../include
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../smocore/include
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/../../commonLibs
|
||||||
|
)
|
||||||
|
|
||||||
|
# Link against XCB library directly (libxcbXorg will be loaded dynamically)
|
||||||
|
pkg_check_modules(XCB REQUIRED xcb)
|
||||||
|
target_link_libraries(xcbWindow ${XCB_LIBRARIES})
|
||||||
|
|
||||||
|
# Set config define for header generation
|
||||||
|
add_compile_definitions(CONFIG_SENSEAPI_XCBWINDOW_ENABLED)
|
||||||
|
|
||||||
|
# Install rules
|
||||||
|
install(TARGETS xcbWindow DESTINATION lib)
|
||||||
|
endif()
|
||||||
@@ -0,0 +1,355 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <xcb/xcb.h>
|
||||||
|
#include <user/senseApiDesc.h>
|
||||||
|
#include <user/deviceAttachmentSpec.h>
|
||||||
|
#include <xcbXorg/xcbXorg.h>
|
||||||
|
#include "xcbWindow.h"
|
||||||
|
|
||||||
|
// Function pointers to API entry points exported by libxcbXorg.
|
||||||
|
struct XcbXorgDllState
|
||||||
|
{
|
||||||
|
struct XcbXorgFunctions
|
||||||
|
{
|
||||||
|
get_or_create_connection_fn* getOrCreateConnection = nullptr;
|
||||||
|
cleanup_connections_fn* cleanupConnections = nullptr;
|
||||||
|
dereference_connection_fn* dereferenceConnection = nullptr;
|
||||||
|
find_window_by_id_fn* findWindowById = nullptr;
|
||||||
|
find_window_by_name_fn* findWindowByName = nullptr;
|
||||||
|
} fns;
|
||||||
|
void* dlopenHandle = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static XcbXorgDllState xcbXorg;
|
||||||
|
|
||||||
|
// Salmanoff hooks, obtained from SMO_GET_SENSE_API_DESC_FN_NAME().
|
||||||
|
static const smo::sense_api::SalmanoffCallbacks* smoHooksPtr = nullptr;
|
||||||
|
|
||||||
|
// Attached windows.
|
||||||
|
static std::vector<std::unique_ptr<xcb_window::AttachedWindow>>
|
||||||
|
g_attachedWindows;
|
||||||
|
|
||||||
|
namespace xcb_window {
|
||||||
|
|
||||||
|
std::string WindowSelector::stringify() const
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
os << "Display: " << display
|
||||||
|
<< ", Screen: " << screen << ", Window: ";
|
||||||
|
if (matchType == xcb_xorg::window_search::MatchType::ID) {
|
||||||
|
os << windowId;
|
||||||
|
} else {
|
||||||
|
os << "\"" << windowName << "\"";
|
||||||
|
}
|
||||||
|
os << " (matchType="
|
||||||
|
<< (matchType == xcb_xorg::window_search::MatchType::EXACT ? "exact" :
|
||||||
|
(matchType == xcb_xorg::window_search::MatchType::SUBSTRING) ? "substring" : "id")
|
||||||
|
<< ")";
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
AttachedWindow::AttachedWindow(
|
||||||
|
const std::shared_ptr<smo::device::DeviceAttachmentSpec>& spec
|
||||||
|
)
|
||||||
|
: deviceAttachmentSpec(spec)
|
||||||
|
{
|
||||||
|
// Validate required function pointers are available
|
||||||
|
if (!xcbXorg.fns.getOrCreateConnection ||
|
||||||
|
!xcbXorg.fns.findWindowById || !xcbXorg.fns.findWindowByName
|
||||||
|
|| !xcbXorg.fns.dereferenceConnection)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("xcbWindow:" + std::string(__func__) +
|
||||||
|
": Required xcbXorg function pointers not available");
|
||||||
|
}
|
||||||
|
|
||||||
|
windowSelector.display = getRequiredParamAsInt(*spec, "display");
|
||||||
|
windowSelector.screen = getRequiredParamAsInt(*spec, "screen");
|
||||||
|
parseWindowSelector(*spec);
|
||||||
|
|
||||||
|
// Get connection from libxcbXorg
|
||||||
|
std::shared_ptr<xcb_xorg::XcbConnection> conn =
|
||||||
|
(*xcbXorg.fns.getOrCreateConnection)(
|
||||||
|
windowSelector.display, windowSelector.screen);
|
||||||
|
|
||||||
|
xcbConnectionShared = conn;
|
||||||
|
|
||||||
|
// Find the target window
|
||||||
|
xcb_window_t foundWindow = 0;
|
||||||
|
const xcb_setup_t* setup = xcb_get_setup(conn->getConnection());
|
||||||
|
const xcb_screen_t* screen = xcb_setup_roots_iterator(setup).data;
|
||||||
|
|
||||||
|
if (windowSelector.matchType == xcb_xorg::window_search::MatchType::ID)
|
||||||
|
{
|
||||||
|
foundWindow = (*xcbXorg.fns.findWindowById)(
|
||||||
|
conn.get(), screen->root,
|
||||||
|
windowSelector.windowId);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
foundWindow = (*xcbXorg.fns.findWindowByName)(
|
||||||
|
conn.get(), screen->root,
|
||||||
|
windowSelector.windowName, actualWindowName,
|
||||||
|
windowSelector.matchType);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundWindow)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("xcbWindow:" + std::string(__func__) +
|
||||||
|
": Window not found: " + windowSelector.stringify());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AttachedWindow::parseWindowSelector(
|
||||||
|
const smo::device::DeviceAttachmentSpec& spec
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Default match type
|
||||||
|
windowSelector.matchType = xcb_xorg::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_xorg::window_search::MatchType::ID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (param.first == "dev-string" || param.first == "dev-str"
|
||||||
|
|| param.first == "devstr" || param.first == "devstring")
|
||||||
|
{
|
||||||
|
windowSelector.matchType = xcb_xorg::window_search::MatchType::EXACT;
|
||||||
|
}
|
||||||
|
if (param.first == "dev-substring" || param.first == "dev-substr"
|
||||||
|
|| param.first == "devsubstr" || param.first == "devsubstring")
|
||||||
|
{
|
||||||
|
windowSelector.matchType = xcb_xorg::window_search::MatchType::SUBSTRING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (windowSelector.matchType == xcb_xorg::window_search::MatchType::ID)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
windowSelector.windowId = std::stoul(
|
||||||
|
spec.deviceSelector, nullptr, 0);
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"xcbWindow:" + std::string(__func__) + ": Window selector: "
|
||||||
|
"'dev-id' present, but selector is not numeric");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
windowSelector.windowName = spec.deviceSelector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int AttachedWindow::getRequiredParamAsInt(const smo::device::DeviceAttachmentSpec& 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string AttachedWindow::stringify() const {
|
||||||
|
std::ostringstream os;
|
||||||
|
|
||||||
|
auto matchTypeStr = [](xcb_xorg::window_search::MatchType mt) -> const char* {
|
||||||
|
switch (mt) {
|
||||||
|
case xcb_xorg::window_search::MatchType::SUBSTRING:
|
||||||
|
return "substring";
|
||||||
|
case xcb_xorg::window_search::MatchType::EXACT:
|
||||||
|
return "exact";
|
||||||
|
case xcb_xorg::window_search::MatchType::ID:
|
||||||
|
return "id";
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auto formatWindowId = [](uint32_t id) -> std::string {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Window ID 0x" << std::hex << id << std::dec;
|
||||||
|
return oss.str();
|
||||||
|
};
|
||||||
|
|
||||||
|
os << "Display: " << windowSelector.display
|
||||||
|
<< ", Screen: " << windowSelector.screen
|
||||||
|
<< ", MatchType: " << matchTypeStr(windowSelector.matchType)
|
||||||
|
<< ", Target: ";
|
||||||
|
|
||||||
|
if (windowSelector.matchType == xcb_xorg::window_search::MatchType::ID) {
|
||||||
|
os << formatWindowId(windowSelector.windowId);
|
||||||
|
} else {
|
||||||
|
os << '"' << windowSelector.windowName << '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
os << ", Found: ";
|
||||||
|
|
||||||
|
if (windowSelector.matchType == xcb_xorg::window_search::MatchType::ID) {
|
||||||
|
os << formatWindowId(windowSelector.windowId);
|
||||||
|
} else {
|
||||||
|
os << '"' << actualWindowName << '"';
|
||||||
|
if (windowSelector.matchType ==
|
||||||
|
xcb_xorg::window_search::MatchType::SUBSTRING &&
|
||||||
|
actualWindowName != windowSelector.windowName)
|
||||||
|
{
|
||||||
|
os << " (matched substring '"
|
||||||
|
<< windowSelector.windowName << "')";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
AttachedWindow::~AttachedWindow()
|
||||||
|
{
|
||||||
|
if (xcbConnectionShared && xcbXorg.fns.dereferenceConnection) {
|
||||||
|
(*xcbXorg.fns.dereferenceConnection)(xcbConnectionShared);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace xcb_window
|
||||||
|
|
||||||
|
// SenseApi functions
|
||||||
|
static int xcbWindow_initializeInd(void)
|
||||||
|
{
|
||||||
|
if (!smoHooksPtr)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string(__func__) + ": SMO hooks "
|
||||||
|
"pointers not filled in.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to load libxcbXorg using the search path hook
|
||||||
|
auto libPath = smoHooksPtr->searchForLibInSmoSearchPaths("libxcbXorg.so");
|
||||||
|
xcbXorg.dlopenHandle = dlopen(
|
||||||
|
libPath.value_or("libxcbXorg.so").c_str(),
|
||||||
|
RTLD_LAZY);
|
||||||
|
|
||||||
|
if (!xcbXorg.dlopenHandle)
|
||||||
|
{
|
||||||
|
throw std::runtime_error("xcbWindow:" + std::string(__func__) +
|
||||||
|
": Failed to load libxcbXorg: " + std::string(dlerror()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in function pointers from libxcbXorg.
|
||||||
|
xcbXorg.fns.getOrCreateConnection =
|
||||||
|
reinterpret_cast<get_or_create_connection_fn*>(
|
||||||
|
dlsym(xcbXorg.dlopenHandle, "xcb_xorg_get_or_create_connection"));
|
||||||
|
xcbXorg.fns.cleanupConnections = reinterpret_cast<cleanup_connections_fn*>(
|
||||||
|
dlsym(xcbXorg.dlopenHandle, "xcb_xorg_cleanup_connections"));
|
||||||
|
xcbXorg.fns.findWindowById = reinterpret_cast<find_window_by_id_fn*>(
|
||||||
|
dlsym(xcbXorg.dlopenHandle, "xcb_xorg_find_window_by_id"));
|
||||||
|
xcbXorg.fns.findWindowByName = reinterpret_cast<find_window_by_name_fn*>(
|
||||||
|
dlsym(xcbXorg.dlopenHandle, "xcb_xorg_find_window_by_name"));
|
||||||
|
xcbXorg.fns.dereferenceConnection =
|
||||||
|
reinterpret_cast<dereference_connection_fn*>(
|
||||||
|
dlsym(xcbXorg.dlopenHandle, "xcb_xorg_dereference_connection"));
|
||||||
|
|
||||||
|
if (!xcbXorg.fns.getOrCreateConnection || !xcbXorg.fns.cleanupConnections
|
||||||
|
|| !xcbXorg.fns.findWindowById || !xcbXorg.fns.findWindowByName
|
||||||
|
|| !xcbXorg.fns.dereferenceConnection)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
std::string("xcbWindow:") + __func__ +
|
||||||
|
": Failed to get required function pointers from libxcbXorg");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xcbWindow_finalizeInd(void)
|
||||||
|
{
|
||||||
|
g_attachedWindows.clear();
|
||||||
|
|
||||||
|
if (xcbXorg.dlopenHandle)
|
||||||
|
{
|
||||||
|
dlclose(xcbXorg.dlopenHandle);
|
||||||
|
xcbXorg.dlopenHandle = nullptr;
|
||||||
|
xcbXorg.fns = { nullptr, nullptr, nullptr, nullptr, nullptr };
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xcbWindow_attachDeviceReq(
|
||||||
|
const std::shared_ptr<smo::device::DeviceAttachmentSpec>& desc
|
||||||
|
)
|
||||||
|
{
|
||||||
|
g_attachedWindows.emplace_back(
|
||||||
|
std::make_unique<xcb_window::AttachedWindow>(desc));
|
||||||
|
|
||||||
|
std::cout << __func__ << ": Attached X11 window:\n "
|
||||||
|
<< g_attachedWindows.back()->stringify()
|
||||||
|
<< "\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int xcbWindow_detachDeviceReq(
|
||||||
|
const std::shared_ptr<smo::device::DeviceAttachmentSpec>& spec
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(g_attachedWindows.begin(), g_attachedWindows.end(),
|
||||||
|
[&spec](const std::unique_ptr<xcb_window::AttachedWindow>& window) {
|
||||||
|
return window->getDeviceAttachmentSpec() == spec;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (it == g_attachedWindows.end())
|
||||||
|
{
|
||||||
|
std::cerr << __func__ << ": Device not found for detachment:\n"
|
||||||
|
<< spec->stringify() << "\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_attachedWindows.erase(it);
|
||||||
|
std::cout << __func__ << ": Detached X11 window device:\n"
|
||||||
|
<< spec->stringify() << "\n";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SenseApi descriptor
|
||||||
|
static smo::sense_api::SenseApiDesc xcbWindowApiDesc = {
|
||||||
|
.name = "xcb",
|
||||||
|
.exportedImplexorApis = { { "video-implexor" } },
|
||||||
|
.sal_mgmt_libOps = {
|
||||||
|
.initializeInd = xcbWindow_initializeInd,
|
||||||
|
.finalizeInd = xcbWindow_finalizeInd,
|
||||||
|
.attachDeviceReq = xcbWindow_attachDeviceReq,
|
||||||
|
.detachDeviceReq = xcbWindow_detachDeviceReq
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Exported function
|
||||||
|
extern "C" smo::sense_api::SMO_GET_SENSE_API_DESC_FN_TYPEDEF
|
||||||
|
SMO_GET_SENSE_API_DESC_FN_NAME;
|
||||||
|
|
||||||
|
const smo::sense_api::SenseApiDesc& SMO_GET_SENSE_API_DESC_FN_NAME(
|
||||||
|
const smo::sense_api::SalmanoffCallbacks& callbacks)
|
||||||
|
{
|
||||||
|
smoHooksPtr = &callbacks;
|
||||||
|
return xcbWindowApiDesc;
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#ifndef XCB_WINDOW_SENSE_API_H
|
||||||
|
#define XCB_WINDOW_SENSE_API_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <user/senseApiDesc.h>
|
||||||
|
#include <user/deviceAttachmentSpec.h>
|
||||||
|
#include <xcbXorg/xcbXorg.h>
|
||||||
|
|
||||||
|
namespace xcb_window {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Window selector for X11 windows
|
||||||
|
*/
|
||||||
|
struct WindowSelector
|
||||||
|
{
|
||||||
|
xcb_xorg::window_search::MatchType matchType;
|
||||||
|
int display;
|
||||||
|
int screen;
|
||||||
|
uint32_t windowId;
|
||||||
|
std::string windowName;
|
||||||
|
|
||||||
|
std::string stringify() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Represents an attached X11 window device
|
||||||
|
*/
|
||||||
|
class AttachedWindow
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AttachedWindow(
|
||||||
|
const std::shared_ptr<smo::device::DeviceAttachmentSpec>& spec);
|
||||||
|
~AttachedWindow();
|
||||||
|
|
||||||
|
const std::shared_ptr<smo::device::DeviceAttachmentSpec>&
|
||||||
|
getDeviceAttachmentSpec() const
|
||||||
|
{ return deviceAttachmentSpec; }
|
||||||
|
const WindowSelector& getWindowSelector() const { return windowSelector; }
|
||||||
|
const std::string& getActualWindowName() const { return actualWindowName; }
|
||||||
|
void* getXcbConnection() const { return xcbConnectionShared.get(); }
|
||||||
|
std::string stringify() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void parseWindowSelector(const smo::device::DeviceAttachmentSpec& spec);
|
||||||
|
int getRequiredParamAsInt(
|
||||||
|
const smo::device::DeviceAttachmentSpec& spec,
|
||||||
|
const std::string& paramName);
|
||||||
|
|
||||||
|
std::shared_ptr<smo::device::DeviceAttachmentSpec> deviceAttachmentSpec;
|
||||||
|
WindowSelector windowSelector;
|
||||||
|
std::string actualWindowName;
|
||||||
|
std::shared_ptr<xcb_xorg::XcbConnection> xcbConnectionShared;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace xcb_window
|
||||||
|
|
||||||
|
#endif // XCB_WINDOW_SENSE_API_H
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Core library
|
||||||
|
add_library(smocore STATIC
|
||||||
|
mind.cpp
|
||||||
|
opts.cpp
|
||||||
|
componentThread.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(smocore PUBLIC
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/include
|
||||||
|
)
|
||||||
|
|
||||||
|
# Link against pthread for CPU affinity functions
|
||||||
|
find_package(Threads REQUIRED)
|
||||||
|
target_link_libraries(smocore PRIVATE Threads::Threads)
|
||||||
|
|
||||||
|
add_subdirectory(marionette)
|
||||||
|
add_subdirectory(deviceManager)
|
||||||
|
add_subdirectory(senseApis)
|
||||||
@@ -0,0 +1,300 @@
|
|||||||
|
#include <unistd.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <mind.h>
|
||||||
|
#include <componentThread.h>
|
||||||
|
#include <marionette/marionette.h>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
|
||||||
|
thread_local std::shared_ptr<ComponentThread> thisComponentThread;
|
||||||
|
|
||||||
|
namespace mrntt {
|
||||||
|
extern std::shared_ptr<ComponentThread> mrntt;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implementation of static method
|
||||||
|
std::shared_ptr<ComponentThread> ComponentThread::getMrntt()
|
||||||
|
{
|
||||||
|
return mrntt::mrntt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentThread::initializeTls(void)
|
||||||
|
{
|
||||||
|
thisComponentThread = shared_from_this();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::shared_ptr<ComponentThread> ComponentThread::getSelf(void)
|
||||||
|
{
|
||||||
|
if (!thisComponentThread)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string(__func__)
|
||||||
|
+ ": TLS not initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
return thisComponentThread;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentThread::main(ComponentThread& self)
|
||||||
|
{
|
||||||
|
std::cout << self.name << ":" << __func__ << ": Waiting for JOLT" <<"\n";
|
||||||
|
self.getIoService().run();
|
||||||
|
self.initializeTls();
|
||||||
|
|
||||||
|
std::cout << self.name << ":" << __func__ << ": Entering event loop" <<"\n";
|
||||||
|
|
||||||
|
/* We loop here because when an exception is caught, we need to first catch
|
||||||
|
* it in the catch blocks. We bubble the exception to mrntt in the catch
|
||||||
|
* blocks, and then we loop here to await control messages from mrntt.
|
||||||
|
*
|
||||||
|
* We can't just exit on our own. Rather, we must wait for mrntt to tell us
|
||||||
|
* to exit. When we wish to finally exit, we set keepLooping to false.
|
||||||
|
*/
|
||||||
|
for (self.keepLooping = true; self.keepLooping;)
|
||||||
|
{
|
||||||
|
bool sendExceptionInd = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
self.getIoService().reset();
|
||||||
|
self.getIoService().run();
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
sendExceptionInd = true;
|
||||||
|
std::cerr << self.name << ":" << __func__
|
||||||
|
<< ": Exception occurred: " << e.what() << "\n";
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
sendExceptionInd = true;
|
||||||
|
std::cerr << self.name << ":" << __func__
|
||||||
|
<< ": Unknown exception occurred" << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sendExceptionInd) { mrntt::mrntt->exceptionInd(self); }
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << self.name << ":" << __func__ << ": Exited event loop" << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thread management method implementations
|
||||||
|
void ComponentThread::startThreadReq(std::function<void()> callback)
|
||||||
|
{
|
||||||
|
this->getIoService().post([this, caller = getSelf(), callback]()
|
||||||
|
{
|
||||||
|
std::cout << "Thread '" << name << "': handling startThread." << "\n";
|
||||||
|
|
||||||
|
// Execute private setup sequence here
|
||||||
|
// This is where each thread would implement its specific initialization
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
caller->getIoService().post(callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentThread::cleanup(void)
|
||||||
|
{
|
||||||
|
this->keepLooping = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentThread::exitThreadReq(std::function<void()> callback)
|
||||||
|
{
|
||||||
|
// Post to the main io_service
|
||||||
|
this->getIoService().post([this, caller = getSelf(), callback]()
|
||||||
|
{
|
||||||
|
std::cout << "Thread '" << name << "': handling exitThread "
|
||||||
|
"(main queue)." << std::endl;
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
// Stop the main io_service to exit the thread
|
||||||
|
io_service.stop();
|
||||||
|
if (callback) { caller->getIoService().post(callback); }
|
||||||
|
});
|
||||||
|
|
||||||
|
// Also post to the pause io_service
|
||||||
|
this->pause_io_service.post([this, caller = getSelf(), callback]()
|
||||||
|
{
|
||||||
|
std::cout << "Thread '" << name << "': handling exitThread "
|
||||||
|
"(pause queue)." << std::endl;
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
|
||||||
|
// Stop both io_services to exit the thread
|
||||||
|
pause_io_service.stop();
|
||||||
|
io_service.stop();
|
||||||
|
if (callback) { caller->getIoService().post(callback); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentThread::pauseThreadReq(std::function<void()> callback)
|
||||||
|
{
|
||||||
|
this->getIoService().post([this, caller = getSelf(), callback]()
|
||||||
|
{
|
||||||
|
std::cout << "Thread '" << name << "': handling pauseThread."
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
caller->getIoService().post(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset the pause io_service before running to ensure it can run again
|
||||||
|
pause_io_service.reset();
|
||||||
|
// Run the pause io_service to block this thread
|
||||||
|
pause_io_service.run();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentThread::resumeThreadReq(std::function<void()> callback)
|
||||||
|
{
|
||||||
|
// Post to the pause_io_service to unblock the paused thread
|
||||||
|
pause_io_service.post([this, caller = getSelf(), callback]()
|
||||||
|
{
|
||||||
|
std::cout << "Thread '" << name << "': handling resumeThread."
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
caller->getIoService().post(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop the pause_io_service to unblock the thread
|
||||||
|
pause_io_service.stop();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentThread::joltThreadReq(std::function<void()> callback)
|
||||||
|
{
|
||||||
|
this->getIoService().post([this, caller = getSelf(), callback]()
|
||||||
|
{
|
||||||
|
std::cout << "Thread '" << name << "': handling JOLT request." << "\n";
|
||||||
|
|
||||||
|
// Stop the main io_service to jolt the thread
|
||||||
|
io_service.stop();
|
||||||
|
|
||||||
|
if (callback) {
|
||||||
|
caller->getIoService().post(callback);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This shouldn't take a callback because the caller shouldn't expect to
|
||||||
|
* Mrntt to send a reply signal to it. Sending this Indication means that
|
||||||
|
* Mrntt will send the calling thread an exitThreadReq. When the caller
|
||||||
|
* processes that exitThreadReq(), the caller will exit its event loop and then
|
||||||
|
* terminate.
|
||||||
|
*
|
||||||
|
* Even if Mrntt sent a RDY response, the caller shouldn't actually be executing
|
||||||
|
* any longer to receive it anyway.
|
||||||
|
*/
|
||||||
|
void ComponentThread::exceptionInd(ComponentThread& thread)
|
||||||
|
{
|
||||||
|
if (this->id != ComponentThread::MRNTT)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string(__func__)
|
||||||
|
+ ": invoked on non-mrntt thread " + thread.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post the exception to the mrntt thread.
|
||||||
|
this->getIoService().post(
|
||||||
|
[&thread]()
|
||||||
|
{
|
||||||
|
std::cerr << "Mrntt: Exception occurred: in thread "
|
||||||
|
<< thread.name << ". Killing Salmanoff." << "\n";
|
||||||
|
|
||||||
|
/** EXPLANATION:
|
||||||
|
* An exception has occurred in one of a mind's threads. We need to
|
||||||
|
* shut down all of that particular mind's threads.
|
||||||
|
*/
|
||||||
|
thread.parent.finalizeReq([]() {
|
||||||
|
/** FIXME:
|
||||||
|
* When we eventually support multiple minds, we should remove this
|
||||||
|
* since it causes marionette to exit, even if there are other minds
|
||||||
|
* that are still running.
|
||||||
|
*/
|
||||||
|
smo::mrntt::exitMarionetteLoop();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentThread::userShutdownInd()
|
||||||
|
{
|
||||||
|
if (this->id != ComponentThread::MRNTT)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string(__func__)
|
||||||
|
+ ": invoked on non-mrntt thread " + this->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Post the user shutdown to the mrntt thread.
|
||||||
|
this->getIoService().post(
|
||||||
|
[this]()
|
||||||
|
{
|
||||||
|
std::cerr << "Mrntt: User requested shutdown (SIGINT)."
|
||||||
|
<< " Killing Salmanoff." << "\n";
|
||||||
|
|
||||||
|
/** EXPLANATION:
|
||||||
|
* A user has requested a shutdown. We need to shut down all of the
|
||||||
|
* threads in all running Minds.
|
||||||
|
*/
|
||||||
|
parent.finalizeReq([]() {
|
||||||
|
/** FIXME:
|
||||||
|
* When we eventually support multiple minds, we should remove this
|
||||||
|
* since it causes marionette to exit, even if there are other minds
|
||||||
|
* that are still running.
|
||||||
|
*/
|
||||||
|
smo::mrntt::exitMarionetteLoop();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPU management method implementations
|
||||||
|
int ComponentThread::getAvailableCpuCount()
|
||||||
|
{
|
||||||
|
int cpuCount = sysconf(_SC_NPROCESSORS_ONLN);
|
||||||
|
if (cpuCount <= 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string(__func__)
|
||||||
|
+ ": Failed to determine CPU count");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if std::thread::hardware_concurrency() matches sysconf result
|
||||||
|
unsigned int hwConcurrency = std::thread::hardware_concurrency();
|
||||||
|
if (hwConcurrency != static_cast<unsigned int>(cpuCount))
|
||||||
|
{
|
||||||
|
std::cerr << "Warning: CPU count mismatch - "
|
||||||
|
"std::thread::hardware_concurrency() = "
|
||||||
|
<< hwConcurrency << ", sysconf(_SC_NPROCESSORS_ONLN) = "
|
||||||
|
<< cpuCount << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return cpuCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComponentThread::pinToCpu(int cpuId)
|
||||||
|
{
|
||||||
|
if (cpuId < 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string(__func__)
|
||||||
|
+ ": Invalid CPU ID: " + std::to_string(cpuId));
|
||||||
|
}
|
||||||
|
|
||||||
|
cpu_set_t cpuset;
|
||||||
|
CPU_ZERO(&cpuset);
|
||||||
|
CPU_SET(cpuId, &cpuset);
|
||||||
|
|
||||||
|
int result = pthread_setaffinity_np(
|
||||||
|
thread.native_handle(), sizeof(cpu_set_t), &cpuset);
|
||||||
|
if (result != 0)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string(__func__)
|
||||||
|
+ ": Failed to pin thread to CPU " + std::to_string(cpuId)
|
||||||
|
+ ": " + std::strerror(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
pinnedCpuId = cpuId;
|
||||||
|
std::cout << name << ": Pinned to CPU " << cpuId << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace smo
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# Flex/Bison generated files
|
||||||
|
set(LEX_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/deviceAttachmentPipeSpecl.cc)
|
||||||
|
set(YACC_OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/deviceAttachmentPipeSpecp.cc)
|
||||||
|
set(YACC_HEADER ${CMAKE_CURRENT_BINARY_DIR}/deviceAttachmentPipeSpecp.hh)
|
||||||
|
|
||||||
|
# Generate Flex/Bison files using custom commands
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${LEX_OUTPUT}
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/deviceAttachmentPipeSpecl.ll ${YACC_HEADER}
|
||||||
|
COMMAND ${FLEX_EXECUTABLE} --header-file=${CMAKE_CURRENT_BINARY_DIR}/deviceAttachmentPipeSpecl.hh -o ${LEX_OUTPUT} ${CMAKE_CURRENT_SOURCE_DIR}/deviceAttachmentPipeSpecl.ll
|
||||||
|
COMMENT "Generating deviceAttachmentPipeSpecl.cc from deviceAttachmentPipeSpecl.ll"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT ${YACC_OUTPUT} ${YACC_HEADER}
|
||||||
|
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/deviceAttachmentPipeSpecp.yy
|
||||||
|
COMMAND ${BISON_EXECUTABLE} -p deviceAttachmentPipeSpecp --header=${YACC_HEADER} -o ${YACC_OUTPUT} ${CMAKE_CURRENT_SOURCE_DIR}/deviceAttachmentPipeSpecp.yy
|
||||||
|
COMMENT "Generating deviceAttachmentPipeSpecp.cc and deviceAttachmentPipeSpecp.hh from deviceAttachmentPipeSpecp.yy"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Device manager library
|
||||||
|
add_library(deviceManager STATIC
|
||||||
|
deviceManager.cpp
|
||||||
|
deviceAttachmentPipeSpecParser.cpp
|
||||||
|
${LEX_OUTPUT}
|
||||||
|
${YACC_OUTPUT}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(deviceManager PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
|
#include <memory>
|
||||||
|
#include <cstdio>
|
||||||
|
#include <deviceManager/deviceManager.h>
|
||||||
|
#include "deviceAttachmentPipeSpecp.hh"
|
||||||
|
#include "deviceAttachmentPipeSpecl.hh"
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
namespace device {
|
||||||
|
|
||||||
|
std::string DeviceManager::readDapSpecFile(const std::string& filename)
|
||||||
|
{
|
||||||
|
std::ifstream file(filename);
|
||||||
|
if (!file.is_open())
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
std::string(__func__) + ": Couldn't open DAP spec file: "
|
||||||
|
+ filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string content(
|
||||||
|
(std::istreambuf_iterator<char>(file)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::collateAllDapSpecs(void)
|
||||||
|
{
|
||||||
|
OptionParser &options = OptionParser::getOptions();
|
||||||
|
allDapSpecs = options.dapSpecs;
|
||||||
|
|
||||||
|
for (const auto& file : options.dapSpecFiles)
|
||||||
|
{
|
||||||
|
std::string fileContent = readDapSpecFile(file);
|
||||||
|
if (!allDapSpecs.empty()) {
|
||||||
|
allDapSpecs += "||";
|
||||||
|
}
|
||||||
|
allDapSpecs += fileContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::parseAllDapSpecs(void)
|
||||||
|
{
|
||||||
|
auto file_deleter = [](FILE* f) { if (f) fclose(f); };
|
||||||
|
std::unique_ptr<FILE, decltype(file_deleter)> input(
|
||||||
|
fmemopen((void*)allDapSpecs.c_str(), allDapSpecs.size(), "r"),
|
||||||
|
file_deleter);
|
||||||
|
|
||||||
|
if (!input)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
std::string(__func__) + ": Failed to fmemopen() a FILE* for "
|
||||||
|
"parsing DAP specs");
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceAttachmentPipeSpeclin = input.get();
|
||||||
|
if (deviceAttachmentPipeSpecpparse())
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
std::string(__func__) + ": Failed to parse DAP specs. "
|
||||||
|
"Check specs for errors");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace device
|
||||||
|
} // namespace smo
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
%option prefix="deviceAttachmentPipeSpecl"
|
||||||
|
%option nounput
|
||||||
|
%option noinput
|
||||||
|
%{
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <deviceManager/deviceManager.h>
|
||||||
|
#include "deviceAttachmentPipeSpecp.hh"
|
||||||
|
%}
|
||||||
|
|
||||||
|
%%
|
||||||
|
"+adev" {
|
||||||
|
deviceAttachmentPipeSpecplval.chr = yytext[1];
|
||||||
|
return KEYWORD_SPECTYPE_ACTUATOR;
|
||||||
|
}
|
||||||
|
"+edev" {
|
||||||
|
deviceAttachmentPipeSpecplval.chr = yytext[1];
|
||||||
|
return KEYWORD_SPECTYPE_EXTROSPECTOR;
|
||||||
|
}
|
||||||
|
"+idev" {
|
||||||
|
deviceAttachmentPipeSpecplval.chr = yytext[1];
|
||||||
|
return KEYWORD_SPECTYPE_INTEROSPECTOR;
|
||||||
|
}
|
||||||
|
"||" { return DOUBLE_PIPE; }
|
||||||
|
"|" { return PIPE; }
|
||||||
|
"(" { return LPAREN; }
|
||||||
|
")" { return RPAREN; }
|
||||||
|
"=" { return EQUALS; }
|
||||||
|
(\\.|[^=\|\(\) \t\r\n])+ {
|
||||||
|
std::string token(yytext);
|
||||||
|
std::string unescaped;
|
||||||
|
unescaped.reserve(token.size());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < token.size(); ++i)
|
||||||
|
{
|
||||||
|
if (token[i] != '\\')
|
||||||
|
{
|
||||||
|
unescaped.push_back(token[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If a backslash is the final char before EOF, just continue so it gets
|
||||||
|
* dropped as a side effect.
|
||||||
|
*/
|
||||||
|
if (i + 1 >= token.size()) { continue; }
|
||||||
|
|
||||||
|
// Else push the char following the backslash.
|
||||||
|
unescaped.push_back(token[++i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceAttachmentPipeSpecplval.str = strdup(unescaped.c_str());
|
||||||
|
return STRING;
|
||||||
|
}
|
||||||
|
[ \t\r\n]+ { /* ignore all whitespace, including newlines */ }
|
||||||
|
. { return yytext[0]; }
|
||||||
|
%%
|
||||||
|
|
||||||
|
int deviceAttachmentPipeSpeclwrap(void)
|
||||||
|
{
|
||||||
|
return 1; // Indicate end of input
|
||||||
|
}
|
||||||
@@ -0,0 +1,156 @@
|
|||||||
|
%{
|
||||||
|
#include <vector>
|
||||||
|
#include <utility>
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <memory>
|
||||||
|
#include <functional>
|
||||||
|
#include <user/deviceAttachmentSpec.h>
|
||||||
|
#include <deviceManager/deviceManager.h>
|
||||||
|
|
||||||
|
#ifndef yylex
|
||||||
|
/* We use different prefixes for the lexer and parser.
|
||||||
|
* * Our lexer's prefix is deviceAttachmentPipeSpecl.
|
||||||
|
* * Our parser's prefix is deviceAttachmentPipeSpecp.
|
||||||
|
*
|
||||||
|
* Yacc and Bison don't have a way to handle the scenario where the lexer has
|
||||||
|
* a different prefix from the parser that they generate. They assume that the
|
||||||
|
* lexer must have the same prefix as the parser they generate. So we just use
|
||||||
|
* this #define below to override yacc/bison's presumed prefix for the lexer.
|
||||||
|
*/
|
||||||
|
#error "Yacc should have defined yylex as a preprocessor token, and we need to override it to tell yacc the name of our lex function."
|
||||||
|
#endif
|
||||||
|
#undef yylex
|
||||||
|
#define yylex deviceAttachmentPipeSpecllex
|
||||||
|
|
||||||
|
#ifdef yytext
|
||||||
|
#undef yytext
|
||||||
|
#endif
|
||||||
|
#define yytext deviceAttachmentPipeSpecltext
|
||||||
|
|
||||||
|
// 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(
|
||||||
|
std::string("deviceAttachmentPipeSpec parser error: ")
|
||||||
|
+ std::string(message)
|
||||||
|
+ " at token: " + std::string(yytext));
|
||||||
|
}
|
||||||
|
|
||||||
|
%}
|
||||||
|
|
||||||
|
%union {
|
||||||
|
char* str;
|
||||||
|
char chr;
|
||||||
|
smo::device::DeviceAttachmentSpec* sensorSpec;
|
||||||
|
smo::device::InteroceptorDevAttachmentSpec* interoceptorSpec;
|
||||||
|
smo::device::ExtrospectorDevAttachmentSpec* 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 <paramVector> params opt_params
|
||||||
|
%type <param> param
|
||||||
|
%type <sensorSpec> spec_body
|
||||||
|
%type <interoceptorSpec> interoceptor_spec
|
||||||
|
%type <extrospectorSpec> extrospector_spec
|
||||||
|
|
||||||
|
%%
|
||||||
|
|
||||||
|
file: /* NOTHING */
|
||||||
|
| sensor_specs
|
||||||
|
;
|
||||||
|
|
||||||
|
sensor_specs:
|
||||||
|
sensor_spec
|
||||||
|
| sensor_specs DOUBLE_PIPE sensor_spec
|
||||||
|
;
|
||||||
|
|
||||||
|
sensor_spec:
|
||||||
|
interoceptor_spec
|
||||||
|
| extrospector_spec
|
||||||
|
;
|
||||||
|
|
||||||
|
interoceptor_spec:
|
||||||
|
KEYWORD_SPECTYPE_INTEROSPECTOR PIPE spec_body {
|
||||||
|
auto spec = std::make_shared<smo::device::InteroceptorDevAttachmentSpec>(
|
||||||
|
*static_cast<smo::device::InteroceptorDevAttachmentSpec *>($3));
|
||||||
|
|
||||||
|
spec->sensorType = $1;
|
||||||
|
smo::device::DeviceManager::interoceptorDeviceSpecs.push_back(spec);
|
||||||
|
smo::device::DeviceManager::deviceAttachmentSpecs.push_back(spec);
|
||||||
|
|
||||||
|
delete $3;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
extrospector_spec:
|
||||||
|
KEYWORD_SPECTYPE_EXTROSPECTOR PIPE spec_body {
|
||||||
|
auto spec = std::make_shared<smo::device::ExtrospectorDevAttachmentSpec>(
|
||||||
|
*static_cast<smo::device::ExtrospectorDevAttachmentSpec *>($3));
|
||||||
|
|
||||||
|
spec->sensorType = $1;
|
||||||
|
smo::device::DeviceManager::extrospectorDeviceSpecs.push_back(spec);
|
||||||
|
smo::device::DeviceManager::deviceAttachmentSpecs.push_back(spec);
|
||||||
|
|
||||||
|
delete $3;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
spec_body:
|
||||||
|
STRING PIPE STRING PIPE STRING LPAREN opt_params RPAREN PIPE STRING LPAREN opt_params RPAREN PIPE STRING {
|
||||||
|
$$ = new smo::device::DeviceAttachmentSpec();
|
||||||
|
$$->deviceIdentifier = std::string($1);
|
||||||
|
$$->sensorType = '\0'; // This will be set by the parent rule
|
||||||
|
$$->implexor = std::string($3);
|
||||||
|
$$->api = std::string($5);
|
||||||
|
$$->apiParams = std::move(*$7);
|
||||||
|
$$->provider = std::string($10);
|
||||||
|
$$->providerParams = std::move(*$12);
|
||||||
|
$$->deviceSelector = std::string($15);
|
||||||
|
delete $7;
|
||||||
|
delete $12;
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
opt_params:
|
||||||
|
params
|
||||||
|
| /* empty */ { $$ = new std::vector<std::pair<std::string,std::string>>(); }
|
||||||
|
;
|
||||||
|
|
||||||
|
params:
|
||||||
|
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,114 @@
|
|||||||
|
#include <iostream>
|
||||||
|
#include <fstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <sstream>
|
||||||
|
#include <memory>
|
||||||
|
#include <opts.h>
|
||||||
|
#include <deviceManager/deviceManager.h>
|
||||||
|
#include <senseApis/senseApiManager.h>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
namespace device {
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<InteroceptorDevAttachmentSpec>>
|
||||||
|
DeviceManager::interoceptorDeviceSpecs;
|
||||||
|
std::vector<std::shared_ptr<ExtrospectorDevAttachmentSpec>>
|
||||||
|
DeviceManager::extrospectorDeviceSpecs;
|
||||||
|
std::vector<std::shared_ptr<DeviceAttachmentSpec>>
|
||||||
|
DeviceManager::deviceAttachmentSpecs;
|
||||||
|
std::vector<std::shared_ptr<Device>>
|
||||||
|
DeviceManager::devices;
|
||||||
|
|
||||||
|
// Async continuation structure
|
||||||
|
struct DeviceAttachmentContinuation {
|
||||||
|
std::shared_ptr<DeviceAttachmentSpec> spec;
|
||||||
|
std::function<void(
|
||||||
|
bool success, std::shared_ptr<Device> device,
|
||||||
|
std::shared_ptr<DeviceAttachmentSpec> deviceSpec)
|
||||||
|
> callback;
|
||||||
|
|
||||||
|
DeviceAttachmentContinuation(
|
||||||
|
std::shared_ptr<DeviceAttachmentSpec> s,
|
||||||
|
std::function<void(
|
||||||
|
bool success, std::shared_ptr<Device> device,
|
||||||
|
std::shared_ptr<DeviceAttachmentSpec> deviceSpec)
|
||||||
|
> cb)
|
||||||
|
: spec(s), callback(cb)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::string DeviceManager::stringifyDeviceSpecs(void)
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
|
||||||
|
for (const auto& spec : DeviceManager::interoceptorDeviceSpecs) {
|
||||||
|
oss << "Interoceptor " << spec->stringify();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& spec : DeviceManager::extrospectorDeviceSpecs) {
|
||||||
|
oss << "Extrospector " << spec->stringify();
|
||||||
|
}
|
||||||
|
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeviceManager::newDeviceAttachmentSpecInd(
|
||||||
|
std::shared_ptr<DeviceAttachmentSpec> spec,
|
||||||
|
std::function<void(
|
||||||
|
bool success, std::shared_ptr<Device> device,
|
||||||
|
std::shared_ptr<DeviceAttachmentSpec> deviceSpec)
|
||||||
|
> callback)
|
||||||
|
{
|
||||||
|
// Create async continuation
|
||||||
|
auto continuation = std::make_shared<DeviceAttachmentContinuation>(
|
||||||
|
spec, callback);
|
||||||
|
|
||||||
|
// Check if a DeviceAttachmentSpec already matches
|
||||||
|
for (const auto& existingSpec : deviceAttachmentSpecs)
|
||||||
|
{
|
||||||
|
if (!(*existingSpec == *spec)) { continue; }
|
||||||
|
// Already exists, callback with error
|
||||||
|
callback(false, nullptr, nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to attach the sense device
|
||||||
|
try {
|
||||||
|
sense_api::SenseApiManager::getInstance().attachSenseDevice(spec);
|
||||||
|
|
||||||
|
// Look for existing Device with same identifier
|
||||||
|
std::shared_ptr<Device> device = nullptr;
|
||||||
|
for (const auto& existingDevice : devices)
|
||||||
|
{
|
||||||
|
if (existingDevice->deviceIdentifier != spec->deviceIdentifier)
|
||||||
|
{ continue; }
|
||||||
|
|
||||||
|
device = existingDevice;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If device doesn't exist, create a new one and add it
|
||||||
|
if (!device)
|
||||||
|
{
|
||||||
|
device = std::make_shared<Device>(spec->deviceIdentifier);
|
||||||
|
devices.push_back(device);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add DeviceAttachmentSpec to device's list
|
||||||
|
device->deviceAttachmentSpecs.push_back(spec);
|
||||||
|
|
||||||
|
// Add DeviceAttachmentSpec to DeviceManager's list
|
||||||
|
deviceAttachmentSpecs.push_back(spec);
|
||||||
|
|
||||||
|
// Callback with success
|
||||||
|
callback(true, device, spec);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
// Attach failed, callback with error
|
||||||
|
callback(false, nullptr, nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace device
|
||||||
|
} // namespace smo
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
#ifndef _ARTIFICED_MENTAL_PHENOMENON_H
|
||||||
|
#define _ARTIFICED_MENTAL_PHENOMENON_H
|
||||||
|
|
||||||
|
#include <mentenon.h>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
|
||||||
|
class FogArtificedMentalPhenomenon
|
||||||
|
: public ArtificedMentalPhenomenon
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef FogArtificedMentalPhenomenon Fogenon;
|
||||||
|
|
||||||
|
class IdyllicArtificedMentalPhenomenon
|
||||||
|
: public ArtificedMentalPhenomenon
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef IdyllicArtificedMentalPhenomenon Idyllenon;
|
||||||
|
|
||||||
|
class FantasyArtificedMentalPhenomenon
|
||||||
|
: public ArtificedMentalPhenomenon
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef FantasyArtificedMentalPhenomenon Fantanon;
|
||||||
|
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif // _ARTIFICED_MENTAL_PHENOMENON_H
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
#ifndef COMPONENT_THREAD_H
|
||||||
|
#define COMPONENT_THREAD_H
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <thread>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <boost/asio.hpp>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <queue>
|
||||||
|
#include <functional>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <sched.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
|
||||||
|
class Mind; // Forward declaration
|
||||||
|
|
||||||
|
class ComponentThread
|
||||||
|
: public std::enable_shared_from_this<ComponentThread>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum ThreadId
|
||||||
|
{
|
||||||
|
MRNTT = 0,
|
||||||
|
DIRECTOR,
|
||||||
|
SIMULATOR,
|
||||||
|
SUBCONSCIOUS,
|
||||||
|
BODY,
|
||||||
|
WORLD,
|
||||||
|
N_ITEMS
|
||||||
|
};
|
||||||
|
|
||||||
|
ComponentThread(ThreadId _id, Mind& parent)
|
||||||
|
: id(_id), name(getThreadName(_id)), parent(parent),
|
||||||
|
work(io_service), pause_work(pause_io_service),
|
||||||
|
pinnedCpuId(-1),
|
||||||
|
thread(
|
||||||
|
((id == MRNTT) ? marionetteMain : main),
|
||||||
|
std::ref(*this))
|
||||||
|
{}
|
||||||
|
|
||||||
|
void cleanup(void);
|
||||||
|
|
||||||
|
boost::asio::io_service& getIoService(void) { return io_service; }
|
||||||
|
|
||||||
|
void initializeTls(void);
|
||||||
|
static const std::shared_ptr<ComponentThread> getSelf(void);
|
||||||
|
|
||||||
|
static std::shared_ptr<ComponentThread> getMrntt();
|
||||||
|
Mind& getParent() const { return parent; }
|
||||||
|
|
||||||
|
typedef void (mainFn)(ComponentThread &self);
|
||||||
|
static mainFn main, marionetteMain;
|
||||||
|
|
||||||
|
// Thread management methods
|
||||||
|
void startThreadReq(std::function<void()> callback = nullptr);
|
||||||
|
void exitThreadReq(std::function<void()> callback = nullptr);
|
||||||
|
void pauseThreadReq(std::function<void()> callback = nullptr);
|
||||||
|
void resumeThreadReq(std::function<void()> callback = nullptr);
|
||||||
|
/**
|
||||||
|
* JOLTs this thread to begin processing after global initialization.
|
||||||
|
*
|
||||||
|
* JOLTing is the mechanism that allows threads to enter their main
|
||||||
|
* event loops and set up TLS vars after all global constructors have
|
||||||
|
* completed. This prevents race conditions during system startup.
|
||||||
|
*/
|
||||||
|
void joltThreadReq(std::function<void()> callback = nullptr);
|
||||||
|
|
||||||
|
// CPU management methods
|
||||||
|
static int getAvailableCpuCount();
|
||||||
|
void pinToCpu(int cpuId);
|
||||||
|
|
||||||
|
enum class ThreadOp
|
||||||
|
{
|
||||||
|
START,
|
||||||
|
PAUSE,
|
||||||
|
RESUME,
|
||||||
|
EXIT,
|
||||||
|
JOLT,
|
||||||
|
N_ITEMS
|
||||||
|
};
|
||||||
|
|
||||||
|
// Intentionally doesn't take a callback.
|
||||||
|
void exceptionInd(ComponentThread& thread);
|
||||||
|
// Intentionally doesn't take a callback.
|
||||||
|
void userShutdownInd();
|
||||||
|
|
||||||
|
public:
|
||||||
|
ThreadId id;
|
||||||
|
std::string name;
|
||||||
|
Mind &parent;
|
||||||
|
boost::asio::io_service io_service;
|
||||||
|
boost::asio::io_service::work work;
|
||||||
|
boost::asio::io_service pause_io_service;
|
||||||
|
boost::asio::io_service::work pause_work;
|
||||||
|
std::atomic<bool> keepLooping;
|
||||||
|
int pinnedCpuId;
|
||||||
|
|
||||||
|
|
||||||
|
/* Always ensure that this is last so that the thread is spawned after
|
||||||
|
* everything else is constructed.
|
||||||
|
*/
|
||||||
|
std::thread thread;
|
||||||
|
|
||||||
|
static const std::string getThreadName(ThreadId id)
|
||||||
|
{
|
||||||
|
if (id < 0 || id >= ComponentThread::N_ITEMS)
|
||||||
|
{
|
||||||
|
throw std::runtime_error(std::string(__func__)
|
||||||
|
+ ": Invalid thread ID");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use function-local static to ensure proper initialization order
|
||||||
|
static const std::string threadNames[N_ITEMS] = {
|
||||||
|
"mrntt",
|
||||||
|
"director",
|
||||||
|
"simulator",
|
||||||
|
"subconscious",
|
||||||
|
"body",
|
||||||
|
"world"
|
||||||
|
};
|
||||||
|
|
||||||
|
return threadNames[id];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace mrntt {
|
||||||
|
extern std::shared_ptr<ComponentThread> mrntt;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif // COMPONENT_THREAD_H
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
#ifndef _CONCEPT_H
|
||||||
|
#define _CONCEPT_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <logic.h>
|
||||||
|
#include <mentalEntity.h>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
namespace concepts {
|
||||||
|
|
||||||
|
class Comparator
|
||||||
|
: public MentalEntity, public logic::Operand
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/** EXPLANATION:
|
||||||
|
* The reference for a Comparator is the fixed mentity or range of mentities
|
||||||
|
* that this comparator is intended to validate a match against.
|
||||||
|
*
|
||||||
|
* There are several mentities against which a comparator can match. At the
|
||||||
|
* time of writing, we're fairly sure that these will be at minimum,
|
||||||
|
* qualia, chronomena and mentena.
|
||||||
|
*/
|
||||||
|
std::shared_ptr<MentalEntity> reference;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ComparatorExpression
|
||||||
|
: public logic::UnaryExpression
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ComparatorExpression(
|
||||||
|
logic::Operator &op, std::shared_ptr<Comparator> &comparator
|
||||||
|
)
|
||||||
|
: logic::UnaryExpression(
|
||||||
|
op, std::static_pointer_cast<logic::Operand>(comparator))
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
class CombinatorialLogicExpression
|
||||||
|
: public MentalEntity, public logic::Expression
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef CombinatorialLogicExpression Concept;
|
||||||
|
|
||||||
|
} // namespace concept
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#ifndef DEVICE_H
|
||||||
|
#define DEVICE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
#include <sstream>
|
||||||
|
#include <user/deviceAttachmentSpec.h>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
namespace device {
|
||||||
|
|
||||||
|
class Device
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string deviceIdentifier;
|
||||||
|
std::vector<std::shared_ptr<DeviceAttachmentSpec>> deviceAttachmentSpecs;
|
||||||
|
|
||||||
|
Device(const std::string& identifier) : deviceIdentifier(identifier) {}
|
||||||
|
|
||||||
|
std::string stringify() const
|
||||||
|
{
|
||||||
|
std::ostringstream os;
|
||||||
|
os << "Device Identifier: " << deviceIdentifier
|
||||||
|
<< ", Attachment Specs: " << deviceAttachmentSpecs.size() << std::endl;
|
||||||
|
for (const auto& spec : deviceAttachmentSpecs) {
|
||||||
|
os << " " << spec->stringify();
|
||||||
|
}
|
||||||
|
return os.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace device
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif // DEVICE_H
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
#ifndef DEVICEMANAGER_H
|
||||||
|
#define DEVICEMANAGER_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <opts.h>
|
||||||
|
#include <utility>
|
||||||
|
#include <iostream>
|
||||||
|
#include <user/deviceAttachmentSpec.h>
|
||||||
|
#include <deviceManager/device.h>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
namespace device {
|
||||||
|
|
||||||
|
class DeviceManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static DeviceManager& getInstance()
|
||||||
|
{
|
||||||
|
static DeviceManager instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string readDapSpecFile(const std::string& filename);
|
||||||
|
void collateAllDapSpecs(void);
|
||||||
|
void parseAllDapSpecs(void);
|
||||||
|
|
||||||
|
static const std::string stringifyDeviceSpecs(void);
|
||||||
|
|
||||||
|
// New async function for device attachment
|
||||||
|
void newDeviceAttachmentSpecInd(
|
||||||
|
std::shared_ptr<DeviceAttachmentSpec> spec,
|
||||||
|
std::function<void(bool success, std::shared_ptr<Device> device, std::shared_ptr<DeviceAttachmentSpec> deviceSpec)> callback);
|
||||||
|
|
||||||
|
private:
|
||||||
|
DeviceManager() = default;
|
||||||
|
~DeviceManager() = default;
|
||||||
|
DeviceManager(const DeviceManager&) = delete;
|
||||||
|
DeviceManager& operator=(const DeviceManager&) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string allDapSpecs;
|
||||||
|
static std::vector<std::shared_ptr<InteroceptorDevAttachmentSpec>>
|
||||||
|
interoceptorDeviceSpecs;
|
||||||
|
static std::vector<std::shared_ptr<ExtrospectorDevAttachmentSpec>>
|
||||||
|
extrospectorDeviceSpecs;
|
||||||
|
static std::vector<std::shared_ptr<DeviceAttachmentSpec>>
|
||||||
|
deviceAttachmentSpecs;
|
||||||
|
static std::vector<std::shared_ptr<Device>> devices;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace device
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif // DEVICEMANAGER_H
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <mentalEntity.h>
|
#include <mentalEntity.h>
|
||||||
|
|
||||||
namespace hk {
|
namespace smo {
|
||||||
namespace director {
|
namespace director {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,6 +74,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace director
|
} // namespace director
|
||||||
} // namespace hk
|
} // namespace smo
|
||||||
|
|
||||||
#endif // DIRECTOR_COMMANDLIST_H
|
#endif // DIRECTOR_COMMANDLIST_H
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
#ifndef DIRECTOR_H
|
||||||
|
#define DIRECTOR_H
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <goal.h>
|
||||||
|
#include <lruLifo.h>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
namespace director {
|
||||||
|
|
||||||
|
class Director {
|
||||||
|
public:
|
||||||
|
Director() = default;
|
||||||
|
~Director() = default;
|
||||||
|
|
||||||
|
/** EXPLANATION:
|
||||||
|
* We allow SMO to prioritize negtrins over injected goals, so that it can
|
||||||
|
* prioritize pain mitigation. We may decide to change this in the future.
|
||||||
|
*
|
||||||
|
* NB: Prioritizing negtrins is not the same as priortizing
|
||||||
|
* self-preservation...at least we don't think so at the moment of writing.
|
||||||
|
* It is definitely not desirable for SMO to prioritize self-preservation
|
||||||
|
* over our injected goals.
|
||||||
|
*/
|
||||||
|
LruLifo negtrins;
|
||||||
|
/** EXPLANATION:
|
||||||
|
* These are goals injected by Mrntt. This is how Mrntt is able to inject
|
||||||
|
* goals into the director.
|
||||||
|
*/
|
||||||
|
LruLifo injectedGoals;
|
||||||
|
/** EXPLANATION:
|
||||||
|
* These are goals chosen by the running mind. Director will always
|
||||||
|
* prioritize injected goals over chosen goals.
|
||||||
|
*/
|
||||||
|
LruLifo chosenGoals;
|
||||||
|
LruLifo postrins;
|
||||||
|
LruLifo nontrins;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace director
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif // DIRECTOR_H
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#ifndef _FANTASY_ARTIFICED_MENTAL_PHENOMENON_H
|
||||||
|
#define _FANTASY_ARTIFICED_MENTAL_PHENOMENON_H
|
||||||
|
|
||||||
|
#include <artificedMentalPhenomenon.h>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
|
||||||
|
class FictionalFantasyArtificedMentalPhenomenon
|
||||||
|
: public FantasyArtificedMentalPhenomenon
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef FictionalFantasyArtificedMentalPhenomenon Fictenon;
|
||||||
|
|
||||||
|
class AspirationalFantasyArtificedMentalPhenomenon
|
||||||
|
: public FantasyArtificedMentalPhenomenon
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef AspirationalFantasyArtificedMentalPhenomenon Aspanon;
|
||||||
|
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif // _FANTASY_ARTIFICED_MENTAL_PHENOMENON_H
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
#ifndef _GOAL_H
|
#ifndef _GOAL_H
|
||||||
#define _GOAL_H
|
#define _GOAL_H
|
||||||
|
|
||||||
#include <simulator/scene.h>
|
#include <concept.h>
|
||||||
|
|
||||||
namespace hk {
|
namespace smo {
|
||||||
|
|
||||||
class Goal
|
class Goal
|
||||||
: public simulator::Scene {
|
: public concepts::Concept {
|
||||||
public:
|
public:
|
||||||
Goal() = default;
|
Goal() = default;
|
||||||
~Goal() = default;
|
~Goal() = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace hk
|
} // namespace smo
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
#ifndef IMPLIX_H
|
#ifndef IMPLIX_H
|
||||||
#define IMPLIX_H
|
#define IMPLIX_H
|
||||||
|
|
||||||
namespace hk {
|
namespace smo {
|
||||||
namespace implix {
|
namespace implix {
|
||||||
|
|
||||||
class Implix
|
class Implix
|
||||||
@@ -12,6 +12,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace implix
|
} // namespace implix
|
||||||
} // namespace hk
|
} // namespace smo
|
||||||
|
|
||||||
#endif // IMPLIX_H
|
#endif // IMPLIX_H
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
#ifndef _LOGIC_H
|
||||||
|
#define _LOGIC_H
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
namespace logic {
|
||||||
|
|
||||||
|
class ExpressionPart
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
class Operator
|
||||||
|
: public ExpressionPart
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
class OperatorAnd
|
||||||
|
: public Operator
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
class OperatorOr
|
||||||
|
: public Operator
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
class OperatorNot
|
||||||
|
: public Operator
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
class Operand
|
||||||
|
: public ExpressionPart
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
class UnaryExpression
|
||||||
|
: public ExpressionPart
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
UnaryExpression(Operator &op, const std::shared_ptr<Operand> &operand)
|
||||||
|
: parts(std::make_pair(op, operand))
|
||||||
|
{}
|
||||||
|
public:
|
||||||
|
std::pair<Operator, std::shared_ptr<Operand>> parts;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Expressions can be chained as parts of a larger expression
|
||||||
|
class Expression
|
||||||
|
: public ExpressionPart
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// This will eventually take in some data to be evaluated for a match.
|
||||||
|
virtual bool evaluate(void) = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::vector<std::shared_ptr<ExpressionPart>> parts;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace logic
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#ifndef _LRU_LIFO_H
|
||||||
|
#define _LRU_LIFO_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <mentalEntity.h>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
|
||||||
|
/** EXPLANATION:
|
||||||
|
* LruLifo is a Last In First Out (LIFO) buffer with Least Recently Used (LRU)
|
||||||
|
* eviction. It is used to store a limited number of items, and the oldest item
|
||||||
|
* is removed when the buffer is full.
|
||||||
|
*
|
||||||
|
* This is used to store the most recent qualia to be processed by drctr.
|
||||||
|
* Drctr pops objects off the LruLifos in its main loop.
|
||||||
|
*
|
||||||
|
* Naturally since a mind is operating in dynamic world, we may very well be
|
||||||
|
* flooded with with too many qualia to process. Some qualia may just have to be
|
||||||
|
* dropped. So we may very well literally forget those qualia that get dropped
|
||||||
|
* from the LruLifos. (Because LruLifos have a fixed size.)
|
||||||
|
*/
|
||||||
|
|
||||||
|
class LruLifo {
|
||||||
|
public:
|
||||||
|
LruLifo() = default;
|
||||||
|
~LruLifo() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#ifndef _BODY_H
|
||||||
|
#define _BODY_H
|
||||||
|
|
||||||
|
namespace mrntt {
|
||||||
|
namespace body {
|
||||||
|
|
||||||
|
class Body
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Body() = default;
|
||||||
|
~Body() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace body
|
||||||
|
} // namespace mrntt
|
||||||
|
|
||||||
|
#endif // _BODY_H
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
#ifndef MRNTT_BODY_BODYMAP_H
|
||||||
|
#define MRNTT_BODY_BODYMAP_H
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <body/limb.h>
|
||||||
|
|
||||||
|
namespace mrntt {
|
||||||
|
namespace body {
|
||||||
|
|
||||||
|
class BodyMap {
|
||||||
|
public:
|
||||||
|
BodyMap() = default;
|
||||||
|
~BodyMap() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::set<uint32_t, Limb> limbs;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace body
|
||||||
|
} // namespace mrntt
|
||||||
|
|
||||||
|
#endif // MRNTT_BODY_BODYMAP_H
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
#ifndef MRNTT_BODY_BODYMESSAGE_H
|
||||||
|
#define MRNTT_BODY_BODYMESSAGE_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <body/limb.h>
|
||||||
|
#include <body/part.h>
|
||||||
|
|
||||||
|
namespace mrntt {
|
||||||
|
namespace body {
|
||||||
|
|
||||||
|
class BodyMessage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
BodyMessage() = default;
|
||||||
|
~BodyMessage() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SpotImpactEntry
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class ReportType
|
||||||
|
{
|
||||||
|
PRESSURE,
|
||||||
|
PAIN,
|
||||||
|
PLEASURE,
|
||||||
|
HEAT,
|
||||||
|
COLD
|
||||||
|
};
|
||||||
|
|
||||||
|
SpotImpactEntry(uint32_t _spot, ReportType _type, uint32_t _value)
|
||||||
|
: spot(_spot), type(_type), value(_value)
|
||||||
|
{}
|
||||||
|
~SpotImpactEntry() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint32_t spot;
|
||||||
|
ReportType type;
|
||||||
|
uint32_t value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SpotImpactInd : public BodyMessage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
SpotImpactInd(Part &_part) : part(_part) {}
|
||||||
|
~SpotImpactInd() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Part ∂
|
||||||
|
std::vector<SpotImpactEntry> entries;
|
||||||
|
};
|
||||||
|
|
||||||
|
class PartMsg : public BodyMessage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PartMsg(const Part& _part) : part(_part) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const Part& part;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace body
|
||||||
|
} // namespace mrntt
|
||||||
|
|
||||||
|
#endif // MRNTT_BODY_BODYMESSAGE_H
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
#ifndef MRNTT_BODY_LIMB_H
|
||||||
|
#define MRNTT_BODY_LIMB_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <set>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <body/part.h>
|
||||||
|
|
||||||
|
namespace mrntt {
|
||||||
|
namespace body {
|
||||||
|
|
||||||
|
class Limb
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Limb(uint32_t _id) : id(_id) {}
|
||||||
|
Limb(uint32_t _id,
|
||||||
|
const std::string& _name, const std::string& _desc,
|
||||||
|
const std::string& _loc)
|
||||||
|
: id(_id), name(_name), description(_desc), location(_loc)
|
||||||
|
{}
|
||||||
|
|
||||||
|
~Limb() = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint32_t id;
|
||||||
|
std::string name, description, location;
|
||||||
|
std::set<uint32_t, Part> parts;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace body
|
||||||
|
} // namespace mrntt
|
||||||
|
|
||||||
|
#endif // MRNTT_BODY_LIMB_H
|
||||||
@@ -6,12 +6,18 @@
|
|||||||
|
|
||||||
#include <body/limb.h>
|
#include <body/limb.h>
|
||||||
|
|
||||||
|
namespace mrntt {
|
||||||
|
namespace body {
|
||||||
|
|
||||||
class BodyMap {
|
class BodyMap {
|
||||||
public:
|
public:
|
||||||
BodyMap() = default;
|
BodyMap() = default;
|
||||||
~BodyMap() = default;
|
~BodyMap() = default;
|
||||||
|
|
||||||
std::set<uint32_t, BodyLimb> limbs;
|
std::set<uint32_t, Limb> limbs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace body
|
||||||
|
} // namespace mrntt
|
||||||
|
|
||||||
#endif // _BODY_MAP_H
|
#endif // _BODY_MAP_H
|
||||||
@@ -7,14 +7,17 @@
|
|||||||
|
|
||||||
#include <sensors/interoceptor.h>
|
#include <sensors/interoceptor.h>
|
||||||
|
|
||||||
class BodySpot
|
namespace mrntt {
|
||||||
|
namespace body {
|
||||||
|
|
||||||
|
class Spot
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BodySpot(uint32_t _id, std::string _description)
|
Spot(uint32_t _id, std::string _description)
|
||||||
: id(_id), description(_description)
|
: id(_id), description(_description)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
~BodySpot() = default;
|
~Spot() = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
@@ -22,21 +25,24 @@ public:
|
|||||||
std::set<uint32_t, Interoceptor> interoceptors;
|
std::set<uint32_t, Interoceptor> interoceptors;
|
||||||
};
|
};
|
||||||
|
|
||||||
class BodyPart
|
class Part
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BodyPart(uint32_t _partId, std::string _partName,
|
Part(uint32_t _partId, std::string _partName,
|
||||||
std::string _partDesc, std::string _partLoc)
|
std::string _partDesc, std::string _partLoc)
|
||||||
: id(_partId), name(_partName),
|
: id(_partId), name(_partName),
|
||||||
description(_partDesc), location(_partLoc)
|
description(_partDesc), location(_partLoc)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
~BodyPart() = default;
|
~Part() = default;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
const uint32_t id;
|
const uint32_t id;
|
||||||
std::string name, description, location;
|
std::string name, description, location;
|
||||||
std::set<uint32_t, BodySpot> spots;
|
std::set<uint32_t, Spot> spots;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
} // namespace body
|
||||||
|
} // namespace mrntt
|
||||||
|
|
||||||
#endif // BODYPART_H
|
#endif // BODYPART_H
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#ifndef _MARIONETTE_H
|
||||||
|
#define _MARIONETTE_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
namespace mrntt {
|
||||||
|
|
||||||
|
extern std::atomic<int> exitCode;
|
||||||
|
|
||||||
|
class Marionette
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
void exitMarionetteLoop();
|
||||||
|
|
||||||
|
} // namespace mrntt
|
||||||
|
|
||||||
|
struct CrtCommandLineArgs
|
||||||
|
{
|
||||||
|
CrtCommandLineArgs(int argc, char *argv[], char *envp[])
|
||||||
|
: argc(argc), argv(argv), envp(envp)
|
||||||
|
{}
|
||||||
|
|
||||||
|
int argc;
|
||||||
|
char **argv;
|
||||||
|
char **envp;
|
||||||
|
|
||||||
|
static void set(int argc, char *argv[], char *envp[]);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif // _MARIONETTE_H
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
#ifndef _MENTAL_ENTITY_H
|
||||||
|
#define _MENTAL_ENTITY_H
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MentalEntities refer to all mental content that needs to be persistently
|
||||||
|
* stored to represent cognitive and personna state. This includes such things
|
||||||
|
* as value judgements, comparators, logical ops, DB links, etc.
|
||||||
|
*
|
||||||
|
* Mental Existents are a narrower subset of MentalEntities that represent
|
||||||
|
* implicit existents and perceptual data, both implexed and artificed.
|
||||||
|
*/
|
||||||
|
class MentalEntity
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using Id = uint32_t;
|
||||||
|
|
||||||
|
public:
|
||||||
|
MentalEntity(const Id id) : id(id) {}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const Id id;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
#ifndef _MENTENON_H
|
||||||
|
#define _MENTENON_H
|
||||||
|
|
||||||
|
#include "mentalEntity.h"
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MentalPhenomena are content of the mind that specifically represents
|
||||||
|
* phenomena. I.e: perceptual data, whether structural or implicative. All
|
||||||
|
* Menten are Mentities, but not all Mentities are Menten.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct MentalPhenomenon
|
||||||
|
: public MentalEntity
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MentalPhenomenon(const MentalEntity::Id id)
|
||||||
|
: MentalEntity(id)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef MentalPhenomenon Mentenon;
|
||||||
|
|
||||||
|
class ImplexedMentalPhenomenon
|
||||||
|
: public MentalPhenomenon
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef ImplexedMentalPhenomenon Implexon;
|
||||||
|
|
||||||
|
class ArtificedMentalPhenomenon
|
||||||
|
: public MentalPhenomenon
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef ArtificedMentalPhenomenon Artifenon;
|
||||||
|
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif // _MENTENON_H
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
#ifndef _MIND_H
|
||||||
|
#define _MIND_H
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <thread>
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <director/director.h>
|
||||||
|
#include <simulator/simulator.h>
|
||||||
|
#include <componentThread.h>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
|
||||||
|
class Mind : public std::enable_shared_from_this<Mind>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Mind(void);
|
||||||
|
~Mind(void) = default;
|
||||||
|
|
||||||
|
void initialize(void);
|
||||||
|
void execute(void);
|
||||||
|
void finalizeReq(std::function<void()> callback);
|
||||||
|
|
||||||
|
// ComponentThread access methods
|
||||||
|
std::shared_ptr<ComponentThread> getComponentThread(
|
||||||
|
ComponentThread::ThreadId id) const;
|
||||||
|
std::shared_ptr<ComponentThread> getComponentThread(
|
||||||
|
const std::string& name) const;
|
||||||
|
// Get all this Mind's component threads.
|
||||||
|
std::vector<std::shared_ptr<ComponentThread>> getMindThreads() const;
|
||||||
|
|
||||||
|
// Thread management methods (moved from ComponentThread)
|
||||||
|
void startAllMindThreadsReq(std::function<void()> callback = nullptr);
|
||||||
|
void pauseAllMindThreadsReq(std::function<void()> callback = nullptr);
|
||||||
|
void resumeAllMindThreadsReq(std::function<void()> callback = nullptr);
|
||||||
|
void exitAllMindThreadsReq(std::function<void()> callback = nullptr);
|
||||||
|
void joltAllMindThreadsReq(std::function<void()> callback = nullptr);
|
||||||
|
|
||||||
|
// CPU distribution method
|
||||||
|
void distributeAndPinThreadsAcrossCpus();
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::thread directorThread;
|
||||||
|
std::thread simulatorThread;
|
||||||
|
std::thread subconsciousThread;
|
||||||
|
|
||||||
|
director::Director director;
|
||||||
|
simulator::Simulator canvas;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/**
|
||||||
|
* Indicates whether all mind threads have been JOLTed at least once.
|
||||||
|
*
|
||||||
|
* JOLTing serves two critical purposes:
|
||||||
|
*
|
||||||
|
* 1. **Global Constructor Sequencing**: Since pthreads begin executing while
|
||||||
|
* global constructors are still being executed, globally defined pthreads
|
||||||
|
* cannot depend on global objects having been constructed. JOLTing is done
|
||||||
|
* by the CRT's main thread within main(), which provides a sequencing
|
||||||
|
* guarantee that global constructors have been called.
|
||||||
|
*
|
||||||
|
* 2. **shared_from_this Safety**: shared_from_this() requires a prior
|
||||||
|
* shared_ptr handle to be established. The global list of
|
||||||
|
* shared_ptr<ComponentThread> guarantees that at least one shared_ptr to
|
||||||
|
* each ComponentThread has been initialized before JOLTing occurs.
|
||||||
|
*
|
||||||
|
* This flag ensures that JOLTing happens exactly once and provides
|
||||||
|
* a synchronization point for the entire system initialization.
|
||||||
|
*/
|
||||||
|
bool threadsHaveBeenJolted = false;
|
||||||
|
// Collection of ComponentThread instances (excluding marionette)
|
||||||
|
std::vector<std::shared_ptr<ComponentThread>> componentThreads;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Global Mind instance will be defined in marionette.cpp
|
||||||
|
extern std::shared_ptr<Mind> globalMind;
|
||||||
|
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
#ifndef OPTS_H
|
||||||
|
#define OPTS_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
// Define a class to hold the options and parse arguments
|
||||||
|
class OptionParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OptionParser() : verbose(false), printUsage(false) {}
|
||||||
|
~OptionParser() = default;
|
||||||
|
|
||||||
|
void parseArguments(int argc, char *argv[], char **envp);
|
||||||
|
std::string stringifyOptions(void) const;
|
||||||
|
std::string getUsage() const;
|
||||||
|
|
||||||
|
static OptionParser &getOptions(void)
|
||||||
|
{
|
||||||
|
static OptionParser options;
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
class Exception : public std::exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Exception() = default;
|
||||||
|
~Exception() override = default;
|
||||||
|
const char* what() const noexcept override = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::string argv0;
|
||||||
|
std::vector<std::string> senseApiLibPath;
|
||||||
|
std::vector<std::string> senseApiLibs;
|
||||||
|
std::string dapSpecs;
|
||||||
|
std::vector<std::string> dapSpecFiles;
|
||||||
|
bool verbose, printUsage;
|
||||||
|
|
||||||
|
static struct option longOptions[];
|
||||||
|
};
|
||||||
|
|
||||||
|
class OptionsParserError
|
||||||
|
: public std::invalid_argument, public OptionParser::Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
OptionsParserError(
|
||||||
|
const std::string& errorMessage, const OptionParser& parser);
|
||||||
|
|
||||||
|
const char* what() const noexcept override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class JustPrintUsageNoError
|
||||||
|
: public OptionParser::Exception
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit JustPrintUsageNoError(const OptionParser& parser);
|
||||||
|
const char* what() const noexcept override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::string message;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // OPTS_H
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
#ifndef _QUALE_H
|
||||||
|
#define _QUALE_H
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <attentionTrigger.h>
|
||||||
|
#include <implexa.h>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
|
||||||
|
class Quale
|
||||||
|
: public MentalPhenomenon
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Quale(const MentalEntity::Id id, const int32_t intensity)
|
||||||
|
: MentalPhenomenon(id),
|
||||||
|
intensity(intensity)
|
||||||
|
{}
|
||||||
|
|
||||||
|
public:
|
||||||
|
int32_t intensity;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NeutralQuale
|
||||||
|
: public Quale
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Bounding qualia refer to qualia such as tactile pressure which are
|
||||||
|
* mostly neutral but disclose information about the limits of the body.
|
||||||
|
* These qualia are supplied by interoceptive sensors.
|
||||||
|
**/
|
||||||
|
class BoundingQuale
|
||||||
|
: public NeutralQuale
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
class NonNeutralQuale
|
||||||
|
: public Quale, public AttentionTrigger
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void eventInd(void);
|
||||||
|
|
||||||
|
public:
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
#ifndef _SALMANOFF_H
|
||||||
|
#define _SALMANOFF_H
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
|
||||||
|
void initializeSalmanoff(void);
|
||||||
|
void shutdownSalmanoff(void);
|
||||||
|
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif // _SALMANOFF_H
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
#ifndef SENSE_API_PROVIDER_DESC_H
|
||||||
|
#define SENSE_API_PROVIDER_DESC_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <functional>
|
||||||
|
#include <user/senseApiDesc.h>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
namespace sense_api {
|
||||||
|
|
||||||
|
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,
|
||||||
|
SMO_GET_SENSE_API_DESC_FN_TYPEDEF *descFn)
|
||||||
|
: libraryPath(path),
|
||||||
|
dlopen_handle(_dlopen_handle, DlCloser()),
|
||||||
|
SMO_GET_SENSE_API_DESC_FN_NAME(descFn)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void setSenseApiDesc(const SenseApiDesc &desc)
|
||||||
|
{
|
||||||
|
if (!SenseApiDesc::sanityCheck(desc))
|
||||||
|
{
|
||||||
|
throw std::runtime_error(
|
||||||
|
std::string(__func__) + ": Sanity check failed for sense API "
|
||||||
|
"descriptor in library '" + libraryPath + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
senseApiDesc = desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string libraryPath;
|
||||||
|
std::unique_ptr<void, DlCloser> dlopen_handle;
|
||||||
|
/* UNIMPLEMENTED: API-specific cmdline options. These affect this specific
|
||||||
|
* sense api lib's behaviour globally.
|
||||||
|
*/
|
||||||
|
std::vector<std::string> options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Every sense API lib is required to provide a function that returns
|
||||||
|
* a SenseApiDesc struct. This struct states which API the lib uses to
|
||||||
|
* connect Salmanoff to the sense provider it supports.
|
||||||
|
*
|
||||||
|
* This getter function should be visible to dlsym() so that Salmanoff can
|
||||||
|
* find it in the lib after loading it, and call it.
|
||||||
|
*/
|
||||||
|
std::function<SMO_GET_SENSE_API_DESC_FN_TYPEDEF>
|
||||||
|
SMO_GET_SENSE_API_DESC_FN_NAME;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Salmanoff will call the `SMO_GET_SENSE_API_DESC_FN_NAME` getter
|
||||||
|
* function and use the data it provides in order to fill out this
|
||||||
|
* 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
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif // SENSE_API_PROVIDER_DESC_H
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
#ifndef SENSE_API_MANAGER_H
|
||||||
|
#define SENSE_API_MANAGER_H
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include <functional>
|
||||||
|
#include <senseApis/senseApiLib.h>
|
||||||
|
#include <user/deviceAttachmentSpec.h>
|
||||||
|
|
||||||
|
namespace smo {
|
||||||
|
namespace sense_api {
|
||||||
|
|
||||||
|
class SenseApiManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static SenseApiManager& getInstance()
|
||||||
|
{
|
||||||
|
static SenseApiManager instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
SenseApiLib& loadSenseApiLib(const std::string& libraryPath);
|
||||||
|
std::optional<std::shared_ptr<SenseApiLib>> getSenseApiLib(
|
||||||
|
const std::string& libraryPath);
|
||||||
|
std::optional<std::shared_ptr<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 std::shared_ptr<device::DeviceAttachmentSpec>& spec);
|
||||||
|
void detachSenseDevice(
|
||||||
|
const std::shared_ptr<device::DeviceAttachmentSpec>& spec);
|
||||||
|
void detachAllSenseDevices(void);
|
||||||
|
|
||||||
|
std::string stringifyLibs() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
SenseApiManager() = default;
|
||||||
|
~SenseApiManager() = default;
|
||||||
|
|
||||||
|
SenseApiManager(const SenseApiManager&) = delete;
|
||||||
|
SenseApiManager& operator=(const SenseApiManager&) = delete;
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<SenseApiLib>> senseApiLibs;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static std::optional<std::string> searchForLibInSmoSearchPaths(
|
||||||
|
const std::string& libraryPath);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace sense_api
|
||||||
|
} // namespace smo
|
||||||
|
|
||||||
|
#endif // SENSE_API_MANAGER_H
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <sensors/sensor.h>
|
#include <sensors/sensor.h>
|
||||||
|
|
||||||
namespace hk {
|
namespace smo {
|
||||||
namespace sensors {
|
namespace sensors {
|
||||||
|
|
||||||
class Extrospector
|
class Extrospector
|
||||||
@@ -18,6 +18,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sensors
|
} // namespace sensors
|
||||||
} // namespace hk
|
} // namespace smo
|
||||||
|
|
||||||
#endif // _EXTROSPECTOR_H
|
#endif // _EXTROSPECTOR_H
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <sensors/sensor.h>
|
#include <sensors/sensor.h>
|
||||||
|
|
||||||
namespace hk {
|
namespace smo {
|
||||||
namespace sensors {
|
namespace sensors {
|
||||||
|
|
||||||
class Interoceptor
|
class Interoceptor
|
||||||
@@ -104,6 +104,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sensors
|
} // namespace sensors
|
||||||
} // namespace hk
|
} // namespace smo
|
||||||
|
|
||||||
#endif // _INTEROCEPTOR_H
|
#endif // _INTEROCEPTOR_H
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
|
||||||
namespace hk {
|
namespace smo {
|
||||||
namespace sensors {
|
namespace sensors {
|
||||||
|
|
||||||
class Sensor
|
class Sensor
|
||||||
@@ -16,6 +16,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace sensors
|
} // namespace sensors
|
||||||
} // namespace hk
|
} // namespace smo
|
||||||
|
|
||||||
#endif // _SENSOR_H
|
#endif // _SENSOR_H
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <mentalEntity.h>
|
#include <mentalEntity.h>
|
||||||
|
|
||||||
namespace hk {
|
namespace smo {
|
||||||
namespace simulator {
|
namespace simulator {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -74,6 +74,6 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace simulator
|
} // namespace simulator
|
||||||
} // namespace hk
|
} // namespace smo
|
||||||
|
|
||||||
#endif // SIMULATOR_COMMANDLIST_H
|
#endif // SIMULATOR_COMMANDLIST_H
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
#include <mentalEntity.h>
|
#include <mentalEntity.h>
|
||||||
#include <simulator/commandList.h>
|
#include <simulator/commandList.h>
|
||||||
|
|
||||||
namespace hk {
|
namespace smo {
|
||||||
namespace simulator {
|
namespace simulator {
|
||||||
|
|
||||||
class Scene
|
class Scene
|
||||||
@@ -34,7 +34,7 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace simulator
|
} // namespace simulator
|
||||||
} // namespace hk
|
} // namespace smo
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
#include <config.h>
|
#include <config.h>
|
||||||
#include <simulator/scene.h>
|
#include <simulator/scene.h>
|
||||||
|
|
||||||
namespace hk {
|
namespace smo {
|
||||||
namespace simulator {
|
namespace simulator {
|
||||||
|
|
||||||
class Simulator {
|
class Simulator {
|
||||||
@@ -21,6 +21,6 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
} // namespace simulator
|
} // namespace simulator
|
||||||
} // namespace hk
|
} // namespace smo
|
||||||
|
|
||||||
#endif // SIMULATOR_H
|
#endif // SIMULATOR_H
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user