From b353b4b5b3432988c26c98ef08a8408d1e9b1e96 Mon Sep 17 00:00:00 2001 From: Mk16kawai <1041324852hzq@sina.com> Date: Fri, 29 Nov 2024 10:21:39 +0800 Subject: [PATCH] Added: lvgl simultaneous use of touchscreen and USB mouse. USB mouse hot swappable. --- components/3rd_party/lvgl/driver/mouse.cpp | 174 +------- components/3rd_party/lvgl/driver/mouse.h | 2 +- .../3rd_party/lvgl/driver/pointing_device.cpp | 374 ++++++++++++++++++ .../3rd_party/lvgl/driver/pointing_device.hpp | 10 + components/3rd_party/lvgl/src/maix_lvgl.cpp | 23 +- 5 files changed, 400 insertions(+), 183 deletions(-) create mode 100644 components/3rd_party/lvgl/driver/pointing_device.cpp create mode 100644 components/3rd_party/lvgl/driver/pointing_device.hpp diff --git a/components/3rd_party/lvgl/driver/mouse.cpp b/components/3rd_party/lvgl/driver/mouse.cpp index 877af6c8..f71fdbb5 100644 --- a/components/3rd_party/lvgl/driver/mouse.cpp +++ b/components/3rd_party/lvgl/driver/mouse.cpp @@ -23,6 +23,8 @@ #include "monitor.h" +#include + /********************* * DEFINES *********************/ @@ -48,176 +50,22 @@ #if CONFIG_LVGL_USE_MOUSE -static int mouse_fd = -1; -// static lv_obj_t * mouse_cursor = nullptr; - -bool is_usb_mouse(const std::string& device) { - std::string command = "udevadm info --name=" + device + " 2>/dev/null"; - FILE* pipe = ::popen(command.c_str(), "r"); - if (!pipe) { - return false; - } - - char buffer[128]; - std::ostringstream result; - while (::fgets(buffer, sizeof(buffer), pipe) != nullptr) { - result << buffer; - } - - ::pclose(pipe); - - std::string output = result.str(); - if (output.find("ID_INPUT_MOUSE=1") != std::string::npos) { - maix::log::info("found usb mouse: %s", device.c_str()); - return true; - } - return false; -} - -std::string find_usb_mice() { - maix::log::info("%s, %d", __PRETTY_FUNCTION__, __LINE__); - DIR* dir; - struct dirent* ent; - - if ((dir = ::opendir("/dev/input")) == nullptr) { - maix::log::error("Could not open /dev/input"); - return ""; - } - - int cnt = 0; - while ((ent = readdir(dir)) != nullptr) { - if (::strncmp(ent->d_name, "event", 5) != 0 && strcmp(ent->d_name, "mice") != 0) - continue; - std::string device_path = "/dev/input/" + std::string(ent->d_name); - // maix::log::info("device: %s", device_path.c_str()); - if (is_usb_mouse(device_path)) - return device_path; - ++cnt; - } - closedir(dir); - maix::log::info("device cnt: %d", cnt); - - return ""; -} - -static void linux_mouse_init(const std::string& device) +int mouse_init([[maybe_unused]]lv_indev_t * indev_drv) { - maix::log::info("linux mouse init"); - mouse_fd = ::open(device.c_str(), O_RDONLY | O_NONBLOCK); - if (mouse_fd < 0) { - maix::log::error("Unable to open mouse device: %s, use touchscreen", device.c_str()); - return; - } + std::stringstream ss; + ss << "In " << __FILE__ << " line:" << __LINE__ << "\n\tUse deprecated function: " << __PRETTY_FUNCTION__; + throw std::runtime_error(ss.str()); + return 0; } -static void linux_mouse_read(lv_indev_t *indev_drv, lv_indev_data_t *data) +void mouse_read([[maybe_unused]]lv_indev_t * indev, lv_indev_data_t * data) { - (void) indev_drv; // Unused - - int w, h; - monitor_rect(&w, &h); - - static int x = 0, y = 0; - static bool pressed = false; - - struct input_event ie; - ssize_t n = read(mouse_fd, &ie, sizeof(struct input_event)); - - if (n != (ssize_t)sizeof(struct input_event)) { - data->point.x = (lv_coord_t)x; - data->point.y = (lv_coord_t)y; - data->state = pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; - data->continue_reading = false; - return; - } - - if (ie.type == EV_REL) { - if (ie.code == REL_X) { - x += ie.value; - x = (x<0) ? 0 : x; - x = (x>w) ? w : x; - } else if (ie.code == REL_Y) { - y += ie.value; - y = (y<0) ? 0 : y; - y = (y>h) ? h : y; - } - // } else if (ie.code == REL_WHEEL) { - // const int wheel_data = ie.value * (h/10); - // lv_obj_t* child = lv_obj_get_child(lv_scr_act(), 0); - // lv_obj_scroll_by(child, 0, wheel_data, LV_ANIM_ON); - // } - } else if (ie.type == EV_KEY) { - if (ie.code == BTN_LEFT){ - pressed = (ie.value == 1); - } - } - - data->point.x = (lv_coord_t)x; - data->point.y = (lv_coord_t)y; - data->state = pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; - data->continue_reading = true; - - // maix::log::info("x:%d, y:%d, s: %u", x, y, pressed); -} - -static void touch_screen_read(lv_indev_t * indev_drv, lv_indev_data_t * data) -{ - (void) indev_drv; /*Unused*/ - - static int x, y; - static bool pressed, continue_reading; - if(maix::maix_touchscreen->read0(x, y, pressed) == maix::err::ERR_NOT_READY) { - data->point.x = (lv_coord_t)x; - data->point.y = (lv_coord_t)y; - data->state = pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; - return; - } - continue_reading = maix::maix_touchscreen->available(); - - data->point.x = (lv_coord_t)x; - data->point.y = (lv_coord_t)y; - data->state = pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; - data->continue_reading = continue_reading; -} - -/** - * Initialize the mouse - */ -MouseInputDevice mouse_init(lv_indev_t * indev_drv) -{ - // maix::thread::Thread t = maix::thread::Thread([](void *args){ - // lv_indev_t *indev_drv = (lv_indev_t *)args; - // while (!maix::app::need_exit()) - // { - // maix::maix_touchscreen->available(-1); - // printf("mouse thread read\n"); - // // lv_indev_read(indev_drv); - // } - // }, indev_drv); - // t.detach(); - const auto usb_mouse_device = find_usb_mice(); - if (usb_mouse_device.empty()) { - maix::log::info("type: touchscreen"); - return MouseInputDevice::TOUCHSCREEN; - } - maix::log::info("type: usb mouse"); - linux_mouse_init(usb_mouse_device); - return MouseInputDevice::USB_MOUSE; + std::stringstream ss; + ss << "In " << __FILE__ << " line:" << __LINE__ << "\n\tUse deprecated function: " << __PRETTY_FUNCTION__; + throw std::runtime_error(ss.str()); } -/** - * Get the current position and state of the mouse - * @param indev_drv pointer to the related input device driver - * @param data store the mouse data here - */ -void mouse_read(lv_indev_t * indev_drv, lv_indev_data_t * data) -{ - if (mouse_fd > 0) - linux_mouse_read(indev_drv, data); - else - touch_screen_read(indev_drv, data); -} #endif /********************** * STATIC FUNCTIONS diff --git a/components/3rd_party/lvgl/driver/mouse.h b/components/3rd_party/lvgl/driver/mouse.h index 972119de..7b352659 100644 --- a/components/3rd_party/lvgl/driver/mouse.h +++ b/components/3rd_party/lvgl/driver/mouse.h @@ -61,7 +61,7 @@ enum class MouseInputDevice { /** * Initialize the mouse */ -MouseInputDevice mouse_init(lv_indev_t * indev_drv); +int mouse_init(lv_indev_t * indev_drv); /** * Get the current position and state of the mouse diff --git a/components/3rd_party/lvgl/driver/pointing_device.cpp b/components/3rd_party/lvgl/driver/pointing_device.cpp new file mode 100644 index 00000000..4b6248fe --- /dev/null +++ b/components/3rd_party/lvgl/driver/pointing_device.cpp @@ -0,0 +1,374 @@ +#include "pointing_device.hpp" + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#include "maix_basic.hpp" +#include "maix_lvgl.hpp" +#include "monitor.h" +#include "mouse.h" + + + +LV_IMAGE_DECLARE(cursor_96); +LV_IMAGE_DECLARE(cursor_48); + + +struct PointingData { + int x; + int y; + bool pressed; + bool continue_reading; +}; + +enum class PointingType { + REL, + ABS +}; + +class PointingDevice { +public: + virtual std::string name() const noexcept = 0; + virtual PointingType type() const noexcept = 0; + virtual bool need_cursor() const noexcept = 0; + virtual bool check_initialized() const noexcept = 0; + virtual bool try_init() = 0; + virtual PointingData read() = 0; + + virtual ~PointingDevice() {} +}; + + +namespace pointing_private { + inline static std::vector> device_list; + inline static lv_obj_t *cursor_rect = nullptr; + inline static int windows_w{0}; + inline static int windows_h{0}; + + + /** + * @brief USB Mouse IMPL + * + */ + class UsbMouseDevice final : public PointingDevice { + public: + virtual std::string name() const noexcept override { + return "USB Mouse"; + } + virtual PointingType type() const noexcept override { + return PointingType::REL; + } + virtual bool check_initialized() const noexcept override { + return this->_init; + } + virtual bool need_cursor() const noexcept override { + return true; + } + virtual bool try_init() override { + if (this->_init) + return true; + + auto now = maix::time::ticks_ms(); + if (now - this->_prev_time_ms < this->_TIME_INTERVAL_MS) + return false; + + // maix::log::info("try_init... %llu", now); + this->_prev_time_ms = now; + + const auto dev = this->find_usb_mice(); + if (dev.empty()) + return false; + this->_mouse_fd = ::open(dev.c_str(), O_RDONLY | O_NONBLOCK); + if (this->_mouse_fd < 0) { + maix::log::error("Unable to open mouse device: %s", dev.c_str()); + return false; + } + + ::monitor_rect(&this->_w, &this->_h); + this->_data.x = 0; + this->_data.y = 0; + this->_data.pressed = false; + this->_data.continue_reading = false; + + this->_dev_path = dev; + this->_init = true; + maix::log::info("USB Mouse<%s> connected!", dev.c_str()); + + this->_empty_data.x = this->_empty_data.y = -1; + this->_empty_data.pressed = this->_empty_data.continue_reading = false; + return true; + } + virtual PointingData read() override { + if (!std::filesystem::exists(this->_dev_path)) { + maix::log::info("USB Mouse<%s> disconnected!", this->_dev_path.c_str()); + this->_init = false; + this->_blacklist.clear(); + if (this->_mouse_fd >= 0) + ::close(this->_mouse_fd); + this->_mouse_fd = -1; + this->_dev_path.clear(); + this->_prev_cnt = 0; + this->_prev_time_ms = 0; + + return this->_empty_data; + } + + struct input_event ie; + ssize_t n = ::read(this->_mouse_fd, &ie, sizeof(struct input_event)); + + if (n != (ssize_t)sizeof(struct input_event)) { + PointingData data2; + data2.x = -1; + data2.y = -1; + data2.pressed = false; + data2.continue_reading = false; + return data2; + } + + this->_data.continue_reading = true; + if (ie.type == EV_REL) { + if (ie.code == REL_X) { + this->_data.x = ie.value/2; + } else if (ie.code == REL_Y) { + this->_data.y = ie.value/2; + } + // } else if (ie.code == REL_WHEEL) { + // const int wheel_data = ie.value * (h/10); + // lv_obj_t* child = lv_obj_get_child(lv_scr_act(), 0); + // lv_obj_scroll_by(child, 0, wheel_data, LV_ANIM_ON); + // } + } else if (ie.type == EV_KEY) { + if (ie.code == BTN_LEFT){ + this->_data.pressed = (ie.value == 1); + } + } + // else return this->_empty_data; + + return this->_data; + } + private: + bool is_usb_mouse(const std::string& device) { + // maix::log::info("input device: %s", device.c_str()); + // maix::time::sleep_ms(10); + std::string command = "udevadm info --name=" + device + " 2>/dev/null"; + FILE* pipe = ::popen(command.c_str(), "r"); + if (!pipe) { + return false; + } + + char buffer[256]; + std::ostringstream result; + while (::fgets(buffer, sizeof(buffer), pipe) != nullptr) { + result << buffer; + } + + ::pclose(pipe); + + std::string output = result.str(); + + // maix::log::info("input device info:\n%s", output.c_str()); + + if (output.find("ID_INPUT_MOUSE=1") != std::string::npos) { + // maix::log::info("input device<%s> is an USB Mouse!", device.c_str()); + return true; + } + // maix::log::info("input device<%s> is not a USB Mouse!", device.c_str()); + return false; + } + std::string find_usb_mice() { + DIR* dir; + struct dirent* ent; + static bool need_recheck_by_deley = false; + static const uint64_t DELAY_MS = 500; + static uint64_t ltime = 0; + + if ((dir = ::opendir("/dev/input")) == nullptr) { + maix::log::error("Could not open /dev/input"); + return ""; + } + + int cnt = 0; + while ((ent = ::readdir(dir)) != nullptr) { + ++cnt; + if (::strncmp(ent->d_name, "event", 5) != 0 && ::strcmp(ent->d_name, "mice") != 0) + continue; + std::string device_path = "/dev/input/" + std::string(ent->d_name); + if (std::find(this->_blacklist.begin(), this->_blacklist.end(), device_path) != this->_blacklist.end()) + continue; + // maix::log::info("device: %s", device_path.c_str()); + if (this->is_usb_mouse(device_path)) + return device_path; + this->_blacklist.push_back(device_path); + } + ::closedir(dir); + + if (cnt > this->_prev_cnt) { + // maix::log::info("need recheck input device!"); + need_recheck_by_deley = true; + ltime = maix::time::ticks_ms(); + } + + if (need_recheck_by_deley && maix::time::ticks_ms()-ltime>=DELAY_MS) { + // maix::log::info("recheck input device start!"); + need_recheck_by_deley = false; + this->_blacklist.clear(); + } + + // maix::log::info("device cnt: %d", cnt); + this->_prev_cnt = cnt; + return ""; + } + private: + bool _init{false}; + int _mouse_fd{-1}; + std::vector _blacklist; + int _w{0}; + int _h{0}; + PointingData _data; + std::filesystem::path _dev_path; + int _prev_cnt{0}; + uint64_t _prev_time_ms{0}; + static const uint64_t _TIME_INTERVAL_MS = 1000; + PointingData _empty_data; + }; + + /** + * @brief TouchScreen IMPL + * + */ + class TouchScreenDevice : public PointingDevice { + public: + TouchScreenDevice() = default; + TouchScreenDevice(const TouchScreenDevice&) = delete; + TouchScreenDevice& operator=(const TouchScreenDevice&) = delete; + + virtual std::string name() const noexcept override { + return "TouchScreen"; + } + virtual PointingType type() const noexcept override { + return PointingType::ABS; + } + virtual bool check_initialized() const noexcept override { + return true; + } + virtual bool try_init() override { + return true; + } + virtual bool need_cursor() const noexcept override { + return false; + } + virtual PointingData read() { + int x = 0; + int y = 0; + bool pressed = false; + if(maix::maix_touchscreen->read0(x, y, pressed) == maix::err::ERR_NOT_READY) { + PointingData data2; + data2.x = -1; + data2.y = -1; + data2.pressed = false; + data2.continue_reading = false; + return data2; + } + this->_data.continue_reading = maix::maix_touchscreen->available(); + + this->_data.x = x; + this->_data.y = y; + this->_data.pressed = pressed; + + return this->_data; + } + private: + PointingData _data; + }; + + /* Add your device */ + // class YourDevice : public PointingDevice { + // }; +} + +void pointing_device_init(lv_indev_t * indev_drv) +{ + using namespace pointing_private; + device_list.emplace_back(std::make_unique()); + device_list.emplace_back(std::make_unique()); + /* add your device */ + // device_list.emplace_back(std::make_unique()); + + cursor_rect = lv_img_create(lv_scr_act()); + + ::monitor_rect(&windows_w, &windows_h); + if (windows_w > 640) { + lv_img_set_src(cursor_rect, &cursor_96); + maix::log::info("use cursor size 96x96"); + } else { + lv_img_set_src(cursor_rect, &cursor_48); + maix::log::info("use cursor size 48x48"); + } + lv_obj_set_style_bg_color(cursor_rect, lv_color_hex(0xFF0000), 0); + lv_indev_set_cursor(indev_drv, cursor_rect); + lv_obj_add_flag(cursor_rect, LV_OBJ_FLAG_HIDDEN); +} + +void pointing_device_read([[maybe_unused]]lv_indev_t * indev_drv, lv_indev_data_t * data) +{ + using namespace pointing_private; + static PointingData d { + .x = 0, + .y = 0, + .pressed = false, + .continue_reading = false + }; + static bool prev_cursor_sta = false; + + d.continue_reading = false; + for (auto& i : device_list) { + if (!i->check_initialized()) { + if (!i->try_init()) + continue; + } + auto __d__ = i->read(); + if (__d__.x==-1 && __d__.y==-1) + continue; + if (i->type() == PointingType::ABS) { + d.x = __d__.x; + d.y = __d__.y; + d.pressed = __d__.pressed; + } else { + d.x += __d__.x; + d.x = (d.x>windows_w) ? windows_w : d.x; + d.x = (d.x<0) ? 0 : d.x; + d.y += __d__.y; + d.y = (d.y>windows_h) ? windows_h : d.y; + d.y = (d.y<0) ? 0 : d.y; + d.pressed = __d__.pressed; + } + d.continue_reading = __d__.continue_reading; + if (i->need_cursor() && !prev_cursor_sta) { + lv_obj_remove_flag(cursor_rect, LV_OBJ_FLAG_HIDDEN); + prev_cursor_sta = true; + } else if (!i->need_cursor() && prev_cursor_sta) { + lv_obj_add_flag(cursor_rect, LV_OBJ_FLAG_HIDDEN); + prev_cursor_sta = false; + } + break; + } + + data->point.x = (lv_coord_t)d.x; + data->point.y = (lv_coord_t)d.y; + data->state = d.pressed ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED; + data->continue_reading = d.continue_reading; + +} \ No newline at end of file diff --git a/components/3rd_party/lvgl/driver/pointing_device.hpp b/components/3rd_party/lvgl/driver/pointing_device.hpp new file mode 100644 index 00000000..16327cd7 --- /dev/null +++ b/components/3rd_party/lvgl/driver/pointing_device.hpp @@ -0,0 +1,10 @@ +#ifndef __INPUT_DEVICE_HPP__ +#define __INPUT_DEVICE_HPP__ + +// #include "maix_lvgl.hpp" +#include "lvgl.h" + +void pointing_device_init(lv_indev_t * indev_drv); +void pointing_device_read(lv_indev_t * indev_drv, lv_indev_data_t * data); + +#endif // __INPUT_DEVICE_HPP__ \ No newline at end of file diff --git a/components/3rd_party/lvgl/src/maix_lvgl.cpp b/components/3rd_party/lvgl/src/maix_lvgl.cpp index e06e0331..91155cf9 100644 --- a/components/3rd_party/lvgl/src/maix_lvgl.cpp +++ b/components/3rd_party/lvgl/src/maix_lvgl.cpp @@ -6,6 +6,8 @@ #include "lvgl.h" #include "maix_basic.hpp" +#include "pointing_device.hpp" + // extern "C" { // #include "cursor_48.c" // #include "cursor_96.c" @@ -97,26 +99,9 @@ namespace maix * Use the 'mouse' driver which reads the PC's mouse*/ #if CONFIG_LVGL_USE_MOUSE lv_indev_t * indev = lv_indev_create(); - MouseInputDevice type = mouse_init(indev); lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER); - lv_indev_set_read_cb(indev, mouse_read); - // lv_indev_set_mode(indev, LV_INDEV_MODE_EVENT); - if (type == MouseInputDevice::USB_MOUSE) { - static lv_obj_t *cursor_rect = lv_img_create(lv_scr_act()); - int w; - monitor_rect(&w, nullptr); - if (w > 640) { - lv_img_set_src(cursor_rect, &cursor_96); - maix::log::info("use cursor size 48x48"); - } else { - lv_img_set_src(cursor_rect, &cursor_48); - maix::log::info("use cursor size 96x96"); - } - // w = static_cast((20.0/640)*w); - // lv_obj_set_size(cursor_rect, w, w); - lv_obj_set_style_bg_color(cursor_rect, lv_color_hex(0xFF0000), 0); - lv_indev_set_cursor(indev, cursor_rect); - } + pointing_device_init(indev); + lv_indev_set_read_cb(indev, pointing_device_read); #endif #if USE_KEYBOARD