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_mouse_builder.h"
17
18#include <getopt.h>
19#include <fstream>
20#include <iostream>
21#include <unordered_map>
22
23#include <linux/input.h>
24
25#include "input_manager.h"
26
27#include "devicestatus_define.h"
28#include "fi_log.h"
29#include "utility.h"
30#include "virtual_mouse.h"
31
32#undef LOG_TAG
33#define LOG_TAG "VirtualMouseBuilder"
34
35namespace OHOS {
36namespace Msdp {
37namespace DeviceStatus {
38namespace {
39constexpr int32_t MAXIMUM_LEVEL_ALLOWED { 3 };
40constexpr uint32_t IO_FLAG_WIDTH { 6 };
41const std::unordered_map<std::string, int32_t> mouseBtns {
42    { "BTN_LEFT", BTN_LEFT }, { "BTN_RIGHT", BTN_RIGHT },
43    { "BTN_MIDDLE", BTN_MIDDLE }, { "BTN_SIDE", BTN_SIDE },
44    { "BTN_EXTRA", BTN_EXTRA }, { "BTN_FORWARD", BTN_FORWARD },
45    { "BTN_BACK", BTN_BACK }, { "BTN_TASK", BTN_TASK } };
46} // namespace
47
48VirtualMouseBuilder::VirtualMouseBuilder() : VirtualDeviceBuilder(GetDeviceName(), BUS_USB, 0x93a, 0x2510)
49{
50    eventTypes_ = { EV_KEY, EV_REL, EV_MSC };
51    keys_ = { BTN_LEFT, BTN_RIGHT, BTN_MIDDLE, BTN_SIDE, BTN_EXTRA, BTN_FORWARD, BTN_BACK, BTN_TASK };
52    relBits_ = { REL_X, REL_Y, REL_WHEEL, REL_WHEEL_HI_RES };
53    miscellaneous_ = { MSC_SCAN };
54}
55
56class MouseEventMonitor final : public MMI::IInputEventConsumer {
57public:
58    MouseEventMonitor() = default;
59    ~MouseEventMonitor() = default;
60
61    void OnInputEvent(std::shared_ptr<MMI::KeyEvent> keyEvent) const override {};
62    void OnInputEvent(std::shared_ptr<MMI::PointerEvent> pointerEvent) const override;
63    void OnInputEvent(std::shared_ptr<MMI::AxisEvent> axisEvent) const override {};
64};
65
66void MouseEventMonitor::OnInputEvent(std::shared_ptr<MMI::PointerEvent> pointerEvent) const
67{
68    CHKPV(pointerEvent);
69    if (pointerEvent->GetSourceType() != MMI::PointerEvent::SOURCE_TYPE_MOUSE) {
70        return;
71    }
72    MMI::PointerEvent::PointerItem pointerItem;
73    if (!pointerEvent->GetPointerItem(pointerEvent->GetPointerId(), pointerItem)) {
74        return;
75    }
76    std::cout << "\rcurrent pointer position - x: " << std::setw(IO_FLAG_WIDTH) << std::left <<
77        pointerItem.GetDisplayX() << "y: " << pointerItem.GetDisplayY() << "            ";
78    std::cout.flush();
79}
80
81std::string VirtualMouseBuilder::GetDeviceName()
82{
83    return std::string("Virtual Mouse");
84}
85
86void VirtualMouseBuilder::ShowUsage()
87{
88    std::cout << "Usage: vdevadm act -t M [-d <mouse-button>] [-u <mouse-button>] [-s <dv>]" << std::endl;
89    std::cout << "          [-m <dx> [<dy>]] [-M <x> <y>] [-w <ms>] [-f <FILE>] [-r <FILE>]" << std::endl;
90    std::cout << "      -d <mouse-button>" << std::endl;
91    std::cout << "                  Down the <mouse-button>" << std::endl;
92    std::cout << "      -u <mouse-button>" << std::endl;
93    std::cout << "                  Release the <mouse-button>" << std::endl;
94    std::cout << "      -s <dy>     Scroll the mouse wheel" << std::endl;
95    std::cout << "      -m <dx> [<dy>]" << std::endl;
96    std::cout << "                  Move the mouse along <dx, dy>; if <dy> is missing, then set dy=dx" << std::endl;
97    std::cout << "      -M <x> <y>  Move the pointer to <x, y>" << std::endl;
98    std::cout << "      -D <SLOT> <sx> <sy> <tx> <ty> Drag the touch <SLOT> to (tx, ty)" << std::endl;
99    std::cout << "      -w <ms>     Wait for <ms> milliseconds." << std::endl;
100    std::cout << "      -f <FILE>   Read actions from <FILE>" << std::endl;
101    std::cout << "      -r <FILE>   Read raw input data from <FILE>." << std::endl;
102    std::cout << std::endl;
103    std::cout << "          <mouse-button> can be:" << std::endl;
104    std::cout << "              L   For left mouse button" << std::endl;
105    std::cout << "              R   For right mouse button" << std::endl;
106    std::cout << "              M   For middle mouse button" << std::endl;
107}
108
109void VirtualMouseBuilder::Mount()
110{
111    CALL_DEBUG_ENTER;
112    std::cout << "Start to mount virtual mouse." << std::endl;
113    if (VirtualMouse::GetDevice() != nullptr) {
114        std::cout << "Virtual mouse has been mounted." << std::endl;
115        return;
116    }
117    VirtualMouseBuilder vMouse;
118    if (!vMouse.SetUp()) {
119        std::cout << "Failed to mount virtual mouse." << std::endl;
120        return;
121    }
122
123    int32_t nTries = 6;
124    do {
125        std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_TIME));
126    } while ((nTries-- > 0) && (VirtualMouse::GetDevice() == nullptr));
127    if (VirtualMouse::GetDevice() == nullptr) {
128        std::cout << "Failed to mount virtual mouse." << std::endl;
129        return;
130    }
131
132    std::cout << "Mount virtual mouse successfully." << std::endl;
133    VirtualDeviceBuilder::Daemonize();
134
135    for (;;) {
136        std::this_thread::sleep_for(std::chrono::minutes(1));
137    }
138}
139
140void VirtualMouseBuilder::Unmount()
141{
142    CALL_DEBUG_ENTER;
143    VirtualDeviceBuilder::Unmount("mouse", "M");
144}
145
146void VirtualMouseBuilder::Clone()
147{
148    CALL_DEBUG_ENTER;
149    if (VirtualMouse::GetDevice() != nullptr) {
150        std::cout << "Virtual mouse has been mounted." << std::endl;
151        return;
152    }
153
154    std::vector<std::shared_ptr<VirtualDevice>> vDevs;
155    int32_t ret = VirtualDeviceBuilder::ScanFor(
156        [](std::shared_ptr<VirtualDevice> vDev) { return ((vDev != nullptr) && vDev->IsMouse()); }, vDevs);
157    if (ret != RET_OK) {
158        std::cout << "Failed while scanning for mouse." << std::endl;
159        return;
160    }
161    auto vDev = VirtualDeviceBuilder::Select(vDevs, "mouse");
162    CHKPV(vDev);
163
164    std::cout << "Cloning \'" << vDev->GetName() << "\'." << std::endl;
165    VirtualDeviceBuilder vBuilder(GetDeviceName(), vDev);
166    if (!vBuilder.SetUp()) {
167        std::cout << "Clone  \' " << vDev->GetName() << " \' is failed." << std::endl;
168        return;
169    }
170    int32_t nTries = 3;
171    do {
172        std::this_thread::sleep_for(std::chrono::seconds(1));
173    } while ((nTries-- > 0) && (VirtualMouse::GetDevice() == nullptr));
174    if (VirtualMouse::GetDevice() == nullptr) {
175        std::cout << "Failed to clone \' " << vDev->GetName() << " \'." << std::endl;
176        return;
177    }
178
179    std::cout << "Clone \'" << vDev->GetName() << "\' successfully." << std::endl;
180    VirtualDeviceBuilder::Daemonize();
181    for (;;) {
182        std::this_thread::sleep_for(std::chrono::minutes(1));
183    }
184}
185
186void VirtualMouseBuilder::Monitor()
187{
188    CALL_DEBUG_ENTER;
189    MMI::InputManager *inputMgr = MMI::InputManager::GetInstance();
190    CHKPV(inputMgr);
191    auto monitor = std::make_shared<MouseEventMonitor>();
192    int32_t monitorId = inputMgr->AddMonitor(monitor);
193    if (monitorId < 0) {
194        std::cout << "Failed to add monitor." << std::endl;
195        return;
196    }
197    for (;;) {
198        std::this_thread::sleep_for(std::chrono::minutes(1));
199    }
200}
201
202void VirtualMouseBuilder::Act(int32_t argc, char *argv[])
203{
204    CALL_DEBUG_ENTER;
205    int32_t opt = getopt(argc, argv, "d:u:s:m:M:f:r:w:D:");
206    if (opt < 0) {
207        std::cout << "Vdevadm act: required option is missing" << std::endl;
208        VirtualMouseBuilder::ShowUsage();
209        return;
210    }
211    if (VirtualMouse::GetDevice() == nullptr) {
212        std::cout << "No virtual mouse." << std::endl;
213        return;
214    }
215    do {
216        {
217            auto action = ruleMouseActions_.find(opt);
218            if (action != ruleMouseActions_.end()) {
219                action->second();
220                continue;
221            }
222        }
223        {
224            auto action = readMouseActions_.find(opt);
225            if (action != readMouseActions_.end()) {
226                action->second(optarg);
227                continue;
228            }
229        }
230        {
231            auto action = moveMouseActions_.find(opt);
232            if (action != moveMouseActions_.end()) {
233                action->second(argc, argv);
234                continue;
235            }
236        }
237        if (opt == 'w') {
238            VirtualDeviceBuilder::WaitFor(optarg, "mouse");
239        } else {
240            ShowUsage();
241        }
242    } while ((opt = getopt(argc, argv, "d:u:s:m:M:f:r:w:D:")) >= 0);
243}
244
245void VirtualMouseBuilder::ReadDownAction()
246{
247    CALL_DEBUG_ENTER;
248    CHKPV(optarg);
249
250    if (strcmp(optarg, "L") == 0) {
251        std::cout << "[mouse] down button: BTN_LEFT" << std::endl;
252        VirtualMouse::GetDevice()->DownButton(BTN_LEFT);
253    } else if (strcmp(optarg, "M") == 0) {
254        std::cout << "[mouse] down button: BTN_MIDDLE" << std::endl;
255        VirtualMouse::GetDevice()->DownButton(BTN_MIDDLE);
256    } else if (strcmp(optarg, "R") == 0) {
257        std::cout << "[mouse] down button: BTN_RIGHT" << std::endl;
258        VirtualMouse::GetDevice()->DownButton(BTN_RIGHT);
259    } else {
260        std::cout << "Invalid argument for option \'-d\'." << std::endl;
261        ShowUsage();
262    }
263}
264
265void VirtualMouseBuilder::ReadMoveAction(int32_t argc, char *argv[])
266{
267    CALL_DEBUG_ENTER;
268    CHKPV(optarg);
269    if (!Utility::IsInteger(std::string(optarg)) || (optind < 0) || (optind >= argc) ||
270        !Utility::IsInteger(argv[optind])) {
271        std::cout << "Invalid arguments for Option \'-m\'." << std::endl;
272        ShowUsage();
273        return;
274    }
275    int32_t dx = std::atoi(optarg);
276    int32_t dy = dx;
277
278    if ((argv[optind] != nullptr) && Utility::IsInteger(std::string(argv[optind]))) {
279        dy = std::atoi(argv[optind++]);
280    }
281    std::cout << "[mouse] move: (" << dx << "," << dy << ")" << std::endl;
282    VirtualMouse::GetDevice()->MoveProcess(dx, dy);
283}
284
285void VirtualMouseBuilder::ReadMoveToAction(int32_t argc, char *argv[])
286{
287    CALL_DEBUG_ENTER;
288    CHKPV(optarg);
289
290    if (!Utility::IsInteger(optarg) || (optind < 0) || (optind >= argc) || !Utility::IsInteger(argv[optind])) {
291        std::cout << "Invalid arguments for Option \'-M\'." << std::endl;
292        ShowUsage();
293        return;
294    }
295    int32_t x = std::atoi(optarg);
296    int32_t y = std::atoi(argv[optind]);
297    std::cout << "[mouse] move-to (" << x << "," << y << ")" << std::endl;
298    VirtualMouse::GetDevice()->MoveTo(x, y);
299    while ((optind < argc) && Utility::IsInteger(argv[optind])) {
300        optind++;
301    }
302}
303
304void VirtualMouseBuilder::ReadDragToAction(int32_t argc, char *argv[])
305{
306    CALL_DEBUG_ENTER;
307    CHKPV(optarg);
308    if (!Utility::IsInteger(optarg) || (optind < 0) || (optind >= argc) || !Utility::IsInteger(argv[optind])) {
309        std::cout << "Invalid arguments for Option \'-D\'." << std::endl;
310        ShowUsage();
311        return;
312    }
313    int32_t x = std::atoi(optarg);
314    int32_t y = std::atoi(argv[optind]);
315
316    std::cout << "[mouse] drag-to (" << x << "," << y << ")" << std::endl;
317    VirtualMouse::GetDevice()->DownButton(BTN_LEFT);
318    VirtualDeviceBuilder::WaitFor("mouse", SLEEP_TIME);
319    VirtualMouse::GetDevice()->MoveTo(x, y);
320    VirtualMouse::GetDevice()->UpButton(BTN_LEFT);
321    while ((optind < argc) && Utility::IsInteger(argv[optind])) {
322        optind++;
323    }
324}
325
326void VirtualMouseBuilder::ReadUpAction()
327{
328    CALL_DEBUG_ENTER;
329    CHKPV(optarg);
330
331    if (strcmp(optarg, "L") == 0) {
332        std::cout << "[mouse] release button: BTN_LEFT" << std::endl;
333        VirtualMouse::GetDevice()->UpButton(BTN_LEFT);
334    } else if (strcmp(optarg, "M") == 0) {
335        std::cout << "[mouse] release button: BTN_MIDDLE" << std::endl;
336        VirtualMouse::GetDevice()->UpButton(BTN_MIDDLE);
337    } else if (strcmp(optarg, "R") == 0) {
338        std::cout << "[mouse] release button: BTN_RIGHT" << std::endl;
339        VirtualMouse::GetDevice()->UpButton(BTN_RIGHT);
340    } else {
341        std::cout << "Invalid argument for option \'-u\'." << std::endl;
342        ShowUsage();
343    }
344}
345
346void VirtualMouseBuilder::ReadScrollAction()
347{
348    CALL_DEBUG_ENTER;
349    CHKPV(optarg);
350    if (!Utility::IsInteger(std::string(optarg))) {
351        std::cout << "Invalid arguments for Option \'-s\'." << std::endl;
352        ShowUsage();
353        return;
354    }
355    int32_t dy = std::atoi(optarg);
356    std::cout << "[mouse] scroll: " << dy << std::endl;
357    VirtualMouse::GetDevice()->Scroll(dy);
358}
359
360void VirtualMouseBuilder::ReadActions(const char *path)
361{
362    CALL_DEBUG_ENTER;
363    json model;
364    int32_t result = VirtualDeviceBuilder::ReadFile(path, model);
365    if (result == RET_ERR) {
366        FI_HILOGE("Failed to read mouse data from the files");
367        return;
368    }
369    ReadModel(model, MAXIMUM_LEVEL_ALLOWED);
370}
371
372void VirtualMouseBuilder::ReadModel(const nlohmann::json &model, int32_t level)
373{
374    CALL_DEBUG_ENTER;
375    if (model.is_object()) {
376        auto tIter = model.find("actions");
377        if (tIter != model.cend() && tIter->is_array()) {
378            std::for_each(tIter->cbegin(), tIter->cend(), [](const auto &item) { ReadAction(item); });
379        }
380    } else if (model.is_array() && level > 0) {
381        for (const auto &m : model) {
382            ReadModel(m, level - 1);
383        }
384    }
385}
386
387void VirtualMouseBuilder::ReadAction(const nlohmann::json &model)
388{
389    CALL_DEBUG_ENTER;
390    if (!model.is_object()) {
391        FI_HILOGD("Not an object");
392        return;
393    }
394    auto it = model.find("action");
395    if (it != model.cend() && it->is_string()) {
396        static const std::unordered_map<std::string, std::function<void(const nlohmann::json &model)>> actions {
397            { "down", &HandleDown },
398            { "move", &HandleMove },
399            { "up", &HandleUp },
400            { "scroll", &HandleScroll },
401            { "wait", &HandleWait }
402        };
403        auto actionItr = actions.find(it.value());
404        if (actionItr != actions.cend()) {
405            actionItr->second(model);
406        }
407    }
408}
409
410void VirtualMouseBuilder::HandleDown(const nlohmann::json &model)
411{
412    CALL_DEBUG_ENTER;
413    auto it = model.find("button");
414    if (it != model.cend() && it->is_string()) {
415        auto tIter = mouseBtns.find(it.value());
416        if (tIter != mouseBtns.cend()) {
417            std::cout << "[mouse] down button: " << tIter->first << std::endl;
418            VirtualMouse::GetDevice()->DownButton(tIter->second);
419        }
420    }
421}
422
423void VirtualMouseBuilder::HandleMove(const nlohmann::json &model)
424{
425    CALL_DEBUG_ENTER;
426    int32_t dx = 0;
427    int32_t dy = 0;
428
429    auto it = model.find("dx");
430    if (it != model.cend() && it->is_number_integer()) {
431        dx = it.value();
432    }
433    it = model.find("dy");
434    if (it != model.cend() && it->is_number_integer()) {
435        dy = it.value();
436    }
437    std::cout << "[mouse] move: (" << dx << "," << dy << ")" << std::endl;
438    VirtualMouse::GetDevice()->Move(dx, dy);
439}
440
441void VirtualMouseBuilder::HandleUp(const nlohmann::json &model)
442{
443    CALL_DEBUG_ENTER;
444    auto it = model.find("button");
445    if (it != model.cend() && it->is_string()) {
446        auto tIter = mouseBtns.find(it.value());
447        if (tIter != mouseBtns.cend()) {
448            std::cout << "[mouse] release button: " << tIter->first << std::endl;
449            VirtualMouse::GetDevice()->UpButton(tIter->second);
450        }
451    }
452}
453
454void VirtualMouseBuilder::HandleScroll(const nlohmann::json &model)
455{
456    CALL_DEBUG_ENTER;
457    auto it = model.find("dy");
458    if (it != model.cend() && it->is_number_integer()) {
459        int32_t dy = it.value();
460        std::cout << "[mouse] scroll: " << dy << std::endl;
461        VirtualMouse::GetDevice()->Scroll(dy);
462    }
463}
464
465void VirtualMouseBuilder::HandleWait(const nlohmann::json &model)
466{
467    CALL_DEBUG_ENTER;
468    auto it = model.find("duration");
469    if (it != model.cend() && it->is_number_integer()) {
470        int32_t waitTime = it.value();
471        std::cout << "[mouse] wait for " << waitTime << " milliseconds" << std::endl;
472        VirtualDeviceBuilder::WaitFor("mouse", waitTime);
473    }
474}
475
476void VirtualMouseBuilder::ReadRawInput(const char *path)
477{
478    CALL_DEBUG_ENTER;
479    json model;
480    int32_t result = VirtualDeviceBuilder::ReadFile(path, model);
481    if (result == RET_ERR) {
482        FI_HILOGE("Failed to read the raw mouse data");
483        return;
484    }
485    ReadRawModel(model, MAXIMUM_LEVEL_ALLOWED);
486}
487
488void VirtualMouseBuilder::ReadRawModel(const nlohmann::json &model, int32_t level)
489{
490    CALL_DEBUG_ENTER;
491    if (model.is_object()) {
492        auto typeIter = model.find("type");
493        if (typeIter == model.cend() || !typeIter->is_string() || (std::string(typeIter.value()).compare("raw") != 0)) {
494            std::cout << "Expect raw input data." << std::endl;
495            return;
496        }
497        auto actionIter = model.find("actions");
498        if (actionIter != model.cend() && actionIter->is_array()) {
499            std::for_each(actionIter->cbegin(), actionIter->cend(), [](const auto &item) { ReadRawData(item); });
500        }
501    } else if (model.is_array() && level > 0) {
502        for (const auto &m : model) {
503            ReadRawModel(m, level - 1);
504        }
505    }
506}
507
508void VirtualMouseBuilder::ReadRawData(const nlohmann::json &model)
509{
510    CALL_DEBUG_ENTER;
511    if (!model.is_object()) {
512        FI_HILOGD("Not an object");
513        return;
514    }
515    auto codeIter = model.find("code");
516    if (codeIter == model.cend() || !codeIter->is_number_integer()) {
517        return;
518    }
519    auto typeIter = model.find("type");
520    if (typeIter == model.cend() || !typeIter->is_number_integer()) {
521        return;
522    }
523    auto valueIter = model.find("value");
524    if (valueIter == model.cend() || !valueIter->is_number_integer()) {
525        return;
526    }
527    std::cout << "[virtual mouse] raw input: [" << typeIter.value() << ", " << codeIter.value() << ", " <<
528        valueIter.value() << "]" << std::endl;
529    VirtualMouse::GetDevice()->SendEvent(typeIter.value(), codeIter.value(), valueIter.value());
530}
531} // namespace DeviceStatus
532} // namespace Msdp
533} // namespace OHOS