393 lines
17 KiB
C++
393 lines
17 KiB
C++
#include "NativeManager.h"
|
||
#include <ace/xcomponent/native_interface_xcomponent.h>
|
||
#include <cstdint>
|
||
#include <cstdio>
|
||
#include <hilog/log.h>
|
||
#include <string>
|
||
#include "arkui/native_node.h"
|
||
#include "arkui/native_node_napi.h"
|
||
#include "arkui/native_interface.h"
|
||
#include "common.h"
|
||
#include <random>
|
||
#include <map>
|
||
#define COLUMN_MARGIN 10
|
||
#define XC_WIDTH 800
|
||
#define XC_HEIGHT 600
|
||
#define ARG_CNT 2
|
||
#ifndef NATIVE_TAG
|
||
#define NATIVE_TAG "NativeManager"
|
||
#endif
|
||
|
||
namespace NativeOpenCAX {
|
||
// [Start plugin_manager_cpp]
|
||
// plugin_manager.cpp
|
||
std::unordered_map<std::string, ArkUI_NodeHandle> NativeManager::nodeHandleMap_;
|
||
std::unordered_map<void *, OH_ArkUI_SurfaceCallback *> NativeManager::callbackMap_;
|
||
std::unordered_map<void *, OH_ArkUI_SurfaceHolder *> NativeManager::surfaceHolderMap_;
|
||
ArkUI_AccessibilityProvider *NativeManager::provider_ = nullptr;
|
||
ArkUI_NativeNodeAPI_1 *nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1 *>(
|
||
OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1"));
|
||
// [StartExclude plugin_manager_cpp]
|
||
NativeManager NativeManager::pluginManager_;
|
||
OH_NativeXComponent_Callback NativeManager::xSurfaceTouchEventCallBack;
|
||
OH_NativeXComponent_MouseEvent_Callback NativeManager::xMouseEventCallBack;
|
||
std::string comId;
|
||
std::string nodeId;
|
||
int32_t NativeManager::hasDraw_ = 0;
|
||
int32_t NativeManager::hasChangeColor_ = 0;
|
||
static std::map<int64_t, std::shared_ptr<NativeRenderThread>> renderThreadMap;
|
||
static std::mutex mapMutex;
|
||
|
||
|
||
NativeManager::~NativeManager() {
|
||
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "~NativeManager");
|
||
nativeXComponentMap_.clear();
|
||
int64_t id = 0;
|
||
std::lock_guard<std::mutex> lock(mapMutex);
|
||
if (renderThreadMap.find(id) != renderThreadMap.end()) {
|
||
renderThreadMap[id]->stop();
|
||
renderThreadMap.erase(id);
|
||
}
|
||
|
||
for (auto iter = pluginManagerMap_.begin(); iter != pluginManagerMap_.end(); ++iter) {
|
||
if (iter->second != nullptr) {
|
||
delete iter->second;
|
||
iter->second = nullptr;
|
||
}
|
||
}
|
||
pluginManagerMap_.clear();
|
||
}
|
||
|
||
std::string uuid_v4() {
|
||
// 使用当前时间戳和随机数生成UUID
|
||
auto timestamp = std::chrono::high_resolution_clock::now().time_since_epoch().count();
|
||
std::random_device rd;
|
||
std::mt19937_64 gen(rd());
|
||
std::uniform_int_distribution<uint64_t> dis(0, std::numeric_limits<uint64_t>::max());
|
||
uint64_t random_part = dis(gen);
|
||
|
||
// 合并时间戳和随机数,并转换为UUID格式
|
||
uint64_t full_uuid = (timestamp << 32) | (random_part & 0xFFFFFFFF);
|
||
std::stringstream ss;
|
||
ss << std::hex << std::uppercase << std::setw(8) << std::setfill('0') << (full_uuid & 0xFFFFFFFF)
|
||
<< "-" << std::setw(4) << std::setfill('0') << (full_uuid >> 32 & 0xFFFF)
|
||
<< "-4" // UUID版本4,中间四位固定为04xx
|
||
<< std::setw(3) << std::setfill('0') << (random_part >> 48 & 0x0FFF | 0x4000) // 设置版本号和一些随机位
|
||
<< "-" << std::setw(4) << std::setfill('0') << (random_part >> 32 & 0xFFFF)
|
||
<< "-" << std::setw(12) << std::setfill('0') << (random_part & 0xFFFFFFFFFFFF);
|
||
return ss.str();
|
||
}
|
||
// Surface回调事件
|
||
//void OnSurfaceCreatedNative(OH_ArkUI_SurfaceHolder *holder) {
|
||
// auto window = OH_ArkUI_XComponent_GetNativeWindow(holder); // 获取native window
|
||
// auto eglCore = reinterpret_cast<EGLCore *>(OH_ArkUI_SurfaceHolder_GetUserData(holder));
|
||
// eglCore->EglContextInit(window, 800, 600);
|
||
//}
|
||
//void OnSurfaceChangedNative(OH_ArkUI_SurfaceHolder *holder, uint64_t width, uint64_t height) {
|
||
// EGLCore *render = reinterpret_cast<EGLCore *>(OH_ArkUI_SurfaceHolder_GetUserData(holder));
|
||
// render->UpdateSize(width, height); // 设置绘制区域大小
|
||
// //render->Draw(NativeManager::hasDraw_); // 绘制五角星
|
||
//}
|
||
//void OnSurfaceDestroyedNative(OH_ArkUI_SurfaceHolder *holder) {
|
||
// OH_LOG_Print(LOG_APP, LOG_ERROR, 0xff00, "onBind", "on destroyed");
|
||
// EGLCore *render = reinterpret_cast<EGLCore *>(OH_ArkUI_SurfaceHolder_GetUserData(holder));
|
||
// render->Release(); // 销毁eglSurface相关资源
|
||
//}
|
||
//void OnSurfaceShowNative(OH_ArkUI_SurfaceHolder *holder) {
|
||
// OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on surface show");
|
||
//}
|
||
//void OnSurfaceHideNative(OH_ArkUI_SurfaceHolder *holder) {
|
||
// OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on surface hide");
|
||
//}
|
||
//void OnFrameCallbackNative(ArkUI_NodeHandle node, uint64_t timestamp, uint64_t targetTimestamp) {
|
||
// if (!NativeManager::surfaceHolderMap_.count(node)) {
|
||
// return;
|
||
// }
|
||
// static uint64_t count = 0;
|
||
// count++;
|
||
// // 在头文件plugin_manager.h中定义,FRAME_COUNT的值为50
|
||
// if (count % FRAME_COUNT == 0) {
|
||
// OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "OnFrameCallback count = %{public}ld", count);
|
||
// }
|
||
//}
|
||
void OnSurfaceCreatedCB(OH_NativeXComponent *component, void *window) {
|
||
// [StartExclude plugin_on_surface_created]
|
||
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "XComponent_Native", "OnSurfaceCreatedCB");
|
||
int32_t ret;
|
||
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
|
||
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
|
||
ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
|
||
if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
|
||
return;
|
||
}
|
||
|
||
std::string id(idStr);
|
||
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "XComponent_Native", "OnSurfaceCreatedCB id=%{public}s",
|
||
id.c_str());
|
||
// [EndExclude plugin_on_surface_created]
|
||
// 初始化环境与绘制背景
|
||
auto *pluginManger = NativeManager::GetInstance();
|
||
pluginManger->OnSurfaceCreated(component, window);
|
||
}
|
||
void OnSurfaceChangedCB(OH_NativeXComponent *component, void *window) {
|
||
// [StartExclude plugin_on_surface_changed]
|
||
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "XComponent_Native", "OnSurfaceChangedCB");
|
||
int32_t ret;
|
||
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
|
||
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
|
||
ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
|
||
if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
|
||
return;
|
||
}
|
||
std::string id(idStr);
|
||
// [EndExclude plugin_on_surface_changed]
|
||
auto *pluginManger = NativeManager::GetInstance();
|
||
// 封装OnSurfaceChanged方法
|
||
pluginManger->OnSurfaceChanged(component, window);
|
||
}
|
||
void OnSurfaceDestroyedCB(OH_NativeXComponent *component, void *window) {
|
||
// [StartExclude plugin_on_surface_destroyed]
|
||
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback", "OnSurfaceDestroyedCB");
|
||
int32_t ret;
|
||
char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {};
|
||
uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
|
||
ret = OH_NativeXComponent_GetXComponentId(component, idStr, &idSize);
|
||
if (ret != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
|
||
return;
|
||
}
|
||
std::string id(idStr);
|
||
// [EndExclude plugin_on_surface_destroyed]
|
||
auto *pluginManger = NativeManager::GetInstance();
|
||
pluginManger->OnSurfaceDestroyed(component, window);
|
||
}
|
||
//NativeManager回调
|
||
void NativeManager::OnSurfaceCreated(OH_NativeXComponent *component, void *window) {
|
||
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "XComponent_Native", "PluginManager::OnSurfaceCreated");
|
||
if (!window) {
|
||
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "XComponent_Native",
|
||
"ERROR: window is NULL in OnSurfaceCreated!");
|
||
return;
|
||
}
|
||
OH_NativeXComponent_GetXComponentSize(component, window, &width_, &height_);
|
||
int64_t id = 0;
|
||
std::lock_guard<std::mutex> lock(mapMutex);
|
||
if (renderThreadMap.find(id) == renderThreadMap.end()) {
|
||
auto renderThread = std::make_shared<NativeRenderThread>();
|
||
renderThreadMap[id] = renderThread;
|
||
renderThread->start(reinterpret_cast<OHNativeWindow*>(window));
|
||
}
|
||
}
|
||
void NativeManager::OnSurfaceDestroyed(OH_NativeXComponent *component, void *window) {
|
||
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "XComponent_Native", "PluginManager::OnSurfaceDestroyed");
|
||
int64_t id = 0;
|
||
{
|
||
std::lock_guard<std::mutex> lock(mapMutex);
|
||
if (renderThreadMap.find(id) != renderThreadMap.end()) {
|
||
renderThreadMap[id]->stop();
|
||
renderThreadMap.erase(id);
|
||
}
|
||
}
|
||
}
|
||
void NativeManager::OnSurfaceChanged(OH_NativeXComponent *component, void *window) {
|
||
OH_NativeXComponent_GetXComponentSize(component, window, &width_, &height_);
|
||
|
||
int64_t id = 0;
|
||
{
|
||
std::lock_guard<std::mutex> lock(mapMutex);
|
||
if (renderThreadMap.find(id) != renderThreadMap.end()) {
|
||
HILOG_ERROR(NATIVE_TAG,"uint64_t Size:%{public}dX%{public}d",width_,height_);
|
||
uint32_t _width=static_cast<uint32_t>(width_);
|
||
uint32_t _height=static_cast<uint32_t>(height_);
|
||
HILOG_ERROR(NATIVE_TAG,"uint32_t Size:%{public}dX%{public}d",_width,_height);
|
||
ArkUI_NumberValue comSizeData[] = {{.u32=_width}, {.u32=_height}};
|
||
ArkUI_AttributeItem comSizeItem = {comSizeData, 2};
|
||
renderThreadMap[id]->resizeWindow(width_, height_);
|
||
}
|
||
}
|
||
}
|
||
void onEvent(ArkUI_NodeEvent *event) {
|
||
auto eventType = OH_ArkUI_NodeEvent_GetEventType(event); // 获取组件事件类型
|
||
HILOG_ERROR(NATIVE_TAG,"EventType:%{public}d",eventType);
|
||
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on event");
|
||
if (eventType == NODE_TOUCH_EVENT) {
|
||
ArkUI_NodeHandle handle = OH_ArkUI_NodeEvent_GetNodeHandle(event); // 获取触发该事件的组件对象
|
||
auto holder = NativeManager::surfaceHolderMap_[handle];
|
||
EGLCore *render = reinterpret_cast<EGLCore *>(OH_ArkUI_SurfaceHolder_GetUserData(holder));
|
||
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "onBind", "on touch");
|
||
}
|
||
}
|
||
static std::string value2String(napi_env env, napi_value value) {
|
||
size_t stringSize = 0;
|
||
napi_get_value_string_utf8(env, value, nullptr, 0, &stringSize);
|
||
std::string valueString;
|
||
valueString.resize(stringSize);
|
||
napi_get_value_string_utf8(env, value, &valueString[0], stringSize + 1, &stringSize);
|
||
return valueString;
|
||
}
|
||
// XComponent回调事件
|
||
NativeManager::NativeManager() {
|
||
xSurfaceTouchEventCallBack.OnSurfaceCreated = OnSurfaceCreatedCB;
|
||
xSurfaceTouchEventCallBack.OnSurfaceChanged = OnSurfaceChangedCB;
|
||
xSurfaceTouchEventCallBack.OnSurfaceDestroyed = OnSurfaceDestroyedCB;
|
||
}
|
||
// 创建节点组件
|
||
ArkUI_NodeHandle CreateNodeHandle(const std::string &tag) {
|
||
nodeId=tag;
|
||
// 创建Node也名创建ROW Column等容器
|
||
ArkUI_NodeHandle nodeHandel = nodeAPI->createNode(ARKUI_NODE_RELATIVE_CONTAINER);
|
||
// 节点默认宽度or高度
|
||
ArkUI_NumberValue nodeMarginData[] = {{.u32 = 0}, {.f32 = 0}};
|
||
// ArkUI_AttributeItem
|
||
// 参数1:指向数值数组
|
||
// 参数2:数组元素个数
|
||
// 参数3:属性名(可隐藏)
|
||
ArkUI_AttributeItem nodeMarginItem = {nodeMarginData, 2};
|
||
// 设置Node宽度or高度
|
||
nodeAPI->setAttribute(nodeHandel, NODE_MARGIN, &nodeMarginItem);
|
||
NativeManager::nodeHandleMap_[tag]=nodeHandel;
|
||
// 创建XComponent组件
|
||
// 组件类型Item
|
||
ArkUI_NumberValue comTypeData[] = {ARKUI_XCOMPONENT_TYPE_SURFACE};
|
||
ArkUI_AttributeItem comTypeItem = {comTypeData, 1};
|
||
// 组件ID Item
|
||
comId=uuid_v4();
|
||
ArkUI_AttributeItem comIdItem = {.string = comId.c_str(), .size = 1};
|
||
// 组件Surface Size
|
||
// 创建组件
|
||
ArkUI_NodeHandle xc;
|
||
xc = nodeAPI->createNode(ARKUI_NODE_XCOMPONENT);
|
||
// 设置XComponent组件属性
|
||
nodeAPI->setAttribute(xc, NODE_XCOMPONENT_TYPE, &comTypeItem);
|
||
nodeAPI->setAttribute(xc, NODE_XCOMPONENT_ID, &comIdItem);
|
||
|
||
// 焦点设置
|
||
ArkUI_NumberValue focusable[] = {1};
|
||
focusable[0].i32 = 1;
|
||
ArkUI_AttributeItem focusableItem = {focusable, 1};
|
||
nodeAPI->setAttribute(xc, NODE_FOCUSABLE, &focusableItem);
|
||
// 节点ID
|
||
ArkUI_AttributeItem nodeIdItem = {.string = uuid_v4().c_str(), .size = 1};
|
||
nodeAPI->setAttribute(xc, NODE_ID, &nodeIdItem);
|
||
|
||
auto *nativeXComponent = OH_NativeXComponent_GetNativeXComponent(xc);
|
||
if (!nativeXComponent) {
|
||
HILOG_ERROR(NATIVE_TAG,"GetNativeXComponent error");
|
||
return nodeHandel;
|
||
}
|
||
// 注册XComponent回调函数
|
||
OH_NativeXComponent_RegisterCallback(nativeXComponent, &NativeManager::xSurfaceTouchEventCallBack);
|
||
//注册XComponent组件鼠标回调事件
|
||
OH_NativeXComponent_RegisterMouseEventCallback(nativeXComponent, &NativeManager::xMouseEventCallBack);
|
||
// 组件类型
|
||
auto comTypeRet = nodeAPI->getAttribute(xc, NODE_XCOMPONENT_TYPE);
|
||
HILOG_INFO(NATIVE_TAG,"XCom type: %{public}d",comTypeRet->value[0].i32);
|
||
// 组件ID
|
||
auto comIdRet = nodeAPI->getAttribute(xc, NODE_XCOMPONENT_ID);
|
||
HILOG_INFO(NATIVE_TAG,"XCom ID: %{public}d",comIdRet->string);
|
||
// 增加组件到节点
|
||
nodeAPI->addChild(nodeHandel, xc);
|
||
return nodeHandel;
|
||
}
|
||
// Native侧创建Node
|
||
napi_value NativeManager::createNativeNode(napi_env env, napi_callback_info info) {
|
||
if ((env == nullptr) || (info == nullptr)) {
|
||
HILOG_ERROR(NATIVE_TAG,"CreateNativeNode env or info is null");
|
||
return nullptr;
|
||
}
|
||
//获取传入NodeContent实例
|
||
size_t argCnt = 1;
|
||
napi_value args[1] = {nullptr};
|
||
if (napi_get_cb_info(env, info, &argCnt, args, nullptr, nullptr) != napi_ok) {
|
||
HILOG_ERROR(NATIVE_TAG,"CreateNativeNode napi_get_cb_info failed");
|
||
}
|
||
ArkUI_NodeContentHandle _nodeContentHandle = nullptr;
|
||
// 获取ArkTS侧创建的NodeContent对象,映射到Native侧的
|
||
OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &_nodeContentHandle);
|
||
// 查询指定的模块接口名
|
||
nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1 *>(OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1"));
|
||
//下面的代码主要用于创建一个Native侧和arkui侧绑定数据的结构传递传输的操作
|
||
//node_data可以理解为一个结构体等等之类的指针
|
||
// 生成节点名采用NODE_ID_+UUID
|
||
std::string node_data = uuid_v4();
|
||
HILOG_INFO(NATIVE_TAG,"NODE_DATA:%{public}d",node_data.c_str());
|
||
// 在NodeContent对象上保存自定义数据。
|
||
int32_t ret = OH_ArkUI_NodeContent_SetUserData(_nodeContentHandle, new std::string(node_data));
|
||
if (ret != ARKUI_ERROR_CODE_NO_ERROR) {
|
||
HILOG_ERROR(NATIVE_TAG,"setUserData failed error=%{public}d",ret);
|
||
}
|
||
if (nodeAPI != nullptr && nodeAPI->createNode != nullptr && nodeAPI->addChild != nullptr) {
|
||
auto nodeContentEvent = [](ArkUI_NodeContentEvent *event) {
|
||
// 获取Node连接事件的对应Node的Handle
|
||
ArkUI_NodeContentHandle handle = OH_ArkUI_NodeContentEvent_GetNodeContentHandle(event);
|
||
// 获取对应Handle的UserData
|
||
std::string *userDate = reinterpret_cast<std::string *>(OH_ArkUI_NodeContent_GetUserData(handle));
|
||
if (OH_ArkUI_NodeContentEvent_GetEventType(event) == NODE_CONTENT_EVENT_ON_ATTACH_TO_WINDOW) {
|
||
ArkUI_NodeHandle testNode;
|
||
if (userDate) {
|
||
testNode = CreateNodeHandle(*userDate);
|
||
delete userDate;
|
||
userDate = nullptr;
|
||
} else {
|
||
testNode = CreateNodeHandle("noUserData");
|
||
}
|
||
// 向NodeContent中添加子组件
|
||
OH_ArkUI_NodeContent_AddNode(handle, testNode);
|
||
}
|
||
};
|
||
OH_ArkUI_NodeContent_RegisterCallback(_nodeContentHandle, nodeContentEvent);
|
||
}
|
||
return nullptr;
|
||
}
|
||
//设置期望帧率
|
||
napi_value NativeManager::SetFrameRate(napi_env env, napi_callback_info info) {
|
||
size_t argc = 4;
|
||
napi_value args[4] = {nullptr};
|
||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||
std::string nodeId = value2String(env, args[0]);
|
||
auto node = nodeHandleMap_[nodeId];
|
||
|
||
int32_t min = 0;
|
||
napi_get_value_int32(env, args[FIRST_ARG], &min);
|
||
|
||
int32_t max = 0;
|
||
napi_get_value_int32(env, args[SECOND_ARG], &max);
|
||
|
||
int32_t expected = 0;
|
||
napi_get_value_int32(env, args[THIRD_ARG], &expected);
|
||
OH_NativeXComponent_ExpectedRateRange range = {.min = min, .max = max, .expected = expected};
|
||
OH_ArkUI_XComponent_SetExpectedFrameRateRange(node, range); // 设置期望帧率
|
||
return nullptr;
|
||
}
|
||
napi_value NativeManager::SetNeedSoftKeyboard(napi_env env, napi_callback_info info) {
|
||
size_t argc = 2;
|
||
napi_value args[2] = {nullptr};
|
||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||
std::string nodeId = value2String(env, args[0]);
|
||
ArkUI_NodeHandle node;
|
||
if (nodeHandleMap_.find(nodeId) == nodeHandleMap_.end()) {
|
||
OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "SetNeedSoftKeyboard", "nodeId not exit error");
|
||
return nullptr;
|
||
}
|
||
node = nodeHandleMap_[nodeId];
|
||
|
||
bool needSoftKeyboard = false;
|
||
napi_get_value_bool(env, args[1], &needSoftKeyboard);
|
||
OH_ArkUI_XComponent_SetNeedSoftKeyboard(node, needSoftKeyboard); // 设置是否需要软键盘
|
||
return nullptr;
|
||
}
|
||
napi_value NativeManager::NapiLoadModel(napi_env env, napi_callback_info info) {
|
||
size_t argc = 1;
|
||
napi_value args[1];
|
||
napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
|
||
std::string path = value2String(env, args[0]);
|
||
int64_t id = 0;
|
||
{
|
||
std::lock_guard<std::mutex> lock(mapMutex);
|
||
if (renderThreadMap.find(id) != renderThreadMap.end()) {
|
||
renderThreadMap[id]->loadModel(std::string(path));
|
||
}
|
||
}
|
||
return nullptr;
|
||
}
|
||
} // namespace NativeOpenCAX
|