mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-03-20 04:18:30 +08:00
[feat] show server window in the bottom-right corner of the screen
This commit is contained in:
@@ -949,24 +949,43 @@ int Render::CreateServerWindow() {
|
||||
return -1;
|
||||
}
|
||||
ImGui::SetCurrentContext(server_ctx_);
|
||||
if (!SDL_CreateWindowAndRenderer(
|
||||
"Server window", (int)server_window_width_,
|
||||
(int)server_window_height_,
|
||||
SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_BORDERLESS,
|
||||
&server_window_, &server_renderer_)) {
|
||||
if (!SDL_CreateWindowAndRenderer("Server window", (int)server_window_width_,
|
||||
(int)server_window_height_,
|
||||
SDL_WINDOW_HIGH_PIXEL_DENSITY |
|
||||
SDL_WINDOW_BORDERLESS |
|
||||
SDL_WINDOW_TRANSPARENT,
|
||||
&server_window_, &server_renderer_)) {
|
||||
LOG_ERROR("Error creating server_window_ and server_renderer_: {}",
|
||||
SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
SDL_SetWindowResizable(server_window_, true);
|
||||
|
||||
// Set window position to bottom-right corner
|
||||
SDL_Rect display_bounds;
|
||||
if (SDL_GetDisplayUsableBounds(SDL_GetDisplayForWindow(server_window_),
|
||||
&display_bounds)) {
|
||||
int window_x =
|
||||
display_bounds.x + display_bounds.w - (int)server_window_width_;
|
||||
int window_y =
|
||||
display_bounds.y + display_bounds.h - (int)server_window_height_;
|
||||
SDL_SetWindowPosition(server_window_, window_x, window_y);
|
||||
}
|
||||
|
||||
SDL_SetWindowResizable(server_window_, false);
|
||||
|
||||
SDL_SetRenderDrawBlendMode(server_renderer_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
// for window region action
|
||||
SDL_SetWindowHitTest(server_window_, HitTestCallback, this);
|
||||
SetupFontAndStyle(false);
|
||||
ImGui_ImplSDL3_InitForSDLRenderer(server_window_, server_renderer_);
|
||||
ImGui_ImplSDLRenderer3_Init(server_renderer_);
|
||||
|
||||
server_window_created_ = true;
|
||||
server_window_inited_ = true;
|
||||
|
||||
LOG_INFO("Server window inited");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1237,12 +1256,28 @@ int Render::DrawServerWindow() {
|
||||
LOG_ERROR("Server context is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (server_window_) {
|
||||
int w = 0, h = 0;
|
||||
SDL_GetWindowSize(server_window_, &w, &h);
|
||||
if (w > 0 && h > 0) {
|
||||
server_window_width_ = (float)w;
|
||||
server_window_height_ = (float)h;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetCurrentContext(server_ctx_);
|
||||
ImGui_ImplSDLRenderer3_NewFrame();
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
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_RenderClear(server_renderer_);
|
||||
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), server_renderer_);
|
||||
SDL_RenderPresent(server_renderer_);
|
||||
@@ -1430,7 +1465,7 @@ void Render::MainLoop() {
|
||||
DrawStreamWindow();
|
||||
}
|
||||
|
||||
if (need_to_send_host_info_) {
|
||||
if (is_server_mode_) {
|
||||
DrawServerWindow();
|
||||
}
|
||||
|
||||
@@ -1530,6 +1565,11 @@ void Render::HandleServerWindow() {
|
||||
CreateServerWindow();
|
||||
need_to_create_server_window_ = false;
|
||||
}
|
||||
|
||||
if (need_to_destroy_server_window_) {
|
||||
DestroyServerWindow();
|
||||
need_to_destroy_server_window_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Render::Cleanup() {
|
||||
@@ -1910,6 +1950,16 @@ void Render::ProcessSdlEvent(const SDL_Event& event) {
|
||||
}
|
||||
}
|
||||
|
||||
if (server_window_inited_) {
|
||||
if (server_ctx_) {
|
||||
ImGui::SetCurrentContext(server_ctx_);
|
||||
ImGui_ImplSDL3_ProcessEvent(&event);
|
||||
} else {
|
||||
LOG_ERROR("Server context is null");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
if (stream_window_inited_) {
|
||||
|
||||
@@ -500,18 +500,28 @@ class Render {
|
||||
|
||||
// server window properties
|
||||
bool need_to_create_server_window_ = false;
|
||||
bool need_to_destroy_server_window_ = false;
|
||||
bool server_window_created_ = false;
|
||||
bool server_window_inited_ = false;
|
||||
int server_window_width_default_ = 600;
|
||||
int server_window_height_default_ = 400;
|
||||
float server_window_width_ = 600;
|
||||
float server_window_height_ = 400;
|
||||
int server_window_width_default_ = 300;
|
||||
int server_window_height_default_ = 450;
|
||||
float server_window_width_ = 300;
|
||||
float server_window_height_ = 450;
|
||||
float server_window_title_bar_height_ = 30.0f;
|
||||
SDL_PixelFormat server_pixformat_ = SDL_PIXELFORMAT_NV12;
|
||||
int server_window_width_real_ = 600;
|
||||
int server_window_height_real_ = 400;
|
||||
int server_window_width_real_ = 400;
|
||||
int server_window_height_real_ = 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;
|
||||
|
||||
bool label_inited_ = false;
|
||||
bool connect_button_pressed_ = false;
|
||||
bool password_validating_ = false;
|
||||
@@ -528,6 +538,7 @@ class Render {
|
||||
bool fullscreen_button_pressed_ = false;
|
||||
bool focus_on_input_widget_ = true;
|
||||
bool is_client_mode_ = false;
|
||||
bool is_server_mode_ = false;
|
||||
bool reload_recent_connections_ = true;
|
||||
bool show_confirm_delete_connection_ = false;
|
||||
bool delete_connection_ = false;
|
||||
|
||||
@@ -623,7 +623,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;
|
||||
#ifdef CROSSDESK_DEBUG
|
||||
@@ -648,8 +648,8 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
|
||||
kv.second == ConnectionStatus::Failed ||
|
||||
kv.second == ConnectionStatus::Disconnected;
|
||||
})) {
|
||||
render->need_to_create_server_window_ = false;
|
||||
render->need_to_send_host_info_ = false;
|
||||
render->need_to_destroy_server_window_ = true;
|
||||
render->is_server_mode_ = false;
|
||||
render->start_screen_capturer_ = false;
|
||||
render->start_speaker_capturer_ = false;
|
||||
render->start_mouse_controller_ = false;
|
||||
|
||||
@@ -1,22 +1,224 @@
|
||||
#include <cmath>
|
||||
#include <vector>
|
||||
|
||||
#include "rd_log.h"
|
||||
#include "render.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
static void SetServerWindowCircleShape(SDL_Window* window, int diameter) {
|
||||
if (!window || diameter <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const int pitch = diameter * 4;
|
||||
std::vector<unsigned char> pixels((size_t)diameter * (size_t)diameter * 4, 0);
|
||||
|
||||
// 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;
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
SDL_Surface* shape = SDL_CreateSurfaceFrom(
|
||||
diameter, diameter, SDL_PIXELFORMAT_RGBA32, pixels.data(), pitch);
|
||||
if (!shape) {
|
||||
LOG_ERROR("SDL_CreateSurfaceFrom failed: {}", SDL_GetError());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SDL_SetWindowShape(window, shape)) {
|
||||
LOG_ERROR("SDL_SetWindowShape failed: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_DestroySurface(shape);
|
||||
}
|
||||
|
||||
int Render::ServerWindow() {
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(main_window_width_ - 2 * main_child_window_x_padding_,
|
||||
main_window_height_ - status_bar_height_ -
|
||||
main_window_text_y_padding_ - main_child_window_y_padding_),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(main_child_window_x_padding_, main_window_text_y_padding_),
|
||||
ImGuiCond_Always);
|
||||
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);
|
||||
// server window content goes here
|
||||
ImGui::Text("Server Window Content");
|
||||
|
||||
// Compact mode: show a 50x50 clickable square that restores the window.
|
||||
if (server_window_compact_) {
|
||||
ImDrawList* draw_list = ImGui::GetWindowDrawList();
|
||||
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_);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
ImGui::End();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||
ImGui::BeginChild(
|
||||
"ServerTitleBar",
|
||||
ImVec2(server_window_width_, server_window_title_bar_height_),
|
||||
ImGuiChildFlags_Border,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
|
||||
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_;
|
||||
|
||||
float minimize_button_pos_x =
|
||||
server_window_width_ - server_title_bar_button_width * 2;
|
||||
ImGui::SetCursorPos(ImVec2(minimize_button_pos_x, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0.1f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
float minimize_pos_x =
|
||||
minimize_button_pos_x + server_title_bar_button_width * 0.33f;
|
||||
float minimize_pos_y = server_title_bar_button_height * 0.5f;
|
||||
std::string server_minimize_button = "##minimize"; // ICON_FA_MINUS;
|
||||
if (ImGui::Button(server_minimize_button.c_str(),
|
||||
ImVec2(server_title_bar_button_width,
|
||||
server_title_bar_button_height))) {
|
||||
if (server_window_) {
|
||||
int w = 0, h = 0;
|
||||
int x = 0, 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;
|
||||
}
|
||||
}
|
||||
draw_list->AddLine(
|
||||
ImVec2(minimize_pos_x, minimize_pos_y),
|
||||
ImVec2(minimize_pos_x + server_title_bar_button_width * 0.33f,
|
||||
minimize_pos_y),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
float xmark_button_pos_x =
|
||||
server_window_width_ - server_title_bar_button_width;
|
||||
ImGui::SetCursorPos(ImVec2(xmark_button_pos_x, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0, 0, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 0, 0, 0.5f));
|
||||
|
||||
float xmark_pos_x = xmark_button_pos_x + server_title_bar_button_width * 0.5f;
|
||||
float xmark_pos_y = server_title_bar_button_height * 0.5f;
|
||||
float xmark_size = server_title_bar_button_width * 0.33f;
|
||||
std::string server_close_button = "##xmark"; // ICON_FA_XMARK;
|
||||
if (ImGui::Button(server_close_button.c_str(),
|
||||
ImVec2(server_title_bar_button_width,
|
||||
server_title_bar_button_height))) {
|
||||
LOG_ERROR("Close button clicked");
|
||||
LeaveConnection(peer_, self_hosted_id_);
|
||||
}
|
||||
|
||||
draw_list->AddLine(ImVec2(xmark_pos_x - xmark_size / 2 - 0.25f,
|
||||
xmark_pos_y - xmark_size / 2 + 0.75f),
|
||||
ImVec2(xmark_pos_x + xmark_size / 2 - 1.5f,
|
||||
xmark_pos_y + xmark_size / 2 - 0.5f),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
draw_list->AddLine(
|
||||
ImVec2(xmark_pos_x + xmark_size / 2 - 1.75f,
|
||||
xmark_pos_y - xmark_size / 2 + 0.75f),
|
||||
ImVec2(xmark_pos_x - xmark_size / 2, xmark_pos_y + xmark_size / 2 - 1.0f),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::End();
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user