* Copyright (c) 2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <ani.h>
#include <chrono>
#include <cstdint>
#include "frameworks/base/log/log_wrapper.h"
#include "core/event/key_event.h"
#include "core/pipeline_ng/pipeline_context.h"
namespace {
const char ANI_TEST_NS[] = "@ohos.arkui.Test.ETSGLOBAL";
}
namespace OHOS::Ace {
bool CheckIsUndefined(ani_env* env, ani_object obj)
{
ani_boolean isUndefined;
if (ANI_OK != env->Reference_IsUndefined(obj, &isUndefined)) {
return true;
}
return static_cast<bool>(isUndefined);
}
bool GetEnumByName(ani_env* env, ani_object obj, const std::string& name, int32_t& value)
{
ani_ref aniValueRef;
if (ANI_OK != env->Object_GetPropertyByName_Ref(obj, name.c_str(), &aniValueRef)) {
return false;
}
if (CheckIsUndefined(env, static_cast<ani_object>(aniValueRef))) {
return false;
}
ani_int aniValue;
if (ANI_OK != env->EnumItem_GetValue_Int(static_cast<ani_enum_item>(aniValueRef), &aniValue)) {
return false;
}
value = static_cast<int32_t>(aniValue);
return true;
}
bool GetIntByName(ani_env* env, ani_object obj, const std::string& name, int32_t& value)
{
ani_int aniValue;
if (ANI_OK != env->Object_GetPropertyByName_Int(obj, name.c_str(), &aniValue)) {
return false;
}
value = static_cast<int32_t>(aniValue);
return true;
}
bool GetFloatByName(ani_env* env, ani_object obj, const std::string& name, float& value)
{
ani_double valueDouble;
if (ANI_OK != env->Object_GetPropertyByName_Double(obj, name.c_str(), &valueDouble)) {
return false;
}
value = static_cast<float>(valueDouble);
return true;
}
bool GetLongByName(ani_env* env, ani_object obj, const std::string& name, int64_t& value)
{
ani_long aniValue;
if (ANI_OK != env->Object_GetPropertyByName_Long(obj, name.c_str(), &aniValue)) {
return false;
}
value = static_cast<int64_t>(aniValue);
return true;
}
KeyEvent GetKeyEvent(ani_env* env, ani_object obj)
{
CHECK_NULL_RETURN(env, KeyEvent());
int32_t action;
int32_t code;
if (!GetEnumByName(env, obj, "type", action)) {
return KeyEvent();
}
if (!GetIntByName(env, obj, "keyCode", code)) {
return KeyEvent();
}
KeyEvent keyEvent(static_cast<KeyCode>(code), static_cast<KeyAction>(action));
int32_t keySource;
if (GetEnumByName(env, obj, "keySource", keySource)) {
keyEvent.sourceType = static_cast<SourceType>(keySource);
}
int32_t deviceId;
if (GetIntByName(env, obj, "deviceId", deviceId)) {
keyEvent.deviceId = deviceId;
}
GetIntByName(env, obj, "metaKey", keyEvent.metaKey);
int64_t timestamp;
if (GetLongByName(env, obj, "timestamp", timestamp)) {
keyEvent.SetTimeStamp(timestamp);
}
ani_ref aniValueRef;
if (ANI_OK != env->Object_GetPropertyByName_Ref(obj, "unicode", &aniValueRef)) {
return keyEvent;
}
ani_object aniValueObject = static_cast<ani_object>(aniValueRef);
if (CheckIsUndefined(env, aniValueObject)) {
return keyEvent;
}
ani_long aniValue;
if (ANI_OK == env->Object_CallMethodByName_Long(aniValueObject, "toLong", nullptr, &aniValue)) {
keyEvent.unicode = static_cast<int64_t>(aniValue);
}
return keyEvent;
}
TouchEvent GetTouchEvent(ani_env* env, ani_object obj)
{
TouchEvent touchEvent;
CHECK_NULL_RETURN(env, touchEvent);
int32_t type;
if (GetEnumByName(env, obj, "type", type)) {
touchEvent.type = static_cast<TouchType>(type);
}
GetIntByName(env, obj, "id", touchEvent.id);
GetFloatByName(env, obj, "x", touchEvent.x);
GetFloatByName(env, obj, "y", touchEvent.y);
touchEvent.time = std::chrono::high_resolution_clock::now();
return touchEvent;
}
MouseEvent GetMouseEvent(ani_env* env, ani_object obj)
{
MouseEvent mouseEvent;
int32_t button;
if (GetEnumByName(env, obj, "button", button)) {
mouseEvent.button = static_cast<MouseButton>(button);
}
int32_t action;
if (GetEnumByName(env, obj, "action", action)) {
mouseEvent.action = static_cast<MouseAction>(action);
}
if (GetFloatByName(env, obj, "x", mouseEvent.x)) {
mouseEvent.deltaX = mouseEvent.x;
}
if (GetFloatByName(env, obj, "y", mouseEvent.y)) {
mouseEvent.deltaY = mouseEvent.y;
}
mouseEvent.time = std::chrono::high_resolution_clock::now();
mouseEvent.sourceType = SourceType::MOUSE;
return mouseEvent;
}
static ani_boolean ANI_SendTouchEvent(ani_env* env, ani_object obj)
{
if (CheckIsUndefined(env, obj)) {
return ANI_FALSE;
}
auto pipelineContext = NG::PipelineContext::GetCurrentContextSafely();
CHECK_NULL_RETURN(pipelineContext, ANI_FALSE);
auto taskExecutor = pipelineContext->GetTaskExecutor();
CHECK_NULL_RETURN(taskExecutor, ANI_FALSE);
auto event = GetTouchEvent(env, obj);
auto result = taskExecutor->PostTask(
[pipelineContext, event]() { pipelineContext->OnTouchEvent(event); },
TaskExecutor::TaskType::UI, "ArkUIAniSendTouchEvent");
return static_cast<ani_boolean>(result);
}
static ani_boolean ANI_SendKeyEvent(ani_env* env, ani_object obj)
{
if (CheckIsUndefined(env, obj)) {
return ANI_FALSE;
}
auto pipelineContext = NG::PipelineContext::GetCurrentContextSafely();
CHECK_NULL_RETURN(pipelineContext, ANI_FALSE);
auto taskExecutor = pipelineContext->GetTaskExecutor();
CHECK_NULL_RETURN(taskExecutor, ANI_FALSE);
auto event = GetKeyEvent(env, obj);
auto result = taskExecutor->PostTask(
[pipelineContext, event]() { pipelineContext->OnNonPointerEvent(event); },
TaskExecutor::TaskType::UI, "ArkUIAniSendKeyEvent");
return static_cast<ani_boolean>(result);
}
static ani_boolean ANI_SendMouseEvent(ani_env* env, ani_object obj)
{
if (CheckIsUndefined(env, obj)) {
return ANI_FALSE;
}
auto pipelineContext = NG::PipelineContext::GetCurrentContextSafely();
CHECK_NULL_RETURN(pipelineContext, ANI_FALSE);
auto taskExecutor = pipelineContext->GetTaskExecutor();
CHECK_NULL_RETURN(taskExecutor, ANI_FALSE);
auto event = GetMouseEvent(env, obj);
auto result = taskExecutor->PostTask(
[pipelineContext, event]() { pipelineContext->OnMouseEvent(event); },
TaskExecutor::TaskType::UI, "ArkUIAniSendMouseEvent");
return static_cast<ani_boolean>(result);
}
}
ANI_EXPORT ani_status ANI_Constructor(ani_vm* vm, uint32_t* result)
{
ani_env* env;
if (ANI_OK != vm->GetEnv(ANI_VERSION_1, &env)) {
return ANI_ERROR;
}
ani_namespace ns;
if (ANI_OK != env->FindNamespace(ANI_TEST_NS, &ns)) {
return ANI_ERROR;
}
std::array methods = {
ani_native_function { "sendTouchEvent", nullptr, reinterpret_cast<void*>(OHOS::Ace::ANI_SendTouchEvent) },
ani_native_function { "sendKeyEvent", nullptr, reinterpret_cast<void*>(OHOS::Ace::ANI_SendKeyEvent) },
ani_native_function { "sendMouseEvent", nullptr, reinterpret_cast<void*>(OHOS::Ace::ANI_SendMouseEvent) },
};
if (ANI_OK != env->Namespace_BindNativeFunctions(ns, methods.data(), methods.size())) {
TAG_LOGE(OHOS::Ace::AceLogTag::ACE_GESTURE, "ANI BindNativeFunctions failed!");
return ANI_ERROR;
}
*result = ANI_VERSION_1;
return ANI_OK;
}