diff --git a/src/gui/render.cpp b/src/gui/render.cpp index 6a33b35..d929f6f 100644 --- a/src/gui/render.cpp +++ b/src/gui/render.cpp @@ -825,7 +825,7 @@ int Render::CreateMainWindow() { // for window region action SDL_SetWindowHitTest(main_window_, HitTestCallback, this); - SetupFontAndStyle(true); + SetupFontAndStyle(&main_windows_system_chinese_font_); ImGuiStyle& style = ImGui::GetStyle(); style.ScaleAllSizes(dpi_scale_); @@ -894,7 +894,7 @@ int Render::CreateStreamWindow() { // for window region action SDL_SetWindowHitTest(stream_window_, HitTestCallback, this); - SetupFontAndStyle(false); + SetupFontAndStyle(&stream_windows_system_chinese_font_); ImGuiStyle& style = ImGui::GetStyle(); style.ScaleAllSizes(dpi_scale_); @@ -977,7 +977,13 @@ int Render::CreateServerWindow() { // for window region action SDL_SetWindowHitTest(server_window_, HitTestCallback, this); - SetupFontAndStyle(false); + + SetupFontAndStyle(&server_windows_system_chinese_font_); + + ImGuiStyle& style = ImGui::GetStyle(); + style.ScaleAllSizes(dpi_scale_); + style.FontScaleDpi = dpi_scale_; + ImGui_ImplSDL3_InitForSDLRenderer(server_window_, server_renderer_); ImGui_ImplSDLRenderer3_Init(server_renderer_); @@ -1007,7 +1013,7 @@ int Render::DestroyServerWindow() { return 0; } -int Render::SetupFontAndStyle(bool main_window) { +int Render::SetupFontAndStyle(ImFont** system_chinese_font_out) { float font_size = 32.0f; // Setup Dear ImGui style @@ -1029,10 +1035,8 @@ int Render::SetupFontAndStyle(bool main_window) { // Load system Chinese font as fallback config.MergeMode = false; config.FontDataOwnedByAtlas = false; - if (main_window) { - main_windows_system_chinese_font_ = nullptr; - } else { - stream_windows_system_chinese_font_ = nullptr; + if (system_chinese_font_out) { + *system_chinese_font_out = nullptr; } #if defined(_WIN32) @@ -1059,61 +1063,36 @@ int Render::SetupFontAndStyle(bool main_window) { std::ifstream font_file(font_paths[i], std::ios::binary); if (font_file.good()) { font_file.close(); - if (main_window) { - main_windows_system_chinese_font_ = - io.Fonts->AddFontFromFileTTF(font_paths[i], font_size, &config, - io.Fonts->GetGlyphRangesChineseFull()); - if (main_windows_system_chinese_font_ != nullptr) { - // Merge FontAwesome icons into the Chinese font - config.MergeMode = true; - static const ImWchar icon_ranges[] = {ICON_MIN_FA, ICON_MAX_FA, 0}; - io.Fonts->AddFontFromMemoryTTF(fa_solid_900_ttf, fa_solid_900_ttf_len, - font_size, &config, icon_ranges); - config.MergeMode = false; - LOG_INFO("Loaded system Chinese font with icons: {}", font_paths[i]); - break; - } - } else { - stream_windows_system_chinese_font_ = - io.Fonts->AddFontFromFileTTF(font_paths[i], font_size, &config, - io.Fonts->GetGlyphRangesChineseFull()); - if (stream_windows_system_chinese_font_ != nullptr) { - // Merge FontAwesome icons into the Chinese font - config.MergeMode = true; - static const ImWchar icon_ranges[] = {ICON_MIN_FA, ICON_MAX_FA, 0}; - io.Fonts->AddFontFromMemoryTTF(fa_solid_900_ttf, fa_solid_900_ttf_len, - font_size, &config, icon_ranges); - config.MergeMode = false; - LOG_INFO("Loaded system Chinese font with icons: {}", font_paths[i]); - break; - } + if (!system_chinese_font_out) { + break; + } + + *system_chinese_font_out = + io.Fonts->AddFontFromFileTTF(font_paths[i], font_size, &config, + io.Fonts->GetGlyphRangesChineseFull()); + if (*system_chinese_font_out != nullptr) { + // Merge FontAwesome icons into the Chinese font + config.MergeMode = true; + static const ImWchar icon_ranges[] = {ICON_MIN_FA, ICON_MAX_FA, 0}; + io.Fonts->AddFontFromMemoryTTF(fa_solid_900_ttf, fa_solid_900_ttf_len, + font_size, &config, icon_ranges); + config.MergeMode = false; + LOG_INFO("Loaded system Chinese font with icons: {}", font_paths[i]); + break; } } } // If no system font found, use default font - if (main_window) { - if (main_windows_system_chinese_font_ == nullptr) { - main_windows_system_chinese_font_ = io.Fonts->AddFontDefault(&config); - // Merge FontAwesome icons into the default font - config.MergeMode = true; - static const ImWchar icon_ranges[] = {ICON_MIN_FA, ICON_MAX_FA, 0}; - io.Fonts->AddFontFromMemoryTTF(fa_solid_900_ttf, fa_solid_900_ttf_len, - font_size, &config, icon_ranges); - config.MergeMode = false; - LOG_WARN("System Chinese font not found, using default font with icons"); - } - } else { - if (stream_windows_system_chinese_font_ == nullptr) { - stream_windows_system_chinese_font_ = io.Fonts->AddFontDefault(&config); - // Merge FontAwesome icons into the default font - config.MergeMode = true; - static const ImWchar icon_ranges[] = {ICON_MIN_FA, ICON_MAX_FA, 0}; - io.Fonts->AddFontFromMemoryTTF(fa_solid_900_ttf, fa_solid_900_ttf_len, - font_size, &config, icon_ranges); - config.MergeMode = false; - LOG_WARN("System Chinese font not found, using default font with icons"); - } + if (system_chinese_font_out && *system_chinese_font_out == nullptr) { + *system_chinese_font_out = io.Fonts->AddFontDefault(&config); + // Merge FontAwesome icons into the default font + config.MergeMode = true; + static const ImWchar icon_ranges[] = {ICON_MIN_FA, ICON_MAX_FA, 0}; + io.Fonts->AddFontFromMemoryTTF(fa_solid_900_ttf, fa_solid_900_ttf_len, + font_size, &config, icon_ranges); + config.MergeMode = false; + LOG_WARN("System Chinese font not found, using default font with icons"); } ImGui::StyleColorsLight(); @@ -1258,7 +1237,8 @@ int Render::DrawServerWindow() { } if (server_window_) { - int w = 0, h = 0; + int w = 0; + int h = 0; SDL_GetWindowSize(server_window_, &w, &h); if (w > 0 && h > 0) { server_window_width_ = (float)w; @@ -1272,12 +1252,7 @@ int Render::DrawServerWindow() { ImGui::NewFrame(); ServerWindow(); ImGui::Render(); - // Transparent clear for compact (shaped) mode; opaque clear for normal mode. - if (server_window_compact_) { - SDL_SetRenderDrawColor(server_renderer_, 0, 0, 0, 0); - } else { - SDL_SetRenderDrawColor(server_renderer_, 255, 255, 255, 255); - } + SDL_SetRenderDrawColor(server_renderer_, 255, 255, 255, 255); SDL_RenderClear(server_renderer_); ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), server_renderer_); SDL_RenderPresent(server_renderer_); diff --git a/src/gui/render.h b/src/gui/render.h index 5a4a1c6..ee363ae 100644 --- a/src/gui/render.h +++ b/src/gui/render.h @@ -230,7 +230,7 @@ class Render { int DestroyStreamWindow(); int CreateServerWindow(); int DestroyServerWindow(); - int SetupFontAndStyle(bool main_window); + int SetupFontAndStyle(ImFont** system_chinese_font_out); int DestroyMainWindowContext(); int DestroyStreamWindowContext(); int DestroyServerWindowContext(); @@ -395,6 +395,7 @@ class Render { ImGuiContext* main_ctx_ = nullptr; ImFont* main_windows_system_chinese_font_ = nullptr; ImFont* stream_windows_system_chinese_font_ = nullptr; + ImFont* server_windows_system_chinese_font_ = nullptr; bool exit_ = false; const int sdl_refresh_ms_ = 16; // ~60 FPS #if _WIN32 @@ -507,20 +508,27 @@ class Render { int server_window_height_default_ = 450; float server_window_width_ = 300; float server_window_height_ = 450; - float server_window_title_bar_height_ = 30.0f; + float server_window_title_bar_height_ = 50.0f; SDL_PixelFormat server_pixformat_ = SDL_PIXELFORMAT_NV12; - int server_window_width_real_ = 400; - int server_window_height_real_ = 450; + int server_window_normal_width_ = 300; + int server_window_normal_height_ = 450; float server_window_dpi_scaling_w_ = 1.0f; float server_window_dpi_scaling_h_ = 1.0f; - // server window compact mode (50x50) toggle - bool server_window_compact_ = false; - int server_window_width_before_compact_ = 0; - int server_window_height_before_compact_ = 0; - int server_window_x_before_compact_ = 0; - int server_window_y_before_compact_ = 0; - bool server_window_bounds_saved_ = false; + // server window collapsed mode + bool server_window_collapsed_ = false; + bool server_window_collapsed_dragging_ = false; + float server_window_collapsed_drag_start_mouse_x_ = 0.0f; + float server_window_collapsed_drag_start_mouse_y_ = 0.0f; + int server_window_collapsed_drag_start_win_x_ = 0; + int server_window_collapsed_drag_start_win_y_ = 0; + + // server window drag normal mode + bool server_window_dragging_ = false; + float server_window_drag_start_mouse_x_ = 0.0f; + float server_window_drag_start_mouse_y_ = 0.0f; + int server_window_drag_start_win_x_ = 0; + int server_window_drag_start_win_y_ = 0; bool label_inited_ = false; bool connect_button_pressed_ = false; diff --git a/src/gui/render_callback.cpp b/src/gui/render_callback.cpp index 51ae876..679cda9 100644 --- a/src/gui/render_callback.cpp +++ b/src/gui/render_callback.cpp @@ -568,6 +568,7 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id, switch (status) { case ConnectionStatus::Connected: { + render->need_to_send_host_info_ = true; if (!render->need_to_create_stream_window_ && !render->client_properties_.empty()) { render->need_to_create_stream_window_ = true; @@ -623,6 +624,7 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id, switch (status) { case ConnectionStatus::Connected: { render->need_to_create_server_window_ = true; + render->need_to_send_host_info_ = true; render->is_server_mode_ = true; render->start_screen_capturer_ = true; render->start_speaker_capturer_ = true; diff --git a/src/gui/windows/server_window.cpp b/src/gui/windows/server_window.cpp index 5daa5e9..7009a39 100644 --- a/src/gui/windows/server_window.cpp +++ b/src/gui/windows/server_window.cpp @@ -1,110 +1,95 @@ -#include -#include - #include "rd_log.h" #include "render.h" namespace crossdesk { -static void SetServerWindowCircleShape(SDL_Window* window, int diameter) { - if (!window || diameter <= 0) { +namespace { +constexpr float kDragThresholdPx = 3.0f; + +// Handles dragging for the *last submitted ImGui item*. +// `reset_on_deactivate` should be false when the caller needs to know whether a +// deactivation was a click (no drag) vs a drag-release (dragging==true). +inline void HandleWindowDragForLastItem(SDL_Window* window, bool& dragging, + float& start_mouse_x, + float& start_mouse_y, int& start_win_x, + int& start_win_y, + bool reset_on_deactivate = true) { + if (!window) { return; } - const int pitch = diameter * 4; - std::vector pixels((size_t)diameter * (size_t)diameter * 4, 0); + if (ImGui::IsItemActivated()) { + SDL_GetGlobalMouseState(&start_mouse_x, &start_mouse_y); + SDL_GetWindowPosition(window, &start_win_x, &start_win_y); + dragging = false; + } - // Sub-pixel centered circle helps symmetry on even diameters. - const float r = (float)diameter * 0.5f; - const float cx = r - 0.5f; - const float cy = r - 0.5f; + if (ImGui::IsItemActive()) { + if (!dragging && + ImGui::IsMouseDragging(ImGuiMouseButton_Left, kDragThresholdPx)) { + dragging = true; + } - for (int y = 0; y < diameter; ++y) { - for (int x = 0; x < diameter; ++x) { - const float dx = (float)x - cx; - const float dy = (float)y - cy; - const float dist = std::sqrt(dx * dx + dy * dy); - - // 1px soft edge to reduce jaggies on small circles. - float a = r + 0.5f - dist; - if (a < 0.0f) a = 0.0f; - if (a > 1.0f) a = 1.0f; - const unsigned char alpha = (unsigned char)(a * 255.0f); - const size_t idx = ((size_t)y * (size_t)diameter + (size_t)x) * 4; - pixels[idx + 0] = 255; // R - pixels[idx + 1] = 255; // G - pixels[idx + 2] = 255; // B - pixels[idx + 3] = alpha; // A + if (dragging) { + float mouse_x = 0.0f; + float mouse_y = 0.0f; + SDL_GetGlobalMouseState(&mouse_x, &mouse_y); + const int dx = (int)(mouse_x - start_mouse_x); + const int dy = (int)(mouse_y - start_mouse_y); + SDL_SetWindowPosition(window, start_win_x + dx, start_win_y + dy); } } - SDL_Surface* shape = SDL_CreateSurfaceFrom( - diameter, diameter, SDL_PIXELFORMAT_RGBA32, pixels.data(), pitch); - if (!shape) { - LOG_ERROR("SDL_CreateSurfaceFrom failed: {}", SDL_GetError()); - return; + if (reset_on_deactivate && ImGui::IsItemDeactivated()) { + dragging = false; } - - if (!SDL_SetWindowShape(window, shape)) { - LOG_ERROR("SDL_SetWindowShape failed: {}", SDL_GetError()); - } - - SDL_DestroySurface(shape); } +} // namespace int Render::ServerWindow() { ImGui::SetNextWindowSize(ImVec2(server_window_width_, server_window_height_), ImGuiCond_Always); ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always); - if (server_window_compact_) { - ImGui::SetNextWindowBgAlpha(0.0f); - } - ImGui::Begin("##server_window", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); - // Compact mode: show a 50x50 clickable square that restores the window. - if (server_window_compact_) { - ImDrawList* draw_list = ImGui::GetWindowDrawList(); + // Collapsed mode: no buttons; drag to move; click to restore. + if (server_window_collapsed_) { ImGui::SetCursorPos(ImVec2(0.0f, 0.0f)); - if (ImGui::InvisibleButton( - "##server_compact_restore", - ImVec2(server_window_width_, server_window_height_))) { - if (server_window_) { - SDL_SetWindowShape(server_window_, nullptr); - } - if (server_window_ && server_window_bounds_saved_ && - server_window_width_before_compact_ > 0 && - server_window_height_before_compact_ > 0) { - SDL_SetWindowSize(server_window_, server_window_width_before_compact_, - server_window_height_before_compact_); - SDL_SetWindowPosition(server_window_, server_window_x_before_compact_, - server_window_y_before_compact_); + ImGui::InvisibleButton("##server_collapsed_area", + ImVec2(server_window_width_, server_window_height_)); - server_window_width_ = (float)server_window_width_before_compact_; - server_window_height_ = (float)server_window_height_before_compact_; - } - server_window_compact_ = false; - server_window_bounds_saved_ = false; + HandleWindowDragForLastItem(server_window_, + server_window_collapsed_dragging_, + server_window_collapsed_drag_start_mouse_x_, + server_window_collapsed_drag_start_mouse_y_, + server_window_collapsed_drag_start_win_x_, + server_window_collapsed_drag_start_win_y_, + /*reset_on_deactivate=*/false); + + const bool request_restore = + ImGui::IsItemDeactivated() && !server_window_collapsed_dragging_; + if (ImGui::IsItemDeactivated()) { + server_window_collapsed_dragging_ = false; } - // Draw a visible circular affordance. - const float w = server_window_width_; - const float h = server_window_height_; - const float radius = (w < h ? w : h) * 0.5f - 1.0f; - const ImVec2 center(w * 0.5f, h * 0.5f); - draw_list->AddCircleFilled(center, radius, IM_COL32(255, 255, 255, 220), - 32); - draw_list->AddCircle(center, radius, IM_COL32(0, 0, 0, 255), 32, 2.0f); - // A simple "restore" hint. - draw_list->AddRect( - ImVec2(center.x - radius * 0.35f, center.y - radius * 0.35f), - ImVec2(center.x + radius * 0.35f, center.y + radius * 0.35f), - IM_COL32(0, 0, 0, 255), 2.0f, 0, 2.0f); + if (request_restore && server_window_) { + int w = 0; + int x = 0; + int y = 0; + int h = 0; + SDL_GetWindowSize(server_window_, &w, &h); + SDL_GetWindowPosition(server_window_, &x, &y); + + const int normal_h = server_window_normal_height_; + SDL_SetWindowSize(server_window_, w, normal_h); + SDL_SetWindowPosition(server_window_, x, y); + server_window_collapsed_ = false; + } ImGui::End(); return 0; @@ -122,12 +107,23 @@ int Render::ServerWindow() { ImDrawList* draw_list = ImGui::GetWindowDrawList(); - ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); - float server_title_bar_button_width = server_window_title_bar_height_; float server_title_bar_button_height = server_window_title_bar_height_; + // Drag area: the title bar excluding the right-side buttons. + { + const float drag_w = + server_window_width_ - server_title_bar_button_width * 2; + const float drag_h = server_title_bar_button_height; + ImGui::SetCursorPos(ImVec2(0.0f, 0.0f)); + ImGui::InvisibleButton("##server_title_drag_area", ImVec2(drag_w, drag_h)); + + HandleWindowDragForLastItem( + server_window_, server_window_dragging_, + server_window_drag_start_mouse_x_, server_window_drag_start_mouse_y_, + server_window_drag_start_win_x_, server_window_drag_start_win_y_); + } + float minimize_button_pos_x = server_window_width_ - server_title_bar_button_width * 2; ImGui::SetCursorPos(ImVec2(minimize_button_pos_x, 0.0f)); @@ -143,36 +139,18 @@ int Render::ServerWindow() { ImVec2(server_title_bar_button_width, server_title_bar_button_height))) { if (server_window_) { - int w = 0, h = 0; - int x = 0, y = 0; + int w = 0; + int h = 0; + int x = 0; + int y = 0; SDL_GetWindowSize(server_window_, &w, &h); SDL_GetWindowPosition(server_window_, &x, &y); - server_window_width_before_compact_ = w; - server_window_height_before_compact_ = h; - server_window_x_before_compact_ = x; - server_window_y_before_compact_ = y; - server_window_bounds_saved_ = true; - constexpr int kCompactSize = 50; - SDL_SetWindowSize(server_window_, kCompactSize, kCompactSize); - - // Move to bottom-right of the current display's usable bounds. - SDL_Rect display_bounds; - if (SDL_GetDisplayUsableBounds(SDL_GetDisplayForWindow(server_window_), - &display_bounds)) { - int compact_x = display_bounds.x + display_bounds.w - kCompactSize; - int compact_y = display_bounds.y + display_bounds.h - kCompactSize; - SDL_SetWindowPosition(server_window_, compact_x, compact_y); - } - - // Use pixel size to match transparency buffer on HiDPI. - int w_px = kCompactSize; - int h_px = kCompactSize; - SDL_GetWindowSizeInPixels(server_window_, &w_px, &h_px); - const int diameter = (w_px < h_px ? w_px : h_px); - SetServerWindowCircleShape(server_window_, diameter); - - server_window_compact_ = true; + const int collapsed_h = (int)server_window_title_bar_height_; + // Collapse upward: keep top edge stable. + SDL_SetWindowSize(server_window_, w, collapsed_h); + SDL_SetWindowPosition(server_window_, x, y); + server_window_collapsed_ = true; } } draw_list->AddLine( @@ -212,8 +190,6 @@ int Render::ServerWindow() { IM_COL32(0, 0, 0, 255)); ImGui::PopStyleColor(3); - ImGui::PopStyleVar(); - ImGui::PopStyleColor(); ImGui::EndChild(); ImGui::PopStyleVar();