feat.完成Linux下子进程通讯

This commit is contained in:
MistEO
2021-12-26 02:28:35 +08:00
parent 8658395fee
commit 53a30e46f1
4 changed files with 173 additions and 70 deletions

View File

@@ -2,9 +2,13 @@
#ifdef _WIN32
#include <WinUser.h>
#else
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
#endif
#include <stdint.h>
#include <stdint.h>
#include <algorithm>
#include <chrono>
#include <regex>
@@ -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<bool, std::vector<unsigned char>> asst::Controller::call_command(const std::string& cmd)
std::pair<bool, std::vector<unsigned char>> asst::Controller::call_command(const std::string & cmd)
{
LogTraceFunction;
@@ -406,19 +428,47 @@ std::pair<bool, std::vector<unsigned char>> asst::Controller::call_command(const
::CloseHandle(process_info.hThread);
#else
int exit_ret = 1;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
if (!pipe) {
return make_pair(false, std::vector<unsigned char>());
}
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<uchar> pipe_buffer = std::make_unique<uchar>(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<bool, std::vector<unsigned char>> asst::Controller::call_command(const
return make_pair(!exit_ret, std::move(pipe_data));
}
void asst::Controller::convert_lf(std::vector<unsigned char>& data)
void asst::Controller::convert_lf(std::vector<unsigned char>&data)
{
LogTraceFunction;
@@ -459,7 +509,7 @@ void asst::Controller::convert_lf(std::vector<unsigned char>& 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;
}
}