1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "virtual_mouse.h"
17
18#include <cmath>
19
20#include <linux/input.h>
21
22#include "display_manager.h"
23#include "input_manager.h"
24
25#include "devicestatus_define.h"
26#include "fi_log.h"
27#include "virtual_device_defines.h"
28#include "virtual_mouse_builder.h"
29
30#undef LOG_TAG
31#define LOG_TAG "VirtualMouse"
32
33namespace OHOS {
34namespace Msdp {
35namespace DeviceStatus {
36namespace {
37constexpr int32_t REL_WHEEL_VALUE { 1 };
38constexpr int32_t REL_WHEEL_HI_RES_VALUE { 120 };
39constexpr int32_t MAX_SCROLL_LENGTH { 10 };
40constexpr int32_t MINIMUM_INTERVAL { 8 };
41constexpr double FAST_STEP { 5.0 };
42constexpr double TWICE_FAST_STEP { 2.0 * FAST_STEP };
43constexpr double MAXIMUM_STEP_LENGTH { 5000.0 };
44constexpr double STEP_UNIT { 1.0 };
45int32_t g_screenWidth { 720 };
46int32_t g_screenHeight { 1280 };
47} // namespace
48
49class PointerPositionMonitor final : public MMI::IInputEventConsumer {
50public:
51    PointerPositionMonitor() = default;
52    ~PointerPositionMonitor() = default;
53
54    void OnInputEvent(std::shared_ptr<MMI::KeyEvent> keyEvent) const override {};
55    void OnInputEvent(std::shared_ptr<MMI::PointerEvent> pointerEvent) const override;
56    void OnInputEvent(std::shared_ptr<MMI::AxisEvent> axisEvent) const override {};
57
58    size_t GetCount() const
59    {
60        return count_;
61    }
62
63    int32_t GetX() const
64    {
65        return pos_.x;
66    }
67
68    int32_t GetY() const
69    {
70        return pos_.y;
71    }
72
73private:
74    mutable size_t count_ { 0 };
75    mutable Coordinate pos_ {};
76};
77
78void PointerPositionMonitor::OnInputEvent(std::shared_ptr<MMI::PointerEvent> pointerEvent) const
79{
80    CHKPV(pointerEvent);
81    if (pointerEvent->GetSourceType() != MMI::PointerEvent::SOURCE_TYPE_MOUSE) {
82        return;
83    }
84    MMI::PointerEvent::PointerItem pointerItem;
85    if (!pointerEvent->GetPointerItem(pointerEvent->GetPointerId(), pointerItem)) {
86        FI_HILOGE("Pointer event is corrupt");
87        return;
88    }
89
90    pos_.x = pointerItem.GetDisplayX();
91    pos_.y = pointerItem.GetDisplayY();
92    ++count_;
93}
94
95std::shared_ptr<VirtualMouse> VirtualMouse::device_ = nullptr;
96
97std::shared_ptr<VirtualMouse> VirtualMouse::GetDevice()
98{
99    if (device_ == nullptr) {
100        std::string node;
101        if (VirtualDevice::FindDeviceNode(VirtualMouseBuilder::GetDeviceName(), node)) {
102            auto vMouse = std::make_shared<VirtualMouse>(node);
103            CHKPP(vMouse);
104            if (vMouse->IsActive()) {
105                device_ = vMouse;
106            }
107        }
108    }
109    return device_;
110}
111
112VirtualMouse::VirtualMouse(const std::string &name) : VirtualDevice(name)
113{
114    VirtualDevice::SetMinimumInterval(MINIMUM_INTERVAL);
115}
116
117int32_t VirtualMouse::DownButton(int32_t button)
118{
119    CALL_DEBUG_ENTER;
120    if (button < BTN_MOUSE || button > BTN_TASK) {
121        FI_HILOGE("Not mouse button:%{public}d", button);
122        return RET_ERR;
123    }
124
125    SendEvent(EV_MSC, MSC_SCAN, OBFUSCATED);
126    SendEvent(EV_KEY, button, DOWN_VALUE);
127    SendEvent(EV_SYN, SYN_REPORT, SYNC_VALUE);
128    return RET_OK;
129}
130
131int32_t VirtualMouse::UpButton(int32_t button)
132{
133    CALL_DEBUG_ENTER;
134    if (button < BTN_MOUSE || button > BTN_TASK) {
135        FI_HILOGE("Not mouse button:%{public}d", button);
136        return RET_ERR;
137    }
138
139    SendEvent(EV_MSC, MSC_SCAN, OBFUSCATED);
140    SendEvent(EV_KEY, button, UP_VALUE);
141    SendEvent(EV_SYN, SYN_REPORT, SYNC_VALUE);
142    return RET_OK;
143}
144
145void VirtualMouse::Scroll(int32_t dy)
146{
147    CALL_DEBUG_ENTER;
148    int32_t wheelValue = REL_WHEEL_VALUE;
149    int32_t wheelHiResValue = REL_WHEEL_HI_RES_VALUE;
150
151    if (dy < 0) {
152        wheelValue = -wheelValue;
153        wheelHiResValue = -wheelHiResValue;
154        dy = -dy;
155    }
156    if (dy > MAX_SCROLL_LENGTH) {
157        dy = MAX_SCROLL_LENGTH;
158    }
159    for (; dy >= REL_WHEEL_VALUE; dy -= REL_WHEEL_VALUE) {
160        SendEvent(EV_REL, REL_WHEEL, wheelValue);
161        SendEvent(EV_REL, REL_HWHEEL_HI_RES, wheelHiResValue);
162        SendEvent(EV_SYN, SYN_REPORT, SYNC_VALUE);
163    }
164}
165
166void VirtualMouse::Move(int32_t dx, int32_t dy)
167{
168    CALL_DEBUG_ENTER;
169    double delta = ::hypot(dx, dy);
170    if (std::abs(delta) < STEP_UNIT) {
171        FI_HILOGE("Mouse not moving");
172        return;
173    }
174    double total = std::min(delta, MAXIMUM_STEP_LENGTH);
175    double step = FAST_STEP;
176    int32_t count = static_cast<int32_t>(ceil(total / FAST_STEP));
177    while (count-- > 0) {
178        if (total < TWICE_FAST_STEP) {
179            if (total > FAST_STEP) {
180                step = total / HALF_VALUE;
181            } else {
182                step = total;
183            }
184        } else {
185            step = FAST_STEP;
186        }
187        double tx = round(step * static_cast<double>(dx) / delta);
188        double ty = round(step * static_cast<double>(dy) / delta);
189
190        if ((std::abs(tx) >= STEP_UNIT) && (std::abs(tx) <= MAXIMUM_STEP_LENGTH)) {
191            SendEvent(EV_REL, REL_X, static_cast<int32_t>(tx));
192        }
193        if ((std::abs(ty) >= STEP_UNIT) && (std::abs(ty) <= MAXIMUM_STEP_LENGTH)) {
194            SendEvent(EV_REL, REL_Y, static_cast<int32_t>(ty));
195        }
196        if (((std::abs(tx) >= STEP_UNIT) && (std::abs(tx) <= MAXIMUM_STEP_LENGTH)) ||
197            ((std::abs(ty) >= STEP_UNIT) && (std::abs(ty) <= MAXIMUM_STEP_LENGTH))) {
198            SendEvent(EV_SYN, SYN_REPORT, SYNC_VALUE);
199        }
200        total -= step;
201    }
202}
203
204int32_t VirtualMouse::MoveTo(int32_t x, int32_t y)
205{
206    CALL_DEBUG_ENTER;
207    MMI::InputManager *inputMgr = MMI::InputManager::GetInstance();
208    CHKPR(inputMgr, RET_ERR);
209    auto monitor = std::make_shared<PointerPositionMonitor>();
210    int32_t monitorId = inputMgr->AddMonitor(monitor);
211    if (monitorId < 0) {
212        return RET_ERR;
213    }
214    size_t count = monitor->GetCount();
215    int32_t nLoops = 5;
216    Move(MOVE_VALUE_X, MOVE_VALUE_Y);
217    int32_t ret = RET_OK;
218    while (nLoops-- > 0) {
219        int32_t nTries = 125;
220        for (;;) {
221            if (monitor->GetCount() > count) {
222                count = monitor->GetCount();
223                break;
224            }
225            if (--nTries < 0) {
226                FI_HILOGE("No pointer motion detected");
227                ret = RET_ERR;
228                goto CLEANUP;
229            }
230            std::this_thread::sleep_for(std::chrono::milliseconds(MINIMUM_INTERVAL));
231        }
232        FI_HILOGD("Current position: (%{private}d, %{private}d)", monitor->GetX(), monitor->GetY());
233        if (x == monitor->GetX() && y == monitor->GetY()) {
234            ret = RET_OK;
235            goto CLEANUP;
236        }
237        Move(x - monitor->GetX(), y - monitor->GetY());
238    }
239CLEANUP:
240    inputMgr->RemoveMonitor(monitorId);
241    return ret;
242}
243
244void VirtualMouse::MoveProcess(int32_t dx, int32_t dy)
245{
246    CALL_DEBUG_ENTER;
247    sptr<Rosen::Display> display = Rosen::DisplayManager::GetInstance().GetDisplayById(0);
248    CHKPV(display);
249    g_screenWidth = display->GetWidth();
250    g_screenHeight = display->GetHeight();
251    MMI::InputManager *inputMgr = MMI::InputManager::GetInstance();
252    CHKPV(inputMgr);
253    auto monitor = std::make_shared<PointerPositionMonitor>();
254    int32_t monitorId = inputMgr->AddMonitor(monitor);
255    if (monitorId < 0) {
256        FI_HILOGE("Failed to add mouse monitor");
257        return;
258    }
259    Move(MOVE_VALUE_X, MOVE_VALUE_Y);
260    Move(-MOVE_VALUE_Y, -MOVE_VALUE_X);
261    int32_t currentX = monitor->GetX();
262    int32_t currentY = monitor->GetY();
263    int32_t targetX = currentX + dx;
264    int32_t targetY = currentY + dy;
265    FI_HILOGD("Expected coordinates, (targetX, targetY):(%{private}d, %{private}d)", targetX, targetY);
266    Move(dx, dy);
267    if ((targetX <= g_screenWidth && targetX >= 0) && (targetY <= g_screenHeight && targetY >= 0) &&
268        (currentX < g_screenWidth && currentX > 0) && (currentY < g_screenHeight && currentY > 0)) {
269        int32_t nLoops = 5;
270        while (nLoops-- > 0) {
271            currentX = monitor->GetX();
272            currentY = monitor->GetY();
273            if (targetX == currentX && targetY == currentY) {
274                break;
275            }
276            Move(targetX - currentX, targetY - currentY);
277        }
278    }
279    inputMgr->RemoveMonitor(monitorId);
280}
281} // namespace DeviceStatus
282} // namespace Msdp
283} // namespace OHOS