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 37namespace OHOS { 38namespace MMI { 39namespace { 40constexpr int32_t WAIT_TIME_FOR_INPUT { 10 }; 41constexpr int32_t MAX_RETRY_COUNT { 5 }; 42constexpr int32_t MIN_RIGHT_BTN_AREA_PERCENT { 0 }; 43constexpr int32_t MAX_RIGHT_BTN_AREA_PERCENT { 100 }; 44constexpr int32_t INVALID_RIGHT_BTN_AREA { -1 }; 45 46void 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 69int32_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 75void 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 99constexpr 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 143bool 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 163void 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 181void 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 195void LibinputAdapter::ProcessPendingEvents() 196{ 197 OnEventHandler(); 198} 199 200void 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 215void LibinputAdapter::ReloadDevice() 216{ 217 CALL_DEBUG_ENTER; 218 CHKPV(input_); 219 libinput_suspend(input_); 220 libinput_resume(input_); 221} 222 223void 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 248void 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