From 53a30e46f1f8d83d8ce925fbbe682c3ce364077d Mon Sep 17 00:00:00 2001 From: MistEO Date: Sun, 26 Dec 2021 02:28:35 +0800 Subject: [PATCH] =?UTF-8?q?feat.=E5=AE=8C=E6=88=90Linux=E4=B8=8B=E5=AD=90?= =?UTF-8?q?=E8=BF=9B=E7=A8=8B=E9=80=9A=E8=AE=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/MeoAssistant/AsstUtils.hpp | 64 +++++++++-- src/MeoAssistant/Controller.cpp | 171 +++++++++++++++++++----------- src/MeoAssistant/Controller.h | 6 ++ src/MeoAssistant/Linux编译教程.md | 2 +- 4 files changed, 173 insertions(+), 70 deletions(-) diff --git a/src/MeoAssistant/AsstUtils.hpp b/src/MeoAssistant/AsstUtils.hpp index fa915320fb..4f750f119e 100644 --- a/src/MeoAssistant/AsstUtils.hpp +++ b/src/MeoAssistant/AsstUtils.hpp @@ -10,6 +10,8 @@ #include #include #include +#include +#include #endif namespace asst @@ -60,7 +62,7 @@ namespace asst "%04d-%02d-%02d %02d:%02d:%02d.%03d", curtime.wYear, curtime.wMonth, curtime.wDay, curtime.wHour, curtime.wMinute, curtime.wSecond, curtime.wMilliseconds); - + #else // ! _WIN32 struct timeval tv = { 0 }; gettimeofday(&tv, NULL); @@ -166,6 +168,7 @@ namespace asst static std::string callcmd(const std::string& cmdline) { + constexpr int BuffSize = 4096; std::string pipe_str; #ifdef _WIN32 SECURITY_ATTRIBUTES pipe_sec_attr = { 0 }; @@ -174,7 +177,7 @@ namespace asst pipe_sec_attr.bInheritHandle = TRUE; HANDLE pipe_read = nullptr; HANDLE pipe_child_write = nullptr; - CreatePipe(&pipe_read, &pipe_child_write, &pipe_sec_attr, 4096); + CreatePipe(&pipe_read, &pipe_child_write, &pipe_sec_attr, BuffSize); STARTUPINFOA si = { 0 }; si.cb = sizeof(STARTUPINFO); @@ -212,15 +215,58 @@ namespace asst ::CloseHandle(pipe_read); ::CloseHandle(pipe_child_write); - return pipe_str; #else - char buff[4096] = { 0 }; - std::unique_ptr pipe(popen(cmdline.c_str(), "r"), pclose); - if (!pipe) { - return std::string(); + constexpr static int PIPE_READ = 0; + constexpr static int PIPE_WRITE = 1; + int m_pipe_in[2] = { 0 }; + int m_pipe_out[2] = { 0 }; + int pipe_in_ret = pipe(m_pipe_in); + int pipe_out_ret = pipe(m_pipe_out); + fcntl(m_pipe_out[PIPE_READ], F_SETFL, O_NONBLOCK); + int exit_ret = 0; + int m_child = fork(); + if (m_child == 0) { + // child process + dup2(m_pipe_in[PIPE_READ], STDIN_FILENO); + dup2(m_pipe_out[PIPE_WRITE], STDOUT_FILENO); + dup2(m_pipe_out[PIPE_WRITE], STDERR_FILENO); + + // all these are for use by parent only + close(m_pipe_in[PIPE_READ]); + close(m_pipe_in[PIPE_WRITE]); + close(m_pipe_out[PIPE_READ]); + close(m_pipe_out[PIPE_WRITE]); + + exit_ret = execlp("sh", "sh", "-c", cmdline.c_str(), NULL); + exit(exit_ret); } - while (fgets(buff, sizeof(buff), pipe.get()) != nullptr) { - pipe_str += buff; + else if (m_child > 0) { + // parent process + // LogTraceScope("Parent process: " + cmd); + std::unique_ptr pipe_buffer = std::make_unique(BuffSize); + + // close unused file descriptors, these are for child only + close(m_pipe_in[PIPE_READ]); + close(m_pipe_out[PIPE_WRITE]); + do { + ssize_t read_num = read(m_pipe_out[PIPE_READ], pipe_buffer.get(), BuffSize); + + while (read_num > 0) { + pipe_str.append(pipe_buffer.get(), pipe_buffer.get() + read_num); + read_num = read(m_pipe_out[PIPE_READ], pipe_buffer.get(), BuffSize); + }; + + } while (::waitpid(m_child, NULL, WNOHANG) == 0); + + close(m_pipe_in[PIPE_WRITE]); + close(m_pipe_out[PIPE_READ]); + } + else { + // failed to create child process + close(m_pipe_in[PIPE_READ]); + close(m_pipe_in[PIPE_WRITE]); + close(m_pipe_out[PIPE_READ]); + close(m_pipe_out[PIPE_WRITE]); } #endif return pipe_str; diff --git a/src/MeoAssistant/Controller.cpp b/src/MeoAssistant/Controller.cpp index c0e8b3b8d9..051ea28676 100644 --- a/src/MeoAssistant/Controller.cpp +++ b/src/MeoAssistant/Controller.cpp @@ -2,9 +2,13 @@ #ifdef _WIN32 #include +#else +#include +#include +#include #endif -#include +#include #include #include #include @@ -45,13 +49,56 @@ asst::Controller::Controller() m_child_startup_info.hStdInput = m_pipe_child_read; m_child_startup_info.hStdOutput = m_pipe_child_write; m_child_startup_info.hStdError = m_pipe_child_write; +#else + int pipe_in_ret = pipe(m_pipe_in); + int pipe_out_ret = pipe(m_pipe_out); + fcntl(m_pipe_out[PIPE_READ], F_SETFL, O_NONBLOCK); + + if (pipe_in_ret < 0 || pipe_out_ret < 0) { + throw "controller pipe created failed"; + } + #endif auto bind_pipe_working_proc = std::bind(&Controller::pipe_working_proc, this); m_cmd_thread = std::thread(bind_pipe_working_proc); } -bool asst::Controller::connect_adb(const std::string& address) +asst::Controller::~Controller() +{ + LogTraceFunction; + + m_thread_exit = true; + //m_thread_idle = true; + m_cmd_condvar.notify_all(); + m_completed_id = UINT_MAX; // make all WinMacor::wait to exit + + if (m_cmd_thread.joinable()) { + m_cmd_thread.join(); + } + +#ifndef _WIN32 + if (m_child) { +#else + if (true) { +#endif + call_command(m_emulator_info.adb.release); + } + +#ifdef _WIN32 + ::CloseHandle(m_pipe_read); + ::CloseHandle(m_pipe_write); + ::CloseHandle(m_pipe_child_read); + ::CloseHandle(m_pipe_child_write); +#else + close(m_pipe_in[PIPE_READ]); + close(m_pipe_in[PIPE_WRITE]); + close(m_pipe_out[PIPE_READ]); + close(m_pipe_out[PIPE_WRITE]); +#endif +} + +bool asst::Controller::connect_adb(const std::string & address) { LogTraceScope("connect_adb " + address); @@ -111,32 +158,7 @@ bool asst::Controller::connect_adb(const std::string& address) return true; } -asst::Controller::~Controller() -{ - LogTraceFunction; - - m_thread_exit = true; - //m_thread_idle = true; - m_cmd_condvar.notify_all(); - m_completed_id = UINT_MAX; // make all WinMacor::wait to exit - - if (m_cmd_thread.joinable()) { - m_cmd_thread.join(); - } - - if (!m_emulator_info.adb.release.empty()) { - call_command(m_emulator_info.adb.release); - } - -#ifdef _WIN32 - ::CloseHandle(m_pipe_read); - ::CloseHandle(m_pipe_write); - ::CloseHandle(m_pipe_child_read); - ::CloseHandle(m_pipe_child_write); -#endif -} - -asst::Rect asst::Controller::shaped_correct(const Rect& rect) const +asst::Rect asst::Controller::shaped_correct(const Rect & rect) const { if (rect.empty()) { return rect; @@ -231,17 +253,10 @@ void asst::Controller::set_dirname(std::string dirname) noexcept m_dirname = std::move(dirname); } -bool asst::Controller::try_capture(const EmulatorInfo& info, bool without_handle) +bool asst::Controller::try_capture(const EmulatorInfo & info, bool without_handle) { LogTraceScope("try_capture | " + info.name); -#ifndef _WIN32 - if (without_handle) { - Log.error("Capture handle is only supported on Windows!"); - return false; - } -#endif - const HandleInfo& handle_info = info.handle; std::string adb_path; @@ -311,11 +326,18 @@ bool asst::Controller::try_capture(const EmulatorInfo& info, bool without_handle adb_path = emulator_path.substr(0, emulator_path.find_last_of('\\') + 1); adb_path = '"' + utils::string_replace_all(m_emulator_info.adb.path, "[EmulatorPath]", adb_path) + '"'; adb_path = utils::string_replace_all(adb_path, "[ExecDir]", m_dirname); +#else + Log.error("Capture handle is only supported on Windows!"); + return false; #endif } else { // 使用辅助自带的标准adb m_emulator_info = info; +#ifdef _WIN32 adb_path = '"' + utils::string_replace_all(m_emulator_info.adb.path, "[ExecDir]", m_dirname) + '"'; +#else + adb_path = m_emulator_info.adb.path; +#endif } m_emulator_info.adb.path = std::move(adb_path); @@ -367,7 +389,7 @@ bool asst::Controller::try_capture(const EmulatorInfo& info, bool without_handle // } //} -std::pair> asst::Controller::call_command(const std::string& cmd) +std::pair> asst::Controller::call_command(const std::string & cmd) { LogTraceFunction; @@ -406,19 +428,47 @@ std::pair> asst::Controller::call_command(const ::CloseHandle(process_info.hThread); #else - int exit_ret = 1; - std::unique_ptr pipe(popen(cmd.c_str(), "r"), pclose); - if (!pipe) { - return make_pair(false, std::vector()); - } - char pipe_buffer[4096] = { 0 }; - while (fgets(pipe_buffer, sizeof(pipe_buffer), pipe.get()) != nullptr) { - pipe_data.insert(pipe_data.end(), pipe_buffer, pipe_buffer + sizeof(pipe_buffer)); - } + int exit_ret = 0; + m_child = fork(); + if (m_child == 0) { + // child process + LogTraceScope("Child process: " + cmd); + std::cout << ("Child process: " + cmd) << std::endl; + dup2(m_pipe_in[PIPE_READ], STDIN_FILENO); + dup2(m_pipe_out[PIPE_WRITE], STDOUT_FILENO); + dup2(m_pipe_out[PIPE_WRITE], STDERR_FILENO); + + // all these are for use by parent only + // close(m_pipe_in[PIPE_READ]); + // close(m_pipe_in[PIPE_WRITE]); + // close(m_pipe_out[PIPE_READ]); + // close(m_pipe_out[PIPE_WRITE]); + + exit_ret = execlp("sh", "sh", "-c", cmd.c_str(), NULL); + exit(exit_ret); + } + else if (m_child > 0) { + // parent process + // LogTraceScope("Parent process: " + cmd); + constexpr int BuffSize = 4096; + std::unique_ptr pipe_buffer = std::make_unique(BuffSize); + do { + ssize_t read_num = read(m_pipe_out[PIPE_READ], pipe_buffer.get(), BuffSize); + + while (read_num > 0) { + pipe_data.insert(pipe_data.end(), pipe_buffer.get(), pipe_buffer.get() + read_num); + read_num = read(m_pipe_out[PIPE_READ], pipe_buffer.get(), BuffSize); + }; + + } while (::waitpid(m_child, NULL, WNOHANG) == 0); + } + else { + // failed to create child process + } #endif - Log.trace("Call", cmd, "ret", exit_ret, ", output size:", pipe_data.size()); + Log.trace("Call `", cmd, "` ret", exit_ret, ", output size:", pipe_data.size()); if (!pipe_data.empty() && pipe_data.size() < 4096) { Log.trace("output:", std::string(pipe_data.cbegin(), pipe_data.cend())); } @@ -426,7 +476,7 @@ std::pair> asst::Controller::call_command(const return make_pair(!exit_ret, std::move(pipe_data)); } -void asst::Controller::convert_lf(std::vector& data) +void asst::Controller::convert_lf(std::vector&data) { LogTraceFunction; @@ -459,7 +509,7 @@ void asst::Controller::convert_lf(std::vector& data) data.erase(next_iter, data.end()); } -asst::Point asst::Controller::rand_point_in_rect(const Rect& rect) +asst::Point asst::Controller::rand_point_in_rect(const Rect & rect) { int x = 0, y = 0; if (rect.width == 0) { @@ -500,7 +550,7 @@ void asst::Controller::random_delay() const } } -int asst::Controller::push_cmd(const std::string& cmd) +int asst::Controller::push_cmd(const std::string & cmd) { random_delay(); @@ -551,7 +601,7 @@ bool asst::Controller::screencap() //m_cache_image = std::move(temp_image); } -int asst::Controller::click(const Point& p, bool block) +int asst::Controller::click(const Point & p, bool block) { int x = p.x * m_control_scale; int y = p.y * m_control_scale; @@ -560,12 +610,12 @@ int asst::Controller::click(const Point& p, bool block) return click_without_scale(Point(x, y), block); } -int asst::Controller::click(const Rect& rect, bool block) +int asst::Controller::click(const Rect & rect, bool block) { return click(rand_point_in_rect(rect), block); } -int asst::Controller::click_without_scale(const Point& p, bool block) +int asst::Controller::click_without_scale(const Point & p, bool block) { if (p.x < 0 || p.x >= m_emulator_info.adb.display_width || p.y < 0 || p.y >= m_emulator_info.adb.display_height) { Log.error("click point out of range"); @@ -579,12 +629,12 @@ int asst::Controller::click_without_scale(const Point& p, bool block) return id; } -int asst::Controller::click_without_scale(const Rect& rect, bool block) +int asst::Controller::click_without_scale(const Rect & rect, bool block) { return click_without_scale(rand_point_in_rect(rect), block); } -int asst::Controller::swipe(const Point& p1, const Point& p2, int duration, bool block, int extra_delay, bool extra_swipe) +int asst::Controller::swipe(const Point & p1, const Point & p2, int duration, bool block, int extra_delay, bool extra_swipe) { int x1 = p1.x * m_control_scale; int y1 = p1.y * m_control_scale; @@ -595,12 +645,12 @@ int asst::Controller::swipe(const Point& p1, const Point& p2, int duration, bool return swipe_without_scale(Point(x1, y1), Point(x2, y2), duration, block, extra_delay, extra_swipe); } -int asst::Controller::swipe(const Rect& r1, const Rect& r2, int duration, bool block, int extra_delay, bool extra_swipe) +int asst::Controller::swipe(const Rect & r1, const Rect & r2, int duration, bool block, int extra_delay, bool extra_swipe) { return swipe(rand_point_in_rect(r1), rand_point_in_rect(r2), duration, block, extra_delay, extra_swipe); } -int asst::Controller::swipe_without_scale(const Point& p1, const Point& p2, int duration, bool block, int extra_delay, bool extra_swipe) +int asst::Controller::swipe_without_scale(const Point & p1, const Point & p2, int duration, bool block, int extra_delay, bool extra_swipe) { if (p1.x < 0 || p1.x >= m_emulator_info.adb.display_width || p1.y < 0 || p1.y >= m_emulator_info.adb.display_height || p2.x < 0 || p2.x >= m_emulator_info.adb.display_width || p2.y < 0 || p2.y >= m_emulator_info.adb.display_height) { Log.error("swipe point out of range"); @@ -654,13 +704,14 @@ int asst::Controller::swipe_without_scale(const Point& p1, const Point& p2, int return id; } -int asst::Controller::swipe_without_scale(const Rect& r1, const Rect& r2, int duration, bool block, int extra_delay, bool extra_swipe) +int asst::Controller::swipe_without_scale(const Rect & r1, const Rect & r2, int duration, bool block, int extra_delay, bool extra_swipe) { return swipe_without_scale(rand_point_in_rect(r1), rand_point_in_rect(r2), duration, block, extra_delay, extra_swipe); } cv::Mat asst::Controller::get_image(bool raw) { + LogTraceFunction; // 有些模拟器adb偶尔会莫名其妙截图失败,多试几次 for (int i = 0; i != 20; ++i) { if (screencap()) { @@ -675,9 +726,9 @@ cv::Mat asst::Controller::get_image(bool raw) if (m_cache_image.empty()) { return m_cache_image; } - const static cv::Size size(m_scale_size.first, m_scale_size.second); + const static cv::Size newsize(m_scale_size.first, m_scale_size.second); cv::Mat resize_mat; - cv::resize(m_cache_image, resize_mat, size, cv::INPAINT_NS); + cv::resize(m_cache_image, resize_mat, newsize, cv::INPAINT_NS); return resize_mat; } } diff --git a/src/MeoAssistant/Controller.h b/src/MeoAssistant/Controller.h index 4ea2dc3a57..2cd1d85ff4 100644 --- a/src/MeoAssistant/Controller.h +++ b/src/MeoAssistant/Controller.h @@ -100,6 +100,12 @@ namespace asst HANDLE m_pipe_child_write = nullptr; // 子进程的写管道句柄 SECURITY_ATTRIBUTES m_pipe_sec_attr = { 0 }; // 管道安全描述符 STARTUPINFOA m_child_startup_info = { 0 }; // 子进程启动信息 +#else + constexpr static int PIPE_READ = 0; + constexpr static int PIPE_WRITE = 1; + int m_pipe_in[2] = { 0 }; + int m_pipe_out[2] = { 0 }; + int m_child = 0; #endif EmulatorInfo m_emulator_info; diff --git a/src/MeoAssistant/Linux编译教程.md b/src/MeoAssistant/Linux编译教程.md index be199d06de..52599d51aa 100644 --- a/src/MeoAssistant/Linux编译教程.md +++ b/src/MeoAssistant/Linux编译教程.md @@ -4,7 +4,7 @@ 虽然没想明白为什么 Linux 下需要用助手挂模拟器,嘛总之大家有这个需求还是弄一下_(:з」∠)_ -作者对 Linux 一些环境问题也是半懂不懂,所以虽说是教程,也只是分享一下自己的踩坑经历,如果遇到其他问题欢迎提出 ISSUE 一起讨论下 orz +作者是 Linux 小白,所以虽说是教程,也只是分享一下自己的踩坑经历,如果遇到其他问题欢迎提出 ISSUE 一起讨论下 orz ## 下载编译第三方库