1 /*
2 * Copyright (c) 2025 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 "device_manager.h"
17
18 #include <algorithm>
19 #include <charconv>
20 #include <cstring>
21 #include <iostream>
22 #include <iomanip>
23 #include <map>
24
25 #include <dirent.h>
26
27 namespace OHOS {
28 namespace MMI {
29 namespace {
30 constexpr size_t EVENT_PREFIX_LENGTH = 5;
31 constexpr const char* EVENT_PREFIX = "event";
32 constexpr int32_t ID_WIDTH = 5;
33 constexpr int32_t PATH_WIDTH = 20;
34 constexpr int32_t TOTAL_WIDTH = 80;
35 constexpr int32_t INVALID_DEVICE_ID = -1;
36 constexpr const char* INPUT_DEVICE_DIR = "/dev/input";
37 }
38
ExtractEventNumber(const std::string & fileName)39 int32_t DeviceManager::ExtractEventNumber(const std::string& fileName)
40 {
41 if (fileName.length() < EVENT_PREFIX_LENGTH || fileName.substr(0, EVENT_PREFIX_LENGTH) != EVENT_PREFIX) {
42 return INVALID_DEVICE_ID;
43 }
44 std::string numberPart = fileName.substr(EVENT_PREFIX_LENGTH);
45 if (!std::all_of(numberPart.begin(), numberPart.end(), ::isdigit)) {
46 return INVALID_DEVICE_ID;
47 }
48 int32_t eventNum;
49 auto [ptr, ec] = std::from_chars(numberPart.data(), numberPart.data() + numberPart.size(), eventNum);
50 if (ec == std::errc() &&
51 ptr == numberPart.data() + numberPart.size() &&
52 eventNum >= 0) {
53 return eventNum;
54 }
55 return INVALID_DEVICE_ID;
56 }
57
BuildDevicePath(const std::string & fileName) const58 std::string DeviceManager::BuildDevicePath(const std::string& fileName) const
59 {
60 return std::string(INPUT_DEVICE_DIR) + "/" + fileName;
61 }
62
DiscoverDevices()63 std::vector<InputDevice> DeviceManager::DiscoverDevices()
64 {
65 std::vector<InputDevice> devices;
66 DIR* dir = opendir(INPUT_DEVICE_DIR);
67 if (!dir) {
68 PrintError("Cannot open /dev/input directory: %s", strerror(errno));
69 return devices;
70 }
71 struct dirent* entry;
72 while ((entry = readdir(dir)) != nullptr) {
73 std::string fileName(entry->d_name);
74 std::string path = BuildDevicePath(entry->d_name);
75 int32_t eventNum = ExtractEventNumber(fileName);
76 if (eventNum >= 0) {
77 InputDevice device(path, static_cast<uint32_t>(eventNum));
78 if (device.IsOpen()) {
79 devices.push_back(std::move(device));
80 }
81 }
82 }
83 closedir(dir);
84 std::sort(devices.begin(), devices.end(),
85 [](const InputDevice& a, const InputDevice& b) {
86 return a.GetId() < b.GetId();
87 });
88 return devices;
89 }
90
PrintDeviceList()91 void DeviceManager::PrintDeviceList()
92 {
93 auto devices = DiscoverDevices();
94 if (devices.empty()) {
95 std::cout << "No input devices found." << std::endl;
96 return;
97 }
98 std::cout << "Available input devices:" << std::endl;
99 std::cout << std::setw(ID_WIDTH) << "ID" << " | "
100 << std::setw(PATH_WIDTH) << "Path" << " | "
101 << "Name" << std::endl;
102 std::cout << std::string(TOTAL_WIDTH, '-') << std::endl;
103 for (const auto& device : devices) {
104 std::cout << std::setw(ID_WIDTH) << device.GetId() << " | "
105 << std::setw(PATH_WIDTH) << device.GetPath() << " | "
106 << device.GetName() << std::endl;
107 }
108 }
109 } // namespace MMI
110 } // namespace OHOS