1 /*
2  * Copyright (c) 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 "general_device.h"
17 
18 #include <iostream>
19 #include <sstream>
20 #include <thread>
21 
22 namespace OHOS {
23 namespace MMI {
24 namespace {
25 constexpr size_t DEFAULT_BUF_SIZE { 1024 };
26 constexpr int32_t SLEEP_TIME { 500 };
27 }
28 
Close()29 void GeneralDevice::Close()
30 {
31     vDev_.reset();
32 }
33 
SendEvent(uint16_t type,uint16_t code,int32_t value)34 void GeneralDevice::SendEvent(uint16_t type, uint16_t code, int32_t value)
35 {
36     if (vDev_ == nullptr) {
37         std::cout << "No input device" << std::endl;
38         return;
39     }
40     vDev_->SendEvent(type, code, value);
41 }
42 
GetDevPath() const43 std::string GeneralDevice::GetDevPath() const
44 {
45     return (vDev_ != nullptr ? vDev_->GetDevPath() : std::string());
46 }
47 
OpenDevice(const std::string & name)48 bool GeneralDevice::OpenDevice(const std::string &name)
49 {
50     int32_t nTries = 6;
51 
52     while (nTries-- > 0) {
53         std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME));
54         std::string node;
55         if (GeneralDevice::FindDeviceNode(name, node)) {
56             std::cout << "Found node path: " << node << std::endl;
57             auto vInpuDev = std::make_unique<VInputDevice>(node);
58             vInpuDev->Open();
59             if (vInpuDev->IsActive()) {
60                 vDev_ = std::move(vInpuDev);
61                 return true;
62             }
63         }
64     }
65     return false;
66 }
67 
FindDeviceNode(const std::string & name,std::string & node)68 bool GeneralDevice::FindDeviceNode(const std::string &name, std::string &node)
69 {
70     std::map<std::string, std::string> nodes;
71     GetInputDeviceNodes(nodes);
72     std::cout << "There are " << nodes.size() << " device nodes" << std::endl;
73 
74     std::map<std::string, std::string>::const_iterator cItr = nodes.find(name);
75     if (cItr == nodes.cend()) {
76         std::cout << "No virtual stylus were found" << std::endl;
77         return false;
78     }
79     std::cout << "Node name : \'" << cItr->second << "\'" << std::endl;
80     std::ostringstream ss;
81     ss << "/dev/input/" << cItr->second;
82     node = ss.str();
83     return true;
84 }
85 
Execute(std::vector<std::string> & results)86 void GeneralDevice::Execute(std::vector<std::string> &results)
87 {
88     char buffer[DEFAULT_BUF_SIZE] {};
89     FILE *pin = popen("cat /proc/bus/input/devices", "r");
90     if (pin == nullptr) {
91         std::cout << "Failed to popen command" << std::endl;
92         return;
93     }
94     while (!feof(pin)) {
95         if (fgets(buffer, sizeof(buffer), pin) != nullptr) {
96             results.push_back(buffer);
97         }
98     }
99     pclose(pin);
100 }
101 
GetInputDeviceNodes(std::map<std::string,std::string> & nodes)102 void GeneralDevice::GetInputDeviceNodes(std::map<std::string, std::string> &nodes)
103 {
104     std::vector<std::string> results;
105     Execute(results);
106     if (results.empty()) {
107         std::cout << "Failed to list devices" << std::endl;
108         return;
109     }
110     const std::string kname { "Name=\"" };
111     const std::string kevent { "event" };
112     std::string name;
113     for (const auto &res : results) {
114         if (res[0] == 'N') {
115             std::string::size_type spos = res.find(kname);
116             if (spos != std::string::npos) {
117                 spos += kname.size();
118                 std::string::size_type tpos = res.find("\"", spos);
119                 if (tpos != std::string::npos) {
120                     name = res.substr(spos, tpos - spos);
121                 }
122             }
123         } else if (!name.empty() && (res[0] == 'H')) {
124             std::string::size_type spos = res.find(kevent);
125             if (spos != std::string::npos) {
126                 std::map<std::string, std::string>::const_iterator cItr = nodes.find(name);
127                 if (cItr != nodes.end()) {
128                     nodes.erase(cItr);
129                 }
130                 std::string::size_type tpos = spos + kevent.size();
131                 while (std::isalnum(res[tpos])) {
132                     ++tpos;
133                 }
134                 auto [_, ret] = nodes.emplace(name, res.substr(spos, tpos - spos));
135                 if (!ret) {
136                     std::cout << "name is duplicated" << std::endl;
137                 }
138                 name.clear();
139             }
140         }
141     }
142 }
143 } // namespace MMI
144 } // namespace OHOS