From 0d591f769d16748a6433ecdb7514cc82409ba3cd Mon Sep 17 00:00:00 2001 From: dijunkun Date: Thu, 6 Nov 2025 17:31:06 +0800 Subject: [PATCH] [feat] send control commands via JSON --- src/device_controller/device_controller.h | 112 +++++++++++++++++++++- src/gui/render_callback.cpp | 83 ++++++++-------- submodules/minirtc | 2 +- xmake.lua | 3 +- 4 files changed, 153 insertions(+), 47 deletions(-) diff --git a/src/device_controller/device_controller.h b/src/device_controller/device_controller.h index 11410fc..a02a83b 100644 --- a/src/device_controller/device_controller.h +++ b/src/device_controller/device_controller.h @@ -9,7 +9,11 @@ #include +#include +#include + #include "display_info.h" +using json = nlohmann::json; namespace crossdesk { @@ -55,7 +59,7 @@ typedef struct { int* bottom; } HostInfo; -typedef struct { +struct RemoteAction { ControlType type; union { Mouse m; @@ -64,7 +68,111 @@ typedef struct { bool a; int d; }; -} RemoteAction; + + // parse + std::string to_json() const { return ToJson(*this); } + + bool from_json(const std::string& json_str) { + RemoteAction temp; + if (!FromJson(json_str, temp)) return false; + *this = temp; + return true; + } + + static std::string ToJson(const RemoteAction& a) { + json j; + j["type"] = a.type; + switch (a.type) { + case ControlType::mouse: + j["mouse"] = { + {"x", a.m.x}, {"y", a.m.y}, {"s", a.m.s}, {"flag", a.m.flag}}; + break; + case ControlType::keyboard: + j["keyboard"] = {{"key_value", a.k.key_value}, {"flag", a.k.flag}}; + break; + case ControlType::audio_capture: + j["audio_capture"] = a.a; + break; + case ControlType::display_id: + j["display_id"] = a.d; + break; + case ControlType::host_infomation: { + json displays = json::array(); + for (size_t idx = 0; idx < a.i.display_num; idx++) { + displays.push_back( + {{"name", a.i.display_list ? a.i.display_list[idx] : ""}, + {"left", a.i.left ? a.i.left[idx] : 0}, + {"top", a.i.top ? a.i.top[idx] : 0}, + {"right", a.i.right ? a.i.right[idx] : 0}, + {"bottom", a.i.bottom ? a.i.bottom[idx] : 0}}); + } + + j["host_info"] = {{"host_name", a.i.host_name}, + {"display_num", a.i.display_num}, + {"displays", displays}}; + break; + } + } + return j.dump(); + } + + static bool FromJson(const std::string& json_str, RemoteAction& out) { + try { + json j = json::parse(json_str); + out.type = (ControlType)j.at("type").get(); + switch (out.type) { + case ControlType::mouse: + out.m.x = j.at("mouse").at("x").get(); + out.m.y = j.at("mouse").at("y").get(); + out.m.s = j.at("mouse").at("s").get(); + out.m.flag = (MouseFlag)j.at("mouse").at("flag").get(); + break; + case ControlType::keyboard: + out.k.key_value = j.at("keyboard").at("key_value").get(); + out.k.flag = (KeyFlag)j.at("keyboard").at("flag").get(); + break; + case ControlType::audio_capture: + out.a = j.at("audio_capture").get(); + break; + case ControlType::display_id: + out.d = j.at("display_id").get(); + break; + case ControlType::host_infomation: { + std::string host_name = + j.at("host_info").at("host_name").get(); + strncpy(out.i.host_name, host_name.c_str(), sizeof(out.i.host_name)); + out.i.host_name[sizeof(out.i.host_name) - 1] = '\0'; + out.i.host_name_size = host_name.size(); + + out.i.display_num = j.at("host_info").at("display_num").get(); + auto displays = j.at("host_info").at("displays"); + + out.i.display_list = + (char**)malloc(out.i.display_num * sizeof(char*)); + out.i.left = (int*)malloc(out.i.display_num * sizeof(int)); + out.i.top = (int*)malloc(out.i.display_num * sizeof(int)); + out.i.right = (int*)malloc(out.i.display_num * sizeof(int)); + out.i.bottom = (int*)malloc(out.i.display_num * sizeof(int)); + + for (size_t idx = 0; idx < out.i.display_num; idx++) { + std::string name = displays[idx].at("name").get(); + out.i.display_list[idx] = (char*)malloc(name.size() + 1); + strcpy(out.i.display_list[idx], name.c_str()); + out.i.left[idx] = displays[idx].at("left").get(); + out.i.top[idx] = displays[idx].at("top").get(); + out.i.right[idx] = displays[idx].at("right").get(); + out.i.bottom[idx] = displays[idx].at("bottom").get(); + } + break; + } + } + return true; + } catch (const std::exception& e) { + printf("Failed to parse RemoteAction JSON: %s\n", e.what()); + return false; + } + } +}; // int key_code, bool is_down typedef void (*OnKeyAction)(int, bool, void*); diff --git a/src/gui/render_callback.cpp b/src/gui/render_callback.cpp index a03b13e..eb40e07 100644 --- a/src/gui/render_callback.cpp +++ b/src/gui/render_callback.cpp @@ -28,8 +28,10 @@ int Render::SendKeyCommand(int key_code, bool is_down) { client_properties_.end()) { auto props = client_properties_[controlled_remote_id_]; if (props->connection_status_ == ConnectionStatus::Connected) { - SendDataFrame(props->peer_, (const char*)&remote_action, - sizeof(remote_action), props->data_label_.c_str()); + json j = remote_action.to_json(); + std::string msg = j.dump(); + SendDataFrame(props->peer_, msg.c_str(), msg.size(), + props->data_label_.c_str()); } } } @@ -94,8 +96,11 @@ int Render::ProcessMouseEvent(const SDL_Event& event) { if (props->control_bar_hovered_ || props->display_selectable_hovered_) { remote_action.m.flag = MouseFlag::move; } - SendDataFrame(props->peer_, (const char*)&remote_action, - sizeof(remote_action), props->data_label_.c_str()); + + json j = remote_action.to_json(); + std::string msg = j.dump(); + SendDataFrame(props->peer_, msg.c_str(), msg.size(), + props->data_label_.c_str()); } else if (SDL_EVENT_MOUSE_WHEEL == event.type && last_mouse_event.button.x >= props->stream_render_rect_.x && last_mouse_event.button.x <= props->stream_render_rect_.x + @@ -127,8 +132,10 @@ int Render::ProcessMouseEvent(const SDL_Event& event) { (float)(event.button.y - props->stream_render_rect_.y) / render_height; - SendDataFrame(props->peer_, (const char*)&remote_action, - sizeof(remote_action), props->data_label_.c_str()); + json j = remote_action.to_json(); + std::string msg = j.dump(); + SendDataFrame(props->peer_, msg.c_str(), msg.size(), + props->data_label_.c_str()); } } @@ -279,64 +286,54 @@ void Render::OnReceiveDataBufferCb(const char* data, size_t size, return; } + std::string json_str(data, size); RemoteAction remote_action; - memcpy(&remote_action, data, size); + + try { + remote_action.from_json(json_str); + } catch (const std::exception& e) { + LOG_ERROR("Failed to parse RemoteAction JSON: {}", e.what()); + return; + } std::string remote_id(user_id, user_id_size); if (render->client_properties_.find(remote_id) != render->client_properties_.end()) { // local auto props = render->client_properties_.find(remote_id)->second; - RemoteAction host_info; - if (DeserializeRemoteAction(data, size, host_info)) { - if (ControlType::host_infomation == host_info.type && - props->remote_host_name_.empty()) { - props->remote_host_name_ = - std::string(host_info.i.host_name, host_info.i.host_name_size); - LOG_INFO("Remote hostname: [{}]", props->remote_host_name_); - - for (int i = 0; i < host_info.i.display_num; i++) { - props->display_info_list_.push_back(DisplayInfo( - std::string(host_info.i.display_list[i]), host_info.i.left[i], - host_info.i.top[i], host_info.i.right[i], host_info.i.bottom[i])); - LOG_INFO("Remote display [{}:{}], bound [({}, {}) ({}, {})]", i + 1, - props->display_info_list_[i].name, - props->display_info_list_[i].left, - props->display_info_list_[i].top, - props->display_info_list_[i].right, - props->display_info_list_[i].bottom); - } - } - } else { + if (remote_action.type == ControlType::host_infomation && + props->remote_host_name_.empty()) { props->remote_host_name_ = std::string(remote_action.i.host_name, remote_action.i.host_name_size); LOG_INFO("Remote hostname: [{}]", props->remote_host_name_); - LOG_ERROR("No remote display detected"); + + for (int i = 0; i < remote_action.i.display_num; i++) { + props->display_info_list_.push_back( + DisplayInfo(remote_action.i.display_list[i], + remote_action.i.left[i], remote_action.i.top[i], + remote_action.i.right[i], remote_action.i.bottom[i])); + } } - FreeRemoteAction(host_info); + FreeRemoteAction(remote_action); } else { // remote - if (ControlType::mouse == remote_action.type && render->mouse_controller_) { + if (remote_action.type == ControlType::mouse && render->mouse_controller_) { render->mouse_controller_->SendMouseCommand(remote_action, render->selected_display_); - } else if (ControlType::audio_capture == remote_action.type) { - if (remote_action.a) { + } else if (remote_action.type == ControlType::audio_capture) { + if (remote_action.a) render->StartSpeakerCapturer(); - render->audio_capture_ = true; - } else { + else render->StopSpeakerCapturer(); - render->audio_capture_ = false; - } - } else if (ControlType::keyboard == remote_action.type && + } else if (remote_action.type == ControlType::keyboard && render->keyboard_capturer_) { render->keyboard_capturer_->SendKeyboardCommand( (int)remote_action.k.key_value, remote_action.k.flag == KeyFlag::key_down); - } else if (ControlType::display_id == remote_action.type) { - if (render->screen_capturer_) { - render->selected_display_ = remote_action.d; - render->screen_capturer_->SwitchTo(remote_action.d); - } + } else if (remote_action.type == ControlType::display_id && + render->screen_capturer_) { + render->selected_display_ = remote_action.d; + render->screen_capturer_->SwitchTo(remote_action.d); } } } diff --git a/submodules/minirtc b/submodules/minirtc index 860d846..ff6b798 160000 --- a/submodules/minirtc +++ b/submodules/minirtc @@ -1 +1 @@ -Subproject commit 860d8466aded6d0591d8b2117c9083886668d8a3 +Subproject commit ff6b79807e73b709661b422dd9361ff780b138b7 diff --git a/xmake.lua b/xmake.lua index 6fdce6f..e4a030d 100644 --- a/xmake.lua +++ b/xmake.lua @@ -23,6 +23,7 @@ end add_requires("spdlog 1.14.1", {system = false}) add_requires("imgui v1.91.5-docking", {configs = {sdl3 = true, sdl3_renderer = true}}) add_requires("openssl3 3.3.2", {system = false}) +add_requires("nlohmann_json 3.11.3") if is_os("windows") then add_requires("libyuv", "miniaudio 0.11.21") @@ -45,7 +46,7 @@ elseif is_os("macosx") then "CoreMedia", "CoreVideo", "CoreAudio", "AudioToolbox") end -add_packages("spdlog", "imgui") +add_packages("spdlog", "imgui", "nlohmann_json") includes("submodules")