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