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