6e0273f4b4
В прод деплоен gx/cuframes-composer:0.11b-step1 — C++ ядро
работает через ABI shim, старые C-callers (grid_record.c, control.c,
frigate_mqtt.c) использует те же cfc_composer_* функции.
Что в этом коммите:
- src/cpp/composer_c_api.cpp: extern "C" обёртки над cfc::Composer
методами. Полный набор: _create/_destroy/_compose/_add_overlay/
_find_overlay/_set_layout/_current_layout/_add_pool_source/
_set_motion_mode/_get_motion_mode/_motion_pulse/_get_health.
- src/cpp/layouts_c_api.cpp: extern "C" обёртки над template_loader
для cfc_layout_find/_all/_load_file/_reload/_loaded_path/_to_pixels.
- cpp/template_loader: global registry (current_templates / set_*
/ load_into_current) — единый источник истины. Composer и C ABI
shim читают один и тот же mutex-защищённый vector<LayoutTemplate>.
Hot-reload через ZMQ cfc_layout_load_file подхватывается composer'ом
на следующем кадре без рестарта.
- cpp/composer: pick_best_fit, set_layout, maybe_relayout читают
current_templates() вместо локального snapshot.
- cpp/composer: backward-compat overlay list (add_overlay/find_overlay)
+ manual cells support (для C API без motion-mode).
- cpp/composer compose_frame: после Layout.render() рендерит overlays
(CLI text/icon/border + Frigate detbox) поверх.
- Удалены: src/composer.c (заменён composer_c_api.cpp + composer.cpp),
src/layouts.c (заменён layouts_c_api.cpp + template_loader.cpp).
- Оставлено как есть: src/overlay.c (PNG/text/border/detbox CLI overlays
— реализация не меняется, доступ через cfc_overlay_*).
- src/CMakeLists.txt: COMPOSER_SOURCES_C минус composer.c, layouts.c,
COMPOSER_SOURCES_CPP плюс composer_c_api.cpp, layouts_c_api.cpp.
Production smoke (R9-88.23):
[cfc/loader] /opt/templates.json: loaded 7 templates
[cfc/composer] templates loaded: 7 (path='/opt/templates.json')
[cfc/composer] pool+ cam-parking prio=100 / cam-gate_lpr prio=90 / ...
[cfc/composer] motion_mode=1 ttl=45000ms pool=4
[cfc/composer] grow → template='tpl_1' active=1
PASS.
Refs: #195 (Phase 11b C++ refactor).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
137 lines
4.2 KiB
C++
137 lines
4.2 KiB
C++
/* layouts_c_api — extern "C" ABI shim над template_loader (Phase 11b).
|
|
*
|
|
* Сохраняет совместимость с control.c::cmd_list_layouts/_get_layout/_set_layout
|
|
* через старый интерфейс cfc_layout_find / cfc_layout_all.
|
|
*
|
|
* Стратегия: static cache из cfc_layout_t structs, заполняется при
|
|
* load_file/reload. cfc_layout_find/_all возвращают указатели в этот cache.
|
|
*
|
|
* Лицензия: LGPL-2.1+
|
|
*/
|
|
|
|
#include "../../include/cuframes_composer/layouts.h"
|
|
#include "../../include/cuframes_composer/cpp/template.hpp"
|
|
#include "../../include/cuframes_composer/cpp/template_loader.hpp"
|
|
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
|
|
static std::mutex g_mu;
|
|
static std::vector<cfc_layout_t> g_c_cache;
|
|
static std::string g_loaded_path;
|
|
static std::vector<cfc::LayoutTemplate> g_cpp_cache;
|
|
|
|
void rebuild_c_cache_locked()
|
|
{
|
|
g_c_cache.clear();
|
|
g_c_cache.reserve(g_cpp_cache.size());
|
|
for (const auto& t : g_cpp_cache) {
|
|
cfc_layout_t l{};
|
|
std::strncpy(l.name, t.name.c_str(), sizeof(l.name) - 1);
|
|
l.priority = t.priority;
|
|
l.nb_cells = static_cast<int>(t.cells.size());
|
|
if (l.nb_cells > CFC_LAYOUT_MAX_CELLS) l.nb_cells = CFC_LAYOUT_MAX_CELLS;
|
|
l.nb_camera_cells = 0;
|
|
for (int i = 0; i < l.nb_cells; i++) {
|
|
const auto& c = t.cells[i];
|
|
l.cells[i].col = c.col;
|
|
l.cells[i].row = c.row;
|
|
l.cells[i].cs = c.cs;
|
|
l.cells[i].rs = c.rs;
|
|
l.cells[i].role = (c.role == cfc::CellRole::Widget)
|
|
? CFC_CELL_WIDGET : CFC_CELL_CAMERA;
|
|
l.cells[i].order = c.order;
|
|
std::strncpy(l.cells[i].widget, c.widget.c_str(),
|
|
sizeof(l.cells[i].widget) - 1);
|
|
if (l.cells[i].role == CFC_CELL_CAMERA) l.nb_camera_cells++;
|
|
}
|
|
g_c_cache.push_back(l);
|
|
}
|
|
}
|
|
|
|
void ensure_loaded_locked()
|
|
{
|
|
/* Источник истины = global registry; кеш C-структур пересинхронизируется
|
|
* каждый раз когда состав изменился (поэтому простая проверка empty
|
|
* не годится — может появиться обновление через load_file). */
|
|
g_cpp_cache = cfc::current_templates();
|
|
rebuild_c_cache_locked();
|
|
}
|
|
|
|
} // namespace
|
|
|
|
extern "C" {
|
|
|
|
const cfc_layout_t* cfc_layout_find(const char* name)
|
|
{
|
|
if (!name) return nullptr;
|
|
std::lock_guard<std::mutex> lk(g_mu);
|
|
ensure_loaded_locked();
|
|
for (const auto& l : g_c_cache) {
|
|
if (std::strcmp(l.name, name) == 0) return &l;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
const cfc_layout_t* cfc_layout_all(int* out_count)
|
|
{
|
|
std::lock_guard<std::mutex> lk(g_mu);
|
|
ensure_loaded_locked();
|
|
if (out_count) *out_count = static_cast<int>(g_c_cache.size());
|
|
return g_c_cache.data();
|
|
}
|
|
|
|
void cfc_layout_to_pixels(const cfc_cell_t* cell, int W, int H,
|
|
int* out_x, int* out_y, int* out_w, int* out_h)
|
|
{
|
|
if (!cell) return;
|
|
int x = (cell->col * W) / CFC_GRID_COLS;
|
|
int y = (cell->row * H) / CFC_GRID_ROWS;
|
|
int w = (cell->cs * W) / CFC_GRID_COLS;
|
|
int h = (cell->rs * H) / CFC_GRID_ROWS;
|
|
x &= ~1; y &= ~1; w &= ~1; h &= ~1;
|
|
if (x + w > W) w = W - x;
|
|
if (y + h > H) h = H - y;
|
|
if (out_x) *out_x = x;
|
|
if (out_y) *out_y = y;
|
|
if (out_w) *out_w = w;
|
|
if (out_h) *out_h = h;
|
|
}
|
|
|
|
int cfc_layout_load_file(const char* path)
|
|
{
|
|
if (!path) return -3;
|
|
int r = cfc::load_into_current(path); /* обновит global registry */
|
|
if (r > 0) {
|
|
std::lock_guard<std::mutex> lk(g_mu);
|
|
g_cpp_cache = cfc::current_templates();
|
|
rebuild_c_cache_locked();
|
|
g_loaded_path = path;
|
|
}
|
|
return r;
|
|
}
|
|
|
|
int cfc_layout_reload(void)
|
|
{
|
|
std::string path;
|
|
{
|
|
std::lock_guard<std::mutex> lk(g_mu);
|
|
path = g_loaded_path;
|
|
}
|
|
if (path.empty()) return -1;
|
|
return cfc_layout_load_file(path.c_str());
|
|
}
|
|
|
|
const char* cfc_layout_loaded_path(void)
|
|
{
|
|
std::lock_guard<std::mutex> lk(g_mu);
|
|
return g_loaded_path.empty() ? nullptr : g_loaded_path.c_str();
|
|
}
|
|
|
|
} // extern "C"
|