* Copyright (c) 2024-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 "interaction_impl.h"
#include <algorithm>
#include <atomic>
#include <cmath>
#include <deque>
#include <mutex>
#include <unordered_map>
#include "display_manager.h"
#include "adapter/ios/stage/uicontent/ace_container_sg.h"
#include "base/log/log.h"
#include "core/common/container.h"
#include "core/common/interaction/interaction_data.h"
#include "core/gestures/gesture_info.h"
#include "core/pipeline_ng/pipeline_context.h"
#if defined(ENABLE_DRAG_FRAMEWORK)
#include "drag_data.h"
#include "interaction_manager.h"
#endif
#if defined(ENABLE_DRAG_FRAMEWORK)
using namespace OHOS::Msdp::DeviceStatus;
#endif
namespace OHOS::Ace {
#if defined(ENABLE_DRAG_FRAMEWORK)
Msdp::DeviceStatus::DragCursorStyle TranslateDragCursorStyle(OHOS::Ace::DragCursorStyleCore style);
Msdp::DeviceStatus::DragResult TranslateDragResult(DragRet dragResult);
DragRet TranslateDragResult(Msdp::DeviceStatus::DragResult dragResult);
Msdp::DeviceStatus::DragBehavior TranslateDragBehavior(OHOS::Ace::DragBehavior dragBehavior);
OHOS::Ace::DragBehavior TranslateDragBehavior(Msdp::DeviceStatus::DragBehavior dragBehavior);
bool windowCreated_ = false;
std::function<void(const OHOS::Ace::DragNotifyMsg&)> callback_;
constexpr uint64_t INVALID_SYNTHETIC_DRAG_SESSION_ID = 0;
constexpr size_t SYNTHETIC_EVENT_SESSION_CACHE_LIMIT = 64;
constexpr uint32_t SYNTHETIC_POINTER_ID_HASH_SHIFT_BITS = 32;
struct SyntheticPointerDispatchKey {
int32_t pointerId = -1;
int64_t actionTime = 0;
bool operator==(const SyntheticPointerDispatchKey& other) const
{
return pointerId == other.pointerId && actionTime == other.actionTime;
}
};
struct SyntheticPointerDispatchKeyHash {
size_t operator()(const SyntheticPointerDispatchKey& key) const
{
return (static_cast<size_t>(static_cast<uint32_t>(key.pointerId)) <<
SYNTHETIC_POINTER_ID_HASH_SHIFT_BITS) ^
static_cast<size_t>(key.actionTime);
}
};
struct PendingSyntheticDragContext {
uint64_t sessionId = INVALID_SYNTHETIC_DRAG_SESSION_ID;
};
struct SyntheticDragCompensationState {
bool valid = false;
uint64_t sessionId = INVALID_SYNTHETIC_DRAG_SESSION_ID;
int32_t pointerId = -1;
int32_t windowWidth = 0;
int32_t windowHeight = 0;
int32_t shadowWidth = 0;
int32_t shadowHeight = 0;
int32_t hotspotX = -1;
int32_t hotspotY = -1;
Rotation rotation = Rotation::ROTATION_0;
};
struct SyntheticDragRuntimeState {
std::atomic<int32_t> syntheticDragPointerId { -1 };
std::atomic<bool> syntheticDragActive { false };
std::atomic<uint64_t> syntheticDragSessionGenerator { 1 };
std::atomic<uint64_t> activeSyntheticDragSessionId { 0 };
std::mutex syntheticDragCompensationMutex;
std::mutex syntheticDragSessionMutex;
std::unordered_map<SyntheticPointerDispatchKey, PendingSyntheticDragContext, SyntheticPointerDispatchKeyHash>
pendingSyntheticDragContexts;
std::deque<SyntheticPointerDispatchKey> pendingSyntheticDragOrder;
std::unordered_map<int32_t, uint64_t> syntheticPointerSessionBindings;
std::unordered_map<SyntheticPointerDispatchKey, uint64_t, SyntheticPointerDispatchKeyHash>
syntheticPointerDispatchMap;
std::deque<SyntheticPointerDispatchKey> syntheticPointerDispatchOrder;
SyntheticDragCompensationState syntheticDragCompensationState;
void ResetCompensationStateLocked()
{
syntheticDragCompensationState = {};
}
void ResetPendingContextsLocked()
{
pendingSyntheticDragContexts.clear();
pendingSyntheticDragOrder.clear();
}
void ResetPendingContextsLocked(int32_t pointerId)
{
if (pointerId < 0) {
return;
}
for (auto iter = pendingSyntheticDragContexts.begin(); iter != pendingSyntheticDragContexts.end();) {
if (iter->first.pointerId == pointerId) {
iter = pendingSyntheticDragContexts.erase(iter);
} else {
++iter;
}
}
pendingSyntheticDragOrder.erase(
std::remove_if(pendingSyntheticDragOrder.begin(), pendingSyntheticDragOrder.end(),
[pointerId](const SyntheticPointerDispatchKey& key) { return key.pointerId == pointerId; }),
pendingSyntheticDragOrder.end());
}
void RegisterPendingContextLocked(int32_t pointerId, int64_t actionTime, uint64_t sessionId)
{
if (pointerId < 0 || actionTime <= 0 || sessionId == INVALID_SYNTHETIC_DRAG_SESSION_ID) {
return;
}
SyntheticPointerDispatchKey key { pointerId, actionTime };
if (pendingSyntheticDragContexts.find(key) == pendingSyntheticDragContexts.end()) {
pendingSyntheticDragOrder.push_back(key);
}
pendingSyntheticDragContexts[key] = { sessionId };
while (pendingSyntheticDragOrder.size() > SYNTHETIC_EVENT_SESSION_CACHE_LIMIT) {
const auto oldest = pendingSyntheticDragOrder.front();
pendingSyntheticDragOrder.pop_front();
pendingSyntheticDragContexts.erase(oldest);
}
}
uint64_t FindLatestPendingSessionIdLocked(int32_t pointerId, SyntheticPointerDispatchKey* matchedKey)
{
if (pointerId < 0) {
return INVALID_SYNTHETIC_DRAG_SESSION_ID;
}
for (auto iter = pendingSyntheticDragOrder.rbegin(); iter != pendingSyntheticDragOrder.rend(); ++iter) {
if (iter->pointerId != pointerId) {
continue;
}
auto pendingIter = pendingSyntheticDragContexts.find(*iter);
if (pendingIter == pendingSyntheticDragContexts.end()) {
continue;
}
if (matchedKey != nullptr) {
*matchedKey = pendingIter->first;
}
return pendingIter->second.sessionId;
}
return INVALID_SYNTHETIC_DRAG_SESSION_ID;
}
void ConsumePendingContextLocked(const SyntheticPointerDispatchKey& key)
{
pendingSyntheticDragContexts.erase(key);
pendingSyntheticDragOrder.erase(
std::remove(pendingSyntheticDragOrder.begin(), pendingSyntheticDragOrder.end(), key),
pendingSyntheticDragOrder.end());
}
void RegisterDispatchLocked(int32_t pointerId, int64_t actionTime, uint64_t sessionId)
{
if (pointerId < 0 || actionTime <= 0 || sessionId == INVALID_SYNTHETIC_DRAG_SESSION_ID) {
return;
}
SyntheticPointerDispatchKey key { pointerId, actionTime };
if (syntheticPointerDispatchMap.find(key) == syntheticPointerDispatchMap.end()) {
syntheticPointerDispatchOrder.push_back(key);
}
syntheticPointerDispatchMap[key] = sessionId;
while (syntheticPointerDispatchOrder.size() > SYNTHETIC_EVENT_SESSION_CACHE_LIMIT) {
const auto oldest = syntheticPointerDispatchOrder.front();
syntheticPointerDispatchOrder.pop_front();
syntheticPointerDispatchMap.erase(oldest);
}
}
enum class SyntheticSessionResolveSource {
INVALID,
DISPATCH,
POINTER_BINDING,
PENDING_CONTEXT,
};
std::pair<uint64_t, SyntheticSessionResolveSource> ResolveSessionIdWithSourceLocked(
int32_t pointerId, int64_t actionTime)
{
if (pointerId < 0) {
return { INVALID_SYNTHETIC_DRAG_SESSION_ID, SyntheticSessionResolveSource::INVALID };
}
if (actionTime > 0) {
SyntheticPointerDispatchKey key { pointerId, actionTime };
auto keyIter = syntheticPointerDispatchMap.find(key);
if (keyIter != syntheticPointerDispatchMap.end()) {
return { keyIter->second, SyntheticSessionResolveSource::DISPATCH };
}
}
auto bindingIter = syntheticPointerSessionBindings.find(pointerId);
if (bindingIter != syntheticPointerSessionBindings.end()) {
return { bindingIter->second, SyntheticSessionResolveSource::POINTER_BINDING };
}
const auto pendingSessionId = FindLatestPendingSessionIdLocked(pointerId, nullptr);
if (pendingSessionId != INVALID_SYNTHETIC_DRAG_SESSION_ID) {
return { pendingSessionId, SyntheticSessionResolveSource::PENDING_CONTEXT };
}
return { INVALID_SYNTHETIC_DRAG_SESSION_ID, SyntheticSessionResolveSource::INVALID };
}
};
SyntheticDragRuntimeState& GetSyntheticDragRuntimeState()
{
static SyntheticDragRuntimeState state;
return state;
}
Rotation GetSyntheticDragRotation()
{
auto container = Container::Current();
CHECK_NULL_RETURN(container, Rotation::ROTATION_0);
auto displayInfo = container->GetDisplayInfo();
CHECK_NULL_RETURN(displayInfo, Rotation::ROTATION_0);
return displayInfo->GetRotation();
}
void RegisterSyntheticPointerDispatchLocked(int32_t pointerId, int64_t actionTime, uint64_t sessionId)
{
auto& state = GetSyntheticDragRuntimeState();
if (pointerId < 0 || actionTime <= 0 || sessionId == INVALID_SYNTHETIC_DRAG_SESSION_ID) {
return;
}
state.RegisterDispatchLocked(pointerId, actionTime, sessionId);
}
uint64_t ResolveSyntheticDragSessionIdLocked(int32_t pointerId, int64_t actionTime)
{
auto& state = GetSyntheticDragRuntimeState();
const auto sessionId = state.ResolveSessionIdWithSourceLocked(pointerId, actionTime).first;
if (sessionId == INVALID_SYNTHETIC_DRAG_SESSION_ID) {
LOGW("[UITEST_IOS_DRAGFIX] resolve synthetic session missed pointerId=%{public}d actionTime=%{public}" PRId64,
pointerId, actionTime);
}
return sessionId;
}
uint64_t MatchAndConsumeSyntheticDragSessionId(const DragDataCore& dragData, bool isSyntheticDrag)
{
auto& state = GetSyntheticDragRuntimeState();
if (!isSyntheticDrag) {
return INVALID_SYNTHETIC_DRAG_SESSION_ID;
}
std::lock_guard<std::mutex> lock(state.syntheticDragSessionMutex);
SyntheticPointerDispatchKey matchedKey;
const auto sessionId = state.FindLatestPendingSessionIdLocked(dragData.pointerId, &matchedKey);
if (sessionId != INVALID_SYNTHETIC_DRAG_SESSION_ID) {
state.ConsumePendingContextLocked(matchedKey);
return sessionId;
}
auto bindingIter = state.syntheticPointerSessionBindings.find(dragData.pointerId);
if (bindingIter != state.syntheticPointerSessionBindings.end()) {
return bindingIter->second;
}
return INVALID_SYNTHETIC_DRAG_SESSION_ID;
}
void UpdateSyntheticDragCompensationStateLocked(const DragDataCore& dragData,
const std::shared_ptr<OHOS::Rosen::Window>& dragWindow, int32_t width, int32_t height, uint64_t sessionId)
{
auto& compensationState = GetSyntheticDragRuntimeState().syntheticDragCompensationState;
compensationState.valid = true;
compensationState.sessionId = sessionId;
compensationState.pointerId = dragData.pointerId;
compensationState.windowWidth = width;
compensationState.windowHeight = height;
if (dragWindow) {
const auto rect = dragWindow->GetRect();
if (rect.width_ > 0) {
compensationState.windowWidth = rect.width_;
}
if (rect.height_ > 0) {
compensationState.windowHeight = rect.height_;
}
}
compensationState.shadowWidth = 0;
compensationState.shadowHeight = 0;
compensationState.hotspotX = -1;
compensationState.hotspotY = -1;
if (!dragData.shadowInfos.empty() && dragData.shadowInfos.front().pixelMap) {
compensationState.shadowWidth =
dragData.shadowInfos.front().pixelMap->GetWidth();
compensationState.shadowHeight =
dragData.shadowInfos.front().pixelMap->GetHeight();
compensationState.hotspotX = -dragData.shadowInfos.front().x;
compensationState.hotspotY = -dragData.shadowInfos.front().y;
}
compensationState.rotation = GetSyntheticDragRotation();
}
struct SyntheticCompensatedPoint {
int32_t displayX = 0;
int32_t displayY = 0;
};
SyntheticCompensatedPoint CompensateSyntheticDragPointWithHotspotLocked(int32_t displayX, int32_t displayY)
{
const auto& compensationState = GetSyntheticDragRuntimeState().syntheticDragCompensationState;
float compensatedX = static_cast<float>(displayX);
float compensatedY = static_cast<float>(displayY);
const auto hotspotX = static_cast<float>(compensationState.hotspotX);
const auto hotspotY = static_cast<float>(compensationState.hotspotY);
const auto shadowWidth = static_cast<float>(compensationState.shadowWidth);
const auto shadowHeight = static_cast<float>(compensationState.shadowHeight);
switch (compensationState.rotation) {
case Rotation::ROTATION_0:
break;
case Rotation::ROTATION_90:
compensatedX = static_cast<float>(compensationState.windowWidth) -
static_cast<float>(displayY) - hotspotY;
compensatedY = static_cast<float>(displayX) - hotspotX;
break;
case Rotation::ROTATION_180:
compensatedX = static_cast<float>(compensationState.windowWidth) -
static_cast<float>(displayX) - (shadowWidth - 2.0f * hotspotX);
compensatedY = static_cast<float>(compensationState.windowHeight) -
static_cast<float>(displayY) - (shadowHeight - 2.0f * hotspotY);
break;
case Rotation::ROTATION_270:
compensatedX = static_cast<float>(displayY) + hotspotY;
compensatedY = static_cast<float>(compensationState.windowHeight) -
static_cast<float>(displayX) - hotspotX;
break;
default:
break;
}
return { static_cast<int32_t>(std::lround(compensatedX)), static_cast<int32_t>(std::lround(compensatedY)) };
}
SyntheticCompensatedPoint CompensateSyntheticDragPointLocked(int32_t displayX, int32_t displayY, int32_t pointerId)
{
const auto& compensationState = GetSyntheticDragRuntimeState().syntheticDragCompensationState;
if (!compensationState.valid || compensationState.pointerId != pointerId) {
return { displayX, displayY };
}
const bool hasHotspot = compensationState.shadowWidth > 0 &&
compensationState.shadowHeight > 0 &&
compensationState.hotspotX >= 0 &&
compensationState.hotspotY >= 0;
if (!hasHotspot) {
LOGW("[UITEST_IOS_DRAGFIX] fallback synthetic compensation without hotspot "
"pointerId=%{public}d rotation=%{public}d shadow=(%{public}d,%{public}d) "
"hotspot=(%{public}d,%{public}d) display=(%{public}d,%{public}d)",
pointerId, static_cast<int32_t>(compensationState.rotation),
compensationState.shadowWidth, compensationState.shadowHeight,
compensationState.hotspotX, compensationState.hotspotY,
displayX, displayY);
return { displayX, displayY };
}
return CompensateSyntheticDragPointWithHotspotLocked(displayX, displayY);
}
std::shared_ptr<MMI::PointerEvent> CreateSyntheticCompensatedPointerEvent(
const std::shared_ptr<MMI::PointerEvent>& pointerEvent, uint64_t sessionId)
{
CHECK_NULL_RETURN(pointerEvent, nullptr);
auto& state = GetSyntheticDragRuntimeState();
std::lock_guard<std::mutex> lock(state.syntheticDragCompensationMutex);
if (!state.syntheticDragCompensationState.valid ||
state.syntheticDragCompensationState.sessionId != sessionId ||
state.syntheticDragCompensationState.pointerId != pointerEvent->GetPointerId()) {
return pointerEvent;
}
auto adjustedEvent = MMI::PointerEvent::Create();
CHECK_NULL_RETURN(adjustedEvent, pointerEvent);
adjustedEvent->SetPointerId(pointerEvent->GetPointerId());
adjustedEvent->SetDeviceId(pointerEvent->GetDeviceId());
adjustedEvent->SetTargetDisplayId(pointerEvent->GetTargetDisplayId());
adjustedEvent->SetActionTime(pointerEvent->GetActionTime());
adjustedEvent->SetFingerCount(pointerEvent->GetFingerCount());
adjustedEvent->SetPointerAction(pointerEvent->GetPointerAction());
adjustedEvent->SetSourceType(pointerEvent->GetSourceType());
adjustedEvent->SetPullId(pointerEvent->GetPullId());
auto pointerIds = pointerEvent->GetPointerIds();
for (const auto id : pointerIds) {
MMI::PointerEvent::PointerItem pointerItem;
if (!pointerEvent->GetPointerItem(id, pointerItem)) {
return pointerEvent;
}
if (id == pointerEvent->GetPointerId()) {
const auto originalDisplayX = pointerItem.GetDisplayX();
const auto originalDisplayY = pointerItem.GetDisplayY();
const auto compensatedPoint = CompensateSyntheticDragPointLocked(originalDisplayX, originalDisplayY, id);
pointerItem.SetDisplayX(compensatedPoint.displayX);
pointerItem.SetDisplayY(compensatedPoint.displayY);
pointerItem.SetWindowX(pointerItem.GetWindowX() + (compensatedPoint.displayX - originalDisplayX));
pointerItem.SetWindowY(pointerItem.GetWindowY() + (compensatedPoint.displayY - originalDisplayY));
}
adjustedEvent->AddPointerItem(pointerItem);
}
return adjustedEvent;
}
namespace {
struct SyntheticDragHeightContext {
int32_t rootHeight = 0;
int32_t displayHeight = 0;
int32_t coordinateHeight = 0;
};
SyntheticDragHeightContext GetSyntheticDragHeightContext()
{
SyntheticDragHeightContext context;
context.rootHeight = static_cast<int32_t>(NG::PipelineContext::GetCurrentRootHeight());
auto defaultDisplay = Rosen::DisplayManager::GetInstance().GetDefaultDisplaySync();
if (defaultDisplay) {
auto defaultDisplayInfo = defaultDisplay->GetDisplayInfo();
if (defaultDisplayInfo) {
context.displayHeight = defaultDisplayInfo->GetHeight();
}
}
context.coordinateHeight = context.rootHeight > 0 ? context.rootHeight : context.displayHeight;
return context;
}
}
Msdp::DeviceStatus::DragData CreateBaseMsdpDragData(const DragDataCore& dragData)
{
Msdp::DeviceStatus::DragData msdpDragData { {}, dragData.buffer, dragData.udKey, dragData.extraInfo,
dragData.filterInfo, dragData.sourceType, dragData.dragNum, dragData.pointerId,
dragData.displayX, dragData.displayY, dragData.displayId, dragData.mainWindow, dragData.hasCanceledAnimation,
dragData.hasCoordinateCorrected, dragData.summarys };
for (auto& shadowInfo : dragData.shadowInfos) {
if (shadowInfo.pixelMap) {
msdpDragData.shadowInfos.push_back(
{ shadowInfo.pixelMap->GetPixelMapSharedPtr(), shadowInfo.x, shadowInfo.y });
} else {
msdpDragData.shadowInfos.push_back({ nullptr, shadowInfo.x, shadowInfo.y });
}
}
return msdpDragData;
}
void StartManualDragWindow(const DragDataCore& dragData)
{
auto msdpDragData = CreateBaseMsdpDragData(dragData);
InteractionManager::GetInstance()->StartDrag(msdpDragData);
}
void StartSyntheticDragWindow(const DragDataCore& dragData,
const std::shared_ptr<OHOS::Rosen::Window>& dragWindow, int32_t width, int32_t height, uint64_t sessionId)
{
auto& state = GetSyntheticDragRuntimeState();
InitializeSyntheticDragCompensation(dragData, dragWindow, width, height, sessionId);
int32_t adjustedDisplayX = dragData.displayX;
int32_t adjustedDisplayY = dragData.displayY;
{
std::lock_guard<std::mutex> lock(state.syntheticDragCompensationMutex);
const auto compensatedPoint =
CompensateSyntheticDragPointLocked(dragData.displayX, dragData.displayY, dragData.pointerId);
adjustedDisplayX = compensatedPoint.displayX;
adjustedDisplayY = compensatedPoint.displayY;
}
auto msdpDragData = CreateBaseMsdpDragData(dragData);
msdpDragData.displayX = adjustedDisplayX;
msdpDragData.displayY = adjustedDisplayY;
const auto heightContext = GetSyntheticDragHeightContext();
const auto shadowCount = static_cast<int32_t>(dragData.shadowInfos.size());
const auto firstShadowX = shadowCount > 0 ? dragData.shadowInfos.front().x : 0;
const auto firstShadowY = shadowCount > 0 ? dragData.shadowInfos.front().y : 0;
InteractionManager::GetInstance()->StartDrag(msdpDragData);
}
#endif
void UpdateSyntheticDragTouchState(int32_t pointerId, bool active)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
auto& state = GetSyntheticDragRuntimeState();
if (active) {
state.syntheticDragPointerId.store(pointerId, std::memory_order_relaxed);
state.syntheticDragActive.store(true, std::memory_order_release);
return;
}
const auto currentPointerId = state.syntheticDragPointerId.load(std::memory_order_acquire);
if (pointerId < 0 || currentPointerId == pointerId) {
state.syntheticDragActive.store(false, std::memory_order_release);
}
#else
(void)pointerId;
(void)active;
#endif
}
void CompleteSyntheticDragTouchState(int32_t pointerId, bool active)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
auto& state = GetSyntheticDragRuntimeState();
if (active) {
return;
}
const auto currentPointerId = state.syntheticDragPointerId.load(std::memory_order_relaxed);
if (pointerId >= 0 && currentPointerId >= 0 && currentPointerId != pointerId) {
return;
}
{
std::scoped_lock lock(state.syntheticDragSessionMutex, state.syntheticDragCompensationMutex);
state.syntheticPointerSessionBindings.erase(currentPointerId);
if (pointerId >= 0 && pointerId != currentPointerId) {
state.syntheticPointerSessionBindings.erase(pointerId);
}
state.ResetPendingContextsLocked(currentPointerId);
if (pointerId >= 0 && pointerId != currentPointerId) {
state.ResetPendingContextsLocked(pointerId);
}
state.ResetCompensationStateLocked();
state.activeSyntheticDragSessionId.store(INVALID_SYNTHETIC_DRAG_SESSION_ID, std::memory_order_relaxed);
state.syntheticDragPointerId.store(-1, std::memory_order_relaxed);
}
#else
(void)pointerId;
(void)active;
#endif
}
void PrepareSyntheticDragCompensationContext(int32_t pointerId, bool active, bool isStart, int64_t actionTime)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
auto& state = GetSyntheticDragRuntimeState();
std::lock_guard<std::mutex> lock(state.syntheticDragSessionMutex);
if (pointerId < 0) {
return;
}
auto bindingIter = state.syntheticPointerSessionBindings.find(pointerId);
uint64_t sessionId = bindingIter != state.syntheticPointerSessionBindings.end() ?
bindingIter->second : INVALID_SYNTHETIC_DRAG_SESSION_ID;
if (active && (isStart || sessionId == INVALID_SYNTHETIC_DRAG_SESSION_ID)) {
sessionId = state.syntheticDragSessionGenerator.fetch_add(1, std::memory_order_relaxed);
state.syntheticPointerSessionBindings[pointerId] = sessionId;
state.RegisterPendingContextLocked(pointerId, actionTime, sessionId);
} else if (active && sessionId != INVALID_SYNTHETIC_DRAG_SESSION_ID) {
state.RegisterPendingContextLocked(pointerId, actionTime, sessionId);
} else if (!active && sessionId == INVALID_SYNTHETIC_DRAG_SESSION_ID) {
sessionId = state.FindLatestPendingSessionIdLocked(pointerId, nullptr);
}
RegisterSyntheticPointerDispatchLocked(pointerId, actionTime, sessionId);
#else
(void)pointerId;
(void)active;
(void)isStart;
(void)actionTime;
#endif
}
uint64_t ResolveSyntheticDragSessionId(const std::shared_ptr<MMI::PointerEvent>& pointerEvent)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
CHECK_NULL_RETURN(pointerEvent, INVALID_SYNTHETIC_DRAG_SESSION_ID);
auto& state = GetSyntheticDragRuntimeState();
std::lock_guard<std::mutex> lock(state.syntheticDragSessionMutex);
return ResolveSyntheticDragSessionIdLocked(pointerEvent->GetPointerId(),
static_cast<int64_t>(pointerEvent->GetActionTime()));
#else
(void)pointerEvent;
return INVALID_SYNTHETIC_DRAG_SESSION_ID;
#endif
}
void InitializeSyntheticDragCompensation(const DragDataCore& dragData,
const std::shared_ptr<OHOS::Rosen::Window>& dragWindow, int32_t width, int32_t height, uint64_t sessionId)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
auto& state = GetSyntheticDragRuntimeState();
std::lock_guard<std::mutex> lock(state.syntheticDragCompensationMutex);
UpdateSyntheticDragCompensationStateLocked(dragData, dragWindow, width, height, sessionId);
#else
(void)dragData;
(void)dragWindow;
(void)width;
(void)height;
(void)sessionId;
#endif
}
bool IsSyntheticDragTouchActiveForPointer(int32_t pointerId)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
const auto& state = GetSyntheticDragRuntimeState();
if (!state.syntheticDragActive.load(std::memory_order_acquire)) {
return false;
}
return state.syntheticDragPointerId.load(std::memory_order_relaxed) == pointerId;
#else
(void)pointerId;
return false;
#endif
}
InteractionInterface* InteractionInterface::GetInstance()
{
static InteractionImpl instance;
return &instance;
}
int32_t InteractionImpl::UpdateShadowPic(const OHOS::Ace::ShadowInfoCore& shadowInfo)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
auto pixelMap = shadowInfo.pixelMap;
if (!pixelMap) {
Msdp::DeviceStatus::ShadowInfo msdpShadowInfo { nullptr, shadowInfo.x, shadowInfo.y };
return InteractionManager::GetInstance()->UpdateShadowPic(msdpShadowInfo);
}
Msdp::DeviceStatus::ShadowInfo msdpShadowInfo { shadowInfo.pixelMap->GetPixelMapSharedPtr(), shadowInfo.x,
shadowInfo.y };
return InteractionManager::GetInstance()->UpdateShadowPic(msdpShadowInfo);
#endif
return -1;
}
int32_t InteractionImpl::SetDragWindowVisible(bool visible, const std::shared_ptr<Rosen::RSTransaction>& rSTransaction)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
return InteractionManager::GetInstance()->SetDragWindowVisible(visible);
#endif
return -1;
}
int32_t InteractionImpl::SetMouseDragMonitorState(bool state)
{
return -1;
}
int32_t InteractionImpl::StartDrag(
const DragDataCore& dragData, std::function<void(const OHOS::Ace::DragNotifyMsg&)> callback)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
callback_ = callback;
auto& state = GetSyntheticDragRuntimeState();
std::shared_ptr<OHOS::Rosen::Window> window = GetDragWindow();
CHECK_NULL_RETURN(window, -1);
const bool isSyntheticDrag = IsSyntheticDragTouchActiveForPointer(dragData.pointerId);
const auto syntheticSessionId = MatchAndConsumeSyntheticDragSessionId(dragData, isSyntheticDrag);
if (isSyntheticDrag && syntheticSessionId == INVALID_SYNTHETIC_DRAG_SESSION_ID) {
LOGW("[UITEST_IOS_DRAGFIX] synthetic start without session, pointerId=%{public}d", dragData.pointerId);
}
state.activeSyntheticDragSessionId.store(
isSyntheticDrag ? syntheticSessionId : INVALID_SYNTHETIC_DRAG_SESSION_ID, std::memory_order_relaxed);
surfaceNodeListener_ = new SurfaceNodeListener(window, dragData, isSyntheticDrag, syntheticSessionId);
CHECK_NULL_RETURN(surfaceNodeListener_, -1);
window->RegisterSurfaceNodeListener(surfaceNodeListener_);
window->ShowWindow();
RegisterDragWindow();
return 0;
#endif
return -1;
}
void InteractionImpl::RegisterDragWindow()
{
auto callback = [surfaceNodeListener = surfaceNodeListener_] {
CHECK_NULL_VOID(surfaceNodeListener);
CHECK_NULL_VOID(surfaceNodeListener->dragWindow_);
auto window = surfaceNodeListener->dragWindow_;
CHECK_NULL_VOID(window);
window->UnregisterSurfaceNodeListener(surfaceNodeListener);
window->Destroy();
surfaceNodeListener->dragWindow_ = nullptr;
windowCreated_ = false;
};
InteractionManager::GetInstance()->RegisterDragWindow(callback);
}
int32_t InteractionImpl::GetDragBundleInfo(DragBundleInfo& dragBundleInfo)
{
return -1;
}
int32_t InteractionImpl::UpdateDragStyle(OHOS::Ace::DragCursorStyleCore style, const int32_t eventId)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
return InteractionManager::GetInstance()->UpdateDragStyle(TranslateDragCursorStyle(style));
#endif
return -1;
}
int32_t InteractionImpl::UpdatePreviewStyle(const OHOS::Ace::PreviewStyle& previewStyle)
{
return -1;
}
int32_t InteractionImpl::UpdatePreviewStyleWithAnimation(const OHOS::Ace::PreviewStyle& previewStyle,
const OHOS::Ace::PreviewAnimation& animation)
{
return -1;
}
int32_t InteractionImpl::StopDrag(DragDropRet result, std::function<void()> callback)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
auto& state = GetSyntheticDragRuntimeState();
LOGI("InteractionImpl::StopDrag");
Msdp::DeviceStatus::DragDropResult dragDropResult { TranslateDragResult(result.result), result.hasCustomAnimation,
result.mainWindow, TranslateDragBehavior(result.dragBehavior) };
OHOS::Ace::DragNotifyMsg msg { 0, 0, InteractionManager::GetInstance()->GetDragTargetPid(),
TranslateDragResult(dragDropResult.result), TranslateDragBehavior(dragDropResult.dragBehavior) };
if (callback_) {
callback_(msg);
}
state.activeSyntheticDragSessionId.store(INVALID_SYNTHETIC_DRAG_SESSION_ID, std::memory_order_relaxed);
return InteractionManager::GetInstance()->StopDrag(dragDropResult);
#endif
return -1;
}
int32_t InteractionImpl::GetUdKey(std::string& udKey)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
return InteractionManager::GetInstance()->GetUdKey(udKey);
#endif
return -1;
}
int32_t InteractionImpl::GetShadowOffset(ShadowOffsetData& shadowOffsetData)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
return InteractionManager::GetInstance()->GetShadowOffset(
shadowOffsetData.offsetX, shadowOffsetData.offsetY, shadowOffsetData.width, shadowOffsetData.height);
#endif
return -1;
}
int32_t InteractionImpl::GetDragState(DragState& dragState) const
{
#if defined(ENABLE_DRAG_FRAMEWORK)
Msdp::DeviceStatus::DragState state;
int32_t ret = InteractionManager::GetInstance()->GetDragState(state);
LOGI("InteractionImpl::GetDragState = %{public}d", ret);
switch (state) {
case Msdp::DeviceStatus::DragState::ERROR:
dragState = DragState::ERROR;
break;
case Msdp::DeviceStatus::DragState::START:
dragState = DragState::START;
break;
case Msdp::DeviceStatus::DragState::STOP:
dragState = DragState::STOP;
break;
case Msdp::DeviceStatus::DragState::CANCEL:
dragState = DragState::CANCEL;
break;
case Msdp::DeviceStatus::DragState::MOTION_DRAGGING:
dragState = DragState::MOTION_DRAGGING;
break;
default:
dragState = DragState::ERROR;
LOGW("unknow msdp drag state: %d", state);
break;
}
return ret;
#endif
return -1;
}
int32_t InteractionImpl::GetDragSummary(std::map<std::string, int64_t>& summary,
std::map<std::string, int64_t>& detailedSummary, std::map<std::string, std::vector<int32_t>>& summaryFormat,
int32_t& version, int64_t& totalSize, std::string& tag)
{
#ifdef ENABLE_DRAG_FRAMEWORK
Msdp::DeviceStatus::DragSummaryInfo dragSummary;
auto ret = InteractionManager::GetInstance()->GetDragSummaryInfo(dragSummary);
if (ret != 0) {
return ret;
}
summary = dragSummary.summarys;
detailedSummary = dragSummary.detailedSummarys;
summaryFormat = dragSummary.summaryFormat;
version = dragSummary.version;
totalSize = dragSummary.totalSize;
return ret;
#endif
return -1;
}
int32_t InteractionImpl::GetDragExtraInfo(std::string& extraInfo)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
return InteractionManager::GetInstance()->GetExtraInfo(extraInfo);
#endif
return -1;
}
int32_t InteractionImpl::EnterTextEditorArea(bool enable)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
return InteractionManager::GetInstance()->EnterTextEditorArea(enable);
#endif
return -1;
}
int32_t InteractionImpl::AddPrivilege(const std::string& signature, const DragEventData& dragEventData)
{
return -1;
}
int32_t InteractionImpl::RegisterCoordinationListener(std::function<void()> dragOutCallback)
{
return -1;
}
int32_t InteractionImpl::UnRegisterCoordinationListener()
{
return -1;
}
int32_t InteractionImpl::EnableInternalDropAnimation(const std::string& animationInfo)
{
return -1;
}
bool InteractionImpl::IsDragStart() const
{
#if defined(ENABLE_DRAG_FRAMEWORK)
return InteractionManager::GetInstance()->IsDragStart();
#endif
return false;
}
int32_t InteractionImpl::UpdatePointAction(const std::shared_ptr<MMI::PointerEvent>& pointerEvent)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
return InteractionManager::GetInstance()->UpdatePointerAction(pointerEvent);
#endif
return -1;
}
int32_t InteractionImpl::UpdateSyntheticPointAction(const std::shared_ptr<MMI::PointerEvent>& pointerEvent)
{
#if defined(ENABLE_DRAG_FRAMEWORK)
auto& state = GetSyntheticDragRuntimeState();
const auto sessionId = ResolveSyntheticDragSessionId(pointerEvent);
const auto activeSessionId = state.activeSyntheticDragSessionId.load(std::memory_order_relaxed);
if (sessionId == INVALID_SYNTHETIC_DRAG_SESSION_ID) {
LOGW("[UITEST_IOS_DRAGFIX] skip synthetic update due to missing session, "
"pointerId=%{public}d actionTime=%{public}" PRId64,
pointerEvent ? pointerEvent->GetPointerId() : -1,
pointerEvent ? static_cast<int64_t>(pointerEvent->GetActionTime()) : 0);
return 0;
}
if (activeSessionId != INVALID_SYNTHETIC_DRAG_SESSION_ID && sessionId != activeSessionId) {
LOGW("[UITEST_IOS_DRAGFIX] skip stale synthetic update sessionId=%{public}llu "
"activeSessionId=%{public}llu pointerId=%{public}d",
static_cast<unsigned long long>(sessionId), static_cast<unsigned long long>(activeSessionId),
pointerEvent ? pointerEvent->GetPointerId() : -1);
return 0;
}
auto adjustedPointerEvent = CreateSyntheticCompensatedPointerEvent(pointerEvent, sessionId);
return InteractionManager::GetInstance()->UpdatePointerAction(adjustedPointerEvent);
#endif
return -1;
}
std::shared_ptr<OHOS::Rosen::Window> InteractionImpl::GetDragWindow()
{
auto containerId = Container::CurrentId();
auto container = Platform::AceContainerSG::GetContainer(containerId);
CHECK_NULL_RETURN(container, nullptr);
std::string packagePath = container->GetPackagePathStr();
std::string filePath = packagePath + "/systemres" + "/resources";
InteractionManager::GetInstance()->SetSVGFilePath(filePath);
sptr<Rosen::Window> window = container->GetUIWindow(containerId);
CHECK_NULL_RETURN(window, nullptr);
auto dragWindow = Rosen::Window::CreateDragWindow(window->GetContext());
return dragWindow;
}
void SurfaceNodeListener::OnSurfaceNodeChanged(int32_t width, int32_t height, float density)
{
#ifdef ENABLE_DRAG_FRAMEWORK
(void)density;
if (windowCreated_) {
return;
}
InteractionManager::GetInstance()->SetDragWindow(dragWindow_);
if (isSyntheticDrag_) {
StartSyntheticDragWindow(dragData, dragWindow_, width, height, syntheticSessionId_);
} else {
StartManualDragWindow(dragData);
}
windowCreated_ = true;
#endif
}
#if defined(ENABLE_DRAG_FRAMEWORK)
Msdp::DeviceStatus::DragCursorStyle TranslateDragCursorStyle(OHOS::Ace::DragCursorStyleCore style)
{
switch (style) {
case OHOS::Ace::DragCursorStyleCore::DEFAULT:
return Msdp::DeviceStatus::DragCursorStyle::DEFAULT;
case OHOS::Ace::DragCursorStyleCore::FORBIDDEN:
return Msdp::DeviceStatus::DragCursorStyle::FORBIDDEN;
case OHOS::Ace::DragCursorStyleCore::COPY:
return Msdp::DeviceStatus::DragCursorStyle::COPY;
case OHOS::Ace::DragCursorStyleCore::MOVE:
return Msdp::DeviceStatus::DragCursorStyle::MOVE;
default:
return Msdp::DeviceStatus::DragCursorStyle::DEFAULT;
}
}
Msdp::DeviceStatus::DragResult TranslateDragResult(DragRet dragResult)
{
switch (dragResult) {
case DragRet::DRAG_SUCCESS:
return Msdp::DeviceStatus::DragResult::DRAG_SUCCESS;
case DragRet::DRAG_FAIL:
return Msdp::DeviceStatus::DragResult::DRAG_FAIL;
case DragRet::DRAG_CANCEL:
return Msdp::DeviceStatus::DragResult::DRAG_CANCEL;
default:
return Msdp::DeviceStatus::DragResult::DRAG_SUCCESS;
}
}
DragRet TranslateDragResult(Msdp::DeviceStatus::DragResult dragResult)
{
switch (dragResult) {
case Msdp::DeviceStatus::DragResult::DRAG_SUCCESS:
return DragRet::DRAG_SUCCESS;
case Msdp::DeviceStatus::DragResult::DRAG_FAIL:
return DragRet::DRAG_FAIL;
case Msdp::DeviceStatus::DragResult::DRAG_CANCEL:
return DragRet::DRAG_CANCEL;
default:
return DragRet::DRAG_SUCCESS;
}
}
Msdp::DeviceStatus::DragBehavior TranslateDragBehavior(OHOS::Ace::DragBehavior dragBehavior)
{
switch (dragBehavior) {
case OHOS::Ace::DragBehavior::COPY:
return Msdp::DeviceStatus::DragBehavior::COPY;
case OHOS::Ace::DragBehavior::MOVE:
return Msdp::DeviceStatus::DragBehavior::MOVE;
default:
return Msdp::DeviceStatus::DragBehavior::UNKNOWN;
}
}
OHOS::Ace::DragBehavior TranslateDragBehavior(Msdp::DeviceStatus::DragBehavior dragBehavior)
{
switch (dragBehavior) {
case Msdp::DeviceStatus::DragBehavior::COPY:
return OHOS::Ace::DragBehavior::COPY;
case Msdp::DeviceStatus::DragBehavior::MOVE:
return OHOS::Ace::DragBehavior::MOVE;
default:
return OHOS::Ace::DragBehavior::UNKNOWN;
}
}
#endif
}