/* 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 #include #include #include #include namespace { static std::mutex g_mu; static std::vector g_c_cache; static std::string g_loaded_path; static std::vector 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(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 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 lk(g_mu); ensure_loaded_locked(); if (out_count) *out_count = static_cast(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 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 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 lk(g_mu); return g_loaded_path.empty() ? nullptr : g_loaded_path.c_str(); } } // extern "C"