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