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_touchscreen.h"
17 
18 #include <linux/input.h>
19 
20 #include "devicestatus_define.h"
21 #include "fi_log.h"
22 #include "virtual_touchscreen_builder.h"
23 
24 #undef LOG_TAG
25 #define LOG_TAG "VirtualTouchScreen"
26 
27 namespace OHOS {
28 namespace Msdp {
29 namespace DeviceStatus {
30 namespace {
31 constexpr int32_t MINIMUM_INTERVAL { 18 };
32 constexpr int32_t N_SLOTS_AVAILABLE { 10 };
33 constexpr int32_t STEP_LENGTH { 10 };
34 constexpr int32_t TWICE_STEP_LENGTH { 2 * STEP_LENGTH };
35 } // namespaces
36 
37 std::shared_ptr<VirtualTouchScreen> VirtualTouchScreen::device_ = nullptr;
38 
GetDevice()39 std::shared_ptr<VirtualTouchScreen> VirtualTouchScreen::GetDevice()
40 {
41     if (device_ == nullptr) {
42         std::string node;
43         if (VirtualDevice::FindDeviceNode(VirtualTouchScreenBuilder::GetDeviceName(), node)) {
44             auto vTouch = std::make_shared<VirtualTouchScreen>(node);
45             CHKPP(vTouch);
46             if (vTouch->IsActive()) {
47                 device_ = vTouch;
48             }
49         }
50     }
51     return device_;
52 }
53 
VirtualTouchScreen(const std::string & node)54 VirtualTouchScreen::VirtualTouchScreen(const std::string &node) : VirtualDevice(node), slots_(N_SLOTS_AVAILABLE)
55 {
56     VirtualDevice::SetMinimumInterval(MINIMUM_INTERVAL);
57     QueryScreenSize();
58 }
59 
QueryScreenSize()60 void VirtualTouchScreen::QueryScreenSize()
61 {
62     struct input_absinfo absInfo {};
63 
64     if (QueryAbsInfo(ABS_X, absInfo)) {
65         screenWidth_ = (absInfo.maximum - absInfo.minimum + 1);
66     }
67     if (QueryAbsInfo(ABS_MT_POSITION_X, absInfo) &&
68         ((screenWidth_ == std::numeric_limits<int32_t>::max()) ||
69          ((absInfo.maximum - absInfo.minimum + 1) > screenWidth_))) {
70         screenWidth_ = (absInfo.maximum - absInfo.minimum + 1);
71     }
72 
73     if (QueryAbsInfo(ABS_Y, absInfo)) {
74         screenHeight_ = (absInfo.maximum - absInfo.minimum + 1);
75     }
76     if (QueryAbsInfo(ABS_MT_POSITION_Y, absInfo) &&
77         ((screenHeight_ == std::numeric_limits<int32_t>::max()) ||
78          ((absInfo.maximum - absInfo.minimum + 1) > screenHeight_))) {
79         screenHeight_ = (absInfo.maximum - absInfo.minimum + 1);
80     }
81 }
82 
SendTouchEvent()83 void VirtualTouchScreen::SendTouchEvent()
84 {
85     CALL_DEBUG_ENTER;
86     for (int32_t s = 0; s < N_SLOTS_AVAILABLE; ++s) {
87         if (!slots_[s].active) {
88             continue;
89         }
90         SendEvent(EV_ABS, ABS_MT_POSITION_X, slots_[s].coord.x);
91         SendEvent(EV_ABS, ABS_MT_POSITION_Y, slots_[s].coord.y);
92         SendEvent(EV_ABS, ABS_MT_TRACKING_ID, s);
93         SendEvent(EV_SYN, SYN_MT_REPORT, SYNC_VALUE);
94         FI_HILOGD("Send event [%{public}d], (%{public}d, %{public}d)", s, slots_[s].coord.x, slots_[s].coord.y);
95     }
96 }
97 
DownButton(int32_t slot,int32_t x,int32_t y)98 int32_t VirtualTouchScreen::DownButton(int32_t slot, int32_t x, int32_t y)
99 {
100     CALL_DEBUG_ENTER;
101     if (slot < 0 || slot >= N_SLOTS_AVAILABLE) {
102         FI_HILOGD("Slot out of range, slot:%{public}d", slot);
103         slot = N_SLOTS_AVAILABLE - 1;
104     }
105     bool firstTouchDown = std::none_of(slots_.cbegin(), slots_.cend(), [](const auto &slot) { return slot.active; });
106     if (slots_.size() <= static_cast<size_t>(slot)) {
107         FI_HILOGE("The index is out of bounds");
108         return RET_ERR;
109     }
110     slots_[slot].coord.x = std::min(std::max(x, 0), screenWidth_ - 1);
111     slots_[slot].coord.y = std::min(std::max(y, 0), screenHeight_ - 1);
112     slots_[slot].active = true;
113     FI_HILOGD("Press down [%{public}d], (%{public}d, %{public}d)", slot, slots_[slot].coord.x, slots_[slot].coord.y);
114     SendTouchEvent();
115     if (firstTouchDown) {
116         FI_HILOGD("First touch down, send button touch down event");
117         SendEvent(EV_KEY, BTN_TOUCH, DOWN_VALUE);
118     }
119     SendEvent(EV_SYN, SYN_MT_REPORT, SYNC_VALUE);
120     FI_HILOGD("Send sync event");
121     SendEvent(EV_SYN, SYN_REPORT, SYNC_VALUE);
122     return RET_OK;
123 }
124 
UpButton(int32_t slot)125 int32_t VirtualTouchScreen::UpButton(int32_t slot)
126 {
127     CALL_DEBUG_ENTER;
128     if (slot < 0 || slot >= N_SLOTS_AVAILABLE) {
129         FI_HILOGD("Slot out of range, slot:%{public}d", slot);
130         slot = N_SLOTS_AVAILABLE - 1;
131     }
132     if (slots_.size() <= static_cast<size_t>(slot)) {
133         FI_HILOGE("The index is out of bounds");
134         return RET_ERR;
135     }
136     if (!slots_[slot].active) {
137         FI_HILOGE("Slot [%{public}d] is not active", slot);
138         return RET_ERR;
139     }
140     slots_[slot].active = false;
141     FI_HILOGD("Release [%{public}d]", slot);
142     SendTouchEvent();
143     SendEvent(EV_SYN, SYN_MT_REPORT, SYNC_VALUE);
144     FI_HILOGD("Send sync event");
145     SendEvent(EV_SYN, SYN_REPORT, SYNC_VALUE);
146 
147     bool lastTouchUp = std::none_of(slots_.cbegin(), slots_.cend(), [](const auto &slot) { return slot.active; });
148     if (lastTouchUp) {
149         FI_HILOGD("Last touch up, send button touch up event");
150         SendEvent(EV_KEY, BTN_TOUCH, UP_VALUE);
151         SendEvent(EV_SYN, SYN_MT_REPORT, SYNC_VALUE);
152         SendEvent(EV_SYN, SYN_REPORT, SYNC_VALUE);
153     }
154     return RET_OK;
155 }
156 
Move(int32_t slot,int32_t dx,int32_t dy)157 int32_t VirtualTouchScreen::Move(int32_t slot, int32_t dx, int32_t dy)
158 {
159     CALL_DEBUG_ENTER;
160     if (slot < 0 || slot >= N_SLOTS_AVAILABLE) {
161         slot = N_SLOTS_AVAILABLE - 1;
162     }
163     if (slots_.size() <= static_cast<size_t>(slot)) {
164         FI_HILOGE("The index is out of bounds");
165         return RET_ERR;
166     }
167     if (!slots_[slot].active) {
168         FI_HILOGE("slot [%{public}d] is not active", slot);
169         return RET_ERR;
170     }
171     Coordinate tcoord {
172         .x = std::min(std::max(slots_[slot].coord.x + dx, 0), screenWidth_ - 1),
173         .y = std::min(std::max(slots_[slot].coord.y + dy, 0), screenHeight_ - 1)
174     };
175     FI_HILOGD("Move [%{public}d] from (%{public}d, %{public}d) to (%{public}d, %{public}d)",
176         slot, slots_[slot].coord.x, slots_[slot].coord.y, tcoord.x, tcoord.y);
177 
178     while ((tcoord.x != slots_[slot].coord.x) || (tcoord.y != slots_[slot].coord.y)) {
179         double total = ::hypot(tcoord.x - slots_[slot].coord.x, tcoord.y - slots_[slot].coord.y);
180         if (total <= STEP_LENGTH) {
181             slots_[slot].coord.x = tcoord.x;
182             slots_[slot].coord.y = tcoord.y;
183         } else {
184             double step { STEP_LENGTH };
185             if (total < TWICE_STEP_LENGTH) {
186                 step = total / HALF_VALUE;
187             }
188             slots_[slot].coord.x += static_cast<int32_t>(round(step * (tcoord.x - slots_[slot].coord.x) / total));
189             slots_[slot].coord.y += static_cast<int32_t>(round(step * (tcoord.y - slots_[slot].coord.y) / total));
190         }
191         SendTouchEvent();
192         SendEvent(EV_SYN, SYN_MT_REPORT, SYNC_VALUE);
193         SendEvent(EV_SYN, SYN_REPORT, SYNC_VALUE);
194     }
195     return RET_OK;
196 }
197 
MoveTo(int32_t slot,int32_t x,int32_t y)198 int32_t VirtualTouchScreen::MoveTo(int32_t slot, int32_t x, int32_t y)
199 {
200     CALL_DEBUG_ENTER;
201     if (slot < 0 || slot >= N_SLOTS_AVAILABLE) {
202         slot = N_SLOTS_AVAILABLE - 1;
203     }
204     if (slots_.size() <= static_cast<size_t>(slot)) {
205         FI_HILOGE("The index is out of bounds");
206         return RET_ERR;
207     }
208     if (!slots_[slot].active) {
209         FI_HILOGE("slot [%{public}d] is not active", slot);
210         return RET_ERR;
211     }
212     return Move(slot, x - slots_[slot].coord.x, y - slots_[slot].coord.y);
213 }
214 } // namespace DeviceStatus
215 } // namespace Msdp
216 } // namespace OHOS