diff --git a/src/device_controller/device_controller.h b/src/device_controller/device_controller.h index f1f7fd3..c3f006f 100644 --- a/src/device_controller/device_controller.h +++ b/src/device_controller/device_controller.h @@ -40,7 +40,7 @@ typedef enum { wheel_horizontal } MouseFlag; typedef enum { key_down = 0, key_up } KeyFlag; -typedef enum { send_sas = 0 } ServiceCommandFlag; +typedef enum { send_sas = 0, lock_workstation } ServiceCommandFlag; typedef struct { float x; float y; @@ -235,4 +235,4 @@ class DeviceController { // virtual int Unhook(); }; } // namespace crossdesk -#endif \ No newline at end of file +#endif diff --git a/src/gui/assets/localization/localization_data.h b/src/gui/assets/localization/localization_data.h index 76ea5f8..ed4da26 100644 --- a/src/gui/assets/localization/localization_data.h +++ b/src/gui/assets/localization/localization_data.h @@ -51,7 +51,9 @@ struct TranslationRow { X(release_mouse, u8"释放", "Release", u8"Освободить") \ X(audio_capture, u8"声音", "Audio", u8"Звук") \ X(mute, u8" 静音", " Mute", u8"Без звука") \ + X(send_shortcut, u8"发送组合键", "Send Shortcut", u8"Сочетания клавиш") \ X(send_sas, u8"发送SAS", "Send SAS", u8"Отправить SAS") \ + X(lock_remote, u8"锁定远端", "Lock Remote", u8"Заблокировать") \ X(remote_password_box_visible, u8"远端密码框已出现", \ "Remote password box visible", u8"Окно ввода пароля видно") \ X(remote_lock_screen_hint, u8"远端处于锁屏封面,可发送SAS", \ diff --git a/src/gui/panels/remote_peer_panel.cpp b/src/gui/panels/remote_peer_panel.cpp index f94f576..fdd56db 100644 --- a/src/gui/panels/remote_peer_panel.cpp +++ b/src/gui/panels/remote_peer_panel.cpp @@ -204,11 +204,11 @@ int Render::ConnectTo(const std::string& remote_id, const char* password, props->params_.user_id = props->local_id_.c_str(); props->peer_ = CreatePeer(&props->params_); - props->control_window_width_ = title_bar_height_ * 9.0f; + props->control_window_width_ = title_bar_height_ * 10.0f; props->control_window_height_ = title_bar_height_ * 1.3f; props->control_window_min_width_ = title_bar_height_ * 0.65f; props->control_window_min_height_ = title_bar_height_ * 1.3f; - props->control_window_max_width_ = title_bar_height_ * 9.0f; + props->control_window_max_width_ = title_bar_height_ * 10.0f; props->control_window_max_height_ = title_bar_height_ * 7.0f; props->connection_status_ = ConnectionStatus::Connecting; @@ -272,4 +272,4 @@ int Render::ConnectTo(const std::string& remote_id, const char* password, return 0; } -} // namespace crossdesk +} // namespace crossdesk diff --git a/src/gui/render.h b/src/gui/render.h index 732351b..1ab3de0 100644 --- a/src/gui/render.h +++ b/src/gui/render.h @@ -115,6 +115,7 @@ class Render { bool is_control_bar_in_left_ = true; bool control_bar_hovered_ = false; bool display_selectable_hovered_ = false; + bool shortcut_selectable_hovered_ = false; bool control_bar_expand_ = true; bool reset_control_bar_pos_ = false; bool control_window_width_is_changing_ = false; @@ -125,10 +126,10 @@ class Render { float sub_stream_window_width_ = 1280; float sub_stream_window_height_ = 720; float control_window_min_width_ = 20; - float control_window_max_width_ = 230; + float control_window_max_width_ = 300; float control_window_min_height_ = 38; float control_window_max_height_ = 180; - float control_window_width_ = 230; + float control_window_width_ = 300; float control_window_height_ = 38; float control_bar_pos_x_ = 0; float control_bar_pos_y_ = 30; diff --git a/src/gui/render_callback.cpp b/src/gui/render_callback.cpp index e6c75df..975c38e 100644 --- a/src/gui/render_callback.cpp +++ b/src/gui/render_callback.cpp @@ -563,9 +563,9 @@ int Render::ProcessMouseEvent(const SDL_Event& event) { const bool file_transfer_window_hovered = props->file_transfer_.file_transfer_window_hovered_; - const bool overlay_hovered = props->control_bar_hovered_ || - props->display_selectable_hovered_ || - file_transfer_window_hovered; + const bool overlay_hovered = + props->control_bar_hovered_ || props->display_selectable_hovered_ || + props->shortcut_selectable_hovered_ || file_transfer_window_hovered; const SDL_FRect render_rect = props->stream_render_rect_f_; if (render_rect.w <= 1.0f || render_rect.h <= 1.0f) { @@ -1063,6 +1063,11 @@ void Render::OnReceiveDataBufferCb(const char* data, size_t size, if (remote_action.c.flag == ServiceCommandFlag::send_sas) { render->pending_windows_service_sas_.store(true, std::memory_order_relaxed); + } else if (remote_action.c.flag == ServiceCommandFlag::lock_workstation) { + if (!LockWorkStation()) { + LOG_WARN("Remote lock workstation request failed, error={}", + GetLastError()); + } } #endif return; diff --git a/src/gui/toolbars/control_bar.cpp b/src/gui/toolbars/control_bar.cpp index a8fef77..b89d676 100644 --- a/src/gui/toolbars/control_bar.cpp +++ b/src/gui/toolbars/control_bar.cpp @@ -195,6 +195,61 @@ int Render::ControlBar(std::shared_ptr& props) { text_pos, IM_COL32(0, 0, 0, 255), std::to_string(props->selected_display_ + 1).c_str()); + auto send_service_command = [&](ServiceCommandFlag flag, + const char* log_action) { + if (props->connection_status_ == ConnectionStatus::Connected && + props->peer_) { + RemoteAction remote_action; + remote_action.type = ControlType::service_command; + remote_action.c.flag = flag; + std::string msg = remote_action.to_json(); + int ret = SendReliableDataFrame(props->peer_, msg.c_str(), msg.size(), + props->control_data_label_.c_str()); + if (ret != 0) { + LOG_WARN("Send {} command failed, remote_id={}, ret={}", log_action, + props->remote_id_, ret); + } + } + }; + + ImGui::SameLine(); + std::string shortcut = ICON_FA_KEYBOARD; + ImGui::SetWindowFontScale(0.5f); + if (ImGui::Button(shortcut.c_str(), ImVec2(button_width, button_height))) { + ImGui::OpenPopup("shortcut"); + } + + if (ImGui::IsItemHovered()) { + ImGui::BeginTooltip(); + ImGui::SetWindowFontScale(0.5f); + ImGui::Text( + "%s", + localization::send_shortcut[localization_language_index_].c_str()); + ImGui::SetWindowFontScale(1.0f); + ImGui::EndTooltip(); + } + + props->shortcut_selectable_hovered_ = false; + if (ImGui::BeginPopup("shortcut")) { + ImGui::SetWindowFontScale(0.5f); + std::string sas_label = + "Ctrl+Alt+Del - " + + localization::send_sas[localization_language_index_]; + std::string lock_label = + "Win+L - " + localization::lock_remote[localization_language_index_]; + if (ImGui::Selectable(sas_label.c_str())) { + send_service_command(ServiceCommandFlag::send_sas, "SAS"); + } + if (ImGui::Selectable(lock_label.c_str())) { + send_service_command(ServiceCommandFlag::lock_workstation, + "remote lock"); + } + props->shortcut_selectable_hovered_ = + ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows); + ImGui::SetWindowFontScale(1.0f); + ImGui::EndPopup(); + } + ImGui::SameLine(); float mouse_x = ImGui::GetCursorScreenPos().x; float mouse_y = ImGui::GetCursorScreenPos().y; diff --git a/tests/display_popup_hover_state_test.cpp b/tests/display_popup_hover_state_test.cpp index b5ebef8..324e11e 100644 --- a/tests/display_popup_hover_state_test.cpp +++ b/tests/display_popup_hover_state_test.cpp @@ -55,6 +55,22 @@ bool ExpectResetBeforeDisplayPopup(const std::string& value) { return false; } +bool ExpectResetBeforeShortcutPopup(const std::string& value) { + const std::string reset = "props->shortcut_selectable_hovered_ = false;"; + const std::string popup = "ImGui::BeginPopup(\"shortcut\")"; + const size_t reset_pos = value.find(reset); + const size_t popup_pos = value.find(popup); + + if (reset_pos != std::string::npos && popup_pos != std::string::npos && + reset_pos < popup_pos) { + return true; + } + + std::cerr << "control_bar.cpp must clear shortcut_selectable_hovered_ before " + "checking the shortcut popup\n"; + return false; +} + } // namespace int main() { @@ -74,5 +90,8 @@ int main() { "ImGui::IsWindowHovered(" "ImGuiHoveredFlags_RootAndChildWindows)"); ok &= ExpectResetBeforeDisplayPopup(control_bar); + ok &= ExpectContains("control_bar.cpp", control_bar, + "props->shortcut_selectable_hovered_ ="); + ok &= ExpectResetBeforeShortcutPopup(control_bar); return ok ? 0 : 1; }