1 /*
2  * Copyright (c) 2021-2024 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 "libinput_adapter.h"
17 
18 #include <cinttypes>
19 #include <climits>
20 #include <regex>
21 
22 #include <dirent.h>
23 #include <fcntl.h>
24 #include <sys/epoll.h>
25 #include <unistd.h>
26 
27 #include "define_multimodal.h"
28 #include "i_input_windows_manager.h"
29 #include "param_wrapper.h"
30 #include "util.h"
31 
32 #undef MMI_LOG_DOMAIN
33 #define MMI_LOG_DOMAIN MMI_LOG_SERVER
34 #undef MMI_LOG_TAG
35 #define MMI_LOG_TAG "LibinputAdapter"
36 
37 namespace OHOS {
38 namespace MMI {
39 namespace {
40 constexpr int32_t WAIT_TIME_FOR_INPUT { 10 };
41 constexpr int32_t MAX_RETRY_COUNT { 5 };
42 constexpr int32_t MIN_RIGHT_BTN_AREA_PERCENT { 0 };
43 constexpr int32_t MAX_RIGHT_BTN_AREA_PERCENT { 100 };
44 constexpr int32_t INVALID_RIGHT_BTN_AREA { -1 };
45 
HiLogFunc(struct libinput* input, libinput_log_priority priority, const char* fmt, va_list args)46 void HiLogFunc(struct libinput* input, libinput_log_priority priority, const char* fmt, va_list args)
47 {
48     CHKPV(input);
49     CHKPV(fmt);
50     char buffer[256] = {};
51     if (vsnprintf_s(buffer, sizeof(buffer), sizeof(buffer) - 1, fmt, args) == -1) {
52         MMI_HILOGE("Call vsnprintf_s failed");
53         va_end(args);
54         return;
55     }
56     if (strstr(buffer, "LOG_LEVEL_I") != NULL) {
57         MMI_HILOGI("PrintLog_Info:%{public}s", buffer);
58     } else if (strstr(buffer, "LOG_LEVEL_D") != NULL) {
59         MMI_HILOGD("PrintLog_Info:%{public}s", buffer);
60     } else if (strstr(buffer, "LOG_LEVEL_E") != NULL) {
61         MMI_HILOGE("PrintLog_Info:%{public}s", buffer);
62     } else {
63         MMI_HILOGD("PrintLog_Info:%{public}s", buffer);
64     }
65     va_end(args);
66 }
67 } // namespace
68 
DeviceLedUpdate(struct libinput_device *device, int32_t funcKey, bool enable)69 int32_t LibinputAdapter::DeviceLedUpdate(struct libinput_device *device, int32_t funcKey, bool enable)
70 {
71     CHKPR(device, RET_ERR);
72     return libinput_set_led_state(device, funcKey, enable);
73 }
74 
InitRightButtonAreaConfig()75 void LibinputAdapter::InitRightButtonAreaConfig()
76 {
77     CHKPV(input_);
78 
79     int32_t height_percent = OHOS::system::GetIntParameter("const.multimodalinput.rightclick_y_percentage",
80                                                            INVALID_RIGHT_BTN_AREA);
81     if ((height_percent <= MIN_RIGHT_BTN_AREA_PERCENT) || (height_percent > MAX_RIGHT_BTN_AREA_PERCENT)) {
82         MMI_HILOGE("Right button area height percent param is invalid");
83         return;
84     }
85 
86     int32_t width_percent = OHOS::system::GetIntParameter("const.multimodalinput.rightclick_x_percentage",
87                                                           INVALID_RIGHT_BTN_AREA);
88     if ((width_percent <= MIN_RIGHT_BTN_AREA_PERCENT) || (width_percent > MAX_RIGHT_BTN_AREA_PERCENT)) {
89         MMI_HILOGE("Right button area width percent param is invalid");
90         return;
91     }
92 
93     auto status = libinput_config_rightbutton_area(input_, height_percent, width_percent);
94     if (status != LIBINPUT_CONFIG_STATUS_SUCCESS) {
95         MMI_HILOGE("Config the touchpad right button area failed");
96     }
97 }
98 
99 constexpr static libinput_interface LIBINPUT_INTERFACE = {
100     .open_restricted = [](const char *path, int32_t flags, void *user_data)->int32_t {
101         if (path == nullptr) {
102             MMI_HILOGWK("Input device path is nullptr");
103             return RET_ERR;
104         }
105         char realPath[PATH_MAX] = {};
106         if (realpath(path, realPath) == nullptr) {
107             std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_FOR_INPUT));
108             MMI_HILOGWK("The error path is %{public}s", path);
109             return RET_ERR;
110         }
111         int32_t fd = 0;
112         for (int32_t i = 0; i < MAX_RETRY_COUNT; i++) {
113             fd = open(realPath, flags);
114             if (fd >= 0) {
115                 break;
116             }
117             std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_FOR_INPUT));
118         }
119         int32_t errNo = errno;
120         std::regex re("(\\d+)");
121         std::string str_path(path);
122         std::smatch match;
123         int32_t id;
124         if (std::regex_search(str_path, match, re)) {
125             id = std::stoi(match[0]);
126         } else {
127             id = -1;
128         }
129         MMI_HILOGWK("Libinput .open_restricted id:%{public}d, fd:%{public}d, errno:%{public}d",
130             id, fd, errNo);
131         return fd < 0 ? RET_ERR : fd;
132     },
133     .close_restricted = [](int32_t fd, void *user_data)
134     {
135         if (fd < 0) {
136             return;
137         }
138         MMI_HILOGI("Libinput .close_restricted fd:%{public}d", fd);
139         close(fd);
140     },
141 };
142 
Init(FunInputEvent funInputEvent)143 bool LibinputAdapter::Init(FunInputEvent funInputEvent)
144 {
145     CALL_DEBUG_ENTER;
146     CHKPF(funInputEvent);
147     funInputEvent_ = funInputEvent;
148     input_ = libinput_path_create_context(&LIBINPUT_INTERFACE, nullptr);
149     CHKPF(input_);
150     libinput_log_set_handler(input_, &HiLogFunc);
151     fd_ = libinput_get_fd(input_);
152     if (fd_ < 0) {
153         libinput_unref(input_);
154         fd_ = -1;
155         MMI_HILOGE("The fd_ is less than 0");
156         return false;
157     }
158     InitRightButtonAreaConfig();
159     return hotplugDetector_.Init([this](std::string path) { OnDeviceAdded(std::move(path)); },
160         [this](std::string path) { OnDeviceRemoved(std::move(path)); });
161 }
162 
EventDispatch(int32_t fd)163 void LibinputAdapter::EventDispatch(int32_t fd)
164 {
165     CALL_DEBUG_ENTER;
166     if (fd == fd_) {
167         MMI_HILOGD("Start to libinput_dispatch");
168         if (libinput_dispatch(input_) != 0) {
169             MMI_HILOGE("Failed to dispatch libinput");
170             return;
171         }
172         OnEventHandler();
173         MMI_HILOGD("End to OnEventHandler");
174     } else if (fd == hotplugDetector_.GetFd()) {
175         hotplugDetector_.OnEvent();
176     } else {
177         MMI_HILOGE("EventDispatch() called with unknown fd:%{public}d", fd);
178     }
179 }
180 
Stop()181 void LibinputAdapter::Stop()
182 {
183     CALL_DEBUG_ENTER;
184     hotplugDetector_.Stop();
185     if (fd_ >= 0) {
186         close(fd_);
187         fd_ = -1;
188     }
189     if (input_ != nullptr) {
190         libinput_unref(input_);
191         input_ = nullptr;
192     }
193 }
194 
ProcessPendingEvents()195 void LibinputAdapter::ProcessPendingEvents()
196 {
197     OnEventHandler();
198 }
199 
OnEventHandler()200 void LibinputAdapter::OnEventHandler()
201 {
202     CALL_DEBUG_ENTER;
203     CHKPV(funInputEvent_);
204     libinput_event *event = nullptr;
205     int64_t frameTime = GetSysClockTime();
206     while ((event = libinput_get_event(input_))) {
207         funInputEvent_(event, frameTime);
208         libinput_event_destroy(event);
209     }
210     if (event == nullptr) {
211         funInputEvent_(nullptr, 0);
212     }
213 }
214 
ReloadDevice()215 void LibinputAdapter::ReloadDevice()
216 {
217     CALL_DEBUG_ENTER;
218     CHKPV(input_);
219     libinput_suspend(input_);
220     libinput_resume(input_);
221 }
222 
OnDeviceAdded(std::string path)223 void LibinputAdapter::OnDeviceAdded(std::string path)
224 {
225     std::regex re("(\\d+)");
226     std::string str_path(path);
227     std::smatch match;
228     int32_t id;
229     if (std::regex_search(str_path, match, re)) {
230         id = std::stoi(match[0]);
231     } else {
232         id = -1;
233     }
234     MMI_HILOGI("OnDeviceAdded id:%{public}d", id);
235     auto pos = devices_.find(path);
236     if (pos != devices_.end()) {
237         MMI_HILOGD("Path is found");
238         return;
239     }
240     libinput_device* device = libinput_path_add_device(input_, path.c_str());
241     if (device != nullptr) {
242         devices_[std::move(path)] = libinput_device_ref(device);
243         // Libinput doesn't signal device adding event in path mode. Process manually.
244         OnEventHandler();
245     }
246 }
247 
OnDeviceRemoved(std::string path)248 void LibinputAdapter::OnDeviceRemoved(std::string path)
249 {
250     std::regex re("(\\d+)");
251     std::string str_path(path);
252     std::smatch match;
253     int32_t id;
254     if (std::regex_search(str_path, match, re)) {
255         id = std::stoi(match[0]);
256     } else {
257         id = -1;
258     }
259     MMI_HILOGI("OnDeviceRemoved id:%{public}d", id);
260     auto pos = devices_.find(path);
261     if (pos != devices_.end()) {
262         libinput_path_remove_device(pos->second);
263         libinput_device_unref(pos->second);
264         devices_.erase(pos);
265         // Libinput doesn't signal device removing event in path mode. Process manually.
266         OnEventHandler();
267     }
268 }
269 } // namespace MMI
270 } // namespace OHOS
271