* Copyright (c) 2026 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 "touch_controller_impl.h"
#include "define_multimodal.h"
#include "mmi_log.h"
#include "multimodal_input_connect_manager.h"
#include "util.h"
#undef MMI_LOG_TAG
#define MMI_LOG_TAG "TouchControllerImpl"
namespace OHOS {
namespace MMI {
namespace {
constexpr int32_t ERROR_CODE_TOUCH_SEQUENCE_ERROR = 4300001;
constexpr int32_t TOUCH_TOOL_TYPE_FINGER = PointerEvent::TOOL_TYPE_FINGER;
constexpr int32_t INVALID_DEVICE_ID = -1;
constexpr int32_t MIN_TOUCH_ID = 0;
constexpr int32_t MAX_TOUCH_ID = 9;
}
TouchControllerImpl::TouchControllerImpl()
{
MMI_HILOGD("TouchControllerImpl created");
}
TouchControllerImpl::~TouchControllerImpl()
{
MMI_HILOGD("TouchControllerImpl destroyed");
}
bool TouchControllerImpl::IsTouchIdValid(int32_t touchId) const
{
return touchId >= MIN_TOUCH_ID && touchId <= MAX_TOUCH_ID;
}
std::shared_ptr<PointerEvent> TouchControllerImpl::CreatePointerEvent(const PointerEventContext &context,
const std::map<int32_t, TouchContactState> &contacts)
{
auto pointerEvent = PointerEvent::Create();
if (pointerEvent == nullptr) {
MMI_HILOGE("Failed to create PointerEvent");
return nullptr;
}
pointerEvent->SetPointerAction(context.action);
pointerEvent->SetSourceType(PointerEvent::SOURCE_TYPE_TOUCHSCREEN);
pointerEvent->SetPointerId(context.touchId);
pointerEvent->SetDeviceId(INVALID_DEVICE_ID);
pointerEvent->SetTargetDisplayId(context.displayId);
pointerEvent->SetActionTime(context.actionTime);
if (context.action == PointerEvent::POINTER_ACTION_DOWN && contacts.size() == 1) {
pointerEvent->SetActionStartTime(context.actionTime);
}
pointerEvent->AddFlag(InputEvent::EVENT_FLAG_SIMULATE);
pointerEvent->AddFlag(InputEvent::EVENT_FLAG_CONTROLLER);
pointerEvent->RemoveAllPointerItems();
AddPointerItems(pointerEvent, context, contacts);
return pointerEvent;
}
void TouchControllerImpl::AddPointerItems(const std::shared_ptr<PointerEvent> &pointerEvent,
const PointerEventContext &context, const std::map<int32_t, TouchContactState> &contacts) const
{
for (const auto &itemPair : contacts) {
const auto &contact = itemPair.second;
PointerEvent::PointerItem item;
item.SetPointerId(itemPair.first);
item.SetOriginPointerId(itemPair.first);
item.SetDisplayX(contact.displayX);
item.SetDisplayY(contact.displayY);
item.SetDisplayXPos(contact.displayX);
item.SetDisplayYPos(contact.displayY);
item.SetRawDisplayX(contact.displayX);
item.SetRawDisplayY(contact.displayY);
item.SetToolType(TOUCH_TOOL_TYPE_FINGER);
item.SetDeviceId(INVALID_DEVICE_ID);
item.SetDownTime(contact.downTime);
item.SetPressed(itemPair.first == context.touchId ? context.currentPressed : true);
pointerEvent->AddPointerItem(item);
}
}
int32_t TouchControllerImpl::InjectPointerEvent(const std::shared_ptr<PointerEvent> &event)
{
if (event == nullptr) {
MMI_HILOGE("PointerEvent is nullptr");
return RET_ERR;
}
CHKPR(MULTIMODAL_INPUT_CONNECT_MGR, RET_ERR);
int32_t ret = MULTIMODAL_INPUT_CONNECT_MGR->InjectPointerEvent(event, false, PointerEvent::DISPLAY_COORDINATE);
if (ret != RET_OK) {
MMI_HILOGE("InjectPointerEvent failed, ret=%{public}d", ret);
}
return ret;
}
std::shared_ptr<PointerEvent> TouchControllerImpl::BuildTouchDownEvent(int32_t touchId, int32_t displayId,
int32_t displayX, int32_t displayY, int64_t actionTime)
{
std::lock_guard<std::mutex> lock(activePointsMutex_);
if (activePoints_.count(touchId) != 0) {
MMI_HILOGE("Touch id %{public}d already pressed", touchId);
return nullptr;
}
if (!activePoints_.empty() && activeDisplayId_ != displayId) {
MMI_HILOGE("Touch display mismatch, activeDisplayId=%{public}d, input=%{public}d",
activeDisplayId_, displayId);
return nullptr;
}
std::map<int32_t, TouchContactState> contacts { activePoints_ };
contacts[touchId] = { displayId, displayX, displayY, actionTime };
PointerEventContext context { PointerEvent::POINTER_ACTION_DOWN, touchId, displayId, actionTime, true };
return CreatePointerEvent(context, contacts);
}
std::shared_ptr<PointerEvent> TouchControllerImpl::BuildTouchMoveEvent(int32_t touchId, int32_t displayId,
int32_t displayX, int32_t displayY, int64_t actionTime)
{
std::lock_guard<std::mutex> lock(activePointsMutex_);
auto it = activePoints_.find(touchId);
if (it == activePoints_.end()) {
MMI_HILOGE("Touch id %{public}d is not active", touchId);
return nullptr;
}
if (activeDisplayId_ != displayId) {
MMI_HILOGE("Touch display mismatch, activeDisplayId=%{public}d, input=%{public}d",
activeDisplayId_, displayId);
return nullptr;
}
std::map<int32_t, TouchContactState> contacts { activePoints_ };
contacts[touchId] = { displayId, displayX, displayY, it->second.downTime };
PointerEventContext context { PointerEvent::POINTER_ACTION_MOVE, touchId, displayId, actionTime, true };
return CreatePointerEvent(context, contacts);
}
std::shared_ptr<PointerEvent> TouchControllerImpl::BuildTouchUpEvent(int32_t touchId, int32_t displayId,
int32_t displayX, int32_t displayY, int64_t actionTime)
{
std::lock_guard<std::mutex> lock(activePointsMutex_);
auto it = activePoints_.find(touchId);
if (it == activePoints_.end()) {
MMI_HILOGE("Touch id %{public}d is not active", touchId);
return nullptr;
}
if (activeDisplayId_ != displayId) {
MMI_HILOGE("Touch display mismatch, activeDisplayId=%{public}d, input=%{public}d",
activeDisplayId_, displayId);
return nullptr;
}
std::map<int32_t, TouchContactState> contacts { activePoints_ };
contacts[touchId] = { displayId, displayX, displayY, it->second.downTime };
PointerEventContext context { PointerEvent::POINTER_ACTION_UP, touchId, displayId, actionTime, false };
return CreatePointerEvent(context, contacts);
}
void TouchControllerImpl::CommitTouchDownState(int32_t touchId, int32_t displayId, int32_t displayX,
int32_t displayY, int64_t downTime)
{
std::lock_guard<std::mutex> lock(activePointsMutex_);
activePoints_[touchId] = { displayId, displayX, displayY, downTime };
activeDisplayId_ = displayId;
}
void TouchControllerImpl::CommitTouchMoveState(int32_t touchId, int32_t displayId, int32_t displayX, int32_t displayY)
{
std::lock_guard<std::mutex> lock(activePointsMutex_);
auto it = activePoints_.find(touchId);
if (it != activePoints_.end()) {
it->second.displayId = displayId;
it->second.displayX = displayX;
it->second.displayY = displayY;
}
}
void TouchControllerImpl::ClearTouchState(int32_t touchId)
{
std::lock_guard<std::mutex> lock(activePointsMutex_);
activePoints_.erase(touchId);
if (activePoints_.empty()) {
activeDisplayId_ = -1;
}
}
int32_t TouchControllerImpl::TouchDown(int32_t touchId, int32_t displayId, int32_t displayX, int32_t displayY)
{
if (!IsTouchIdValid(touchId)) {
MMI_HILOGE("Invalid touch id %{public}d", touchId);
return ERROR_CODE_TOUCH_SEQUENCE_ERROR;
}
int64_t now = GetSysClockTime();
auto pointerEvent = BuildTouchDownEvent(touchId, displayId, displayX, displayY, now);
if (pointerEvent == nullptr) {
return ERROR_CODE_TOUCH_SEQUENCE_ERROR;
}
int32_t ret = RET_OK;
{
std::lock_guard<std::mutex> lock(injectMutex_);
ret = InjectPointerEvent(pointerEvent);
}
if (ret == RET_OK) {
CommitTouchDownState(touchId, displayId, displayX, displayY, now);
}
return ret;
}
int32_t TouchControllerImpl::TouchMove(int32_t touchId, int32_t displayId, int32_t displayX, int32_t displayY)
{
if (!IsTouchIdValid(touchId)) {
MMI_HILOGE("Invalid touch id %{public}d", touchId);
return ERROR_CODE_TOUCH_SEQUENCE_ERROR;
}
auto pointerEvent = BuildTouchMoveEvent(touchId, displayId, displayX, displayY, GetSysClockTime());
if (pointerEvent == nullptr) {
return ERROR_CODE_TOUCH_SEQUENCE_ERROR;
}
int32_t ret = RET_OK;
{
std::lock_guard<std::mutex> lock(injectMutex_);
ret = InjectPointerEvent(pointerEvent);
}
if (ret == RET_OK) {
CommitTouchMoveState(touchId, displayId, displayX, displayY);
}
return ret;
}
int32_t TouchControllerImpl::TouchUp(int32_t touchId, int32_t displayId, int32_t displayX, int32_t displayY)
{
if (!IsTouchIdValid(touchId)) {
MMI_HILOGE("Invalid touch id %{public}d", touchId);
return ERROR_CODE_TOUCH_SEQUENCE_ERROR;
}
auto pointerEvent = BuildTouchUpEvent(touchId, displayId, displayX, displayY, GetSysClockTime());
if (pointerEvent == nullptr) {
ClearTouchState(touchId);
return ERROR_CODE_TOUCH_SEQUENCE_ERROR;
}
int32_t ret = RET_OK;
{
std::lock_guard<std::mutex> lock(injectMutex_);
ret = InjectPointerEvent(pointerEvent);
}
ClearTouchState(touchId);
return ret;
}
}
}