#pragma once #ifdef _WIN32 #include "SafeWindows.h" #include #endif #include "AsstConf.h" #include #include #include #include #include #include #include #include #include #include #include "NoWarningCVMat.h" #include "AsstMsg.h" #include "AsstTypes.h" #include "SingletonHolder.hpp" namespace asst { class Controller { public: Controller(AsstCallback callback, void* callback_arg); Controller(const Controller&) = delete; Controller(Controller&&) = delete; ~Controller(); bool connect(const std::string& adb_path, const std::string& address, const std::string& config); bool inited() const noexcept; void set_exit_flag(bool* flag); const std::string& get_uuid() const; cv::Mat get_image(bool raw = false); std::vector get_image_encode() const; /* 开启游戏、点击和滑动都是异步执行,返回该任务的id */ std::optional start_game(const std::string& client_type, bool block = true); std::optional stop_game(bool block = true); int click(const Point& p, bool block = true); int click(const Rect& rect, bool block = true); int click_without_scale(const Point& p, bool block = true); int click_without_scale(const Rect& rect, bool block = true); static constexpr int SwipeExtraDelayDefault = 1000; int swipe(const Point& p1, const Point& p2, int duration = 0, bool block = true, int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false); int swipe(const Rect& r1, const Rect& r2, int duration = 0, bool block = true, int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false); int swipe_without_scale(const Point& p1, const Point& p2, int duration = 0, bool block = true, int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false); int swipe_without_scale(const Rect& r1, const Rect& r2, int duration = 0, bool block = true, int extra_delay = SwipeExtraDelayDefault, bool extra_swipe = false); void wait(unsigned id) const noexcept; // 异形屏矫正 // Rect shaped_correct(const Rect& rect) const; std::pair get_scale_size() const noexcept; Controller& operator=(const Controller&) = delete; Controller& operator=(Controller&&) = delete; private: bool need_exit() const; void pipe_working_proc(); std::optional call_command(const std::string& cmd, int64_t timeout = 20000, bool recv_by_socket = false, bool allow_reconnect = true); int push_cmd(const std::string& cmd); bool release(); void kill_adb_daemon(); bool set_inited(bool inited); void try_to_close_socket() noexcept; std::optional try_to_init_socket(const std::string& local_address); using DecodeFunc = std::function; bool screencap(); bool screencap(const std::string& cmd, const DecodeFunc& decode_func, bool by_socket = false); void clear_lf_info(); cv::Mat get_resized_image() const; Point rand_point_in_rect(const Rect& rect); void random_delay() const; void clear_info() noexcept; void callback(AsstMsg msg, const json::value& details); // 转换 data 中的 CRLF 为 LF:有些模拟器自带的 adb,exec-out 输出的 \n 会被替换成 \r\n, // 导致解码错误,所以这里转一下回来(点名批评 mumu 和雷电) static bool convert_lf(std::string& data); bool* m_exit_flag = nullptr; AsstCallback m_callback; void* m_callback_arg = nullptr; std::minstd_rand m_rand_engine; std::mutex m_socket_mutex; #ifdef _WIN32 ASST_AUTO_DEDUCED_ZERO_INIT_START WSADATA m_wsa_data {}; SOCKET m_server_sock = INVALID_SOCKET; sockaddr_in m_server_addr {}; LPFN_ACCEPTEX m_AcceptEx = nullptr; ASST_AUTO_DEDUCED_ZERO_INIT_END #else static constexpr int PIPE_READ = 0; static constexpr int PIPE_WRITE = 1; int m_pipe_in[2] = { 0 }; int m_pipe_out[2] = { 0 }; int m_child = 0; #endif struct AdbProperty { /* command */ std::string connect; std::string click; std::string swipe; std::string screencap_raw_by_nc; std::string screencap_raw_with_gzip; std::string screencap_encode; std::string release; std::string start; std::string stop; /* properties */ enum class ScreencapEndOfLine { UnknownYet, CRLF, LF, CR } screencap_end_of_line = ScreencapEndOfLine::UnknownYet; enum class ScreencapMethod { UnknownYet, // Default, RawByNc, RawWithGzip, Encode } screencap_method = ScreencapMethod::UnknownYet; } m_adb; std::string m_uuid; inline static std::string m_adb_release; // 开了 adb daemon,但是没连上模拟器的时候, // m_adb 并不会存下 release 的命令,但最后仍然需要一次释放。 std::pair m_scale_size = { WindowWidthDefault, WindowHeightDefault }; double m_control_scale = 1.0; int m_width = 0; int m_height = 0; bool m_support_socket = false; bool m_server_started = false; bool m_inited = false; inline static int m_instance_count = 0; mutable std::shared_mutex m_image_mutex; cv::Mat m_cache_image; bool m_thread_exit = false; // bool m_thread_idle = true; std::mutex m_cmd_queue_mutex; std::condition_variable m_cmd_condvar; std::queue m_cmd_queue; std::atomic m_completed_id = 0; unsigned m_push_id = 0; // push_id的自增总是伴随着queue的push,肯定是要上锁的,所以没必要原子 std::thread m_cmd_thread; private: #ifdef _WIN32 // for Windows socket class WsaHelper : public SingletonHolder { friend class SingletonHolder; public: virtual ~WsaHelper() override { WSACleanup(); } bool operator()() const noexcept { return m_supports; } private: WsaHelper() { m_supports = WSAStartup(MAKEWORD(2, 2), &m_wsa_data) == 0 && m_wsa_data.wVersion == MAKEWORD(2, 2); } WSADATA m_wsa_data = { 0 }; bool m_supports = false; }; #endif }; } // namespace asst