1f857971dSopenharmony_ci/*
2f857971dSopenharmony_ci * Copyright (c) 2023 Huawei Device Co., Ltd.
3f857971dSopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4f857971dSopenharmony_ci * you may not use this file except in compliance with the License.
5f857971dSopenharmony_ci * You may obtain a copy of the License at
6f857971dSopenharmony_ci *
7f857971dSopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8f857971dSopenharmony_ci *
9f857971dSopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10f857971dSopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11f857971dSopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12f857971dSopenharmony_ci * See the License for the specific language governing permissions and
13f857971dSopenharmony_ci * limitations under the License.
14f857971dSopenharmony_ci */
15f857971dSopenharmony_ci
16f857971dSopenharmony_ci#include "virtual_touchscreen.h"
17f857971dSopenharmony_ci
18f857971dSopenharmony_ci#include <linux/input.h>
19f857971dSopenharmony_ci
20f857971dSopenharmony_ci#include "devicestatus_define.h"
21f857971dSopenharmony_ci#include "fi_log.h"
22f857971dSopenharmony_ci#include "virtual_touchscreen_builder.h"
23f857971dSopenharmony_ci
24f857971dSopenharmony_ci#undef LOG_TAG
25f857971dSopenharmony_ci#define LOG_TAG "VirtualTouchScreen"
26f857971dSopenharmony_ci
27f857971dSopenharmony_cinamespace OHOS {
28f857971dSopenharmony_cinamespace Msdp {
29f857971dSopenharmony_cinamespace DeviceStatus {
30f857971dSopenharmony_cinamespace {
31f857971dSopenharmony_ciconstexpr int32_t MINIMUM_INTERVAL { 18 };
32f857971dSopenharmony_ciconstexpr int32_t N_SLOTS_AVAILABLE { 10 };
33f857971dSopenharmony_ciconstexpr int32_t STEP_LENGTH { 10 };
34f857971dSopenharmony_ciconstexpr int32_t TWICE_STEP_LENGTH { 2 * STEP_LENGTH };
35f857971dSopenharmony_ci} // namespaces
36f857971dSopenharmony_ci
37f857971dSopenharmony_cistd::shared_ptr<VirtualTouchScreen> VirtualTouchScreen::device_ = nullptr;
38f857971dSopenharmony_ci
39f857971dSopenharmony_cistd::shared_ptr<VirtualTouchScreen> VirtualTouchScreen::GetDevice()
40f857971dSopenharmony_ci{
41f857971dSopenharmony_ci    if (device_ == nullptr) {
42f857971dSopenharmony_ci        std::string node;
43f857971dSopenharmony_ci        if (VirtualDevice::FindDeviceNode(VirtualTouchScreenBuilder::GetDeviceName(), node)) {
44f857971dSopenharmony_ci            auto vTouch = std::make_shared<VirtualTouchScreen>(node);
45f857971dSopenharmony_ci            CHKPP(vTouch);
46f857971dSopenharmony_ci            if (vTouch->IsActive()) {
47f857971dSopenharmony_ci                device_ = vTouch;
48f857971dSopenharmony_ci            }
49f857971dSopenharmony_ci        }
50f857971dSopenharmony_ci    }
51f857971dSopenharmony_ci    return device_;
52f857971dSopenharmony_ci}
53f857971dSopenharmony_ci
54f857971dSopenharmony_ciVirtualTouchScreen::VirtualTouchScreen(const std::string &node) : VirtualDevice(node), slots_(N_SLOTS_AVAILABLE)
55f857971dSopenharmony_ci{
56f857971dSopenharmony_ci    VirtualDevice::SetMinimumInterval(MINIMUM_INTERVAL);
57f857971dSopenharmony_ci    QueryScreenSize();
58f857971dSopenharmony_ci}
59f857971dSopenharmony_ci
60f857971dSopenharmony_civoid VirtualTouchScreen::QueryScreenSize()
61f857971dSopenharmony_ci{
62f857971dSopenharmony_ci    struct input_absinfo absInfo {};
63f857971dSopenharmony_ci
64f857971dSopenharmony_ci    if (QueryAbsInfo(ABS_X, absInfo)) {
65f857971dSopenharmony_ci        screenWidth_ = (absInfo.maximum - absInfo.minimum + 1);
66f857971dSopenharmony_ci    }
67f857971dSopenharmony_ci    if (QueryAbsInfo(ABS_MT_POSITION_X, absInfo) &&
68f857971dSopenharmony_ci        ((screenWidth_ == std::numeric_limits<int32_t>::max()) ||
69f857971dSopenharmony_ci         ((absInfo.maximum - absInfo.minimum + 1) > screenWidth_))) {
70f857971dSopenharmony_ci        screenWidth_ = (absInfo.maximum - absInfo.minimum + 1);
71f857971dSopenharmony_ci    }
72f857971dSopenharmony_ci
73f857971dSopenharmony_ci    if (QueryAbsInfo(ABS_Y, absInfo)) {
74f857971dSopenharmony_ci        screenHeight_ = (absInfo.maximum - absInfo.minimum + 1);
75f857971dSopenharmony_ci    }
76f857971dSopenharmony_ci    if (QueryAbsInfo(ABS_MT_POSITION_Y, absInfo) &&
77f857971dSopenharmony_ci        ((screenHeight_ == std::numeric_limits<int32_t>::max()) ||
78f857971dSopenharmony_ci         ((absInfo.maximum - absInfo.minimum + 1) > screenHeight_))) {
79f857971dSopenharmony_ci        screenHeight_ = (absInfo.maximum - absInfo.minimum + 1);
80f857971dSopenharmony_ci    }
81f857971dSopenharmony_ci}
82f857971dSopenharmony_ci
83f857971dSopenharmony_civoid VirtualTouchScreen::SendTouchEvent()
84f857971dSopenharmony_ci{
85f857971dSopenharmony_ci    CALL_DEBUG_ENTER;
86f857971dSopenharmony_ci    for (int32_t s = 0; s < N_SLOTS_AVAILABLE; ++s) {
87f857971dSopenharmony_ci        if (!slots_[s].active) {
88f857971dSopenharmony_ci            continue;
89f857971dSopenharmony_ci        }
90f857971dSopenharmony_ci        SendEvent(EV_ABS, ABS_MT_POSITION_X, slots_[s].coord.x);
91f857971dSopenharmony_ci        SendEvent(EV_ABS, ABS_MT_POSITION_Y, slots_[s].coord.y);
92f857971dSopenharmony_ci        SendEvent(EV_ABS, ABS_MT_TRACKING_ID, s);
93f857971dSopenharmony_ci        SendEvent(EV_SYN, SYN_MT_REPORT, SYNC_VALUE);
94f857971dSopenharmony_ci        FI_HILOGD("Send event [%{public}d], (%{public}d, %{public}d)", s, slots_[s].coord.x, slots_[s].coord.y);
95f857971dSopenharmony_ci    }
96f857971dSopenharmony_ci}
97f857971dSopenharmony_ci
98f857971dSopenharmony_ciint32_t VirtualTouchScreen::DownButton(int32_t slot, int32_t x, int32_t y)
99f857971dSopenharmony_ci{
100f857971dSopenharmony_ci    CALL_DEBUG_ENTER;
101f857971dSopenharmony_ci    if (slot < 0 || slot >= N_SLOTS_AVAILABLE) {
102f857971dSopenharmony_ci        FI_HILOGD("Slot out of range, slot:%{public}d", slot);
103f857971dSopenharmony_ci        slot = N_SLOTS_AVAILABLE - 1;
104f857971dSopenharmony_ci    }
105f857971dSopenharmony_ci    bool firstTouchDown = std::none_of(slots_.cbegin(), slots_.cend(), [](const auto &slot) { return slot.active; });
106f857971dSopenharmony_ci    if (slots_.size() <= static_cast<size_t>(slot)) {
107f857971dSopenharmony_ci        FI_HILOGE("The index is out of bounds");
108f857971dSopenharmony_ci        return RET_ERR;
109f857971dSopenharmony_ci    }
110f857971dSopenharmony_ci    slots_[slot].coord.x = std::min(std::max(x, 0), screenWidth_ - 1);
111f857971dSopenharmony_ci    slots_[slot].coord.y = std::min(std::max(y, 0), screenHeight_ - 1);
112f857971dSopenharmony_ci    slots_[slot].active = true;
113f857971dSopenharmony_ci    FI_HILOGD("Press down [%{public}d], (%{public}d, %{public}d)", slot, slots_[slot].coord.x, slots_[slot].coord.y);
114f857971dSopenharmony_ci    SendTouchEvent();
115f857971dSopenharmony_ci    if (firstTouchDown) {
116f857971dSopenharmony_ci        FI_HILOGD("First touch down, send button touch down event");
117f857971dSopenharmony_ci        SendEvent(EV_KEY, BTN_TOUCH, DOWN_VALUE);
118f857971dSopenharmony_ci    }
119f857971dSopenharmony_ci    SendEvent(EV_SYN, SYN_MT_REPORT, SYNC_VALUE);
120f857971dSopenharmony_ci    FI_HILOGD("Send sync event");
121f857971dSopenharmony_ci    SendEvent(EV_SYN, SYN_REPORT, SYNC_VALUE);
122f857971dSopenharmony_ci    return RET_OK;
123f857971dSopenharmony_ci}
124f857971dSopenharmony_ci
125f857971dSopenharmony_ciint32_t VirtualTouchScreen::UpButton(int32_t slot)
126f857971dSopenharmony_ci{
127f857971dSopenharmony_ci    CALL_DEBUG_ENTER;
128f857971dSopenharmony_ci    if (slot < 0 || slot >= N_SLOTS_AVAILABLE) {
129f857971dSopenharmony_ci        FI_HILOGD("Slot out of range, slot:%{public}d", slot);
130f857971dSopenharmony_ci        slot = N_SLOTS_AVAILABLE - 1;
131f857971dSopenharmony_ci    }
132f857971dSopenharmony_ci    if (slots_.size() <= static_cast<size_t>(slot)) {
133f857971dSopenharmony_ci        FI_HILOGE("The index is out of bounds");
134f857971dSopenharmony_ci        return RET_ERR;
135f857971dSopenharmony_ci    }
136f857971dSopenharmony_ci    if (!slots_[slot].active) {
137f857971dSopenharmony_ci        FI_HILOGE("Slot [%{public}d] is not active", slot);
138f857971dSopenharmony_ci        return RET_ERR;
139f857971dSopenharmony_ci    }
140f857971dSopenharmony_ci    slots_[slot].active = false;
141f857971dSopenharmony_ci    FI_HILOGD("Release [%{public}d]", slot);
142f857971dSopenharmony_ci    SendTouchEvent();
143f857971dSopenharmony_ci    SendEvent(EV_SYN, SYN_MT_REPORT, SYNC_VALUE);
144f857971dSopenharmony_ci    FI_HILOGD("Send sync event");
145f857971dSopenharmony_ci    SendEvent(EV_SYN, SYN_REPORT, SYNC_VALUE);
146f857971dSopenharmony_ci
147f857971dSopenharmony_ci    bool lastTouchUp = std::none_of(slots_.cbegin(), slots_.cend(), [](const auto &slot) { return slot.active; });
148f857971dSopenharmony_ci    if (lastTouchUp) {
149f857971dSopenharmony_ci        FI_HILOGD("Last touch up, send button touch up event");
150f857971dSopenharmony_ci        SendEvent(EV_KEY, BTN_TOUCH, UP_VALUE);
151f857971dSopenharmony_ci        SendEvent(EV_SYN, SYN_MT_REPORT, SYNC_VALUE);
152f857971dSopenharmony_ci        SendEvent(EV_SYN, SYN_REPORT, SYNC_VALUE);
153f857971dSopenharmony_ci    }
154f857971dSopenharmony_ci    return RET_OK;
155f857971dSopenharmony_ci}
156f857971dSopenharmony_ci
157f857971dSopenharmony_ciint32_t VirtualTouchScreen::Move(int32_t slot, int32_t dx, int32_t dy)
158f857971dSopenharmony_ci{
159f857971dSopenharmony_ci    CALL_DEBUG_ENTER;
160f857971dSopenharmony_ci    if (slot < 0 || slot >= N_SLOTS_AVAILABLE) {
161f857971dSopenharmony_ci        slot = N_SLOTS_AVAILABLE - 1;
162f857971dSopenharmony_ci    }
163f857971dSopenharmony_ci    if (slots_.size() <= static_cast<size_t>(slot)) {
164f857971dSopenharmony_ci        FI_HILOGE("The index is out of bounds");
165f857971dSopenharmony_ci        return RET_ERR;
166f857971dSopenharmony_ci    }
167f857971dSopenharmony_ci    if (!slots_[slot].active) {
168f857971dSopenharmony_ci        FI_HILOGE("slot [%{public}d] is not active", slot);
169f857971dSopenharmony_ci        return RET_ERR;
170f857971dSopenharmony_ci    }
171f857971dSopenharmony_ci    Coordinate tcoord {
172f857971dSopenharmony_ci        .x = std::min(std::max(slots_[slot].coord.x + dx, 0), screenWidth_ - 1),
173f857971dSopenharmony_ci        .y = std::min(std::max(slots_[slot].coord.y + dy, 0), screenHeight_ - 1)
174f857971dSopenharmony_ci    };
175f857971dSopenharmony_ci    FI_HILOGD("Move [%{public}d] from (%{public}d, %{public}d) to (%{public}d, %{public}d)",
176f857971dSopenharmony_ci        slot, slots_[slot].coord.x, slots_[slot].coord.y, tcoord.x, tcoord.y);
177f857971dSopenharmony_ci
178f857971dSopenharmony_ci    while ((tcoord.x != slots_[slot].coord.x) || (tcoord.y != slots_[slot].coord.y)) {
179f857971dSopenharmony_ci        double total = ::hypot(tcoord.x - slots_[slot].coord.x, tcoord.y - slots_[slot].coord.y);
180f857971dSopenharmony_ci        if (total <= STEP_LENGTH) {
181f857971dSopenharmony_ci            slots_[slot].coord.x = tcoord.x;
182f857971dSopenharmony_ci            slots_[slot].coord.y = tcoord.y;
183f857971dSopenharmony_ci        } else {
184f857971dSopenharmony_ci            double step { STEP_LENGTH };
185f857971dSopenharmony_ci            if (total < TWICE_STEP_LENGTH) {
186f857971dSopenharmony_ci                step = total / HALF_VALUE;
187f857971dSopenharmony_ci            }
188f857971dSopenharmony_ci            slots_[slot].coord.x += static_cast<int32_t>(round(step * (tcoord.x - slots_[slot].coord.x) / total));
189f857971dSopenharmony_ci            slots_[slot].coord.y += static_cast<int32_t>(round(step * (tcoord.y - slots_[slot].coord.y) / total));
190f857971dSopenharmony_ci        }
191f857971dSopenharmony_ci        SendTouchEvent();
192f857971dSopenharmony_ci        SendEvent(EV_SYN, SYN_MT_REPORT, SYNC_VALUE);
193f857971dSopenharmony_ci        SendEvent(EV_SYN, SYN_REPORT, SYNC_VALUE);
194f857971dSopenharmony_ci    }
195f857971dSopenharmony_ci    return RET_OK;
196f857971dSopenharmony_ci}
197f857971dSopenharmony_ci
198f857971dSopenharmony_ciint32_t VirtualTouchScreen::MoveTo(int32_t slot, int32_t x, int32_t y)
199f857971dSopenharmony_ci{
200f857971dSopenharmony_ci    CALL_DEBUG_ENTER;
201f857971dSopenharmony_ci    if (slot < 0 || slot >= N_SLOTS_AVAILABLE) {
202f857971dSopenharmony_ci        slot = N_SLOTS_AVAILABLE - 1;
203f857971dSopenharmony_ci    }
204f857971dSopenharmony_ci    if (slots_.size() <= static_cast<size_t>(slot)) {
205f857971dSopenharmony_ci        FI_HILOGE("The index is out of bounds");
206f857971dSopenharmony_ci        return RET_ERR;
207f857971dSopenharmony_ci    }
208f857971dSopenharmony_ci    if (!slots_[slot].active) {
209f857971dSopenharmony_ci        FI_HILOGE("slot [%{public}d] is not active", slot);
210f857971dSopenharmony_ci        return RET_ERR;
211f857971dSopenharmony_ci    }
212f857971dSopenharmony_ci    return Move(slot, x - slots_[slot].coord.x, y - slots_[slot].coord.y);
213f857971dSopenharmony_ci}
214f857971dSopenharmony_ci} // namespace DeviceStatus
215f857971dSopenharmony_ci} // namespace Msdp
216f857971dSopenharmony_ci} // namespace OHOS