mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-10-16 17:10:20 +08:00
[chore] remove unused ImGui code and old release notes
This commit is contained in:
@@ -1,63 +0,0 @@
|
||||
#include "rounded_corner_button.h"
|
||||
|
||||
namespace crossdesk {
|
||||
bool RoundedCornerButton(const char* label, const ImVec2& size, float rounding,
|
||||
ImDrawFlags round_flags, bool enabled,
|
||||
ImU32 normal_col, ImU32 hover_col, ImU32 active_col,
|
||||
ImU32 border_col) {
|
||||
ImGuiWindow* current_window = ImGui::GetCurrentWindow();
|
||||
if (current_window->SkipItems) return false;
|
||||
|
||||
const ImGuiStyle& style = ImGui::GetStyle();
|
||||
|
||||
ImGuiID button_id = current_window->GetID(label);
|
||||
ImVec2 cursor_pos = current_window->DC.CursorPos;
|
||||
ImVec2 button_size = ImGui::CalcItemSize(size, 0.0f, 0.0f);
|
||||
ImRect button_rect(cursor_pos, ImVec2(cursor_pos.x + button_size.x,
|
||||
cursor_pos.y + button_size.y));
|
||||
ImGui::ItemSize(button_rect);
|
||||
if (!ImGui::ItemAdd(button_rect, button_id)) return false;
|
||||
|
||||
bool is_hovered = false, is_held = false;
|
||||
bool is_pressed = false;
|
||||
if (enabled) {
|
||||
is_pressed =
|
||||
ImGui::ButtonBehavior(button_rect, button_id, &is_hovered, &is_held);
|
||||
}
|
||||
|
||||
if (normal_col == 0) normal_col = ImGui::GetColorU32(ImGuiCol_Button);
|
||||
if (hover_col == 0) hover_col = ImGui::GetColorU32(ImGuiCol_ButtonHovered);
|
||||
if (active_col == 0) active_col = ImGui::GetColorU32(ImGuiCol_ButtonActive);
|
||||
if (border_col == 0) border_col = ImGui::GetColorU32(ImGuiCol_Border);
|
||||
|
||||
ImU32 fill_color = normal_col;
|
||||
if (is_held && is_hovered)
|
||||
fill_color = active_col;
|
||||
else if (is_hovered)
|
||||
fill_color = hover_col;
|
||||
|
||||
if (!enabled) fill_color = IM_COL32(120, 120, 120, 180);
|
||||
|
||||
ImDrawList* window_draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
window_draw_list->AddRectFilled(button_rect.Min, button_rect.Max, fill_color,
|
||||
rounding, round_flags);
|
||||
|
||||
if (style.FrameBorderSize > 0.0f) {
|
||||
window_draw_list->AddRect(button_rect.Min, button_rect.Max, border_col,
|
||||
rounding, round_flags, style.FrameBorderSize);
|
||||
}
|
||||
|
||||
ImU32 text_color =
|
||||
ImGui::GetColorU32(enabled ? ImGuiCol_Text : ImGuiCol_TextDisabled);
|
||||
|
||||
const char* label_end = ImGui::FindRenderedTextEnd(label);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,
|
||||
ImGui::ColorConvertU32ToFloat4(text_color));
|
||||
ImGui::RenderTextClipped(button_rect.Min, button_rect.Max, label, label_end,
|
||||
nullptr, ImVec2(0.5f, 0.5f), &button_rect);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
return is_pressed;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2026-02-26
|
||||
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _ROUNDED_CORNER_BUTTON_H_
|
||||
#define _ROUNDED_CORNER_BUTTON_H_
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_internal.h"
|
||||
|
||||
namespace crossdesk {
|
||||
bool RoundedCornerButton(const char* label, const ImVec2& size, float rounding,
|
||||
ImDrawFlags round_flags, bool enabled = true,
|
||||
ImU32 normal_col = 0, ImU32 hover_col = 0,
|
||||
ImU32 active_col = 0, ImU32 border_col = 0);
|
||||
} // namespace crossdesk
|
||||
|
||||
#endif
|
||||
@@ -10,56 +10,16 @@
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <string>
|
||||
|
||||
namespace crossdesk::gui_detail {
|
||||
|
||||
struct MainWindowState {
|
||||
float title_bar_width_ = 640;
|
||||
float title_bar_height_ = 30;
|
||||
float title_bar_button_width_ = 30;
|
||||
float title_bar_button_height_ = 30;
|
||||
|
||||
bool exit_ = false;
|
||||
const int sdl_refresh_ms_ = 16;
|
||||
|
||||
bool main_window_minimized_ = false;
|
||||
int main_window_width_real_ = 720;
|
||||
int main_window_height_real_ = 540;
|
||||
float main_window_dpi_scaling_w_ = 1.0f;
|
||||
float main_window_dpi_scaling_h_ = 1.0f;
|
||||
float dpi_scale_ = 1.0f;
|
||||
float main_window_width_default_ = 640;
|
||||
float main_window_height_default_ = 480;
|
||||
float main_window_width_ = 640;
|
||||
float main_window_height_ = 480;
|
||||
float main_window_width_last_ = 640;
|
||||
float main_window_height_last_ = 480;
|
||||
float local_window_width_ = 320;
|
||||
float local_window_height_ = 235;
|
||||
float remote_window_width_ = 320;
|
||||
float remote_window_height_ = 235;
|
||||
float local_child_window_width_ = 266;
|
||||
float local_child_window_height_ = 180;
|
||||
float remote_child_window_width_ = 266;
|
||||
float remote_child_window_height_ = 180;
|
||||
float main_window_text_y_padding_ = 10;
|
||||
float main_child_window_x_padding_ = 27;
|
||||
float main_child_window_y_padding_ = 45;
|
||||
float status_bar_height_ = 22;
|
||||
float connection_status_window_width_ = 200;
|
||||
float connection_status_window_height_ = 150;
|
||||
float notification_window_width_ = 200;
|
||||
float notification_window_height_ = 80;
|
||||
float about_window_width_ = 300;
|
||||
float about_window_height_ = 170;
|
||||
float update_notification_window_width_ = 400;
|
||||
float update_notification_window_height_ = 320;
|
||||
};
|
||||
|
||||
// Application-global interaction flags that coordinate SDL events with media
|
||||
// Application-global interaction flags that coordinate UI events with media
|
||||
// and remote-control devices.
|
||||
struct InteractionState {
|
||||
bool start_mouse_controller_ = false;
|
||||
@@ -69,17 +29,11 @@ struct InteractionState {
|
||||
bool start_keyboard_capturer_ = false;
|
||||
bool keyboard_capturer_is_started_ = false;
|
||||
bool keyboard_capturer_uses_window_events_ = false;
|
||||
bool foucs_on_main_window_ = false;
|
||||
bool focus_on_stream_window_ = false;
|
||||
bool audio_capture_ = false;
|
||||
int screen_width_ = 1280;
|
||||
int screen_height_ = 720;
|
||||
int selected_display_ = 0;
|
||||
std::string connect_button_label_ = "Connect";
|
||||
char input_password_tmp_[7] = "";
|
||||
char input_password_[7] = "";
|
||||
std::string random_password_;
|
||||
char new_password_[7] = "";
|
||||
char remote_id_display_[12] = "";
|
||||
unsigned char audio_buffer_[720]{};
|
||||
int audio_len_ = 0;
|
||||
@@ -87,7 +41,6 @@ struct InteractionState {
|
||||
bool need_to_rejoin_ = false;
|
||||
std::chrono::steady_clock::time_point last_rejoin_check_time_ =
|
||||
std::chrono::steady_clock::now();
|
||||
bool just_created_ = false;
|
||||
std::string controlled_remote_id_;
|
||||
std::string focused_remote_id_;
|
||||
std::string remote_client_id_;
|
||||
@@ -100,26 +53,13 @@ struct UpdateState {
|
||||
std::string release_name_;
|
||||
std::string release_notes_;
|
||||
std::string release_date_;
|
||||
bool show_new_version_icon_ = false;
|
||||
bool show_new_version_icon_in_menu_ = true;
|
||||
double new_version_icon_last_trigger_time_ = 0.0;
|
||||
double new_version_icon_render_start_time_ = 0.0;
|
||||
};
|
||||
|
||||
struct StreamWindowState {
|
||||
bool need_to_create_stream_window_ = false;
|
||||
bool stream_window_created_ = false;
|
||||
bool stream_window_inited_ = false;
|
||||
bool window_maximized_ = false;
|
||||
bool control_mouse_ = false;
|
||||
int stream_window_width_default_ = 1280;
|
||||
int stream_window_height_default_ = 720;
|
||||
float stream_window_width_ = 1280;
|
||||
float stream_window_height_ = 720;
|
||||
int stream_window_width_real_ = 1280;
|
||||
int stream_window_height_real_ = 720;
|
||||
float stream_window_dpi_scaling_w_ = 1.0f;
|
||||
float stream_window_dpi_scaling_h_ = 1.0f;
|
||||
};
|
||||
|
||||
struct ServerWindowState {
|
||||
@@ -130,60 +70,22 @@ struct ServerWindowState {
|
||||
std::atomic<bool> need_to_destroy_server_window_{false};
|
||||
bool server_window_created_ = false;
|
||||
bool server_window_inited_ = false;
|
||||
int server_window_width_default_ = 250;
|
||||
int server_window_height_default_ = 150;
|
||||
float server_window_width_ = 250;
|
||||
float server_window_height_ = 150;
|
||||
float server_window_title_bar_height_ = 30.0f;
|
||||
int server_window_normal_width_ = 250;
|
||||
int server_window_normal_height_ = 150;
|
||||
float server_window_dpi_scaling_w_ = 1.0f;
|
||||
float server_window_dpi_scaling_h_ = 1.0f;
|
||||
float window_rounding_ = 6.0f;
|
||||
float window_rounding_default_ = 6.0f;
|
||||
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;
|
||||
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;
|
||||
};
|
||||
|
||||
struct UiState {
|
||||
bool label_inited_ = false;
|
||||
bool connect_button_pressed_ = false;
|
||||
bool password_validating_ = false;
|
||||
uint32_t password_validating_time_ = 0;
|
||||
bool show_settings_window_ = false;
|
||||
bool show_self_hosted_server_config_window_ = false;
|
||||
bool rejoin_ = false;
|
||||
bool local_id_copied_ = false;
|
||||
bool show_password_ = true;
|
||||
bool show_about_window_ = false;
|
||||
bool show_connection_status_window_ = false;
|
||||
bool show_reset_password_window_ = false;
|
||||
bool show_update_notification_window_ = false;
|
||||
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 show_edit_connection_alias_window_ = false;
|
||||
bool show_offline_warning_window_ = false;
|
||||
bool delete_connection_ = false;
|
||||
bool is_tab_bar_hovered_ = false;
|
||||
std::string delete_connection_name_;
|
||||
std::string edit_connection_alias_remote_id_;
|
||||
char edit_connection_alias_[128] = "";
|
||||
std::string offline_warning_text_;
|
||||
bool re_enter_remote_id_ = false;
|
||||
double copy_start_time_ = 0;
|
||||
};
|
||||
|
||||
struct ApplicationState : MainWindowState,
|
||||
|
||||
@@ -1845,7 +1845,6 @@ void GuiApplication::Tick() {
|
||||
}
|
||||
}
|
||||
|
||||
UpdateLabels();
|
||||
const bool was_reloading_recent_connections = reload_recent_connections_;
|
||||
HandleRecentConnections();
|
||||
if (was_reloading_recent_connections && !reload_recent_connections_) {
|
||||
|
||||
@@ -1,323 +0,0 @@
|
||||
#include "application/gui_application.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
void GuiApplication::ProcessSdlEvent(const SDL_Event &event) {
|
||||
if (main_ctx_) {
|
||||
ImGui::SetCurrentContext(main_ctx_);
|
||||
ImGui_ImplSDL3_ProcessEvent(&event);
|
||||
} else {
|
||||
LOG_ERROR("Main context is null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (stream_window_inited_) {
|
||||
if (stream_ctx_) {
|
||||
ImGui::SetCurrentContext(stream_ctx_);
|
||||
ImGui_ImplSDL3_ProcessEvent(&event);
|
||||
} else {
|
||||
LOG_ERROR("Stream context is null");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (server_window_inited_) {
|
||||
if (server_ctx_) {
|
||||
ImGui::SetCurrentContext(server_ctx_);
|
||||
ImGui_ImplSDL3_ProcessEvent(&event);
|
||||
} else {
|
||||
LOG_ERROR("Server context is null");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (APP_EXIT_EVENT != 0 && event.type == APP_EXIT_EVENT) {
|
||||
LOG_INFO("Quit program from system tray");
|
||||
if (stream_window_) {
|
||||
SDL_SetWindowMouseGrab(stream_window_, false);
|
||||
}
|
||||
#if defined(__linux__) && !defined(__APPLE__)
|
||||
if (tray_) {
|
||||
tray_->RemoveTrayIcon();
|
||||
}
|
||||
#endif
|
||||
exit_ = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (clipboard_.event_type() != 0 && event.type == clipboard_.event_type()) {
|
||||
clipboard_.ApplyPendingRemoteText();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case SDL_EVENT_QUIT:
|
||||
if (stream_window_inited_) {
|
||||
LOG_INFO("Destroy stream window");
|
||||
SDL_SetWindowMouseGrab(stream_window_, false);
|
||||
DestroyStreamWindow();
|
||||
DestroyStreamWindowContext();
|
||||
|
||||
{
|
||||
// std::shared_lock lock(remote_sessions_mutex_);
|
||||
for (auto &[host_name, props] : remote_sessions_) {
|
||||
std::shared_ptr<std::vector<unsigned char>> frame_snapshot;
|
||||
int video_width = 0;
|
||||
int video_height = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
|
||||
frame_snapshot = props->front_frame_;
|
||||
video_width = props->video_width_;
|
||||
video_height = props->video_height_;
|
||||
}
|
||||
if (frame_snapshot && !frame_snapshot->empty() && video_width > 0 &&
|
||||
video_height > 0) {
|
||||
thumbnail_->SaveToThumbnail(
|
||||
(char *)frame_snapshot->data(), video_width, video_height,
|
||||
host_name, props->remote_host_name_,
|
||||
props->remember_password_ ? props->remote_password_ : "");
|
||||
}
|
||||
|
||||
if (props->peer_) {
|
||||
std::string client_id = (host_name == client_id_)
|
||||
? "C-" + std::string(client_id_)
|
||||
: client_id_;
|
||||
LOG_INFO("[{}] Leave connection [{}]", client_id, host_name);
|
||||
LeaveConnection(props->peer_, host_name.c_str());
|
||||
LOG_INFO("Destroy peer [{}]", client_id);
|
||||
DestroyPeer(&props->peer_);
|
||||
}
|
||||
|
||||
props->streaming_ = false;
|
||||
props->remember_password_ = false;
|
||||
props->connection_established_ = false;
|
||||
props->audio_capture_button_pressed_ = false;
|
||||
|
||||
memset(&props->net_traffic_stats_, 0,
|
||||
sizeof(props->net_traffic_stats_));
|
||||
SDL_SetWindowFullscreen(main_window_, false);
|
||||
SDL_FlushEvents(STREAM_REFRESH_EVENT, STREAM_REFRESH_EVENT);
|
||||
memset(audio_buffer_, 0, 720);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
// std::unique_lock lock(remote_sessions_mutex_);
|
||||
remote_sessions_.clear();
|
||||
}
|
||||
|
||||
rejoin_ = false;
|
||||
is_client_mode_ = false;
|
||||
reload_recent_connections_ = true;
|
||||
fullscreen_button_pressed_ = false;
|
||||
start_keyboard_capturer_ = false;
|
||||
just_created_ = false;
|
||||
recent_connection_image_save_time_ = SDL_GetTicks();
|
||||
} else {
|
||||
LOG_INFO("Quit program");
|
||||
exit_ = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_WINDOW_CLOSE_REQUESTED:
|
||||
if (stream_window_ &&
|
||||
event.window.windowID == SDL_GetWindowID(stream_window_)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (main_window_ &&
|
||||
event.window.windowID == SDL_GetWindowID(main_window_)) {
|
||||
if (!MinimizeMainWindowToTray()) {
|
||||
SDL_HideWindow(main_window_);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
exit_ = true;
|
||||
break;
|
||||
|
||||
case SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED:
|
||||
if (stream_window_created_ &&
|
||||
event.window.windowID == SDL_GetWindowID(stream_window_)) {
|
||||
UpdateRenderRect();
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_WINDOW_FOCUS_GAINED:
|
||||
if (stream_window_ &&
|
||||
SDL_GetWindowID(stream_window_) == event.window.windowID) {
|
||||
focus_on_stream_window_ = true;
|
||||
} else if (main_window_ &&
|
||||
SDL_GetWindowID(main_window_) == event.window.windowID) {
|
||||
foucs_on_main_window_ = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case SDL_EVENT_WINDOW_FOCUS_LOST:
|
||||
if (stream_window_ &&
|
||||
SDL_GetWindowID(stream_window_) == event.window.windowID) {
|
||||
keyboard_.ForceReleasePressedKeys();
|
||||
focus_on_stream_window_ = false;
|
||||
} else if (main_window_ &&
|
||||
SDL_GetWindowID(main_window_) == event.window.windowID) {
|
||||
foucs_on_main_window_ = false;
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_DROP_FILE:
|
||||
transfers_.HandleDropEvent(event);
|
||||
break;
|
||||
|
||||
case SDL_EVENT_CLIPBOARD_UPDATE:
|
||||
clipboard_.HandleLocalUpdate();
|
||||
break;
|
||||
|
||||
case SDL_EVENT_MOUSE_MOTION:
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
case SDL_EVENT_MOUSE_WHEEL: {
|
||||
Uint32 mouse_window_id = 0;
|
||||
if (event.type == SDL_EVENT_MOUSE_MOTION) {
|
||||
mouse_window_id = event.motion.windowID;
|
||||
} else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN ||
|
||||
event.type == SDL_EVENT_MOUSE_BUTTON_UP) {
|
||||
mouse_window_id = event.button.windowID;
|
||||
} else if (event.type == SDL_EVENT_MOUSE_WHEEL) {
|
||||
mouse_window_id = event.wheel.windowID;
|
||||
}
|
||||
|
||||
if (focus_on_stream_window_ && stream_window_ &&
|
||||
SDL_GetWindowID(stream_window_) == mouse_window_id) {
|
||||
ProcessMouseEvent(event);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
case SDL_EVENT_KEY_UP:
|
||||
if (keyboard_capturer_is_started_ &&
|
||||
keyboard_capturer_uses_window_events_ &&
|
||||
focus_on_stream_window_ && stream_window_ &&
|
||||
SDL_GetWindowID(stream_window_) == event.key.windowID) {
|
||||
ProcessKeyboardEvent(event);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (event.type == STREAM_REFRESH_EVENT) {
|
||||
auto *props = static_cast<RemoteSession *>(event.user.data1);
|
||||
if (!props) {
|
||||
break;
|
||||
}
|
||||
std::shared_ptr<std::vector<unsigned char>> frame_snapshot;
|
||||
int video_width = 0;
|
||||
int video_height = 0;
|
||||
bool render_rect_dirty = false;
|
||||
bool cleanup_pending = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
|
||||
cleanup_pending = props->stream_cleanup_pending_;
|
||||
if (!cleanup_pending) {
|
||||
frame_snapshot = props->front_frame_;
|
||||
video_width = props->video_width_;
|
||||
video_height = props->video_height_;
|
||||
}
|
||||
render_rect_dirty = props->render_rect_dirty_;
|
||||
}
|
||||
|
||||
if (cleanup_pending) {
|
||||
if (props->stream_texture_) {
|
||||
SDL_DestroyTexture(props->stream_texture_);
|
||||
props->stream_texture_ = nullptr;
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
|
||||
props->stream_cleanup_pending_ = false;
|
||||
}
|
||||
|
||||
if (render_rect_dirty) {
|
||||
UpdateRenderRect();
|
||||
std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
|
||||
props->render_rect_dirty_ = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (video_width <= 0 || video_height <= 0) {
|
||||
break;
|
||||
}
|
||||
if (!frame_snapshot || frame_snapshot->empty()) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (props->stream_texture_) {
|
||||
if (video_width != props->texture_width_ ||
|
||||
video_height != props->texture_height_) {
|
||||
props->texture_width_ = video_width;
|
||||
props->texture_height_ = video_height;
|
||||
|
||||
SDL_DestroyTexture(props->stream_texture_);
|
||||
// props->stream_texture_ = SDL_CreateTexture(
|
||||
// stream_renderer_, stream_pixformat_,
|
||||
// SDL_TEXTUREACCESS_STREAMING, props->texture_width_,
|
||||
// props->texture_height_);
|
||||
|
||||
SDL_PropertiesID nvProps = SDL_CreateProperties();
|
||||
SDL_SetNumberProperty(nvProps, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER,
|
||||
props->texture_width_);
|
||||
SDL_SetNumberProperty(nvProps, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER,
|
||||
props->texture_height_);
|
||||
SDL_SetNumberProperty(nvProps, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER,
|
||||
SDL_PIXELFORMAT_NV12);
|
||||
SDL_SetNumberProperty(nvProps,
|
||||
SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER,
|
||||
SDL_COLORSPACE_BT601_LIMITED);
|
||||
props->stream_texture_ =
|
||||
SDL_CreateTextureWithProperties(stream_renderer_, nvProps);
|
||||
SDL_DestroyProperties(nvProps);
|
||||
}
|
||||
} else {
|
||||
props->texture_width_ = video_width;
|
||||
props->texture_height_ = video_height;
|
||||
// props->stream_texture_ = SDL_CreateTexture(
|
||||
// stream_renderer_, stream_pixformat_,
|
||||
// SDL_TEXTUREACCESS_STREAMING, props->texture_width_,
|
||||
// props->texture_height_);
|
||||
|
||||
SDL_PropertiesID nvProps = SDL_CreateProperties();
|
||||
SDL_SetNumberProperty(nvProps, SDL_PROP_TEXTURE_CREATE_WIDTH_NUMBER,
|
||||
props->texture_width_);
|
||||
SDL_SetNumberProperty(nvProps, SDL_PROP_TEXTURE_CREATE_HEIGHT_NUMBER,
|
||||
props->texture_height_);
|
||||
SDL_SetNumberProperty(nvProps, SDL_PROP_TEXTURE_CREATE_FORMAT_NUMBER,
|
||||
SDL_PIXELFORMAT_NV12);
|
||||
SDL_SetNumberProperty(nvProps,
|
||||
SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER,
|
||||
SDL_COLORSPACE_BT601_LIMITED);
|
||||
props->stream_texture_ =
|
||||
SDL_CreateTextureWithProperties(stream_renderer_, nvProps);
|
||||
SDL_DestroyProperties(nvProps);
|
||||
}
|
||||
|
||||
SDL_UpdateTexture(props->stream_texture_, NULL, frame_snapshot->data(),
|
||||
props->texture_width_);
|
||||
|
||||
if (render_rect_dirty) {
|
||||
UpdateRenderRect();
|
||||
std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
|
||||
props->render_rect_dirty_ = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace crossdesk
|
||||
@@ -1,424 +0,0 @@
|
||||
#include "application/gui_application.h"
|
||||
|
||||
#include <remote_action.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
|
||||
#include "device_controller.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
namespace {
|
||||
int TranslateSdlKeypadScancodeToVk(const SDL_KeyboardEvent &event) {
|
||||
const bool numlock_enabled = (event.mod & SDL_KMOD_NUM) != 0;
|
||||
|
||||
switch (event.scancode) {
|
||||
case SDL_SCANCODE_NUMLOCKCLEAR:
|
||||
return 0x90;
|
||||
case SDL_SCANCODE_KP_ENTER:
|
||||
return 0x0D;
|
||||
case SDL_SCANCODE_KP_0:
|
||||
if (!numlock_enabled) {
|
||||
return 0x2D;
|
||||
}
|
||||
return 0x60;
|
||||
case SDL_SCANCODE_KP_1:
|
||||
if (!numlock_enabled) {
|
||||
return 0x23;
|
||||
}
|
||||
return 0x61;
|
||||
case SDL_SCANCODE_KP_2:
|
||||
if (!numlock_enabled) {
|
||||
return 0x28;
|
||||
}
|
||||
return 0x62;
|
||||
case SDL_SCANCODE_KP_3:
|
||||
if (!numlock_enabled) {
|
||||
return 0x22;
|
||||
}
|
||||
return 0x63;
|
||||
case SDL_SCANCODE_KP_4:
|
||||
if (!numlock_enabled) {
|
||||
return 0x25;
|
||||
}
|
||||
return 0x64;
|
||||
case SDL_SCANCODE_KP_5:
|
||||
return 0x65;
|
||||
case SDL_SCANCODE_KP_6:
|
||||
if (!numlock_enabled) {
|
||||
return 0x27;
|
||||
}
|
||||
return 0x66;
|
||||
case SDL_SCANCODE_KP_7:
|
||||
if (!numlock_enabled) {
|
||||
return 0x24;
|
||||
}
|
||||
return 0x67;
|
||||
case SDL_SCANCODE_KP_8:
|
||||
if (!numlock_enabled) {
|
||||
return 0x26;
|
||||
}
|
||||
return 0x68;
|
||||
case SDL_SCANCODE_KP_9:
|
||||
if (!numlock_enabled) {
|
||||
return 0x21;
|
||||
}
|
||||
return 0x69;
|
||||
case SDL_SCANCODE_KP_PERIOD:
|
||||
case SDL_SCANCODE_KP_COMMA:
|
||||
if (!numlock_enabled) {
|
||||
return 0x2E;
|
||||
}
|
||||
return 0x6E;
|
||||
case SDL_SCANCODE_KP_DIVIDE:
|
||||
return 0x6F;
|
||||
case SDL_SCANCODE_KP_MULTIPLY:
|
||||
return 0x6A;
|
||||
case SDL_SCANCODE_KP_MINUS:
|
||||
return 0x6D;
|
||||
case SDL_SCANCODE_KP_PLUS:
|
||||
return 0x6B;
|
||||
case SDL_SCANCODE_KP_EQUALS:
|
||||
return 0xBB;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int TranslateSdlKeyboardEventToVk(const SDL_KeyboardEvent &event) {
|
||||
const int keypad_key_code = TranslateSdlKeypadScancodeToVk(event);
|
||||
if (keypad_key_code >= 0) {
|
||||
return keypad_key_code;
|
||||
}
|
||||
|
||||
const int key = static_cast<int>(event.key);
|
||||
if (key >= 'a' && key <= 'z') {
|
||||
return key - 'a' + 0x41;
|
||||
}
|
||||
if (key >= 'A' && key <= 'Z') {
|
||||
return key;
|
||||
}
|
||||
if (key >= '0' && key <= '9') {
|
||||
return key;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case ';':
|
||||
return 0xBA;
|
||||
case '\'':
|
||||
return 0xDE;
|
||||
case '`':
|
||||
return 0xC0;
|
||||
case ',':
|
||||
return 0xBC;
|
||||
case '.':
|
||||
return 0xBE;
|
||||
case '/':
|
||||
return 0xBF;
|
||||
case '\\':
|
||||
return 0xDC;
|
||||
case '[':
|
||||
return 0xDB;
|
||||
case ']':
|
||||
return 0xDD;
|
||||
case '-':
|
||||
return 0xBD;
|
||||
case '=':
|
||||
return 0xBB;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (event.scancode) {
|
||||
case SDL_SCANCODE_ESCAPE:
|
||||
return 0x1B;
|
||||
case SDL_SCANCODE_RETURN:
|
||||
return 0x0D;
|
||||
case SDL_SCANCODE_SPACE:
|
||||
return 0x20;
|
||||
case SDL_SCANCODE_BACKSPACE:
|
||||
return 0x08;
|
||||
case SDL_SCANCODE_TAB:
|
||||
return 0x09;
|
||||
case SDL_SCANCODE_PRINTSCREEN:
|
||||
return 0x2C;
|
||||
case SDL_SCANCODE_SCROLLLOCK:
|
||||
return 0x91;
|
||||
case SDL_SCANCODE_PAUSE:
|
||||
return 0x13;
|
||||
case SDL_SCANCODE_INSERT:
|
||||
return 0x2D;
|
||||
case SDL_SCANCODE_DELETE:
|
||||
return 0x2E;
|
||||
case SDL_SCANCODE_HOME:
|
||||
return 0x24;
|
||||
case SDL_SCANCODE_END:
|
||||
return 0x23;
|
||||
case SDL_SCANCODE_PAGEUP:
|
||||
return 0x21;
|
||||
case SDL_SCANCODE_PAGEDOWN:
|
||||
return 0x22;
|
||||
case SDL_SCANCODE_LEFT:
|
||||
return 0x25;
|
||||
case SDL_SCANCODE_RIGHT:
|
||||
return 0x27;
|
||||
case SDL_SCANCODE_UP:
|
||||
return 0x26;
|
||||
case SDL_SCANCODE_DOWN:
|
||||
return 0x28;
|
||||
case SDL_SCANCODE_F1:
|
||||
return 0x70;
|
||||
case SDL_SCANCODE_F2:
|
||||
return 0x71;
|
||||
case SDL_SCANCODE_F3:
|
||||
return 0x72;
|
||||
case SDL_SCANCODE_F4:
|
||||
return 0x73;
|
||||
case SDL_SCANCODE_F5:
|
||||
return 0x74;
|
||||
case SDL_SCANCODE_F6:
|
||||
return 0x75;
|
||||
case SDL_SCANCODE_F7:
|
||||
return 0x76;
|
||||
case SDL_SCANCODE_F8:
|
||||
return 0x77;
|
||||
case SDL_SCANCODE_F9:
|
||||
return 0x78;
|
||||
case SDL_SCANCODE_F10:
|
||||
return 0x79;
|
||||
case SDL_SCANCODE_F11:
|
||||
return 0x7A;
|
||||
case SDL_SCANCODE_F12:
|
||||
return 0x7B;
|
||||
case SDL_SCANCODE_CAPSLOCK:
|
||||
return 0x14;
|
||||
case SDL_SCANCODE_LSHIFT:
|
||||
return 0xA0;
|
||||
case SDL_SCANCODE_RSHIFT:
|
||||
return 0xA1;
|
||||
case SDL_SCANCODE_LCTRL:
|
||||
return 0xA2;
|
||||
case SDL_SCANCODE_RCTRL:
|
||||
return 0xA3;
|
||||
case SDL_SCANCODE_LALT:
|
||||
return 0xA4;
|
||||
case SDL_SCANCODE_RALT:
|
||||
return 0xA5;
|
||||
case SDL_SCANCODE_LGUI:
|
||||
return 0x5B;
|
||||
case SDL_SCANCODE_RGUI:
|
||||
return 0x5C;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int GuiApplication::ProcessKeyboardEvent(const SDL_Event &event) {
|
||||
if (event.type != SDL_EVENT_KEY_DOWN && event.type != SDL_EVENT_KEY_UP) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// SDL represents key auto-repeat as additional key-down events. Forward
|
||||
// them so the fallback path has the same long-press behavior as native hooks.
|
||||
const int key_code = TranslateSdlKeyboardEventToVk(event.key);
|
||||
if (key_code < 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return keyboard_.SendKeyCommand(key_code, event.type == SDL_EVENT_KEY_DOWN);
|
||||
}
|
||||
|
||||
int GuiApplication::ProcessMouseEvent(const SDL_Event &event) {
|
||||
controlled_remote_id_ = "";
|
||||
RemoteAction remote_action{};
|
||||
|
||||
if ((event.type == SDL_EVENT_MOUSE_BUTTON_DOWN ||
|
||||
event.type == SDL_EVENT_MOUSE_BUTTON_UP) &&
|
||||
event.button.button != SDL_BUTTON_LEFT &&
|
||||
event.button.button != SDL_BUTTON_RIGHT &&
|
||||
event.button.button != SDL_BUTTON_MIDDLE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
float cursor_x = last_mouse_event.motion.x;
|
||||
float cursor_y = last_mouse_event.motion.y;
|
||||
|
||||
auto normalize_cursor_to_window_space = [&](float *x, float *y) {
|
||||
if (!x || !y || !stream_window_) {
|
||||
return;
|
||||
}
|
||||
|
||||
int window_width = 0;
|
||||
int window_height = 0;
|
||||
int pixel_width = 0;
|
||||
int pixel_height = 0;
|
||||
SDL_GetWindowSize(stream_window_, &window_width, &window_height);
|
||||
SDL_GetWindowSizeInPixels(stream_window_, &pixel_width, &pixel_height);
|
||||
|
||||
if (window_width <= 0 || window_height <= 0 || pixel_width <= 0 ||
|
||||
pixel_height <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((window_width != pixel_width || window_height != pixel_height) &&
|
||||
(*x > static_cast<float>(window_width) + 1.0f ||
|
||||
*y > static_cast<float>(window_height) + 1.0f)) {
|
||||
const float scale_x =
|
||||
static_cast<float>(window_width) / static_cast<float>(pixel_width);
|
||||
const float scale_y =
|
||||
static_cast<float>(window_height) / static_cast<float>(pixel_height);
|
||||
*x *= scale_x;
|
||||
*y *= scale_y;
|
||||
|
||||
static bool logged_pixel_to_window_conversion = false;
|
||||
if (!logged_pixel_to_window_conversion) {
|
||||
LOG_INFO(
|
||||
"Mouse coordinate space converted from pixels to window units: "
|
||||
"window={}x{}, pixels={}x{}, scale=({:.4f},{:.4f})",
|
||||
window_width, window_height, pixel_width, pixel_height, scale_x,
|
||||
scale_y);
|
||||
logged_pixel_to_window_conversion = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (event.type == SDL_EVENT_MOUSE_MOTION) {
|
||||
cursor_x = event.motion.x;
|
||||
cursor_y = event.motion.y;
|
||||
normalize_cursor_to_window_space(&cursor_x, &cursor_y);
|
||||
} else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN ||
|
||||
event.type == SDL_EVENT_MOUSE_BUTTON_UP) {
|
||||
cursor_x = event.button.x;
|
||||
cursor_y = event.button.y;
|
||||
normalize_cursor_to_window_space(&cursor_x, &cursor_y);
|
||||
} else if (event.type == SDL_EVENT_MOUSE_WHEEL) {
|
||||
cursor_x = last_mouse_event.motion.x;
|
||||
cursor_y = last_mouse_event.motion.y;
|
||||
}
|
||||
|
||||
const bool is_pointer_position_event =
|
||||
(event.type == SDL_EVENT_MOUSE_MOTION ||
|
||||
event.type == SDL_EVENT_MOUSE_BUTTON_DOWN ||
|
||||
event.type == SDL_EVENT_MOUSE_BUTTON_UP);
|
||||
|
||||
// std::shared_lock lock(remote_sessions_mutex_);
|
||||
for (auto &it : remote_sessions_) {
|
||||
auto props = it.second;
|
||||
if (!props->control_mouse_) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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_ ||
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_pointer_position_event && cursor_x >= render_rect.x &&
|
||||
cursor_x <= render_rect.x + render_rect.w &&
|
||||
cursor_y >= render_rect.y &&
|
||||
cursor_y <= render_rect.y + render_rect.h) {
|
||||
controlled_remote_id_ = it.first;
|
||||
last_mouse_event.motion.x = cursor_x;
|
||||
last_mouse_event.motion.y = cursor_y;
|
||||
last_mouse_event.button.x = cursor_x;
|
||||
last_mouse_event.button.y = cursor_y;
|
||||
|
||||
remote_action.m.x = (cursor_x - render_rect.x) / render_rect.w;
|
||||
remote_action.m.y = (cursor_y - render_rect.y) / render_rect.h;
|
||||
remote_action.m.x = std::clamp(remote_action.m.x, 0.0f, 1.0f);
|
||||
remote_action.m.y = std::clamp(remote_action.m.y, 0.0f, 1.0f);
|
||||
|
||||
if (SDL_EVENT_MOUSE_BUTTON_DOWN == event.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
if (SDL_BUTTON_LEFT == event.button.button) {
|
||||
remote_action.m.flag = MouseFlag::left_down;
|
||||
} else if (SDL_BUTTON_RIGHT == event.button.button) {
|
||||
remote_action.m.flag = MouseFlag::right_down;
|
||||
} else if (SDL_BUTTON_MIDDLE == event.button.button) {
|
||||
remote_action.m.flag = MouseFlag::middle_down;
|
||||
}
|
||||
} else if (SDL_EVENT_MOUSE_BUTTON_UP == event.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
if (SDL_BUTTON_LEFT == event.button.button) {
|
||||
remote_action.m.flag = MouseFlag::left_up;
|
||||
} else if (SDL_BUTTON_RIGHT == event.button.button) {
|
||||
remote_action.m.flag = MouseFlag::right_up;
|
||||
} else if (SDL_BUTTON_MIDDLE == event.button.button) {
|
||||
remote_action.m.flag = MouseFlag::middle_up;
|
||||
}
|
||||
} else if (SDL_EVENT_MOUSE_MOTION == event.type) {
|
||||
remote_action.type = ControlType::mouse;
|
||||
remote_action.m.flag = MouseFlag::move;
|
||||
}
|
||||
|
||||
if (overlay_hovered) {
|
||||
break;
|
||||
}
|
||||
if (props->peer_) {
|
||||
std::string msg = remote_action.to_json();
|
||||
SendReliableDataFrame(props->peer_, msg.c_str(), msg.size(),
|
||||
props->mouse_label_.c_str());
|
||||
}
|
||||
} else if (SDL_EVENT_MOUSE_WHEEL == event.type &&
|
||||
last_mouse_event.button.x >= render_rect.x &&
|
||||
last_mouse_event.button.x <= render_rect.x + render_rect.w &&
|
||||
last_mouse_event.button.y >= render_rect.y &&
|
||||
last_mouse_event.button.y <= render_rect.y + render_rect.h) {
|
||||
float scroll_x = event.wheel.x;
|
||||
float scroll_y = event.wheel.y;
|
||||
if (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) {
|
||||
scroll_x = -scroll_x;
|
||||
scroll_y = -scroll_y;
|
||||
}
|
||||
|
||||
remote_action.type = ControlType::mouse;
|
||||
|
||||
auto roundUp = [](float value) -> int {
|
||||
if (value > 0) {
|
||||
return static_cast<int>(std::ceil(value));
|
||||
} else if (value < 0) {
|
||||
return static_cast<int>(std::floor(value));
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
if (std::abs(scroll_y) >= std::abs(scroll_x)) {
|
||||
remote_action.m.flag = MouseFlag::wheel_vertical;
|
||||
remote_action.m.s = roundUp(scroll_y);
|
||||
} else {
|
||||
remote_action.m.flag = MouseFlag::wheel_horizontal;
|
||||
remote_action.m.s = roundUp(scroll_x);
|
||||
}
|
||||
|
||||
remote_action.m.x = (last_mouse_event.button.x - render_rect.x) /
|
||||
(std::max)(render_rect.w, 1.0f);
|
||||
remote_action.m.y = (last_mouse_event.button.y - render_rect.y) /
|
||||
(std::max)(render_rect.h, 1.0f);
|
||||
remote_action.m.x = std::clamp(remote_action.m.x, 0.0f, 1.0f);
|
||||
remote_action.m.y = std::clamp(remote_action.m.y, 0.0f, 1.0f);
|
||||
|
||||
if (overlay_hovered) {
|
||||
continue;
|
||||
}
|
||||
if (props->peer_) {
|
||||
std::string msg = remote_action.to_json();
|
||||
SendReliableDataFrame(props->peer_, msg.c_str(), msg.size(),
|
||||
props->mouse_label_.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace crossdesk
|
||||
@@ -1,687 +0,0 @@
|
||||
#include "application/gui_application.h"
|
||||
|
||||
#include <libyuv.h>
|
||||
|
||||
#if defined(__linux__) && !defined(__APPLE__)
|
||||
#include <X11/Xatom.h>
|
||||
#include <X11/Xlib.h>
|
||||
#endif
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "fa_regular_400.h"
|
||||
#include "fa_solid_900.h"
|
||||
#include "layout_relative.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "version_checker.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include "window_util_mac.h"
|
||||
#endif
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
namespace {
|
||||
const ImWchar *GetMultilingualGlyphRanges() {
|
||||
static std::vector<ImWchar> glyph_ranges;
|
||||
if (glyph_ranges.empty()) {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
ImFontGlyphRangesBuilder builder;
|
||||
builder.AddRanges(io.Fonts->GetGlyphRangesDefault());
|
||||
builder.AddRanges(io.Fonts->GetGlyphRangesChineseFull());
|
||||
builder.AddRanges(io.Fonts->GetGlyphRangesCyrillic());
|
||||
|
||||
ImVector<ImWchar> built_ranges;
|
||||
builder.BuildRanges(&built_ranges);
|
||||
glyph_ranges.assign(built_ranges.Data,
|
||||
built_ranges.Data + built_ranges.Size);
|
||||
}
|
||||
return glyph_ranges.empty() ? nullptr : glyph_ranges.data();
|
||||
}
|
||||
|
||||
bool CanReadFontFile(const char *font_path) {
|
||||
if (!font_path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ifstream font_file(font_path, std::ios::binary);
|
||||
return font_file.good();
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
HICON LoadTrayIcon() {
|
||||
HMODULE module = GetModuleHandleW(nullptr);
|
||||
HICON icon = reinterpret_cast<HICON>(
|
||||
LoadImageW(module, L"IDI_ICON1", IMAGE_ICON, 0, 0, LR_DEFAULTSIZE));
|
||||
if (icon) {
|
||||
return icon;
|
||||
}
|
||||
|
||||
return LoadIconW(nullptr, IDI_APPLICATION);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) && !defined(__APPLE__)
|
||||
inline bool X11GetDisplayAndWindow(SDL_Window *window, Display **display_out,
|
||||
::Window *x11_window_out) {
|
||||
if (!window || !display_out || !x11_window_out) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !defined(SDL_PROP_WINDOW_X11_DISPLAY_POINTER) || \
|
||||
!defined(SDL_PROP_WINDOW_X11_WINDOW_NUMBER)
|
||||
// SDL build does not expose X11 window properties.
|
||||
return false;
|
||||
#else
|
||||
SDL_PropertiesID props = SDL_GetWindowProperties(window);
|
||||
Display *display = (Display *)SDL_GetPointerProperty(
|
||||
props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, NULL);
|
||||
const Sint64 x11_window_num =
|
||||
SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
|
||||
const ::Window x11_window = (::Window)x11_window_num;
|
||||
|
||||
if (!display || !x11_window) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*display_out = display;
|
||||
*x11_window_out = x11_window;
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
inline void X11SendNetWmState(Display *display, ::Window x11_window,
|
||||
long action, Atom state1, Atom state2 = 0) {
|
||||
if (!display || !x11_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Atom wm_state = XInternAtom(display, "_NET_WM_STATE", False);
|
||||
|
||||
XEvent event;
|
||||
memset(&event, 0, sizeof(event));
|
||||
event.xclient.type = ClientMessage;
|
||||
event.xclient.serial = 0;
|
||||
event.xclient.send_event = True;
|
||||
event.xclient.message_type = wm_state;
|
||||
event.xclient.window = x11_window;
|
||||
event.xclient.format = 32;
|
||||
event.xclient.data.l[0] = action;
|
||||
event.xclient.data.l[1] = (long)state1;
|
||||
event.xclient.data.l[2] = (long)state2;
|
||||
event.xclient.data.l[3] = 1; // normal source indication
|
||||
event.xclient.data.l[4] = 0;
|
||||
|
||||
XSendEvent(display, DefaultRootWindow(display), False,
|
||||
SubstructureRedirectMask | SubstructureNotifyMask, &event);
|
||||
}
|
||||
|
||||
inline void X11SetWindowTypeUtility(Display *display, ::Window x11_window) {
|
||||
if (!display || !x11_window) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Atom wm_window_type =
|
||||
XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
|
||||
const Atom wm_window_type_utility =
|
||||
XInternAtom(display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
|
||||
|
||||
XChangeProperty(display, x11_window, wm_window_type, XA_ATOM, 32,
|
||||
PropModeReplace, (unsigned char *)&wm_window_type_utility, 1);
|
||||
}
|
||||
|
||||
inline void X11SetWindowAlwaysOnTop(SDL_Window *window) {
|
||||
Display *display = nullptr;
|
||||
::Window x11_window = 0;
|
||||
if (!X11GetDisplayAndWindow(window, &display, &x11_window)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Atom state_above = XInternAtom(display, "_NET_WM_STATE_ABOVE", False);
|
||||
const Atom state_stays_on_top =
|
||||
XInternAtom(display, "_NET_WM_STATE_STAYS_ON_TOP", False);
|
||||
|
||||
// Request _NET_WM_STATE_ADD for ABOVE + STAYS_ON_TOP.
|
||||
X11SendNetWmState(display, x11_window, 1, state_above, state_stays_on_top);
|
||||
XFlush(display);
|
||||
}
|
||||
|
||||
inline void X11SetWindowSkipTaskbar(SDL_Window *window) {
|
||||
Display *display = nullptr;
|
||||
::Window x11_window = 0;
|
||||
if (!X11GetDisplayAndWindow(window, &display, &x11_window)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const Atom skip_taskbar =
|
||||
XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False);
|
||||
const Atom skip_pager =
|
||||
XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", False);
|
||||
|
||||
// Request _NET_WM_STATE_ADD for SKIP_TASKBAR + SKIP_PAGER.
|
||||
X11SendNetWmState(display, x11_window, 1, skip_taskbar, skip_pager);
|
||||
|
||||
// Hint the WM that this is an auxiliary/utility window.
|
||||
X11SetWindowTypeUtility(display, x11_window);
|
||||
|
||||
XFlush(display);
|
||||
}
|
||||
#endif
|
||||
} // namespace
|
||||
|
||||
SDL_HitTestResult GuiApplication::HitTestCallback(SDL_Window *window,
|
||||
const SDL_Point *area,
|
||||
void *data) {
|
||||
GuiApplication *application = (GuiApplication *)data;
|
||||
if (!application) {
|
||||
return SDL_HITTEST_NORMAL;
|
||||
}
|
||||
|
||||
if (application->fullscreen_button_pressed_) {
|
||||
return SDL_HITTEST_NORMAL;
|
||||
}
|
||||
|
||||
// Server window: OS-level dragging for the title bar, but keep the left-side
|
||||
// collapse/expand button clickable.
|
||||
if (application->server_window_ && window == application->server_window_) {
|
||||
const float title_h = application->server_window_title_bar_height_;
|
||||
const float button_w = title_h;
|
||||
if (area->y >= 0 && area->y < title_h) {
|
||||
if (area->x >= 0 && area->x < button_w) {
|
||||
return SDL_HITTEST_NORMAL;
|
||||
}
|
||||
return SDL_HITTEST_DRAGGABLE;
|
||||
}
|
||||
return SDL_HITTEST_NORMAL;
|
||||
}
|
||||
|
||||
int window_width, window_height;
|
||||
SDL_GetWindowSize(window, &window_width, &window_height);
|
||||
|
||||
// check if curosor is in tab bar
|
||||
if (application->stream_window_inited_ && application->stream_window_created_ &&
|
||||
!application->fullscreen_button_pressed_ && application->stream_ctx_) {
|
||||
ImGuiContext *prev_ctx = ImGui::GetCurrentContext();
|
||||
ImGui::SetCurrentContext(application->stream_ctx_);
|
||||
|
||||
ImGuiWindow *tab_bar_window = ImGui::FindWindowByName("TabBar");
|
||||
if (tab_bar_window && tab_bar_window->Active) {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
float scale_x = io.DisplayFramebufferScale.x;
|
||||
float scale_y = io.DisplayFramebufferScale.y;
|
||||
|
||||
float tab_bar_x = tab_bar_window->Pos.x * scale_x;
|
||||
float tab_bar_y = tab_bar_window->Pos.y * scale_y;
|
||||
float tab_bar_width = tab_bar_window->Size.x * scale_x;
|
||||
float tab_bar_height = tab_bar_window->Size.y * scale_y;
|
||||
|
||||
ImGui::SetCurrentContext(prev_ctx);
|
||||
|
||||
if (area->x >= tab_bar_x && area->x <= tab_bar_x + tab_bar_width &&
|
||||
area->y >= tab_bar_y && area->y <= tab_bar_y + tab_bar_height) {
|
||||
return SDL_HITTEST_NORMAL;
|
||||
}
|
||||
} else {
|
||||
ImGui::SetCurrentContext(prev_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
float mouse_grab_padding = application->title_bar_button_width_ * 0.16f;
|
||||
if (area->y < application->title_bar_button_width_ &&
|
||||
area->y > mouse_grab_padding &&
|
||||
area->x < window_width - application->title_bar_button_width_ * 3.0f &&
|
||||
area->x > mouse_grab_padding) {
|
||||
return SDL_HITTEST_DRAGGABLE;
|
||||
}
|
||||
|
||||
// if (!application->streaming_) {
|
||||
// return SDL_HITTEST_NORMAL;
|
||||
// }
|
||||
|
||||
if (area->y < mouse_grab_padding) {
|
||||
if (area->x < mouse_grab_padding) {
|
||||
return SDL_HITTEST_RESIZE_TOPLEFT;
|
||||
} else if (area->x > window_width - mouse_grab_padding) {
|
||||
return SDL_HITTEST_RESIZE_TOPRIGHT;
|
||||
} else {
|
||||
return SDL_HITTEST_RESIZE_TOP;
|
||||
}
|
||||
} else if (area->y > window_height - mouse_grab_padding) {
|
||||
if (area->x < mouse_grab_padding) {
|
||||
return SDL_HITTEST_RESIZE_BOTTOMLEFT;
|
||||
} else if (area->x > window_width - mouse_grab_padding) {
|
||||
return SDL_HITTEST_RESIZE_BOTTOMRIGHT;
|
||||
} else {
|
||||
return SDL_HITTEST_RESIZE_BOTTOM;
|
||||
}
|
||||
} else if (area->x < mouse_grab_padding) {
|
||||
return SDL_HITTEST_RESIZE_LEFT;
|
||||
} else if (area->x > window_width - mouse_grab_padding) {
|
||||
return SDL_HITTEST_RESIZE_RIGHT;
|
||||
}
|
||||
|
||||
return SDL_HITTEST_NORMAL;
|
||||
}
|
||||
|
||||
int GuiApplication::CreateMainWindow() {
|
||||
main_ctx_ = ImGui::CreateContext();
|
||||
if (!main_ctx_) {
|
||||
LOG_ERROR("Main context is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ImGui::SetCurrentContext(main_ctx_);
|
||||
|
||||
if (!SDL_CreateWindowAndRenderer(
|
||||
"CrossDesk Main Window", (int)main_window_width_,
|
||||
(int)main_window_height_,
|
||||
SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_BORDERLESS |
|
||||
SDL_WINDOW_HIDDEN | SDL_WINDOW_TRANSPARENT,
|
||||
&main_window_, &main_renderer_)) {
|
||||
LOG_ERROR("Error creating MainWindow and MainRenderer: {}", SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
float dpi_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());
|
||||
if (std::abs(dpi_scale_ - dpi_scale) > 0.01f) {
|
||||
dpi_scale_ = dpi_scale;
|
||||
|
||||
main_window_width_ = (int)(main_window_width_default_ * dpi_scale_);
|
||||
main_window_height_ = (int)(main_window_height_default_ * dpi_scale_);
|
||||
stream_window_width_ = (int)(stream_window_width_default_ * dpi_scale_);
|
||||
stream_window_height_ = (int)(stream_window_height_default_ * dpi_scale_);
|
||||
server_window_width_ = (int)(server_window_width_default_ * dpi_scale_);
|
||||
server_window_height_ = (int)(server_window_height_default_ * dpi_scale_);
|
||||
server_window_normal_width_ =
|
||||
(int)(server_window_width_default_ * dpi_scale_);
|
||||
server_window_normal_height_ =
|
||||
(int)(server_window_height_default_ * dpi_scale_);
|
||||
window_rounding_ = window_rounding_default_ * dpi_scale_;
|
||||
|
||||
SDL_SetWindowSize(main_window_, (int)main_window_width_,
|
||||
(int)main_window_height_);
|
||||
}
|
||||
|
||||
SDL_SetWindowResizable(main_window_, false);
|
||||
|
||||
// for window region action
|
||||
SDL_SetWindowHitTest(main_window_, HitTestCallback, this);
|
||||
|
||||
SDL_SetRenderDrawBlendMode(main_renderer_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
SetupFontAndStyle(&main_windows_system_chinese_font_);
|
||||
|
||||
ImGuiStyle &style = ImGui::GetStyle();
|
||||
style.ScaleAllSizes(dpi_scale_);
|
||||
style.FontScaleDpi = dpi_scale_;
|
||||
|
||||
#if _WIN32
|
||||
SDL_PropertiesID props = SDL_GetWindowProperties(main_window_);
|
||||
HWND main_hwnd = (HWND)SDL_GetPointerProperty(
|
||||
props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
|
||||
|
||||
HICON tray_icon = LoadTrayIcon();
|
||||
tray_ = std::make_unique<WinTray>(main_hwnd, tray_icon, L"CrossDesk",
|
||||
localization_language_index_);
|
||||
#elif defined(__APPLE__)
|
||||
tray_ = std::make_unique<MacTray>(main_window_, "CrossDesk",
|
||||
localization_language_index_);
|
||||
#elif defined(__linux__) && !defined(__APPLE__)
|
||||
tray_ = std::make_unique<LinuxTray>(
|
||||
main_window_, "CrossDesk", localization_language_index_, APP_EXIT_EVENT);
|
||||
#endif
|
||||
|
||||
ImGui_ImplSDL3_InitForSDLRenderer(main_window_, main_renderer_);
|
||||
ImGui_ImplSDLRenderer3_Init(main_renderer_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::DestroyMainWindow() {
|
||||
if (main_ctx_) {
|
||||
ImGui::SetCurrentContext(main_ctx_);
|
||||
}
|
||||
|
||||
if (main_renderer_) {
|
||||
SDL_DestroyRenderer(main_renderer_);
|
||||
}
|
||||
|
||||
if (main_window_) {
|
||||
SDL_DestroyWindow(main_window_);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::CreateStreamWindow() {
|
||||
if (stream_window_created_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream_window_width_ = (int)(stream_window_width_default_ * dpi_scale_);
|
||||
stream_window_height_ = (int)(stream_window_height_default_ * dpi_scale_);
|
||||
|
||||
stream_ctx_ = ImGui::CreateContext();
|
||||
if (!stream_ctx_) {
|
||||
LOG_ERROR("Stream context is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ImGui::SetCurrentContext(stream_ctx_);
|
||||
|
||||
if (!SDL_CreateWindowAndRenderer(
|
||||
"CrossDesk Stream Window", (int)stream_window_width_,
|
||||
(int)stream_window_height_,
|
||||
SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_BORDERLESS |
|
||||
SDL_WINDOW_TRANSPARENT,
|
||||
&stream_window_, &stream_renderer_)) {
|
||||
LOG_ERROR("Error creating stream_window_ and stream_renderer_: {}",
|
||||
SDL_GetError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
stream_pixformat_ = SDL_PIXELFORMAT_NV12;
|
||||
|
||||
SDL_SetWindowResizable(stream_window_, true);
|
||||
|
||||
// for window region action
|
||||
SDL_SetWindowHitTest(stream_window_, HitTestCallback, this);
|
||||
|
||||
SDL_SetRenderDrawBlendMode(stream_renderer_, SDL_BLENDMODE_BLEND);
|
||||
|
||||
SetupFontAndStyle(&stream_windows_system_chinese_font_);
|
||||
|
||||
ImGuiStyle &style = ImGui::GetStyle();
|
||||
style.ScaleAllSizes(dpi_scale_);
|
||||
style.FontScaleDpi = dpi_scale_;
|
||||
|
||||
ImGui_ImplSDL3_InitForSDLRenderer(stream_window_, stream_renderer_);
|
||||
ImGui_ImplSDLRenderer3_Init(stream_renderer_);
|
||||
|
||||
// change props->stream_render_rect_
|
||||
SDL_Event event;
|
||||
event.type = SDL_EVENT_WINDOW_PIXEL_SIZE_CHANGED;
|
||||
event.window.windowID = SDL_GetWindowID(stream_window_);
|
||||
SDL_PushEvent(&event);
|
||||
|
||||
stream_window_created_ = true;
|
||||
just_created_ = true;
|
||||
|
||||
stream_window_inited_ = true;
|
||||
LOG_INFO("Stream window inited");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::DestroyStreamWindow() {
|
||||
stream_window_width_ = (float)stream_window_width_default_;
|
||||
stream_window_height_ = (float)stream_window_height_default_;
|
||||
|
||||
if (stream_ctx_) {
|
||||
ImGui::SetCurrentContext(stream_ctx_);
|
||||
}
|
||||
|
||||
if (stream_renderer_) {
|
||||
SDL_DestroyRenderer(stream_renderer_);
|
||||
stream_renderer_ = nullptr;
|
||||
}
|
||||
|
||||
if (stream_window_) {
|
||||
SDL_DestroyWindow(stream_window_);
|
||||
stream_window_ = nullptr;
|
||||
}
|
||||
|
||||
stream_window_created_ = false;
|
||||
focus_on_stream_window_ = false;
|
||||
stream_window_grabbed_ = false;
|
||||
control_mouse_ = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::CreateServerWindow() {
|
||||
if (server_window_created_) {
|
||||
return 0;
|
||||
}
|
||||
server_ctx_ = ImGui::CreateContext();
|
||||
if (!server_ctx_) {
|
||||
LOG_ERROR("Server context is null");
|
||||
return -1;
|
||||
}
|
||||
ImGui::SetCurrentContext(server_ctx_);
|
||||
if (!SDL_CreateWindowAndRenderer(
|
||||
"CrossDesk 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;
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
// Hide server window from the taskbar by making it a tool window.
|
||||
{
|
||||
SDL_PropertiesID server_props = SDL_GetWindowProperties(server_window_);
|
||||
HWND server_hwnd = (HWND)SDL_GetPointerProperty(
|
||||
server_props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
|
||||
|
||||
if (server_hwnd) {
|
||||
LONG_PTR ex_style = GetWindowLongPtr(server_hwnd, GWL_EXSTYLE);
|
||||
ex_style |= WS_EX_TOOLWINDOW;
|
||||
ex_style &= ~WS_EX_APPWINDOW;
|
||||
SetWindowLongPtr(server_hwnd, GWL_EXSTYLE, ex_style);
|
||||
|
||||
// Keep the server window above normal windows.
|
||||
SetWindowPos(server_hwnd, HWND_TOPMOST, 0, 0, 0, 0,
|
||||
SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED | SWP_NOACTIVATE);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(__linux__) && !defined(__APPLE__)
|
||||
// Best-effort keep above other windows on X11.
|
||||
X11SetWindowAlwaysOnTop(server_window_);
|
||||
// Best-effort hide from taskbar on X11.
|
||||
X11SetWindowSkipTaskbar(server_window_);
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
// Best-effort keep above other windows on macOS.
|
||||
MacSetWindowAlwaysOnTop(server_window_, true);
|
||||
// Best-effort exclude from Window menu / window cycling.
|
||||
MacSetWindowExcludedFromWindowMenu(server_window_, true);
|
||||
#endif
|
||||
|
||||
// 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(&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_);
|
||||
|
||||
server_window_created_ = true;
|
||||
server_window_inited_ = true;
|
||||
|
||||
LOG_INFO("Server window inited");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::DestroyServerWindow() {
|
||||
if (server_ctx_) {
|
||||
ImGui::SetCurrentContext(server_ctx_);
|
||||
}
|
||||
|
||||
if (server_renderer_) {
|
||||
SDL_DestroyRenderer(server_renderer_);
|
||||
server_renderer_ = nullptr;
|
||||
}
|
||||
|
||||
if (server_window_) {
|
||||
SDL_DestroyWindow(server_window_);
|
||||
server_window_ = nullptr;
|
||||
}
|
||||
|
||||
server_window_created_ = false;
|
||||
server_window_inited_ = false;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::SetupFontAndStyle(ImFont **system_chinese_font_out) {
|
||||
float font_size = 32.0f;
|
||||
|
||||
// Setup Dear ImGui style
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
|
||||
io.IniFilename = NULL; // disable imgui.ini
|
||||
|
||||
// Build one merged atlas: UI font + icon font + multilingual fallback fonts.
|
||||
ImFontConfig config;
|
||||
config.FontDataOwnedByAtlas = false;
|
||||
config.MergeMode = false;
|
||||
|
||||
if (system_chinese_font_out) {
|
||||
*system_chinese_font_out = nullptr;
|
||||
}
|
||||
|
||||
ImFont *ui_font = nullptr;
|
||||
const ImWchar *multilingual_ranges = GetMultilingualGlyphRanges();
|
||||
|
||||
#if defined(_WIN32)
|
||||
const char *base_font_paths[] = {
|
||||
"C:/Windows/Fonts/msyh.ttc", "C:/Windows/Fonts/msyhbd.ttc",
|
||||
"C:/Windows/Fonts/segoeui.ttf", "C:/Windows/Fonts/arial.ttf",
|
||||
"C:/Windows/Fonts/simsun.ttc", nullptr};
|
||||
#elif defined(__APPLE__)
|
||||
const char *base_font_paths[] = {
|
||||
"/System/Library/Fonts/PingFang.ttc",
|
||||
"/System/Library/Fonts/Supplemental/Arial Unicode.ttf",
|
||||
"/System/Library/Fonts/Supplemental/Arial.ttf",
|
||||
"/System/Library/Fonts/SFNS.ttf", nullptr};
|
||||
#else
|
||||
const char *base_font_paths[] = {
|
||||
"/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc",
|
||||
"/usr/share/fonts/opentype/noto/NotoSans-Regular.ttf",
|
||||
"/usr/share/fonts/truetype/wqy/wqy-microhei.ttc",
|
||||
"/usr/share/fonts/truetype/wqy/wqy-zenhei.ttc",
|
||||
"/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
||||
nullptr};
|
||||
#endif
|
||||
|
||||
for (int i = 0; base_font_paths[i] != nullptr && ui_font == nullptr; ++i) {
|
||||
if (!CanReadFontFile(base_font_paths[i])) {
|
||||
continue;
|
||||
}
|
||||
ui_font = io.Fonts->AddFontFromFileTTF(base_font_paths[i], font_size,
|
||||
&config, multilingual_ranges);
|
||||
if (ui_font != nullptr) {
|
||||
LOG_INFO("Loaded base UI font: {}", base_font_paths[i]);
|
||||
}
|
||||
}
|
||||
if (!ui_font) {
|
||||
ui_font = io.Fonts->AddFontDefault(&config);
|
||||
}
|
||||
|
||||
if (!ui_font) {
|
||||
LOG_WARN("Failed to initialize base UI font");
|
||||
ImGui::StyleColorsLight();
|
||||
return 0;
|
||||
}
|
||||
|
||||
ImFontConfig icon_config = config;
|
||||
icon_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, &icon_config, icon_ranges);
|
||||
|
||||
io.FontDefault = ui_font;
|
||||
if (system_chinese_font_out) {
|
||||
*system_chinese_font_out = ui_font;
|
||||
}
|
||||
|
||||
ImGui::StyleColorsLight();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::DestroyMainWindowContext() {
|
||||
if (!main_ctx_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ImGui::SetCurrentContext(main_ctx_);
|
||||
ImGui_ImplSDLRenderer3_Shutdown();
|
||||
ImGui_ImplSDL3_Shutdown();
|
||||
ImGui::DestroyContext(main_ctx_);
|
||||
main_ctx_ = nullptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::DestroyStreamWindowContext() {
|
||||
if (!stream_ctx_) {
|
||||
stream_window_inited_ = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
stream_window_inited_ = false;
|
||||
ImGui::SetCurrentContext(stream_ctx_);
|
||||
ImGui_ImplSDLRenderer3_Shutdown();
|
||||
ImGui_ImplSDL3_Shutdown();
|
||||
ImGui::DestroyContext(stream_ctx_);
|
||||
stream_ctx_ = nullptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::DestroyServerWindowContext() {
|
||||
if (!server_ctx_) {
|
||||
server_window_inited_ = false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
server_window_inited_ = false;
|
||||
ImGui::SetCurrentContext(server_ctx_);
|
||||
ImGui_ImplSDLRenderer3_Shutdown();
|
||||
ImGui_ImplSDL3_Shutdown();
|
||||
ImGui::DestroyContext(server_ctx_);
|
||||
server_ctx_ = nullptr;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace crossdesk
|
||||
@@ -1,236 +0,0 @@
|
||||
#include "application/gui_application.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
int GuiApplication::DrawMainWindow() {
|
||||
if (!main_ctx_) {
|
||||
LOG_ERROR("Main context is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ImGui::SetCurrentContext(main_ctx_);
|
||||
ImGui_ImplSDLRenderer3_NewFrame();
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x, io.DisplaySize.y),
|
||||
ImGuiCond_Always);
|
||||
ImGui::Begin("MainRender", nullptr,
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus |
|
||||
ImGuiWindowFlags_NoDocking);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
TitleBar(true);
|
||||
|
||||
MainWindow();
|
||||
|
||||
UpdateNotificationWindow();
|
||||
|
||||
#if _WIN32 && CROSSDESK_PORTABLE
|
||||
PortableServiceInstallWindow();
|
||||
#endif
|
||||
|
||||
#ifdef __APPLE__
|
||||
if (show_request_permission_window_) {
|
||||
RequestPermissionWindow();
|
||||
}
|
||||
#endif
|
||||
|
||||
ImGui::End();
|
||||
|
||||
// Rendering
|
||||
(void)io;
|
||||
ImGui::Render();
|
||||
SDL_SetRenderScale(main_renderer_, io.DisplayFramebufferScale.x,
|
||||
io.DisplayFramebufferScale.y);
|
||||
SDL_SetRenderDrawColor(main_renderer_, 0, 0, 0, 0);
|
||||
SDL_RenderClear(main_renderer_);
|
||||
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), main_renderer_);
|
||||
SDL_RenderPresent(main_renderer_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::DrawStreamWindow() {
|
||||
if (!stream_ctx_) {
|
||||
LOG_ERROR("Stream context is null");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ImGui::SetCurrentContext(stream_ctx_);
|
||||
ImGui_ImplSDLRenderer3_NewFrame();
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
StreamWindow();
|
||||
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
float stream_title_window_height =
|
||||
fullscreen_button_pressed_ ? 0 : title_bar_height_;
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
// Set minimum window size to 0 to allow exact height control
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(io.DisplaySize.x, stream_title_window_height),
|
||||
ImGuiCond_Always);
|
||||
ImGui::Begin("StreamTitleWindow", nullptr,
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus |
|
||||
ImGuiWindowFlags_NoDocking);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (!fullscreen_button_pressed_) {
|
||||
TitleBar(false);
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
|
||||
// Rendering
|
||||
(void)io;
|
||||
ImGui::Render();
|
||||
SDL_SetRenderScale(stream_renderer_, io.DisplayFramebufferScale.x,
|
||||
io.DisplayFramebufferScale.y);
|
||||
SDL_SetRenderDrawColor(stream_renderer_, 0, 0, 0, 255);
|
||||
SDL_RenderClear(stream_renderer_);
|
||||
|
||||
// std::shared_lock lock(remote_sessions_mutex_);
|
||||
for (auto &it : remote_sessions_) {
|
||||
auto props = it.second;
|
||||
if (props->tab_selected_) {
|
||||
SDL_FRect render_rect_f = {
|
||||
props->stream_render_rect_f_.x, props->stream_render_rect_f_.y,
|
||||
props->stream_render_rect_f_.w, props->stream_render_rect_f_.h};
|
||||
SDL_RenderTexture(stream_renderer_, props->stream_texture_, NULL,
|
||||
&render_rect_f);
|
||||
}
|
||||
}
|
||||
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), stream_renderer_);
|
||||
SDL_RenderPresent(stream_renderer_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::DrawServerWindow() {
|
||||
if (!server_ctx_) {
|
||||
LOG_ERROR("Server context is null");
|
||||
return -1;
|
||||
}
|
||||
ImGui::SetCurrentContext(server_ctx_);
|
||||
ImGui_ImplSDLRenderer3_NewFrame();
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
server_window_width_ = io.DisplaySize.x;
|
||||
server_window_height_ = io.DisplaySize.y;
|
||||
|
||||
ServerWindow();
|
||||
ImGui::Render();
|
||||
SDL_SetRenderScale(server_renderer_, io.DisplayFramebufferScale.x,
|
||||
io.DisplayFramebufferScale.y);
|
||||
SDL_SetRenderDrawColor(server_renderer_, 0, 0, 0, 0);
|
||||
SDL_RenderClear(server_renderer_);
|
||||
ImGui_ImplSDLRenderer3_RenderDrawData(ImGui::GetDrawData(), server_renderer_);
|
||||
SDL_RenderPresent(server_renderer_);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool GuiApplication::MinimizeMainWindowToTray() {
|
||||
if (!main_window_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if defined(_WIN32) || defined(__APPLE__)
|
||||
if (!tray_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tray_->MinimizeToTray();
|
||||
return true;
|
||||
#elif defined(__linux__) && !defined(__APPLE__)
|
||||
if (!tray_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return tray_->MinimizeToTray();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GuiApplication::UpdateRenderRect() {
|
||||
// std::shared_lock lock(remote_sessions_mutex_);
|
||||
for (auto &[_, props] : remote_sessions_) {
|
||||
if (!props->reset_control_bar_pos_) {
|
||||
props->mouse_diff_control_bar_pos_x_ = 0;
|
||||
props->mouse_diff_control_bar_pos_y_ = 0;
|
||||
}
|
||||
|
||||
if (!just_created_) {
|
||||
props->reset_control_bar_pos_ = true;
|
||||
}
|
||||
|
||||
int stream_window_width, stream_window_height;
|
||||
SDL_GetWindowSize(stream_window_, &stream_window_width,
|
||||
&stream_window_height);
|
||||
stream_window_width_ = (float)stream_window_width;
|
||||
stream_window_height_ = (float)stream_window_height;
|
||||
|
||||
float video_ratio =
|
||||
(float)props->video_width_ / (float)props->video_height_;
|
||||
float video_ratio_reverse =
|
||||
(float)props->video_height_ / (float)props->video_width_;
|
||||
|
||||
float render_area_width = props->render_window_width_;
|
||||
float render_area_height = props->render_window_height_;
|
||||
|
||||
props->stream_render_rect_last_ = props->stream_render_rect_;
|
||||
|
||||
SDL_FRect rect_f{props->render_window_x_, props->render_window_y_,
|
||||
render_area_width, render_area_height};
|
||||
if (render_area_width < render_area_height * video_ratio) {
|
||||
rect_f.x = props->render_window_x_;
|
||||
rect_f.y = std::abs(render_area_height -
|
||||
render_area_width * video_ratio_reverse) /
|
||||
2.0f +
|
||||
props->render_window_y_;
|
||||
rect_f.w = render_area_width;
|
||||
rect_f.h = render_area_width * video_ratio_reverse;
|
||||
} else if (render_area_width > render_area_height * video_ratio) {
|
||||
rect_f.x =
|
||||
std::abs(render_area_width - render_area_height * video_ratio) /
|
||||
2.0f +
|
||||
props->render_window_x_;
|
||||
rect_f.y = props->render_window_y_;
|
||||
rect_f.w = render_area_height * video_ratio;
|
||||
rect_f.h = render_area_height;
|
||||
} else {
|
||||
rect_f.x = props->render_window_x_;
|
||||
rect_f.y = props->render_window_y_;
|
||||
rect_f.w = render_area_width;
|
||||
rect_f.h = render_area_height;
|
||||
}
|
||||
|
||||
props->stream_render_rect_f_ = rect_f;
|
||||
props->stream_render_rect_ = {static_cast<int>(std::lround(rect_f.x)),
|
||||
static_cast<int>(std::lround(rect_f.y)),
|
||||
static_cast<int>(std::lround(rect_f.w)),
|
||||
static_cast<int>(std::lround(rect_f.h))};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crossdesk
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,83 +0,0 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-06-14
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LAYOUT_STYLE_H_
|
||||
#define _LAYOUT_STYLE_H_
|
||||
|
||||
#define MENU_WINDOW_WIDTH_CN 300 * dpi_scale_
|
||||
#define MENU_WINDOW_HEIGHT_CN 280 * dpi_scale_
|
||||
#define LOCAL_WINDOW_WIDTH_CN 300 * dpi_scale_
|
||||
#define LOCAL_WINDOW_HEIGHT_CN 280 * dpi_scale_
|
||||
#define REMOTE_WINDOW_WIDTH_CN 300 * dpi_scale_
|
||||
#define REMOTE_WINDOW_HEIGHT_CN 280 * dpi_scale_
|
||||
#define MENU_WINDOW_WIDTH_EN 190 * dpi_scale_
|
||||
#define MENU_WINDOW_HEIGHT_EN 245 * dpi_scale_
|
||||
#define IPUT_WINDOW_WIDTH 160 * dpi_scale_
|
||||
#define INPUT_WINDOW_PADDING_CN 66 * dpi_scale_
|
||||
#define INPUT_WINDOW_PADDING_EN 96 * dpi_scale_
|
||||
#define SETTINGS_WINDOW_WIDTH_CN 202 * dpi_scale_
|
||||
#define SETTINGS_WINDOW_WIDTH_EN 248 * dpi_scale_
|
||||
#if USE_CUDA
|
||||
#if _WIN32
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 405 * dpi_scale_
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 405 * dpi_scale_
|
||||
#else
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 375 * dpi_scale_
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 375 * dpi_scale_
|
||||
#endif
|
||||
#else
|
||||
#if _WIN32
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 375 * dpi_scale_
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 375 * dpi_scale_
|
||||
#else
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 345 * dpi_scale_
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 345 * dpi_scale_
|
||||
#endif
|
||||
#endif
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN 228 * dpi_scale_
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN 275 * dpi_scale_
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN 195 * dpi_scale_
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN 195 * dpi_scale_
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 120 * dpi_scale_
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 167 * dpi_scale_
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 120 * dpi_scale_
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 167 * dpi_scale_
|
||||
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_CN 120 * dpi_scale_
|
||||
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_EN 167 * dpi_scale_
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 120 * dpi_scale_
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 167 * dpi_scale_
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 171 * dpi_scale_
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 218 * dpi_scale_
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_CN 171 * dpi_scale_
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_EN 218 * dpi_scale_
|
||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171 * dpi_scale_
|
||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN 218 * dpi_scale_
|
||||
#define ENABLE_AUTOSTART_PADDING_CN 171 * dpi_scale_
|
||||
#define ENABLE_AUTOSTART_PADDING_EN 218 * dpi_scale_
|
||||
#define ENABLE_DAEMON_PADDING_CN 171 * dpi_scale_
|
||||
#define ENABLE_DAEMON_PADDING_EN 218 * dpi_scale_
|
||||
#define ENABLE_MINIZE_TO_TRAY_PADDING_CN 171 * dpi_scale_
|
||||
#define ENABLE_MINIZE_TO_TRAY_PADDING_EN 218 * dpi_scale_
|
||||
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_CN 90 * dpi_scale_
|
||||
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_EN 137 * dpi_scale_
|
||||
#define SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN 90 * dpi_scale_
|
||||
#define SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN 137 * dpi_scale_
|
||||
#define SETTINGS_SELECT_WINDOW_WIDTH 73 * dpi_scale_
|
||||
#define SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH 130 * dpi_scale_
|
||||
#define SETTINGS_OK_BUTTON_PADDING_CN 65 * dpi_scale_
|
||||
#define SETTINGS_OK_BUTTON_PADDING_EN 83 * dpi_scale_
|
||||
#define SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_CN 78 * dpi_scale_
|
||||
#define SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_EN 91 * dpi_scale_
|
||||
#define UPDATE_NOTIFICATION_OK_BUTTON_PADDING_CN 162 * dpi_scale_
|
||||
#define UPDATE_NOTIFICATION_OK_BUTTON_PADDING_EN 146 * dpi_scale_
|
||||
#define UPDATE_NOTIFICATION_RESERVED_HEIGHT 120 * dpi_scale_
|
||||
#define REQUEST_PERMISSION_WINDOW_WIDTH_CN 130 * dpi_scale_
|
||||
#define REQUEST_PERMISSION_WINDOW_HEIGHT_CN 125 * dpi_scale_
|
||||
#define REQUEST_PERMISSION_WINDOW_WIDTH_EN 260 * dpi_scale_
|
||||
#define REQUEST_PERMISSION_WINDOW_HEIGHT_EN 125 * dpi_scale_
|
||||
#define REQUEST_PERMISSION_WINDOW_CHECKBOX_PADDING_CN 90 * dpi_scale_
|
||||
#define REQUEST_PERMISSION_WINDOW_CHECKBOX_PADDING_EN 210 * dpi_scale_
|
||||
#endif
|
||||
@@ -1,87 +0,0 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2024-06-14
|
||||
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _LAYOUT_STYLE_H_
|
||||
#define _LAYOUT_STYLE_H_
|
||||
|
||||
#define TITLE_BAR_HEIGHT 0.0625f
|
||||
#define TITLE_BAR_BUTTON_WIDTH 0.0625f
|
||||
#define TITLE_BAR_BUTTON_HEIGHT 0.0625f
|
||||
#define STATUS_BAR_HEIGHT 0.05f
|
||||
#define MENU_WINDOW_WIDTH_CN 300
|
||||
#define MENU_WINDOW_HEIGHT_CN 280
|
||||
#define LOCAL_WINDOW_WIDTH_CN 300
|
||||
#define LOCAL_WINDOW_HEIGHT_CN 280
|
||||
#define REMOTE_WINDOW_WIDTH_CN 300
|
||||
#define REMOTE_WINDOW_HEIGHT_CN 280
|
||||
#define MENU_WINDOW_WIDTH_EN 190
|
||||
#define MENU_WINDOW_HEIGHT_EN 245
|
||||
#define IPUT_WINDOW_WIDTH 160
|
||||
#define INPUT_WINDOW_PADDING_CN 66
|
||||
#define INPUT_WINDOW_PADDING_EN 96
|
||||
#define SETTINGS_WINDOW_WIDTH_CN 202
|
||||
#define SETTINGS_WINDOW_WIDTH_EN 248
|
||||
#if USE_CUDA
|
||||
#if _WIN32
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 405
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 405
|
||||
#else
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 375
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 375
|
||||
#endif
|
||||
#else
|
||||
#if _WIN32
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 375
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 375
|
||||
#else
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 345
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 345
|
||||
#endif
|
||||
#endif
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN 228
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN 275
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN 195
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN 195
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_CN 120
|
||||
#define LANGUAGE_SELECT_WINDOW_PADDING_EN 167
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN 120
|
||||
#define VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN 167
|
||||
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_CN 120
|
||||
#define VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_EN 167
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN 120
|
||||
#define VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN 167
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN 171
|
||||
#define ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_CN 171
|
||||
#define ENABLE_TURN_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171
|
||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_AUTOSTART_PADDING_CN 171
|
||||
#define ENABLE_AUTOSTART_PADDING_EN 218
|
||||
#define ENABLE_DAEMON_PADDING_CN 171
|
||||
#define ENABLE_DAEMON_PADDING_EN 218
|
||||
#define ENABLE_MINIZE_TO_TRAY_PADDING_CN 171
|
||||
#define ENABLE_MINIZE_TO_TRAY_PADDING_EN 218
|
||||
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_CN 90
|
||||
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_EN 137
|
||||
#define SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN 90
|
||||
#define SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN 137
|
||||
#define SETTINGS_SELECT_WINDOW_WIDTH 73
|
||||
#define SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH 130
|
||||
#define SETTINGS_OK_BUTTON_PADDING_CN 65
|
||||
#define SETTINGS_OK_BUTTON_PADDING_EN 83
|
||||
#define SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_CN 78
|
||||
#define SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_EN 91
|
||||
#define UPDATE_NOTIFICATION_OK_BUTTON_PADDING_CN 162
|
||||
#define UPDATE_NOTIFICATION_OK_BUTTON_PADDING_EN 146
|
||||
#define UPDATE_NOTIFICATION_RESERVED_HEIGHT 120
|
||||
#define REQUEST_PERMISSION_WINDOW_WIDTH_CN 130
|
||||
#define REQUEST_PERMISSION_WINDOW_HEIGHT_CN 125
|
||||
#define REQUEST_PERMISSION_WINDOW_WIDTH_EN 260
|
||||
#define REQUEST_PERMISSION_WINDOW_HEIGHT_EN 125
|
||||
#define REQUEST_PERMISSION_WINDOW_CHECKBOX_PADDING_CN 90
|
||||
#define REQUEST_PERMISSION_WINDOW_CHECKBOX_PADDING_EN 210
|
||||
#endif
|
||||
@@ -596,22 +596,6 @@ std::string SettingsManager::RecentConnectionDisplayName(
|
||||
return connection.remote_id;
|
||||
}
|
||||
|
||||
void SettingsManager::BeginEditRecentConnectionAlias(
|
||||
const Thumbnail::RecentConnection &connection) {
|
||||
owner_.edit_connection_alias_remote_id_ = connection.remote_id;
|
||||
std::memset(owner_.edit_connection_alias_, 0,
|
||||
sizeof(owner_.edit_connection_alias_));
|
||||
|
||||
const auto alias_it = recent_connection_aliases_.find(connection.remote_id);
|
||||
const std::string alias = alias_it != recent_connection_aliases_.end()
|
||||
? alias_it->second
|
||||
: RecentConnectionDisplayName(connection);
|
||||
CopyString(owner_.edit_connection_alias_, alias.c_str());
|
||||
|
||||
owner_.focus_on_input_widget_ = true;
|
||||
owner_.show_edit_connection_alias_window_ = true;
|
||||
}
|
||||
|
||||
void SettingsManager::SetRecentConnectionAlias(const std::string &remote_id,
|
||||
const std::string &alias) {
|
||||
if (!remote_id.empty()) {
|
||||
|
||||
@@ -25,8 +25,6 @@ public:
|
||||
int SaveRecentConnectionAliases() const;
|
||||
std::string RecentConnectionDisplayName(
|
||||
const Thumbnail::RecentConnection &connection) const;
|
||||
void
|
||||
BeginEditRecentConnectionAlias(const Thumbnail::RecentConnection &connection);
|
||||
void SetRecentConnectionAlias(const std::string &remote_id,
|
||||
const std::string &alias);
|
||||
void EraseRecentConnectionAlias(const std::string &remote_id);
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace crossdesk {
|
||||
|
||||
class GuiApplication;
|
||||
|
||||
// Stable application-facing facade. The SDL application and feature
|
||||
// Stable application-facing facade. The Slint application and feature
|
||||
// controllers remain private implementation details.
|
||||
class Render {
|
||||
public:
|
||||
@@ -31,4 +31,4 @@ private:
|
||||
|
||||
} // namespace crossdesk
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -496,13 +496,6 @@ int GuiRuntime::ConnectTo(const std::string& remote_id, const char* password,
|
||||
props->params_.user_data = props->peer_events_.get();
|
||||
props->peer_ = CreatePeer(&props->params_);
|
||||
|
||||
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_ * 10.0f;
|
||||
props->control_window_max_height_ = title_bar_height_ * 7.0f;
|
||||
|
||||
props->connection_status_.store(ConnectionStatus::Connecting);
|
||||
show_connection_status_window_ = true;
|
||||
|
||||
|
||||
@@ -211,19 +211,6 @@ int GuiRuntime::CreateConnectionPeer() {
|
||||
}
|
||||
}
|
||||
|
||||
void GuiRuntime::UpdateLabels() {
|
||||
if (!label_inited_ ||
|
||||
localization_language_index_last_ != localization_language_index_) {
|
||||
connect_button_label_ =
|
||||
connect_button_pressed_
|
||||
? localization::disconnect[localization_language_index_]
|
||||
: localization::connect[localization_language_index_];
|
||||
label_inited_ = true;
|
||||
localization_language_index_last_ = localization_language_index_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void GuiRuntime::HandleRecentConnections() {
|
||||
if (session_cleanup_tasks_.empty() && reload_recent_connections_ && thumbnail_) {
|
||||
uint32_t now_time = SDL_GetTicks();
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace crossdesk {
|
||||
class VideoRenderer;
|
||||
|
||||
// Shared GUI runtime. It owns subsystem controllers and cross-cutting session
|
||||
// state, but no window lifecycle, ImGui view, or transport callback methods.
|
||||
// state, but no window lifecycle, UI view, or transport callback methods.
|
||||
class GuiRuntime : protected gui_detail::GuiState {
|
||||
protected:
|
||||
using FileTransferState = gui_detail::FileTransferState;
|
||||
@@ -53,7 +53,6 @@ class GuiRuntime : protected gui_detail::GuiState {
|
||||
int RequestSingleDevicePresence(const std::string& remote_id,
|
||||
const char* password, bool remember_password);
|
||||
|
||||
void UpdateLabels();
|
||||
void HandleRecentConnections();
|
||||
void HandleConnectionStatusChange();
|
||||
void HandlePendingPresenceProbe();
|
||||
|
||||
@@ -383,21 +383,6 @@ void PeerEventHandler::OnConnectionStatus(ConnectionStatus status,
|
||||
case ConnectionStatus::IncorrectPassword: {
|
||||
runtime->password_validating_ = false;
|
||||
runtime->password_validating_time_++;
|
||||
if (runtime->connect_button_pressed_) {
|
||||
runtime->connect_button_pressed_ = false;
|
||||
props->connection_established_ = false;
|
||||
runtime->connect_button_label_ =
|
||||
localization::connect[runtime->localization_language_index_];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ConnectionStatus::NoSuchTransmissionId:
|
||||
case ConnectionStatus::RemoteUnavailable: {
|
||||
if (runtime->connect_button_pressed_) {
|
||||
props->connection_established_ = false;
|
||||
runtime->connect_button_label_ =
|
||||
localization::connect[runtime->localization_language_index_];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -39,7 +38,6 @@ struct FileTransferState {
|
||||
std::chrono::steady_clock::time_point file_send_last_update_time_;
|
||||
uint64_t file_send_last_bytes_ = 0;
|
||||
bool file_transfer_window_visible_ = false;
|
||||
bool file_transfer_window_hovered_ = false;
|
||||
std::atomic<uint32_t> current_file_id_{0};
|
||||
|
||||
struct QueuedFile {
|
||||
@@ -93,31 +91,9 @@ struct RemoteSession {
|
||||
bool audio_capture_button_pressed_ = true;
|
||||
bool control_mouse_ = true;
|
||||
bool streaming_ = false;
|
||||
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;
|
||||
bool control_window_height_is_changing_ = false;
|
||||
bool p2p_mode_ = true;
|
||||
bool remember_password_ = false;
|
||||
char remote_password_[7] = "";
|
||||
float sub_stream_window_width_ = 1280;
|
||||
float sub_stream_window_height_ = 720;
|
||||
float control_window_min_width_ = 20;
|
||||
float control_window_max_width_ = 300;
|
||||
float control_window_min_height_ = 38;
|
||||
float control_window_max_height_ = 180;
|
||||
float control_window_width_ = 300;
|
||||
float control_window_height_ = 38;
|
||||
float control_bar_pos_x_ = 0;
|
||||
float control_bar_pos_y_ = 30;
|
||||
float mouse_diff_control_bar_pos_x_ = 0;
|
||||
float mouse_diff_control_bar_pos_y_ = 0;
|
||||
double control_bar_button_pressed_time_ = 0;
|
||||
double net_traffic_stats_button_pressed_time_ = 0;
|
||||
|
||||
// Written by the decode callback thread and consumed by the UI thread.
|
||||
std::mutex video_frame_mutex_;
|
||||
@@ -131,31 +107,13 @@ struct RemoteSession {
|
||||
std::chrono::steady_clock::time_point background_snapshot_time_;
|
||||
bool render_rect_dirty_ = false;
|
||||
bool stream_cleanup_pending_ = false;
|
||||
float mouse_pos_x_ = 0;
|
||||
float mouse_pos_y_ = 0;
|
||||
float mouse_pos_x_last_ = 0;
|
||||
float mouse_pos_y_last_ = 0;
|
||||
int texture_width_ = 1280;
|
||||
int texture_height_ = 720;
|
||||
int video_width_ = 0;
|
||||
int video_height_ = 0;
|
||||
int video_width_last_ = 0;
|
||||
int video_height_last_ = 0;
|
||||
int selected_display_ = 0;
|
||||
size_t video_size_ = 0;
|
||||
uint64_t video_frame_sequence_ = 0;
|
||||
bool tab_selected_ = false;
|
||||
bool tab_opened_ = true;
|
||||
std::optional<float> pos_x_before_docked_;
|
||||
std::optional<float> pos_y_before_docked_;
|
||||
float render_window_x_ = 0;
|
||||
float render_window_y_ = 0;
|
||||
float render_window_width_ = 0;
|
||||
float render_window_height_ = 0;
|
||||
std::string fullscreen_button_label_ = "Fullscreen";
|
||||
std::string net_traffic_stats_button_label_ = "Show Net Traffic Stats";
|
||||
std::string mouse_control_button_label_ = "Mouse Control";
|
||||
std::string audio_capture_button_label_ = "Audio Capture";
|
||||
std::string remote_host_name_;
|
||||
bool remote_service_status_received_ = false;
|
||||
std::mutex privacy_status_mutex_;
|
||||
|
||||
@@ -40,7 +40,6 @@ struct InfrastructureState {
|
||||
std::string dll_log_path_;
|
||||
std::string cache_path_;
|
||||
int localization_language_index_ = -1;
|
||||
int localization_language_index_last_ = -1;
|
||||
bool modules_inited_ = false;
|
||||
unsigned char aes128_key_[16]{};
|
||||
unsigned char aes128_iv_[16]{};
|
||||
@@ -123,7 +122,6 @@ struct PlatformIntegrationState {
|
||||
|
||||
struct UserSettingsState {
|
||||
char client_id_[10] = "";
|
||||
char client_id_display_[12] = "";
|
||||
char client_id_with_password_[17] = "";
|
||||
char password_saved_[7] = "";
|
||||
char self_hosted_id_[17] = "";
|
||||
@@ -155,8 +153,6 @@ struct UserSettingsState {
|
||||
std::string file_transfer_save_path_last_;
|
||||
char signal_server_ip_self_[256] = "";
|
||||
char signal_server_port_self_[6] = "";
|
||||
bool settings_window_pos_reset_ = true;
|
||||
bool self_hosted_server_config_window_pos_reset_ = true;
|
||||
std::string selected_current_file_path_;
|
||||
bool show_file_browser_ = true;
|
||||
};
|
||||
@@ -204,7 +200,6 @@ struct ConnectionState {
|
||||
std::unordered_map<std::string, std::chrono::steady_clock::time_point>
|
||||
last_remote_pointer_input_time_;
|
||||
std::string selected_server_remote_id_;
|
||||
std::string selected_server_remote_hostname_;
|
||||
std::mutex pending_presence_probe_mutex_;
|
||||
bool pending_presence_probe_ = false;
|
||||
bool pending_presence_result_ready_ = false;
|
||||
|
||||
@@ -1,303 +0,0 @@
|
||||
#include <random>
|
||||
|
||||
#include "application/gui_application.h"
|
||||
#include "layout_relative.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
int GuiApplication::LocalWindow() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
float local_window_width = io.DisplaySize.x * 0.5f;
|
||||
float local_window_height =
|
||||
io.DisplaySize.y * (1 - TITLE_BAR_HEIGHT - STATUS_BAR_HEIGHT);
|
||||
float local_window_button_width = io.DisplaySize.x * 0.046f;
|
||||
float local_window_button_height = io.DisplaySize.y * 0.075f;
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(0.0f, io.DisplaySize.y * TITLE_BAR_HEIGHT),
|
||||
ImGuiCond_Always);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_ * 0.5f);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0));
|
||||
ImGui::BeginChild("LocalDesktopWindow",
|
||||
ImVec2(local_window_width, local_window_height),
|
||||
ImGuiChildFlags_None,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SetCursorPos(
|
||||
ImVec2(io.DisplaySize.x * 0.045f, io.DisplaySize.y * 0.02f));
|
||||
|
||||
ImGui::SetWindowFontScale(0.9f);
|
||||
ImGui::TextColored(
|
||||
ImVec4(0.0f, 0.0f, 0.0f, 0.5f), "%s",
|
||||
localization::local_desktop[localization_language_index_].c_str());
|
||||
|
||||
ImGui::Spacing();
|
||||
{
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.045f, io.DisplaySize.y * 0.15f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(239.0f / 255, 240.0f / 255,
|
||||
242.0f / 255, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, window_rounding_ * 1.5f);
|
||||
ImGui::BeginChild(
|
||||
"LocalDesktopPanel",
|
||||
ImVec2(local_window_width * 0.8f, local_window_height * 0.43f),
|
||||
ImGuiChildFlags_Borders,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
{
|
||||
ImGui::SetWindowFontScale(0.8f);
|
||||
ImGui::Text("%s",
|
||||
localization::local_id[localization_language_index_].c_str());
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::SetNextItemWidth(io.DisplaySize.x * 0.25f);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
|
||||
if (strcmp(client_id_display_, client_id_)) {
|
||||
for (int i = 0, j = 0; i < sizeof(client_id_); i++, j++) {
|
||||
client_id_display_[j] = client_id_[i];
|
||||
if (i == 2 || i == 5) {
|
||||
client_id_display_[++j] = ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::InputText(
|
||||
"##local_id", client_id_display_, IM_ARRAYSIZE(client_id_display_),
|
||||
ImGuiInputTextFlags_CharsUppercase | ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0));
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (ImGui::Button(ICON_FA_COPY, ImVec2(local_window_button_width,
|
||||
local_window_button_height))) {
|
||||
local_id_copied_ = true;
|
||||
ImGui::SetClipboardText(client_id_);
|
||||
copy_start_time_ = ImGui::GetTime();
|
||||
}
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
double time_duration = ImGui::GetTime() - copy_start_time_;
|
||||
if (local_id_copied_ && time_duration < 1.0f) {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
|
||||
ImGui::PushStyleColor(
|
||||
ImGuiCol_WindowBg,
|
||||
ImVec4(1.0f, 1.0f, 1.0f, 1.0f - (float)time_duration));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
ImGui::Begin("ConnectionStatusWindow", nullptr,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
auto window_width = ImGui::GetWindowSize().x;
|
||||
auto window_height = ImGui::GetWindowSize().y;
|
||||
ImGui::SetWindowFontScale(0.8f);
|
||||
std::string text = localization::local_id_copied_to_clipboard
|
||||
[localization_language_index_];
|
||||
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
|
||||
ImGui::SetCursorPosY(window_height * 0.4f);
|
||||
ImGui::PushStyleColor(ImGuiCol_Text,
|
||||
ImVec4(0, 0, 0, 1.0f - (float)time_duration));
|
||||
ImGui::Text("%s", text.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::SetWindowFontScale(0.8f);
|
||||
ImGui::Text("%s",
|
||||
localization::password[localization_language_index_].c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::SetNextItemWidth(io.DisplaySize.x * 0.25f);
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
ImGui::InputTextWithHint(
|
||||
"##server_pwd",
|
||||
localization::max_password_len[localization_language_index_].c_str(),
|
||||
password_saved_, IM_ARRAYSIZE(password_saved_),
|
||||
show_password_
|
||||
? ImGuiInputTextFlags_CharsNoBlank | ImGuiInputTextFlags_ReadOnly
|
||||
: ImGuiInputTextFlags_CharsNoBlank |
|
||||
ImGuiInputTextFlags_Password |
|
||||
ImGuiInputTextFlags_ReadOnly);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::SameLine();
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0));
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
auto l_x = ImGui::GetCursorScreenPos().x;
|
||||
auto l_y = ImGui::GetCursorScreenPos().y;
|
||||
if (ImGui::Button(
|
||||
show_password_ ? ICON_FA_EYE : ICON_FA_EYE_SLASH,
|
||||
ImVec2(local_window_button_width, local_window_button_height))) {
|
||||
show_password_ = !show_password_;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(ICON_FA_PEN, ImVec2(local_window_button_width,
|
||||
local_window_button_height))) {
|
||||
show_reset_password_window_ = true;
|
||||
}
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
if (show_reset_password_window_) {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding,
|
||||
window_rounding_ * 0.5f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
|
||||
ImGui::Begin("ResetPasswordWindow", nullptr,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
auto window_width = ImGui::GetWindowSize().x;
|
||||
auto window_height = ImGui::GetWindowSize().y;
|
||||
std::string text =
|
||||
localization::new_password[localization_language_index_];
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
|
||||
ImGui::SetCursorPosY(window_height * 0.2f);
|
||||
ImGui::Text("%s", text.c_str());
|
||||
|
||||
ImGui::SetCursorPosX(window_width * 0.33f);
|
||||
ImGui::SetCursorPosY(window_height * 0.4f);
|
||||
ImGui::SetNextItemWidth(window_width * 0.33f);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
|
||||
if (focus_on_input_widget_) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
focus_on_input_widget_ = false;
|
||||
}
|
||||
|
||||
bool enter_pressed = ImGui::InputText(
|
||||
"##new_password", new_password_, IM_ARRAYSIZE(new_password_),
|
||||
ImGuiInputTextFlags_CharsNoBlank |
|
||||
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::SetCursorPosX(window_width * 0.315f);
|
||||
ImGui::SetCursorPosY(window_height * 0.75f);
|
||||
|
||||
// OK
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
enter_pressed) {
|
||||
if (6 != strlen(new_password_)) {
|
||||
LOG_ERROR("Invalid password length");
|
||||
show_reset_password_window_ = true;
|
||||
focus_on_input_widget_ = true;
|
||||
} else {
|
||||
show_reset_password_window_ = false;
|
||||
memset(&password_saved_, 0, sizeof(password_saved_));
|
||||
strncpy(password_saved_, new_password_,
|
||||
sizeof(password_saved_) - 1);
|
||||
password_saved_[sizeof(password_saved_) - 1] = '\0';
|
||||
|
||||
// if self hosted
|
||||
if (config_center_->IsSelfHosted()) {
|
||||
std::string self_hosted_id_str;
|
||||
if (strlen(self_hosted_id_) > 0) {
|
||||
const char *at_pos = strchr(self_hosted_id_, '@');
|
||||
if (at_pos != nullptr) {
|
||||
self_hosted_id_str =
|
||||
std::string(self_hosted_id_, at_pos - self_hosted_id_);
|
||||
} else {
|
||||
self_hosted_id_str = self_hosted_id_;
|
||||
}
|
||||
} else {
|
||||
self_hosted_id_str = client_id_;
|
||||
}
|
||||
|
||||
std::string new_self_hosted_id =
|
||||
self_hosted_id_str + "@" + password_saved_;
|
||||
memset(&self_hosted_id_, 0, sizeof(self_hosted_id_));
|
||||
strncpy(self_hosted_id_, new_self_hosted_id.c_str(),
|
||||
sizeof(self_hosted_id_) - 1);
|
||||
self_hosted_id_[sizeof(self_hosted_id_) - 1] = '\0';
|
||||
|
||||
} else {
|
||||
std::string client_id_with_password =
|
||||
std::string(client_id_) + "@" + password_saved_;
|
||||
strncpy(client_id_with_password_, client_id_with_password.c_str(),
|
||||
sizeof(client_id_with_password_) - 1);
|
||||
client_id_with_password_[sizeof(client_id_with_password_) - 1] =
|
||||
'\0';
|
||||
}
|
||||
|
||||
settings_.Save();
|
||||
|
||||
memset(new_password_, 0, sizeof(new_password_));
|
||||
|
||||
LeaveConnection(peer_, client_id_);
|
||||
DestroyPeer(&peer_);
|
||||
focus_on_input_widget_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str())) {
|
||||
show_reset_password_window_ = false;
|
||||
focus_on_input_widget_ = true;
|
||||
memset(new_password_, 0, sizeof(new_password_));
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,656 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
#include "application/gui_application.h"
|
||||
#include "layout_relative.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
namespace {
|
||||
|
||||
std::string TrimConnectionAlias(const char *value) {
|
||||
std::string alias = value ? value : "";
|
||||
|
||||
auto not_space = [](unsigned char ch) { return !std::isspace(ch); };
|
||||
alias.erase(alias.begin(),
|
||||
std::find_if(alias.begin(), alias.end(), not_space));
|
||||
alias.erase(std::find_if(alias.rbegin(), alias.rend(), not_space).base(),
|
||||
alias.end());
|
||||
|
||||
return alias;
|
||||
}
|
||||
|
||||
void SetDarkTextTooltip(const char *text) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.05f, 0.05f, 0.05f, 1.0f));
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::Text("%s", text);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::EndTooltip();
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int GuiApplication::RecentConnectionsWindow() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
float recent_connection_window_width = io.DisplaySize.x;
|
||||
float recent_connection_window_height =
|
||||
io.DisplaySize.y * (0.455f - STATUS_BAR_HEIGHT);
|
||||
ImGui::SetNextWindowPos(ImVec2(0, io.DisplaySize.y * 0.55f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::BeginChild(
|
||||
"RecentConnectionsWindow",
|
||||
ImVec2(recent_connection_window_width, recent_connection_window_height),
|
||||
ImGuiChildFlags_Borders,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SetCursorPos(
|
||||
ImVec2(io.DisplaySize.x * 0.045f, io.DisplaySize.y * 0.02f));
|
||||
|
||||
ImGui::SetWindowFontScale(0.9f);
|
||||
ImGui::TextColored(
|
||||
ImVec4(0.0f, 0.0f, 0.0f, 0.5f), "%s",
|
||||
localization::recent_connections[localization_language_index_].c_str());
|
||||
|
||||
ShowRecentConnections();
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::ShowRecentConnections() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
float recent_connection_panel_width = io.DisplaySize.x * 0.912f;
|
||||
float recent_connection_panel_height = io.DisplaySize.y * 0.29f;
|
||||
float recent_connection_image_height = recent_connection_panel_height * 0.6f;
|
||||
float recent_connection_image_width = recent_connection_image_height * 16 / 9;
|
||||
float recent_connection_sub_container_width =
|
||||
recent_connection_image_width * 1.2f;
|
||||
float recent_connection_sub_container_height =
|
||||
recent_connection_image_height * 1.4f;
|
||||
float recent_connection_button_width = recent_connection_image_width * 0.15f;
|
||||
float recent_connection_button_height =
|
||||
recent_connection_image_height * 0.25f;
|
||||
float recent_connection_footer_height =
|
||||
recent_connection_button_height * 1.18f;
|
||||
float recent_connection_name_width = recent_connection_image_width;
|
||||
const float recent_connection_spacing = recent_connection_image_width * 0.16f;
|
||||
const float recent_connections_content_width =
|
||||
recent_connections_.empty()
|
||||
? 0.0f
|
||||
: recent_connections_.size() * recent_connection_sub_container_width +
|
||||
(recent_connections_.size() - 1) * recent_connection_spacing;
|
||||
const float recent_connections_available_width =
|
||||
recent_connection_panel_width - 2.0f * ImGui::GetStyle().WindowPadding.x;
|
||||
const bool has_horizontal_overflow =
|
||||
recent_connections_content_width > recent_connections_available_width;
|
||||
|
||||
ImGui::SetCursorPos(
|
||||
ImVec2(io.DisplaySize.x * 0.045f, io.DisplaySize.y * 0.1f));
|
||||
|
||||
std::map<std::string, ImVec2> sub_containers_pos;
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg,
|
||||
ImVec4(239.0f / 255, 240.0f / 255, 242.0f / 255, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
|
||||
const ImGuiWindowFlags container_flags =
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus |
|
||||
ImGuiWindowFlags_NoScrollWithMouse |
|
||||
(has_horizontal_overflow ? ImGuiWindowFlags_AlwaysHorizontalScrollbar
|
||||
: ImGuiWindowFlags_None);
|
||||
ImGui::BeginChild(
|
||||
"RecentConnectionsContainer",
|
||||
ImVec2(recent_connection_panel_width, recent_connection_panel_height),
|
||||
ImGuiChildFlags_Borders, container_flags);
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
size_t recent_connections_count = recent_connections_.size();
|
||||
int count = 0;
|
||||
for (auto &it : recent_connections_) {
|
||||
sub_containers_pos[it.first] = ImGui::GetCursorPos();
|
||||
std::string recent_connection_sub_window_name =
|
||||
"RecentConnectionsSubContainer" + it.first;
|
||||
// recent connections sub container
|
||||
ImGui::BeginChild(recent_connection_sub_window_name.c_str(),
|
||||
ImVec2(recent_connection_sub_container_width,
|
||||
recent_connection_sub_container_height),
|
||||
ImGuiChildFlags_None,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
std::string connection_info = it.first;
|
||||
|
||||
// remote id length is 9
|
||||
// password length is 6
|
||||
// connection_info -> remote_id + 'Y' + host_name + '@' + password
|
||||
// -> remote_id + 'N' + host_name
|
||||
bool invalid_connection_info = false;
|
||||
if (connection_info.size() > 9 && 'Y' == connection_info[9] &&
|
||||
connection_info.size() >= 16) {
|
||||
size_t pos_y = connection_info.find('Y');
|
||||
size_t pos_at = connection_info.find('@');
|
||||
|
||||
if (pos_y == std::string::npos || pos_at == std::string::npos ||
|
||||
pos_y >= pos_at) {
|
||||
LOG_ERROR("Invalid filename");
|
||||
invalid_connection_info = true;
|
||||
} else {
|
||||
it.second.remote_id = connection_info.substr(0, pos_y);
|
||||
it.second.remote_host_name =
|
||||
connection_info.substr(pos_y + 1, pos_at - pos_y - 1);
|
||||
it.second.password = connection_info.substr(pos_at + 1);
|
||||
it.second.remember_password = true;
|
||||
}
|
||||
} else if (connection_info.size() > 9 && 'N' == connection_info[9] &&
|
||||
connection_info.size() >= 10) {
|
||||
size_t pos_n = connection_info.find('N');
|
||||
|
||||
if (pos_n == std::string::npos) {
|
||||
LOG_ERROR("Invalid filename");
|
||||
invalid_connection_info = true;
|
||||
} else {
|
||||
it.second.remote_id = connection_info.substr(0, pos_n);
|
||||
it.second.remote_host_name = connection_info.substr(pos_n + 1);
|
||||
it.second.password = "";
|
||||
it.second.remember_password = false;
|
||||
}
|
||||
} else {
|
||||
invalid_connection_info = true;
|
||||
}
|
||||
|
||||
if (invalid_connection_info) {
|
||||
it.second.remote_id = connection_info.substr(
|
||||
0, std::min<size_t>(connection_info.size(), 9));
|
||||
it.second.remote_host_name = "unknown";
|
||||
it.second.password = "";
|
||||
it.second.remember_password = false;
|
||||
}
|
||||
|
||||
std::string display_name = settings_.RecentConnectionDisplayName(it.second);
|
||||
bool online = device_presence_cache_.IsOnline(it.second.remote_id);
|
||||
|
||||
ImVec2 image_pos =
|
||||
ImVec2(ImGui::GetCursorPosX() + recent_connection_image_width * 0.05f,
|
||||
ImGui::GetCursorPosY() + recent_connection_image_height * 0.08f);
|
||||
|
||||
ImGui::SetCursorPos(image_pos);
|
||||
ImVec2 image_screen_pos = ImGui::GetCursorScreenPos();
|
||||
ImGui::Image(
|
||||
(ImTextureID)(intptr_t)it.second.texture,
|
||||
ImVec2(recent_connection_image_width, recent_connection_image_height));
|
||||
|
||||
const bool image_item_hovered = ImGui::IsItemHovered();
|
||||
|
||||
ImVec2 card_screen_min = image_screen_pos;
|
||||
ImVec2 card_screen_max =
|
||||
ImVec2(image_screen_pos.x + recent_connection_image_width,
|
||||
image_screen_pos.y + recent_connection_image_height +
|
||||
recent_connection_footer_height);
|
||||
|
||||
const bool card_hovered =
|
||||
ImGui::IsMouseHoveringRect(card_screen_min, card_screen_max, true);
|
||||
|
||||
const float recent_connection_toolbar_width =
|
||||
3.0f * recent_connection_button_width;
|
||||
|
||||
const float recent_connection_toolbar_padding =
|
||||
recent_connection_image_width * 0.025f;
|
||||
|
||||
const ImVec2 toolbar_pos = ImVec2(
|
||||
image_pos.x + recent_connection_image_width -
|
||||
recent_connection_toolbar_width - recent_connection_toolbar_padding,
|
||||
image_pos.y + recent_connection_image_height +
|
||||
(recent_connection_footer_height -
|
||||
recent_connection_button_height) *
|
||||
0.5f);
|
||||
|
||||
const ImVec2 toolbar_screen_pos = ImVec2(
|
||||
image_screen_pos.x + recent_connection_image_width -
|
||||
recent_connection_toolbar_width - recent_connection_toolbar_padding,
|
||||
image_screen_pos.y + recent_connection_image_height +
|
||||
(recent_connection_footer_height -
|
||||
recent_connection_button_height) *
|
||||
0.5f);
|
||||
|
||||
const ImVec2 toolbar_screen_end =
|
||||
ImVec2(toolbar_screen_pos.x + recent_connection_toolbar_width,
|
||||
toolbar_screen_pos.y + recent_connection_button_height);
|
||||
|
||||
const bool toolbar_hovered =
|
||||
card_hovered && ImGui::IsMouseHoveringRect(toolbar_screen_pos,
|
||||
toolbar_screen_end, true);
|
||||
|
||||
const bool show_image_tooltip = image_item_hovered && !toolbar_hovered;
|
||||
|
||||
if (show_image_tooltip) {
|
||||
const ImVec2 mouse_pos = ImGui::GetMousePos();
|
||||
const bool place_tooltip_on_left = mouse_pos.x > io.DisplaySize.x * 0.7f;
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(mouse_pos.x + (place_tooltip_on_left ? -12.0f : 12.0f),
|
||||
mouse_pos.y - 8.0f),
|
||||
ImGuiCond_Always, ImVec2(place_tooltip_on_left ? 1.0f : 0.0f, 1.0f));
|
||||
ImGui::BeginTooltip();
|
||||
|
||||
ImGui::SetWindowFontScale(0.4f);
|
||||
|
||||
ImGui::Text(
|
||||
"%s: %s",
|
||||
localization::device_name[localization_language_index_].c_str(),
|
||||
display_name.c_str());
|
||||
|
||||
if (!it.second.remote_host_name.empty() &&
|
||||
it.second.remote_host_name != display_name) {
|
||||
ImGui::Text("%s", it.second.remote_host_name.c_str());
|
||||
}
|
||||
|
||||
ImGui::Text("%s: %s",
|
||||
localization::remote_id[localization_language_index_].c_str(),
|
||||
it.second.remote_id.c_str());
|
||||
|
||||
ImGui::Text("%s",
|
||||
(online ? localization::online[localization_language_index_]
|
||||
: localization::offline[localization_language_index_])
|
||||
.c_str());
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
// connection name footer
|
||||
{
|
||||
ImVec2 footer_pos =
|
||||
ImVec2(image_pos.x, image_pos.y + recent_connection_image_height);
|
||||
|
||||
ImVec2 footer_screen_pos =
|
||||
ImVec2(image_screen_pos.x,
|
||||
image_screen_pos.y + recent_connection_image_height);
|
||||
|
||||
ImVec2 footer_screen_end =
|
||||
ImVec2(footer_screen_pos.x + recent_connection_name_width,
|
||||
footer_screen_pos.y + recent_connection_footer_height);
|
||||
|
||||
float footer_rounding = recent_connection_footer_height * 0.16f;
|
||||
|
||||
draw_list->AddRectFilled(footer_screen_pos, footer_screen_end,
|
||||
IM_COL32(0, 0, 0, 40), footer_rounding,
|
||||
ImDrawFlags_RoundCornersBottom);
|
||||
|
||||
const float status_left_margin = recent_connection_footer_height * 0.22f;
|
||||
const float status_gap = recent_connection_footer_height * 0.20f;
|
||||
const float dot_radius = recent_connection_footer_height * 0.18f;
|
||||
|
||||
const ImVec2 dot_center(
|
||||
footer_screen_pos.x + status_left_margin + dot_radius,
|
||||
footer_screen_pos.y + recent_connection_footer_height * 0.5f);
|
||||
const ImU32 dot_color =
|
||||
online ? IM_COL32(34, 197, 94, 255) : IM_COL32(156, 163, 175, 255);
|
||||
|
||||
// Layered halo simulates a radial gradient glow for the online state.
|
||||
if (online) {
|
||||
const float halo_radius = dot_radius * 2.2f;
|
||||
const int halo_layers = 8;
|
||||
for (int i = halo_layers; i > 0; --i) {
|
||||
const float t = static_cast<float>(i) / halo_layers;
|
||||
const float r = dot_radius + (halo_radius - dot_radius) * t;
|
||||
const int alpha = static_cast<int>(14.0f * (1.0f - t) + 4.0f);
|
||||
draw_list->AddCircleFilled(dot_center, r,
|
||||
IM_COL32(74, 222, 128, alpha));
|
||||
}
|
||||
}
|
||||
draw_list->AddCircleFilled(dot_center, dot_radius, dot_color);
|
||||
|
||||
const float status_block_end_x = dot_center.x + dot_radius;
|
||||
|
||||
ImVec2 text_min =
|
||||
ImVec2(status_block_end_x + status_gap, footer_screen_pos.y);
|
||||
|
||||
ImVec2 text_max =
|
||||
ImVec2(card_hovered ? toolbar_screen_pos.x - status_gap
|
||||
: footer_screen_end.x -
|
||||
recent_connection_name_width * 0.05f,
|
||||
footer_screen_end.y);
|
||||
|
||||
ImGui::SetWindowFontScale(0.52f);
|
||||
|
||||
ImGui::RenderTextClipped(text_min, text_max, display_name.c_str(),
|
||||
nullptr, nullptr, ImVec2(0.0f, 0.5f));
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
}
|
||||
|
||||
// Toolbar / three buttons, right-aligned in the connection name footer.
|
||||
if (card_hovered) {
|
||||
float toolbar_rounding = recent_connection_button_height * 0.22f;
|
||||
|
||||
draw_list->AddRectFilled(
|
||||
ImVec2(toolbar_screen_pos.x, toolbar_screen_pos.y + 1.0f),
|
||||
ImVec2(toolbar_screen_end.x, toolbar_screen_end.y + 1.0f),
|
||||
IM_COL32(0, 0, 0, 70), toolbar_rounding);
|
||||
|
||||
draw_list->AddRectFilled(toolbar_screen_pos, toolbar_screen_end,
|
||||
IM_COL32(96, 100, 106, 140), toolbar_rounding);
|
||||
|
||||
draw_list->AddRect(toolbar_screen_pos, toolbar_screen_end,
|
||||
IM_COL32(255, 255, 255, 48), toolbar_rounding);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, toolbar_rounding);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
|
||||
ImVec4(1.0f, 1.0f, 1.0f, 0.18f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
|
||||
ImVec4(0.35f, 0.55f, 0.95f, 0.45f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 1.0f, 1.0f, 0.95f));
|
||||
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
|
||||
// edit alias button
|
||||
{
|
||||
ImGui::SetCursorPos(toolbar_pos);
|
||||
|
||||
std::string edit = ICON_FA_PEN;
|
||||
std::string recent_connection_edit_button_name =
|
||||
edit + "##RecentConnectionAlias" + it.first;
|
||||
|
||||
if (ImGui::Button(recent_connection_edit_button_name.c_str(),
|
||||
ImVec2(recent_connection_button_width,
|
||||
recent_connection_button_height))) {
|
||||
settings_.BeginEditRecentConnectionAlias(it.second);
|
||||
}
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
SetDarkTextTooltip(
|
||||
localization::connection_alias[localization_language_index_]
|
||||
.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// trash button
|
||||
{
|
||||
ImVec2 trash_can_button_pos = ImVec2(
|
||||
toolbar_pos.x + recent_connection_button_width, toolbar_pos.y);
|
||||
|
||||
ImGui::SetCursorPos(trash_can_button_pos);
|
||||
|
||||
std::string trash_can = ICON_FA_TRASH_CAN;
|
||||
std::string recent_connection_delete_button_name =
|
||||
trash_can + "##RecentConnectionDelete" + it.first;
|
||||
|
||||
if (ImGui::Button(recent_connection_delete_button_name.c_str(),
|
||||
ImVec2(recent_connection_button_width,
|
||||
recent_connection_button_height))) {
|
||||
show_confirm_delete_connection_ = true;
|
||||
delete_connection_name_ = it.first;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
SetDarkTextTooltip(
|
||||
localization::delete_connection[localization_language_index_]
|
||||
.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
// connect button
|
||||
{
|
||||
ImVec2 connect_button_pos = ImVec2(
|
||||
toolbar_pos.x + 2 * recent_connection_button_width, toolbar_pos.y);
|
||||
|
||||
ImGui::SetCursorPos(connect_button_pos);
|
||||
|
||||
std::string connect = ICON_FA_ARROW_RIGHT_LONG;
|
||||
std::string connect_to_this_connection_button_name =
|
||||
connect + "##ConnectionTo" + it.first;
|
||||
|
||||
if (ImGui::Button(connect_to_this_connection_button_name.c_str(),
|
||||
ImVec2(recent_connection_button_width,
|
||||
recent_connection_button_height))) {
|
||||
ConnectTo(it.second.remote_id, it.second.password.c_str(),
|
||||
it.second.remember_password);
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
SetDarkTextTooltip(localization::connect_to_this_connection
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::PopStyleColor(4);
|
||||
ImGui::PopStyleVar(3);
|
||||
}
|
||||
|
||||
if (delete_connection_ && delete_connection_name_ == it.first) {
|
||||
if (!thumbnail_->DeleteThumbnail(it.first)) {
|
||||
settings_.EraseRecentConnectionAlias(it.second.remote_id);
|
||||
settings_.SaveRecentConnectionAliases();
|
||||
reload_recent_connections_ = true;
|
||||
delete_connection_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
if (count != recent_connections_count - 1) {
|
||||
ImVec2 line_start =
|
||||
ImVec2(image_screen_pos.x + recent_connection_image_width * 1.18f,
|
||||
image_screen_pos.y);
|
||||
ImVec2 line_end =
|
||||
ImVec2(image_screen_pos.x + recent_connection_image_width * 1.18f,
|
||||
image_screen_pos.y + recent_connection_image_height +
|
||||
recent_connection_footer_height);
|
||||
ImGui::GetWindowDrawList()->AddLine(line_start, line_end,
|
||||
IM_COL32(0, 0, 0, 122), 1.0f);
|
||||
}
|
||||
|
||||
count++;
|
||||
ImGui::SameLine(0, count != recent_connections_count
|
||||
? recent_connection_spacing
|
||||
: 0.0f);
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
if (show_confirm_delete_connection_) {
|
||||
ConfirmDeleteConnection();
|
||||
}
|
||||
if (show_edit_connection_alias_window_) {
|
||||
EditRecentConnectionAliasWindow();
|
||||
}
|
||||
if (show_offline_warning_window_) {
|
||||
OfflineWarningWindow();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::ConfirmDeleteConnection() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_ * 0.5f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
|
||||
ImGui::Begin("ConfirmDeleteConnectionWindow", nullptr,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
auto connection_status_window_width = ImGui::GetWindowSize().x;
|
||||
auto connection_status_window_height = ImGui::GetWindowSize().y;
|
||||
|
||||
std::string text =
|
||||
localization::confirm_delete_connection[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width * 0.33f);
|
||||
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
|
||||
|
||||
// ok
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter)) {
|
||||
delete_connection_ = true;
|
||||
show_confirm_delete_connection_ = false;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// cancel
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
delete_connection_ = false;
|
||||
show_confirm_delete_connection_ = false;
|
||||
}
|
||||
|
||||
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
ImGui::SetCursorPosX((connection_status_window_width - text_width) * 0.5f);
|
||||
ImGui::SetCursorPosY(connection_status_window_height * 0.2f);
|
||||
ImGui::Text("%s", text.c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::EditRecentConnectionAliasWindow() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_ * 0.5f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
|
||||
ImGui::Begin("EditRecentConnectionAliasWindow", nullptr,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
auto window_width = ImGui::GetWindowSize().x;
|
||||
auto window_height = ImGui::GetWindowSize().y;
|
||||
std::string text =
|
||||
localization::input_connection_alias[localization_language_index_];
|
||||
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
|
||||
ImGui::SetCursorPosY(window_height * 0.2f);
|
||||
ImGui::Text("%s", text.c_str());
|
||||
|
||||
ImGui::SetCursorPosX(window_width * 0.2f);
|
||||
ImGui::SetCursorPosY(window_height * 0.4f);
|
||||
ImGui::SetNextItemWidth(window_width * 0.6f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
|
||||
if (focus_on_input_widget_) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
focus_on_input_widget_ = false;
|
||||
}
|
||||
|
||||
bool enter_pressed =
|
||||
ImGui::InputText("##recent_connection_alias", edit_connection_alias_,
|
||||
IM_ARRAYSIZE(edit_connection_alias_),
|
||||
ImGuiInputTextFlags_EnterReturnsTrue);
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::SetCursorPosX(window_width * 0.315f);
|
||||
ImGui::SetCursorPosY(window_height * 0.75f);
|
||||
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||
enter_pressed) {
|
||||
std::string alias = TrimConnectionAlias(edit_connection_alias_);
|
||||
if (alias.empty()) {
|
||||
settings_.EraseRecentConnectionAlias(edit_connection_alias_remote_id_);
|
||||
} else {
|
||||
settings_.SetRecentConnectionAlias(edit_connection_alias_remote_id_,
|
||||
alias);
|
||||
}
|
||||
|
||||
settings_.SaveRecentConnectionAliases();
|
||||
show_edit_connection_alias_window_ = false;
|
||||
focus_on_input_widget_ = true;
|
||||
memset(edit_connection_alias_, 0, sizeof(edit_connection_alias_));
|
||||
edit_connection_alias_remote_id_.clear();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_edit_connection_alias_window_ = false;
|
||||
focus_on_input_widget_ = true;
|
||||
memset(edit_connection_alias_, 0, sizeof(edit_connection_alias_));
|
||||
edit_connection_alias_remote_id_.clear();
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::OfflineWarningWindow() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_ * 0.5f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
|
||||
ImGui::Begin("OfflineWarningWindow", nullptr,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
auto window_width = ImGui::GetWindowSize().x;
|
||||
auto window_height = ImGui::GetWindowSize().y;
|
||||
|
||||
ImGui::SetCursorPosX(window_width * 0.43f);
|
||||
ImGui::SetCursorPosY(window_height * 0.67f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_offline_warning_window_ = false;
|
||||
}
|
||||
|
||||
auto text_width = ImGui::CalcTextSize(offline_warning_text_.c_str()).x;
|
||||
ImGui::SetCursorPosX((window_width - text_width) * 0.5f);
|
||||
ImGui::SetCursorPosY(window_height * 0.2f);
|
||||
ImGui::Text("%s", offline_warning_text_.c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
return 0;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,167 +0,0 @@
|
||||
#include "application/gui_application.h"
|
||||
#include "layout_relative.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
static int InputTextCallback(ImGuiInputTextCallbackData *data);
|
||||
|
||||
int GuiApplication::RemoteWindow() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
float remote_window_width = io.DisplaySize.x * 0.5f;
|
||||
float remote_window_height =
|
||||
io.DisplaySize.y * (1 - TITLE_BAR_HEIGHT - STATUS_BAR_HEIGHT);
|
||||
float remote_window_arrow_button_width = io.DisplaySize.x * 0.1f;
|
||||
float remote_window_arrow_button_height = io.DisplaySize.y * 0.078f;
|
||||
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * TITLE_BAR_HEIGHT),
|
||||
ImGuiCond_Always);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_ * 0.5f);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0, 0, 0, 0));
|
||||
ImGui::BeginChild("RemoteDesktopWindow",
|
||||
ImVec2(remote_window_width, remote_window_height),
|
||||
ImGuiChildFlags_None,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SetCursorPos(
|
||||
ImVec2(io.DisplaySize.x * 0.057f, io.DisplaySize.y * 0.02f));
|
||||
|
||||
ImGui::SetWindowFontScale(0.9f);
|
||||
ImGui::TextColored(
|
||||
ImVec4(0.0f, 0.0f, 0.0f, 0.5f), "%s",
|
||||
localization::remote_desktop[localization_language_index_].c_str());
|
||||
|
||||
ImGui::Spacing();
|
||||
{
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.557f, io.DisplaySize.y * 0.15f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(239.0f / 255, 240.0f / 255,
|
||||
242.0f / 255, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
|
||||
|
||||
ImGui::BeginChild(
|
||||
"RemoteDesktopWindow_1",
|
||||
ImVec2(remote_window_width * 0.8f, remote_window_height * 0.43f),
|
||||
ImGuiChildFlags_Borders,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
{
|
||||
ImGui::SetWindowFontScale(0.8f);
|
||||
ImGui::Text(
|
||||
"%s", localization::remote_id[localization_language_index_].c_str());
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::SetNextItemWidth(io.DisplaySize.x * 0.25f);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
if (re_enter_remote_id_) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
re_enter_remote_id_ = false;
|
||||
memset(remote_id_display_, 0, sizeof(remote_id_display_));
|
||||
}
|
||||
bool enter_pressed = ImGui::InputText(
|
||||
"##remote_id_", remote_id_display_, IM_ARRAYSIZE(remote_id_display_),
|
||||
ImGuiInputTextFlags_CharsDecimal |
|
||||
ImGuiInputTextFlags_EnterReturnsTrue |
|
||||
ImGuiInputTextFlags_CallbackEdit,
|
||||
InputTextCallback);
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::SameLine();
|
||||
|
||||
std::string remote_id = remote_id_display_;
|
||||
remote_id.erase(remove_if(remote_id.begin(), remote_id.end(),
|
||||
static_cast<int (*)(int)>(&isspace)),
|
||||
remote_id.end());
|
||||
if (ImGui::Button(ICON_FA_ARROW_RIGHT_LONG,
|
||||
ImVec2(remote_window_arrow_button_width,
|
||||
remote_window_arrow_button_height)) ||
|
||||
enter_pressed) {
|
||||
connect_button_pressed_ = true;
|
||||
bool found = false;
|
||||
std::string target_remote_id;
|
||||
std::string target_password;
|
||||
bool should_connect = false;
|
||||
bool already_connected = false;
|
||||
|
||||
for (auto &[id, props] : recent_connections_) {
|
||||
if (id.find(remote_id) != std::string::npos) {
|
||||
found = true;
|
||||
target_remote_id = props.remote_id;
|
||||
target_password = props.password;
|
||||
{
|
||||
// std::shared_lock lock(remote_sessions_mutex_);
|
||||
if (remote_sessions_.find(remote_id) !=
|
||||
remote_sessions_.end()) {
|
||||
if (!remote_sessions_[remote_id]->connection_established_) {
|
||||
should_connect = true;
|
||||
} else {
|
||||
already_connected = true;
|
||||
}
|
||||
} else {
|
||||
should_connect = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (should_connect) {
|
||||
ConnectTo(target_remote_id, target_password.c_str(), false);
|
||||
} else if (already_connected) {
|
||||
LOG_INFO("Already connected to [{}]", remote_id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
ConnectTo(remote_id, "", false);
|
||||
}
|
||||
}
|
||||
|
||||
// check every 1 second for rejoin
|
||||
if (need_to_rejoin_) {
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
now - last_rejoin_check_time_)
|
||||
.count();
|
||||
|
||||
if (elapsed >= 1000) {
|
||||
last_rejoin_check_time_ = now;
|
||||
need_to_rejoin_ = false;
|
||||
// std::shared_lock lock(remote_sessions_mutex_);
|
||||
for (const auto &[_, props] : remote_sessions_) {
|
||||
if (props->rejoin_) {
|
||||
ConnectTo(props->remote_id_, props->remote_password_,
|
||||
props->remember_password_);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int InputTextCallback(ImGuiInputTextCallbackData *data) {
|
||||
if (data->BufTextLen > 3 && data->Buf[3] != ' ') {
|
||||
data->InsertChars(3, " ");
|
||||
}
|
||||
|
||||
if (data->BufTextLen > 7 && data->Buf[7] != ' ') {
|
||||
data->InsertChars(7, " ");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace crossdesk
|
||||
@@ -1,514 +0,0 @@
|
||||
#include <algorithm>
|
||||
|
||||
#include <remote_action.h>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "application/gui_application.h"
|
||||
#include "file_transfer.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "tinyfiledialogs.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
namespace {
|
||||
|
||||
void ShowControlBarTooltip(const std::string &text) {
|
||||
if (!ImGui::IsItemHovered() || text.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::Text("%s", text.c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int CountDigits(int number) {
|
||||
if (number == 0)
|
||||
return 1;
|
||||
return (int)std::floor(std::log10(std::abs(number))) + 1;
|
||||
}
|
||||
|
||||
int BitrateDisplay(int bitrate) {
|
||||
int num_of_digits = CountDigits(bitrate);
|
||||
if (num_of_digits <= 3) {
|
||||
ImGui::Text("%d bps", bitrate);
|
||||
} else if (num_of_digits > 3 && num_of_digits <= 6) {
|
||||
ImGui::Text("%d kbps", bitrate / 1000);
|
||||
} else {
|
||||
ImGui::Text("%.1f mbps", bitrate / 1000000.0f);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LossRateDisplay(float loss_rate) {
|
||||
if (loss_rate < 0.01f) {
|
||||
ImGui::Text("0%%");
|
||||
} else {
|
||||
ImGui::Text("%.0f%%", loss_rate * 100);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string GuiApplication::OpenFileDialog(std::string title) {
|
||||
const char *path = tinyfd_openFileDialog(title.c_str(),
|
||||
"", // default path
|
||||
0, // number of filters
|
||||
nullptr, // filters
|
||||
nullptr, // filter description
|
||||
0 // no multiple selection
|
||||
);
|
||||
|
||||
return path ? path : "";
|
||||
}
|
||||
|
||||
int GuiApplication::ControlBar(
|
||||
std::shared_ptr<RemoteSession> &props) {
|
||||
float button_width = title_bar_height_ * 0.8f;
|
||||
float button_height = title_bar_height_ * 0.8f;
|
||||
float line_padding = title_bar_height_ * 0.12f;
|
||||
float line_thickness = title_bar_height_ * 0.07f;
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_ * 0.5f);
|
||||
if (props->control_bar_expand_) {
|
||||
ImGui::SetCursorPosX(props->is_control_bar_in_left_
|
||||
? props->control_window_width_ * 0.03f
|
||||
: props->control_window_width_ * 0.17f);
|
||||
|
||||
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
||||
if (!props->is_control_bar_in_left_) {
|
||||
draw_list->AddLine(
|
||||
ImVec2(ImGui::GetCursorScreenPos().x - button_height * 0.5f,
|
||||
ImGui::GetCursorScreenPos().y + button_height * 0.2f),
|
||||
ImVec2(ImGui::GetCursorScreenPos().x - button_height * 0.5f,
|
||||
ImGui::GetCursorScreenPos().y + button_height * 0.8f),
|
||||
IM_COL32(178, 178, 178, 255), 2.0f);
|
||||
}
|
||||
|
||||
std::string display = ICON_FA_DISPLAY;
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (ImGui::Button(display.c_str(), ImVec2(button_width, button_height))) {
|
||||
ImGui::OpenPopup("display");
|
||||
}
|
||||
|
||||
ImVec2 btn_min = ImGui::GetItemRectMin();
|
||||
ImVec2 btn_size_actual = ImGui::GetItemRectSize();
|
||||
ShowControlBarTooltip(
|
||||
localization::select_display[localization_language_index_]);
|
||||
|
||||
props->display_selectable_hovered_ = false;
|
||||
if (ImGui::BeginPopup("display")) {
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
for (int i = 0; i < props->display_info_list_.size(); i++) {
|
||||
const std::string display_label = localization::FormatDisplayLabel(
|
||||
static_cast<size_t>(i), props->display_info_list_[i].name,
|
||||
localization_language_index_);
|
||||
if (ImGui::Selectable(display_label.c_str())) {
|
||||
props->selected_display_ = i;
|
||||
|
||||
RemoteAction remote_action;
|
||||
remote_action.type = ControlType::display_id;
|
||||
remote_action.d = i;
|
||||
if (props->connection_status_.load() == ConnectionStatus::Connected) {
|
||||
std::string msg = remote_action.to_json();
|
||||
SendReliableDataFrame(props->peer_, msg.c_str(), msg.size(),
|
||||
props->control_data_label_.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
props->display_selectable_hovered_ =
|
||||
ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(0.35f);
|
||||
ImVec2 text_size = ImGui::CalcTextSize(
|
||||
std::to_string(props->selected_display_ + 1).c_str());
|
||||
ImVec2 text_pos =
|
||||
ImVec2(btn_min.x + (btn_size_actual.x - text_size.x) * 0.55f,
|
||||
btn_min.y + (btn_size_actual.y - text_size.y) * 0.33f);
|
||||
ImGui::GetWindowDrawList()->AddText(
|
||||
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_.load() == 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");
|
||||
}
|
||||
ShowControlBarTooltip(
|
||||
localization::send_shortcut[localization_language_index_]);
|
||||
|
||||
props->shortcut_selectable_hovered_ = false;
|
||||
if (ImGui::BeginPopup("shortcut")) {
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
std::string sas_label = "Ctrl+Alt+Del";
|
||||
std::string lock_label = "Win+L";
|
||||
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;
|
||||
float disable_mouse_x = mouse_x + line_padding;
|
||||
float disable_mouse_y = mouse_y + line_padding;
|
||||
std::string mouse = ICON_FA_COMPUTER_MOUSE;
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (ImGui::Button(mouse.c_str(), ImVec2(button_width, button_height))) {
|
||||
if (props->connection_established_) {
|
||||
start_keyboard_capturer_ = !start_keyboard_capturer_;
|
||||
props->control_mouse_ = !props->control_mouse_;
|
||||
props->enable_mouse_control_ = !props->enable_mouse_control_;
|
||||
props->mouse_control_button_label_ =
|
||||
props->enable_mouse_control_
|
||||
? localization::release_mouse[localization_language_index_]
|
||||
: localization::control_mouse[localization_language_index_];
|
||||
}
|
||||
}
|
||||
const bool mouse_button_hovered = ImGui::IsItemHovered();
|
||||
const std::string mouse_tooltip =
|
||||
props->enable_mouse_control_
|
||||
? localization::release_mouse[localization_language_index_]
|
||||
: localization::control_mouse[localization_language_index_];
|
||||
ShowControlBarTooltip(mouse_tooltip);
|
||||
|
||||
if (!props->enable_mouse_control_) {
|
||||
draw_list->AddLine(ImVec2(disable_mouse_x, disable_mouse_y),
|
||||
ImVec2(mouse_x + button_width - line_padding,
|
||||
mouse_y + button_height - line_padding),
|
||||
IM_COL32(0, 0, 0, 255), line_thickness);
|
||||
draw_list->AddLine(
|
||||
ImVec2(disable_mouse_x - line_thickness * 0.7f,
|
||||
disable_mouse_y + line_thickness * 0.7f),
|
||||
ImVec2(mouse_x + button_width - line_padding - line_thickness * 0.7f,
|
||||
mouse_y + button_height - line_padding +
|
||||
line_thickness * 0.7f),
|
||||
mouse_button_hovered ? IM_COL32(66, 150, 250, 255)
|
||||
: IM_COL32(179, 213, 253, 255),
|
||||
line_thickness);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// audio capture button
|
||||
float audio_x = ImGui::GetCursorScreenPos().x;
|
||||
float audio_y = ImGui::GetCursorScreenPos().y;
|
||||
float disable_audio_x = audio_x + line_padding;
|
||||
float disable_audio_y = audio_y + line_padding;
|
||||
|
||||
std::string audio = props->audio_capture_button_pressed_
|
||||
? ICON_FA_VOLUME_HIGH
|
||||
: ICON_FA_VOLUME_HIGH;
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (ImGui::Button(audio.c_str(), ImVec2(button_width, button_height))) {
|
||||
if (props->connection_established_) {
|
||||
props->audio_capture_button_pressed_ =
|
||||
!props->audio_capture_button_pressed_;
|
||||
props->audio_capture_button_label_ =
|
||||
props->audio_capture_button_pressed_
|
||||
? localization::audio_capture[localization_language_index_]
|
||||
: localization::mute[localization_language_index_];
|
||||
|
||||
RemoteAction remote_action;
|
||||
remote_action.type = ControlType::audio_capture;
|
||||
remote_action.a = props->audio_capture_button_pressed_;
|
||||
std::string msg = remote_action.to_json();
|
||||
SendReliableDataFrame(props->peer_, msg.c_str(), msg.size(),
|
||||
props->control_data_label_.c_str());
|
||||
}
|
||||
}
|
||||
const bool audio_button_hovered = ImGui::IsItemHovered();
|
||||
const std::string audio_tooltip =
|
||||
props->audio_capture_button_pressed_
|
||||
? localization::mute[localization_language_index_]
|
||||
: localization::audio_capture[localization_language_index_];
|
||||
ShowControlBarTooltip(audio_tooltip);
|
||||
|
||||
if (!props->audio_capture_button_pressed_) {
|
||||
draw_list->AddLine(ImVec2(disable_audio_x, disable_audio_y),
|
||||
ImVec2(audio_x + button_width - line_padding,
|
||||
audio_y + button_height - line_padding),
|
||||
IM_COL32(0, 0, 0, 255), line_thickness);
|
||||
draw_list->AddLine(
|
||||
ImVec2(disable_audio_x - line_thickness * 0.7f,
|
||||
disable_audio_y + line_thickness * 0.7f),
|
||||
ImVec2(audio_x + button_width - line_padding - line_thickness * 0.7f,
|
||||
audio_y + button_height - line_padding +
|
||||
line_thickness * 0.7f),
|
||||
audio_button_hovered ? IM_COL32(66, 150, 250, 255)
|
||||
: IM_COL32(179, 213, 253, 255),
|
||||
line_thickness);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
std::string open_folder = ICON_FA_FOLDER_OPEN;
|
||||
if (ImGui::Button(open_folder.c_str(),
|
||||
ImVec2(button_width, button_height))) {
|
||||
std::string title =
|
||||
localization::select_file[localization_language_index_];
|
||||
std::string path = OpenFileDialog(title);
|
||||
transfers_.ProcessSelectedFile(path, props, file_label_);
|
||||
}
|
||||
ShowControlBarTooltip(
|
||||
localization::select_file[localization_language_index_]);
|
||||
|
||||
ImGui::SameLine();
|
||||
// net traffic stats button
|
||||
bool button_color_style_pushed = false;
|
||||
if (props->net_traffic_stats_button_pressed_) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(66 / 255.0f, 150 / 255.0f,
|
||||
250 / 255.0f, 1.0f));
|
||||
button_color_style_pushed = true;
|
||||
}
|
||||
std::string net_traffic_stats = ICON_FA_SIGNAL;
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (ImGui::Button(net_traffic_stats.c_str(),
|
||||
ImVec2(button_width, button_height))) {
|
||||
props->net_traffic_stats_button_pressed_ =
|
||||
!props->net_traffic_stats_button_pressed_;
|
||||
props->control_window_height_is_changing_ = true;
|
||||
props->net_traffic_stats_button_pressed_time_ = ImGui::GetTime();
|
||||
props->net_traffic_stats_button_label_ =
|
||||
props->net_traffic_stats_button_pressed_
|
||||
? localization::hide_net_traffic_stats
|
||||
[localization_language_index_]
|
||||
: localization::show_net_traffic_stats
|
||||
[localization_language_index_];
|
||||
}
|
||||
const std::string net_traffic_stats_tooltip =
|
||||
props->net_traffic_stats_button_pressed_
|
||||
? localization::hide_net_traffic_stats[localization_language_index_]
|
||||
: localization::show_net_traffic_stats
|
||||
[localization_language_index_];
|
||||
ShowControlBarTooltip(net_traffic_stats_tooltip);
|
||||
|
||||
if (button_color_style_pushed) {
|
||||
ImGui::PopStyleColor();
|
||||
button_color_style_pushed = false;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// fullscreen button
|
||||
std::string fullscreen =
|
||||
fullscreen_button_pressed_ ? ICON_FA_COMPRESS : ICON_FA_EXPAND;
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (ImGui::Button(fullscreen.c_str(),
|
||||
ImVec2(button_width, button_height))) {
|
||||
fullscreen_button_pressed_ = !fullscreen_button_pressed_;
|
||||
props->fullscreen_button_label_ =
|
||||
fullscreen_button_pressed_
|
||||
? localization::exit_fullscreen[localization_language_index_]
|
||||
: localization::fullscreen[localization_language_index_];
|
||||
|
||||
if (fullscreen_button_pressed_) {
|
||||
SDL_SetWindowFullscreen(stream_window_, true);
|
||||
} else {
|
||||
SDL_SetWindowFullscreen(stream_window_, false);
|
||||
}
|
||||
props->reset_control_bar_pos_ = true;
|
||||
}
|
||||
const std::string fullscreen_tooltip =
|
||||
fullscreen_button_pressed_
|
||||
? localization::exit_fullscreen[localization_language_index_]
|
||||
: localization::fullscreen[localization_language_index_];
|
||||
ShowControlBarTooltip(fullscreen_tooltip);
|
||||
|
||||
ImGui::SameLine();
|
||||
// close button
|
||||
std::string close_button = ICON_FA_XMARK;
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (ImGui::Button(close_button.c_str(),
|
||||
ImVec2(button_width, button_height))) {
|
||||
CloseRemoteSession(props);
|
||||
}
|
||||
ShowControlBarTooltip(
|
||||
localization::disconnect[localization_language_index_]);
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (props->is_control_bar_in_left_) {
|
||||
draw_list->AddLine(
|
||||
ImVec2(ImGui::GetCursorScreenPos().x + button_height * 0.13f,
|
||||
ImGui::GetCursorScreenPos().y + button_height * 0.2f),
|
||||
ImVec2(ImGui::GetCursorScreenPos().x + button_height * 0.13f,
|
||||
ImGui::GetCursorScreenPos().y + button_height * 0.8f),
|
||||
IM_COL32(178, 178, 178, 255), 2.0f);
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
}
|
||||
|
||||
float expand_button_pos_x = props->control_bar_expand_
|
||||
? (props->is_control_bar_in_left_
|
||||
? props->control_window_width_ * 0.917f
|
||||
: props->control_window_width_ * 0.03f)
|
||||
: props->control_window_width_ * 0.11f;
|
||||
|
||||
ImGui::SetCursorPosX(expand_button_pos_x);
|
||||
|
||||
std::string control_bar =
|
||||
props->control_bar_expand_
|
||||
? (props->is_control_bar_in_left_ ? ICON_FA_ANGLE_LEFT
|
||||
: ICON_FA_ANGLE_RIGHT)
|
||||
: (props->is_control_bar_in_left_ ? ICON_FA_ANGLE_RIGHT
|
||||
: ICON_FA_ANGLE_LEFT);
|
||||
const std::string control_bar_tooltip =
|
||||
props->control_bar_expand_
|
||||
? localization::collapse_control_bar[localization_language_index_]
|
||||
: localization::expand_control_bar[localization_language_index_];
|
||||
if (ImGui::Button(control_bar.c_str(),
|
||||
ImVec2(button_height * 0.6f, button_height))) {
|
||||
props->control_bar_expand_ = !props->control_bar_expand_;
|
||||
props->control_bar_button_pressed_time_ = ImGui::GetTime();
|
||||
props->control_window_width_is_changing_ = true;
|
||||
|
||||
if (!props->control_bar_expand_) {
|
||||
props->control_window_height_ = props->control_window_min_height_;
|
||||
props->net_traffic_stats_button_pressed_ = false;
|
||||
}
|
||||
}
|
||||
ShowControlBarTooltip(control_bar_tooltip);
|
||||
|
||||
if (props->net_traffic_stats_button_pressed_ && props->control_bar_expand_) {
|
||||
NetTrafficStats(props);
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::NetTrafficStats(
|
||||
std::shared_ptr<RemoteSession> &props) {
|
||||
ImGui::SetCursorPos(ImVec2(props->control_window_width_ * 0.048f,
|
||||
props->control_window_min_height_));
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (ImGui::BeginTable("NetTrafficStats", 4, ImGuiTableFlags_BordersH,
|
||||
ImVec2(props->control_window_max_width_ * 0.9f,
|
||||
props->control_window_max_height_ - 0.9f))) {
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
|
||||
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(" ");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", localization::in[localization_language_index_].c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", localization::out[localization_language_index_].c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",
|
||||
localization::loss_rate[localization_language_index_].c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",
|
||||
localization::video[localization_language_index_].c_str());
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)props->net_traffic_stats_.video_inbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)props->net_traffic_stats_.video_outbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
LossRateDisplay(props->net_traffic_stats_.video_inbound_stats.loss_rate);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",
|
||||
localization::audio[localization_language_index_].c_str());
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)props->net_traffic_stats_.audio_inbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)props->net_traffic_stats_.audio_outbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
LossRateDisplay(props->net_traffic_stats_.audio_inbound_stats.loss_rate);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s", localization::data[localization_language_index_].c_str());
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)props->net_traffic_stats_.data_inbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)props->net_traffic_stats_.data_outbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
LossRateDisplay(props->net_traffic_stats_.data_inbound_stats.loss_rate);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s",
|
||||
localization::total[localization_language_index_].c_str());
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)props->net_traffic_stats_.total_inbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
BitrateDisplay((int)props->net_traffic_stats_.total_outbound_stats.bitrate);
|
||||
ImGui::TableNextColumn();
|
||||
LossRateDisplay(props->net_traffic_stats_.total_inbound_stats.loss_rate);
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("FPS:");
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%d", props->fps_);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%s:",
|
||||
localization::resolution[localization_language_index_].c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text("%dx%d", props->video_width_, props->video_height_);
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(
|
||||
"%s:",
|
||||
localization::connection_mode[localization_language_index_].c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
props->traversal_mode_ == 0
|
||||
? localization::connection_mode_direct[localization_language_index_]
|
||||
.c_str()
|
||||
: localization::connection_mode_relay[localization_language_index_]
|
||||
.c_str());
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,56 +0,0 @@
|
||||
#include "application/gui_application.h"
|
||||
#include "layout_relative.h"
|
||||
#include "localization.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
int GuiApplication::StatusBar() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
float status_bar_width = io.DisplaySize.x;
|
||||
float status_bar_height = io.DisplaySize.y * STATUS_BAR_HEIGHT;
|
||||
|
||||
static bool a, b, c, d, e;
|
||||
ImGui::SetNextWindowPos(ImVec2(0, io.DisplaySize.y * (1 - STATUS_BAR_HEIGHT)),
|
||||
ImGuiCond_Always);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
|
||||
ImGui::BeginChild("StatusBar", ImVec2(status_bar_width, status_bar_height),
|
||||
ImGuiChildFlags_Borders,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleColor(2);
|
||||
|
||||
ImVec2 dot_pos = ImVec2(status_bar_width * 0.025f,
|
||||
io.DisplaySize.y * (1 - STATUS_BAR_HEIGHT * 0.5f));
|
||||
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
||||
draw_list->AddCircleFilled(dot_pos, status_bar_height * 0.25f,
|
||||
ImColor(1.0f, 1.0f, 1.0f), 100);
|
||||
bool tls_cert_error = signal_status_ == SignalStatus::SignalTlsCertError;
|
||||
draw_list->AddCircleFilled(
|
||||
dot_pos, status_bar_height * 0.2f,
|
||||
tls_cert_error ? ImColor(1.0f, 0.65f, 0.0f)
|
||||
: ImColor(signal_connected_ ? 0.0f : 1.0f,
|
||||
signal_connected_ ? 1.0f : 0.0f, 0.0f),
|
||||
100);
|
||||
|
||||
ImGui::SetWindowFontScale(0.6f);
|
||||
const char *signal_status_text =
|
||||
tls_cert_error
|
||||
? localization::signal_tls_cert_error[localization_language_index_]
|
||||
.c_str()
|
||||
: (signal_connected_
|
||||
? localization::signal_connected[localization_language_index_]
|
||||
.c_str()
|
||||
: localization::signal_disconnected
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
draw_list->AddText(ImVec2(status_bar_width * 0.045f,
|
||||
io.DisplaySize.y * (1 - STATUS_BAR_HEIGHT * 0.9f)),
|
||||
ImColor(0.0f, 0.0f, 0.0f), signal_status_text);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::EndChild();
|
||||
return 0;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,329 +0,0 @@
|
||||
#include "application/gui_application.h"
|
||||
#include "layout_relative.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "rounded_corner_button.h"
|
||||
|
||||
constexpr double kNewVersionIconBlinkIntervalSec = 2.0;
|
||||
constexpr double kNewVersionIconBlinkOnTimeSec = 1.0;
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
int GuiApplication::TitleBar(bool main_window) {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
float title_bar_width = title_bar_width_;
|
||||
float title_bar_height = title_bar_height_;
|
||||
float title_bar_height_padding = title_bar_height_;
|
||||
float title_bar_button_width = title_bar_button_width_;
|
||||
float title_bar_button_height = title_bar_button_height_;
|
||||
if (main_window) {
|
||||
// When the main window is minimized, Dear ImGui may report DisplaySize as
|
||||
// (0, 0). Do not overwrite shared title-bar metrics with zeros, otherwise
|
||||
// stream/server windows (which reuse these metrics) will lose their title
|
||||
// bars and appear collapsed.
|
||||
if (io.DisplaySize.x > 0.0f && io.DisplaySize.y > 0.0f) {
|
||||
title_bar_width = io.DisplaySize.x;
|
||||
title_bar_height = io.DisplaySize.y * TITLE_BAR_HEIGHT;
|
||||
title_bar_height_padding = io.DisplaySize.y * TITLE_BAR_HEIGHT;
|
||||
title_bar_button_width = io.DisplaySize.x * TITLE_BAR_BUTTON_WIDTH;
|
||||
title_bar_button_height = io.DisplaySize.y * TITLE_BAR_BUTTON_HEIGHT;
|
||||
|
||||
title_bar_height_ = title_bar_height;
|
||||
title_bar_button_width_ = title_bar_button_width;
|
||||
title_bar_button_height_ = title_bar_button_height;
|
||||
} else {
|
||||
// Keep using last known good values.
|
||||
title_bar_width = title_bar_width_;
|
||||
title_bar_height = title_bar_height_;
|
||||
title_bar_height_padding = title_bar_height_;
|
||||
title_bar_button_width = title_bar_button_width_;
|
||||
title_bar_button_height = title_bar_button_height_;
|
||||
}
|
||||
} else {
|
||||
title_bar_width = io.DisplaySize.x;
|
||||
title_bar_height = title_bar_button_height_;
|
||||
title_bar_button_width = title_bar_button_width_;
|
||||
title_bar_button_height = title_bar_button_height_;
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||
ImGui::BeginChild(main_window ? "MainTitleBar" : "StreamTitleBar",
|
||||
ImVec2(title_bar_width, title_bar_height_padding),
|
||||
ImGuiChildFlags_Borders,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
// get draw list
|
||||
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
||||
|
||||
ImGui::SetCursorPos(
|
||||
ImVec2(title_bar_width - title_bar_button_width * 3, 0.0f));
|
||||
if (main_window) {
|
||||
float bar_pos_x = title_bar_width - title_bar_button_width * 3 +
|
||||
title_bar_button_width * 0.33f;
|
||||
float bar_pos_y = title_bar_button_height * 0.5f;
|
||||
|
||||
// draw menu icon
|
||||
float menu_bar_line_size = title_bar_button_width * 0.33f;
|
||||
draw_list->AddLine(
|
||||
ImVec2(bar_pos_x, bar_pos_y - title_bar_button_height * 0.15f),
|
||||
ImVec2(bar_pos_x + menu_bar_line_size,
|
||||
bar_pos_y - title_bar_button_height * 0.15f),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y),
|
||||
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
draw_list->AddLine(
|
||||
ImVec2(bar_pos_x, bar_pos_y + title_bar_button_height * 0.15f),
|
||||
ImVec2(bar_pos_x + menu_bar_line_size,
|
||||
bar_pos_y + title_bar_button_height * 0.15f),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
|
||||
std::string title_bar_menu_button = "##title_bar_menu"; // ICON_FA_BARS;
|
||||
std::string title_bar_menu = "##title_bar_menu";
|
||||
|
||||
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));
|
||||
if (ImGui::Button(
|
||||
title_bar_menu_button.c_str(),
|
||||
ImVec2(title_bar_button_width, title_bar_button_height))) {
|
||||
ImGui::OpenPopup(title_bar_menu.c_str());
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
if (ImGui::BeginPopup(title_bar_menu.c_str())) {
|
||||
ImGui::SetWindowFontScale(0.6f);
|
||||
if (ImGui::MenuItem(
|
||||
localization::settings[localization_language_index_].c_str())) {
|
||||
show_settings_window_ = true;
|
||||
}
|
||||
|
||||
show_new_version_icon_in_menu_ = false;
|
||||
|
||||
std::string about_str = localization::about[localization_language_index_];
|
||||
if (update_available_) {
|
||||
const double now_time = ImGui::GetTime();
|
||||
|
||||
// every 2 seconds
|
||||
if (now_time - new_version_icon_last_trigger_time_ >=
|
||||
kNewVersionIconBlinkIntervalSec) {
|
||||
show_new_version_icon_ = true;
|
||||
new_version_icon_render_start_time_ = now_time;
|
||||
new_version_icon_last_trigger_time_ = now_time;
|
||||
}
|
||||
|
||||
// render for 1 second
|
||||
if (show_new_version_icon_) {
|
||||
about_str = about_str + " " + ICON_FA_CIRCLE_ARROW_UP;
|
||||
if (now_time - new_version_icon_render_start_time_ >=
|
||||
kNewVersionIconBlinkOnTimeSec) {
|
||||
show_new_version_icon_ = false;
|
||||
}
|
||||
} else {
|
||||
about_str = about_str + " ";
|
||||
}
|
||||
}
|
||||
|
||||
if (ImGui::MenuItem(about_str.c_str())) {
|
||||
show_about_window_ = true;
|
||||
}
|
||||
|
||||
if (update_available_ && ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
std::string new_version_available_str =
|
||||
localization::new_version_available[localization_language_index_] +
|
||||
": " + latest_version_;
|
||||
ImGui::Text("%s", new_version_available_str.c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
} else {
|
||||
show_new_version_icon_in_menu_ = true;
|
||||
}
|
||||
|
||||
if (update_available_ && show_new_version_icon_in_menu_) {
|
||||
const double now_time = ImGui::GetTime();
|
||||
|
||||
// every 2 seconds
|
||||
if (now_time - new_version_icon_last_trigger_time_ >=
|
||||
kNewVersionIconBlinkIntervalSec) {
|
||||
show_new_version_icon_ = true;
|
||||
new_version_icon_render_start_time_ = now_time;
|
||||
new_version_icon_last_trigger_time_ = now_time;
|
||||
}
|
||||
|
||||
// render for 1 second
|
||||
if (show_new_version_icon_) {
|
||||
ImGui::SetWindowFontScale(0.6f);
|
||||
ImGui::SetCursorPos(ImVec2(bar_pos_x + title_bar_button_width * 0.21f,
|
||||
bar_pos_y - title_bar_button_width * 0.24f));
|
||||
ImGui::Text(ICON_FA_CIRCLE_ARROW_UP);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
if (now_time - new_version_icon_render_start_time_ >=
|
||||
kNewVersionIconBlinkOnTimeSec) {
|
||||
show_new_version_icon_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
SettingWindow();
|
||||
SelfHostedServerWindow();
|
||||
AboutWindow();
|
||||
}
|
||||
} else {
|
||||
ImGui::SetWindowFontScale(1.2f);
|
||||
}
|
||||
|
||||
ImGui::SetCursorPos(ImVec2(
|
||||
title_bar_width - title_bar_button_width * (main_window ? 2 : 3), 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 = title_bar_width -
|
||||
title_bar_button_width * (main_window ? 2 : 3) +
|
||||
title_bar_button_width * 0.33f;
|
||||
float minimize_pos_y = title_bar_button_height * 0.5f;
|
||||
std::string window_minimize_button = "##minimize"; // ICON_FA_MINUS;
|
||||
if (ImGui::Button(window_minimize_button.c_str(),
|
||||
ImVec2(title_bar_button_width, title_bar_button_height))) {
|
||||
if (main_window) {
|
||||
last_main_minimize_request_tick_ = SDL_GetTicks();
|
||||
} else {
|
||||
last_stream_minimize_request_tick_ = SDL_GetTicks();
|
||||
}
|
||||
SDL_MinimizeWindow(main_window ? main_window_ : stream_window_);
|
||||
}
|
||||
draw_list->AddLine(
|
||||
ImVec2(minimize_pos_x, minimize_pos_y),
|
||||
ImVec2(minimize_pos_x + title_bar_button_width * 0.33f, minimize_pos_y),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
if (!main_window) {
|
||||
ImGui::SetCursorPos(
|
||||
ImVec2(title_bar_width - title_bar_button_width * 2, 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));
|
||||
|
||||
if (window_maximized_) {
|
||||
float pos_x_top = title_bar_width - title_bar_button_width * 1.65f;
|
||||
float pos_y_top = title_bar_button_height * 0.36f;
|
||||
float pos_x_bottom = title_bar_width - title_bar_button_width * 1.6f;
|
||||
float pos_y_bottom = title_bar_button_height * 0.28f;
|
||||
std::string window_restore_button =
|
||||
"##restore"; // ICON_FA_WINDOW_RESTORE;
|
||||
if (ImGui::Button(
|
||||
window_restore_button.c_str(),
|
||||
ImVec2(title_bar_button_width, title_bar_button_height))) {
|
||||
SDL_RestoreWindow(stream_window_);
|
||||
window_maximized_ = false;
|
||||
}
|
||||
draw_list->AddRect(ImVec2(pos_x_top, pos_y_top),
|
||||
ImVec2(pos_x_top + title_bar_button_height * 0.33f,
|
||||
pos_y_top + title_bar_button_height * 0.33f),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
draw_list->AddRect(ImVec2(pos_x_bottom, pos_y_bottom),
|
||||
ImVec2(pos_x_bottom + title_bar_button_height * 0.33f,
|
||||
pos_y_bottom + title_bar_button_height * 0.33f),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
if (ImGui::IsItemHovered()) {
|
||||
draw_list->AddRectFilled(
|
||||
ImVec2(pos_x_top + title_bar_button_height * 0.02f,
|
||||
pos_y_top + title_bar_button_height * 0.01f),
|
||||
ImVec2(pos_x_top + title_bar_button_height * 0.32f,
|
||||
pos_y_top + title_bar_button_height * 0.31f),
|
||||
IM_COL32(229, 229, 229, 255));
|
||||
} else {
|
||||
draw_list->AddRectFilled(
|
||||
ImVec2(pos_x_top + title_bar_button_height * 0.02f,
|
||||
pos_y_top + title_bar_button_height * 0.01f),
|
||||
ImVec2(pos_x_top + title_bar_button_height * 0.32f,
|
||||
pos_y_top + title_bar_button_height * 0.31f),
|
||||
IM_COL32(255, 255, 255, 255));
|
||||
}
|
||||
} else {
|
||||
float maximize_pos_x = title_bar_width - title_bar_button_width * 1.5f -
|
||||
title_bar_button_height * 0.165f;
|
||||
float maximize_pos_y = title_bar_button_height * 0.33f;
|
||||
std::string window_maximize_button = "##maximize"; // ICON_FA_SQUARE_FULL;
|
||||
if (ImGui::Button(
|
||||
window_maximize_button.c_str(),
|
||||
ImVec2(title_bar_button_width, title_bar_button_height))) {
|
||||
SDL_MaximizeWindow(stream_window_);
|
||||
window_maximized_ = !window_maximized_;
|
||||
}
|
||||
draw_list->AddRect(
|
||||
ImVec2(maximize_pos_x, maximize_pos_y),
|
||||
ImVec2(maximize_pos_x + title_bar_button_height * 0.33f,
|
||||
maximize_pos_y + title_bar_button_height * 0.33f),
|
||||
IM_COL32(0, 0, 0, 255));
|
||||
}
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
|
||||
float xmark_button_pos_x = title_bar_width - 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 + title_bar_button_width * 0.5f;
|
||||
float xmark_pos_y = title_bar_button_height * 0.5f;
|
||||
float xmark_size = title_bar_button_width * 0.33f;
|
||||
std::string close_button = "##xmark"; // ICON_FA_XMARK;
|
||||
bool close_button_clicked = false;
|
||||
if (main_window) {
|
||||
close_button_clicked = RoundedCornerButton(
|
||||
close_button.c_str(),
|
||||
ImVec2(title_bar_button_width, title_bar_button_height), 8.5f,
|
||||
ImDrawFlags_RoundCornersTopRight, true, IM_COL32(0, 0, 0, 0),
|
||||
IM_COL32(250, 0, 0, 255), IM_COL32(255, 0, 0, 128));
|
||||
} else {
|
||||
close_button_clicked =
|
||||
ImGui::Button(close_button.c_str(),
|
||||
ImVec2(title_bar_button_width, title_bar_button_height));
|
||||
}
|
||||
|
||||
if (close_button_clicked) {
|
||||
if (main_window) {
|
||||
if (!MinimizeMainWindowToTray()) {
|
||||
SDL_HideWindow(main_window_);
|
||||
}
|
||||
} else {
|
||||
SDL_Event event;
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
}
|
||||
|
||||
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::EndChild();
|
||||
return 0;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,148 +0,0 @@
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "application/gui_application.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
bool GuiApplication::OpenUrl(const std::string &url) {
|
||||
#if defined(_WIN32)
|
||||
int wide_len = MultiByteToWideChar(CP_UTF8, 0, url.c_str(), -1, nullptr, 0);
|
||||
if (wide_len <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring wide_url(static_cast<size_t>(wide_len), L'\0');
|
||||
MultiByteToWideChar(CP_UTF8, 0, url.c_str(), -1, &wide_url[0], wide_len);
|
||||
if (!wide_url.empty() && wide_url.back() == L'\0') {
|
||||
wide_url.pop_back();
|
||||
}
|
||||
|
||||
std::wstring cmd = L"cmd.exe /c start \"\" \"" + wide_url + L"\"";
|
||||
STARTUPINFOW startup_info = {sizeof(startup_info)};
|
||||
PROCESS_INFORMATION process_info = {};
|
||||
if (!CreateProcessW(nullptr, &cmd[0], nullptr, nullptr, FALSE,
|
||||
CREATE_NO_WINDOW, nullptr, nullptr, &startup_info,
|
||||
&process_info)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CloseHandle(process_info.hThread);
|
||||
CloseHandle(process_info.hProcess);
|
||||
return true;
|
||||
#elif defined(__APPLE__)
|
||||
std::string cmd = "open " + url;
|
||||
return system(cmd.c_str()) == 0;
|
||||
#else
|
||||
std::string cmd = "xdg-open " + url;
|
||||
return system(cmd.c_str()) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void GuiApplication::Hyperlink(const std::string &label, const std::string &url,
|
||||
const float window_width) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(0, 0, 255, 255));
|
||||
ImGui::TextUnformatted(label.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::SetWindowFontScale(0.6f);
|
||||
ImGui::TextUnformatted(url.c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::EndTooltip();
|
||||
if (ImGui::IsMouseClicked(ImGuiMouseButton_Left)) {
|
||||
OpenUrl(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int GuiApplication::AboutWindow() {
|
||||
if (show_about_window_) {
|
||||
float about_window_width = title_bar_button_width_ * 7.5f;
|
||||
float about_window_height = latest_version_.empty()
|
||||
? title_bar_button_width_ * 4.0f
|
||||
: title_bar_button_width_ * 4.9f;
|
||||
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(ImVec2(
|
||||
(viewport->WorkSize.x - viewport->WorkPos.x - about_window_width) / 2,
|
||||
(viewport->WorkSize.y - viewport->WorkPos.y - about_window_height) /
|
||||
2));
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(about_window_width, about_window_height));
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_ * 0.5f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::Begin(
|
||||
localization::about[localization_language_index_].c_str(), nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
|
||||
std::string version;
|
||||
#ifdef CROSSDESK_VERSION
|
||||
version = CROSSDESK_VERSION;
|
||||
#else
|
||||
version = "Unknown";
|
||||
#endif
|
||||
|
||||
std::string text = localization::version[localization_language_index_] +
|
||||
": CrossDesk " + version;
|
||||
ImGui::SetCursorPosX(about_window_width * 0.1f);
|
||||
ImGui::Text("%s", text.c_str());
|
||||
|
||||
if (update_available_ && show_new_version_icon_in_menu_) {
|
||||
std::string new_version_available =
|
||||
localization::new_version_available[localization_language_index_] +
|
||||
": ";
|
||||
ImGui::SetCursorPosX(about_window_width * 0.1f);
|
||||
ImGui::Text("%s", new_version_available.c_str());
|
||||
std::string access_website =
|
||||
localization::access_website[localization_language_index_];
|
||||
ImGui::SetCursorPosX((about_window_width -
|
||||
ImGui::CalcTextSize(latest_version_.c_str()).x) /
|
||||
2.0f);
|
||||
Hyperlink(latest_version_, "https://crossdesk.cn", about_window_width);
|
||||
|
||||
ImGui::Spacing();
|
||||
} else {
|
||||
ImGui::Text("%s", "");
|
||||
}
|
||||
|
||||
std::string copyright_text = "© 2025 by JUNKUN DI. All rights reserved.";
|
||||
std::string license_text = "Licensed under GNU GPL v3.";
|
||||
ImGui::SetCursorPosX(about_window_width * 0.1f);
|
||||
ImGui::Text("%s", copyright_text.c_str());
|
||||
ImGui::SetCursorPosX(about_window_width * 0.1f);
|
||||
ImGui::Text("%s", license_text.c_str());
|
||||
|
||||
ImGui::SetCursorPosX(about_window_width * 0.445f);
|
||||
ImGui::SetCursorPosY(about_window_height * 0.8f);
|
||||
// OK
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str())) {
|
||||
show_about_window_ = false;
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::End();
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,209 +0,0 @@
|
||||
#include "application/gui_application.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
bool GuiApplication::ConnectionStatusWindow(
|
||||
std::shared_ptr<RemoteSession> &props) {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
bool ret_flag = false;
|
||||
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(io.DisplaySize.x * 0.33f, io.DisplaySize.y * 0.33f));
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_ * 0.5f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
|
||||
ImGui::Begin("ConnectionStatusWindow", nullptr,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
auto connection_status_window_width = ImGui::GetWindowSize().x;
|
||||
auto connection_status_window_height = ImGui::GetWindowSize().y;
|
||||
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
std::string text;
|
||||
const ConnectionStatus status = props->connection_status_.load();
|
||||
|
||||
if (ConnectionStatus::Connecting == status) {
|
||||
text = localization::p2p_connecting[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
|
||||
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
|
||||
// cancel
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
re_enter_remote_id_ = true;
|
||||
LOG_INFO("User cancelled connecting to [{}]", props->remote_id_);
|
||||
if (props->peer_) {
|
||||
LeaveConnection(props->peer_, props->remote_id_.c_str());
|
||||
}
|
||||
ret_flag = true;
|
||||
}
|
||||
} else if (ConnectionStatus::Gathering == status) {
|
||||
text = localization::p2p_gathering[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
|
||||
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
|
||||
// cancel
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
re_enter_remote_id_ = true;
|
||||
LOG_INFO("User cancelled connecting to [{}]", props->remote_id_);
|
||||
if (props->peer_) {
|
||||
LeaveConnection(props->peer_, props->remote_id_.c_str());
|
||||
}
|
||||
ret_flag = true;
|
||||
}
|
||||
} else if (ConnectionStatus::Connected == status) {
|
||||
text = localization::p2p_connected[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
|
||||
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
|
||||
// ok
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::Disconnected == status) {
|
||||
text = localization::p2p_disconnected[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
|
||||
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
|
||||
// ok
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::Failed == status) {
|
||||
text = localization::p2p_failed[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
|
||||
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
|
||||
// ok
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::Closed == status) {
|
||||
text = localization::p2p_closed[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
|
||||
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
|
||||
// ok
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
}
|
||||
} else if (ConnectionStatus::IncorrectPassword == status) {
|
||||
if (!password_validating_) {
|
||||
if (password_validating_time_ == 1) {
|
||||
text = localization::input_password[localization_language_index_];
|
||||
} else {
|
||||
text = localization::reinput_password[localization_language_index_];
|
||||
}
|
||||
|
||||
ImGui::SetCursorPosX(connection_status_window_width * 0.336f);
|
||||
ImGui::SetCursorPosY(connection_status_window_height * 0.4f);
|
||||
ImGui::SetNextItemWidth(connection_status_window_width * 0.33f);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
|
||||
if (focus_on_input_widget_) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
focus_on_input_widget_ = false;
|
||||
}
|
||||
|
||||
ImGui::InputText("##password", props->remote_password_,
|
||||
IM_ARRAYSIZE(props->remote_password_),
|
||||
ImGuiInputTextFlags_CharsNoBlank);
|
||||
|
||||
ImGui::SetWindowFontScale(0.4f);
|
||||
|
||||
ImVec2 text_size = ImGui::CalcTextSize(
|
||||
localization::remember_password[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SetCursorPosX((connection_status_window_width - text_size.x) *
|
||||
0.45f);
|
||||
ImGui::Checkbox(
|
||||
localization::remember_password[localization_language_index_].c_str(),
|
||||
&(props->remember_password_));
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImGui::SetCursorPosX(connection_status_window_width * 0.325f);
|
||||
ImGui::SetCursorPosY(connection_status_window_height * 0.75f);
|
||||
// ok
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter)) {
|
||||
show_connection_status_window_ = true;
|
||||
password_validating_ = true;
|
||||
props->rejoin_ = true;
|
||||
need_to_rejoin_ = true;
|
||||
focus_on_input_widget_ = true;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// cancel
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
memset(props->remote_password_, 0, sizeof(props->remote_password_));
|
||||
show_connection_status_window_ = false;
|
||||
focus_on_input_widget_ = true;
|
||||
}
|
||||
} else if (password_validating_) {
|
||||
text = localization::validate_password[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
|
||||
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
|
||||
}
|
||||
} else if (ConnectionStatus::NoSuchTransmissionId == status) {
|
||||
text = localization::no_such_id[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
|
||||
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
|
||||
// ok
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter)) {
|
||||
show_connection_status_window_ = false;
|
||||
re_enter_remote_id_ = true;
|
||||
DestroyPeer(&props->peer_);
|
||||
ret_flag = true;
|
||||
}
|
||||
} else if (ConnectionStatus::RemoteUnavailable == status) {
|
||||
text = localization::device_offline[localization_language_index_];
|
||||
ImGui::SetCursorPosX(connection_status_window_width * 0.43f);
|
||||
ImGui::SetCursorPosY(connection_status_window_height * 0.67f);
|
||||
// ok
|
||||
if (ImGui::Button(localization::ok[localization_language_index_].c_str()) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Enter) ||
|
||||
ImGui::IsKeyPressed(ImGuiKey_Escape)) {
|
||||
show_connection_status_window_ = false;
|
||||
re_enter_remote_id_ = true;
|
||||
DestroyPeer(&props->peer_);
|
||||
ret_flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
auto text_width = ImGui::CalcTextSize(text.c_str()).x;
|
||||
ImGui::SetCursorPosX((connection_status_window_width - text_width) * 0.5f);
|
||||
ImGui::SetCursorPosY(connection_status_window_height * 0.2f);
|
||||
ImGui::Text("%s", text.c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
return ret_flag;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,264 +0,0 @@
|
||||
#include "application/gui_application.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
int GuiApplication::ControlWindow(
|
||||
std::shared_ptr<RemoteSession> &props) {
|
||||
double time_duration =
|
||||
ImGui::GetTime() - props->control_bar_button_pressed_time_;
|
||||
if (props->control_window_width_is_changing_) {
|
||||
if (props->control_bar_expand_) {
|
||||
props->control_window_width_ =
|
||||
(float)(props->control_window_min_width_ +
|
||||
(props->control_window_max_width_ -
|
||||
props->control_window_min_width_) *
|
||||
4 * time_duration);
|
||||
} else {
|
||||
props->control_window_width_ =
|
||||
(float)(props->control_window_max_width_ -
|
||||
(props->control_window_max_width_ -
|
||||
props->control_window_min_width_) *
|
||||
4 * time_duration);
|
||||
}
|
||||
}
|
||||
|
||||
time_duration =
|
||||
ImGui::GetTime() - props->net_traffic_stats_button_pressed_time_;
|
||||
if (props->control_window_height_is_changing_) {
|
||||
if (props->control_bar_expand_ &&
|
||||
props->net_traffic_stats_button_pressed_) {
|
||||
props->control_window_height_ =
|
||||
(float)(props->control_window_min_height_ +
|
||||
(props->control_window_max_height_ -
|
||||
props->control_window_min_height_) *
|
||||
4 * time_duration);
|
||||
} else if (props->control_bar_expand_ &&
|
||||
!props->net_traffic_stats_button_pressed_) {
|
||||
props->control_window_height_ =
|
||||
(float)(props->control_window_max_height_ -
|
||||
(props->control_window_max_height_ -
|
||||
props->control_window_min_height_) *
|
||||
4 * time_duration);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_ * 1.5f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, window_rounding_ * 1.5f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
|
||||
float y_boundary = fullscreen_button_pressed_ ? 0.0f : title_bar_height_;
|
||||
float container_x = 0.0f;
|
||||
float container_y = y_boundary;
|
||||
float container_w = stream_window_width_;
|
||||
float container_h = stream_window_height_ - y_boundary;
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(container_w, container_h), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowPos(ImVec2(container_x, container_y), ImGuiCond_Always);
|
||||
|
||||
float pos_x = 0;
|
||||
float pos_y = 0;
|
||||
|
||||
std::string container_window_title =
|
||||
props->remote_id_ + "ControlContainerWindow";
|
||||
ImGui::Begin(container_window_title.c_str(), nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoDocking |
|
||||
ImGuiWindowFlags_NoBackground);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
ImVec2 container_pos = ImGui::GetWindowPos();
|
||||
|
||||
if (ImGui::IsMouseDown(ImGuiMouseButton_Left) &&
|
||||
props->control_bar_hovered_) {
|
||||
float current_x_rel = props->control_window_pos_.x - container_pos.x;
|
||||
float current_y_rel = props->control_window_pos_.y - container_pos.y;
|
||||
ImVec2 delta = ImGui::GetIO().MouseDelta;
|
||||
pos_x = current_x_rel + delta.x;
|
||||
pos_y = current_y_rel + delta.y;
|
||||
if (pos_x < 0.0f)
|
||||
pos_x = 0.0f;
|
||||
if (pos_y < 0.0f)
|
||||
pos_y = 0.0f;
|
||||
if (pos_x + props->control_window_width_ > container_w)
|
||||
pos_x = container_w - props->control_window_width_;
|
||||
if (pos_y + props->control_window_height_ > container_h)
|
||||
pos_y = container_h - props->control_window_height_;
|
||||
} else if (props->reset_control_bar_pos_) {
|
||||
float new_cursor_pos_x = 0.0f;
|
||||
float new_cursor_pos_y = 0.0f;
|
||||
|
||||
// set control window pos
|
||||
float current_y_rel = props->control_window_pos_.y - container_pos.y;
|
||||
if (current_y_rel + props->control_window_height_ > container_h) {
|
||||
pos_y = container_h - props->control_window_height_;
|
||||
} else if (current_y_rel < 0.0f) {
|
||||
pos_y = 0.0f;
|
||||
} else {
|
||||
pos_y = current_y_rel;
|
||||
}
|
||||
|
||||
if (props->is_control_bar_in_left_) {
|
||||
pos_x = 0.0f;
|
||||
} else {
|
||||
pos_x = container_w - props->control_window_width_;
|
||||
}
|
||||
|
||||
if (0 != props->mouse_diff_control_bar_pos_x_ &&
|
||||
0 != props->mouse_diff_control_bar_pos_y_) {
|
||||
// set cursor pos
|
||||
new_cursor_pos_x =
|
||||
container_pos.x + pos_x + props->mouse_diff_control_bar_pos_x_;
|
||||
new_cursor_pos_y =
|
||||
container_pos.y + pos_y + props->mouse_diff_control_bar_pos_y_;
|
||||
|
||||
SDL_WarpMouseInWindow(stream_window_, (int)new_cursor_pos_x,
|
||||
(int)new_cursor_pos_y);
|
||||
}
|
||||
props->reset_control_bar_pos_ = false;
|
||||
} else if (!props->reset_control_bar_pos_ &&
|
||||
ImGui::IsMouseReleased(ImGuiMouseButton_Left) ||
|
||||
props->control_window_width_is_changing_) {
|
||||
float current_x_rel = props->control_window_pos_.x - container_pos.x;
|
||||
float current_y_rel = props->control_window_pos_.y - container_pos.y;
|
||||
if (current_x_rel <= container_w * 0.5f) {
|
||||
pos_x = 0.0f;
|
||||
if (current_y_rel + props->control_window_height_ > container_h) {
|
||||
pos_y = container_h - props->control_window_height_;
|
||||
} else {
|
||||
pos_y = current_y_rel;
|
||||
}
|
||||
|
||||
if (props->control_bar_expand_) {
|
||||
if (props->control_window_width_ >= props->control_window_max_width_) {
|
||||
props->control_window_width_ = props->control_window_max_width_;
|
||||
props->control_window_width_is_changing_ = false;
|
||||
} else {
|
||||
props->control_window_width_is_changing_ = true;
|
||||
}
|
||||
} else {
|
||||
if (props->control_window_width_ <= props->control_window_min_width_) {
|
||||
props->control_window_width_ = props->control_window_min_width_;
|
||||
props->control_window_width_is_changing_ = false;
|
||||
} else {
|
||||
props->control_window_width_is_changing_ = true;
|
||||
}
|
||||
}
|
||||
props->is_control_bar_in_left_ = true;
|
||||
} else if (current_x_rel > container_w * 0.5f) {
|
||||
pos_x = container_w - props->control_window_width_;
|
||||
pos_y = (current_y_rel >= 0.0f &&
|
||||
current_y_rel <= container_h - props->control_window_height_)
|
||||
? current_y_rel
|
||||
: (current_y_rel < 0.0f
|
||||
? 0.0f
|
||||
: (container_h - props->control_window_height_));
|
||||
|
||||
if (props->control_bar_expand_) {
|
||||
if (props->control_window_width_ >= props->control_window_max_width_) {
|
||||
props->control_window_width_ = props->control_window_max_width_;
|
||||
props->control_window_width_is_changing_ = false;
|
||||
pos_x = container_w - props->control_window_max_width_;
|
||||
} else {
|
||||
props->control_window_width_is_changing_ = true;
|
||||
pos_x = container_w - props->control_window_width_;
|
||||
}
|
||||
} else {
|
||||
if (props->control_window_width_ <= props->control_window_min_width_) {
|
||||
props->control_window_width_ = props->control_window_min_width_;
|
||||
props->control_window_width_is_changing_ = false;
|
||||
pos_x = container_w - props->control_window_min_width_;
|
||||
} else {
|
||||
props->control_window_width_is_changing_ = true;
|
||||
pos_x = container_w - props->control_window_width_;
|
||||
}
|
||||
}
|
||||
props->is_control_bar_in_left_ = false;
|
||||
}
|
||||
|
||||
if (current_y_rel + props->control_window_height_ > container_h) {
|
||||
pos_y = container_h - props->control_window_height_;
|
||||
} else if (current_y_rel < 0.0f) {
|
||||
pos_y = 0.0f;
|
||||
}
|
||||
} else {
|
||||
float current_x_rel = props->control_window_pos_.x - container_pos.x;
|
||||
float current_y_rel = props->control_window_pos_.y - container_pos.y;
|
||||
pos_x = current_x_rel;
|
||||
pos_y = current_y_rel;
|
||||
if (pos_x < 0.0f)
|
||||
pos_x = 0.0f;
|
||||
if (pos_y < 0.0f)
|
||||
pos_y = 0.0f;
|
||||
if (pos_x + props->control_window_width_ > container_w)
|
||||
pos_x = container_w - props->control_window_width_;
|
||||
if (pos_y + props->control_window_height_ > container_h)
|
||||
pos_y = container_h - props->control_window_height_;
|
||||
}
|
||||
|
||||
if (props->control_bar_expand_ && props->control_window_height_is_changing_) {
|
||||
if (props->net_traffic_stats_button_pressed_) {
|
||||
if (props->control_window_height_ >= props->control_window_max_height_) {
|
||||
props->control_window_height_ = props->control_window_max_height_;
|
||||
props->control_window_height_is_changing_ = false;
|
||||
} else {
|
||||
props->control_window_height_is_changing_ = true;
|
||||
}
|
||||
} else {
|
||||
if (props->control_window_height_ <= props->control_window_min_height_) {
|
||||
props->control_window_height_ = props->control_window_min_height_;
|
||||
props->control_window_height_is_changing_ = false;
|
||||
} else {
|
||||
props->control_window_height_is_changing_ = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string control_window_title = props->remote_id_ + "ControlWindow";
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
static bool a, b, c, d, e;
|
||||
float child_cursor_x = pos_x;
|
||||
float child_cursor_y = pos_y;
|
||||
ImGui::SetCursorPos(ImVec2(child_cursor_x, child_cursor_y));
|
||||
|
||||
std::string control_child_window_title =
|
||||
props->remote_id_ + "ControlChildWindow";
|
||||
ImGui::BeginChild(
|
||||
control_child_window_title.c_str(),
|
||||
ImVec2(props->control_window_width_, props->control_window_height_),
|
||||
ImGuiChildFlags_Borders, ImGuiWindowFlags_NoDecoration);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
props->control_window_pos_ = ImGui::GetWindowPos();
|
||||
SDL_GetMouseState(&props->mouse_pos_x_, &props->mouse_pos_y_);
|
||||
props->mouse_diff_control_bar_pos_x_ =
|
||||
props->mouse_pos_x_ - props->control_window_pos_.x;
|
||||
props->mouse_diff_control_bar_pos_y_ =
|
||||
props->mouse_pos_y_ - props->control_window_pos_.y;
|
||||
|
||||
if (props->control_window_pos_.y < container_pos.y ||
|
||||
props->control_window_pos_.y + props->control_window_height_ >
|
||||
(container_pos.y + container_h) ||
|
||||
props->control_window_pos_.x < container_pos.x ||
|
||||
props->control_window_pos_.x + props->control_window_width_ >
|
||||
(container_pos.x + container_w)) {
|
||||
ImGui::ClearActiveID();
|
||||
props->reset_control_bar_pos_ = true;
|
||||
props->mouse_diff_control_bar_pos_x_ = 0;
|
||||
props->mouse_diff_control_bar_pos_y_ = 0;
|
||||
}
|
||||
|
||||
ControlBar(props);
|
||||
props->control_bar_hovered_ = ImGui::IsWindowHovered();
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,253 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
|
||||
#include "IconsFontAwesome6.h"
|
||||
#include "application/gui_application.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
namespace {
|
||||
int CountDigits(int number) {
|
||||
if (number == 0)
|
||||
return 1;
|
||||
return (int)std::floor(std::log10(std::abs(number))) + 1;
|
||||
}
|
||||
|
||||
int BitrateDisplay(int bitrate) {
|
||||
int num_of_digits = CountDigits(bitrate);
|
||||
if (num_of_digits <= 3) {
|
||||
ImGui::Text("%d bps", bitrate);
|
||||
} else if (num_of_digits > 3 && num_of_digits <= 6) {
|
||||
ImGui::Text("%d kbps", bitrate / 1000);
|
||||
} else {
|
||||
ImGui::Text("%.1f mbps", bitrate / 1000000.0f);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int GuiApplication::FileTransferWindow(
|
||||
std::shared_ptr<RemoteSession> &props) {
|
||||
FileTransferState *state = &transfers_.state_for(props);
|
||||
state->file_transfer_window_hovered_ = false;
|
||||
|
||||
// Only show window if there are files in transfer list or currently
|
||||
// transferring
|
||||
std::vector<RemoteSession::FileTransferInfo> file_list;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(state->file_transfer_list_mutex_);
|
||||
file_list = state->file_transfer_list_;
|
||||
}
|
||||
|
||||
// Sort file list: Sending first, then Completed, then Queued, then Failed
|
||||
std::sort(
|
||||
file_list.begin(), file_list.end(),
|
||||
[](const RemoteSession::FileTransferInfo &a,
|
||||
const RemoteSession::FileTransferInfo &b) {
|
||||
// Priority: Sending > Completed > Queued > Failed
|
||||
auto get_priority =
|
||||
[](RemoteSession::FileTransferStatus status) {
|
||||
switch (status) {
|
||||
case RemoteSession::FileTransferStatus::Sending:
|
||||
return 0;
|
||||
case RemoteSession::FileTransferStatus::Completed:
|
||||
return 1;
|
||||
case RemoteSession::FileTransferStatus::Queued:
|
||||
return 2;
|
||||
case RemoteSession::FileTransferStatus::Failed:
|
||||
return 3;
|
||||
}
|
||||
return 3;
|
||||
};
|
||||
return get_priority(a.status) < get_priority(b.status);
|
||||
});
|
||||
|
||||
// Only show window if file_transfer_window_visible_ is true
|
||||
// Window can be closed by user even during transfer
|
||||
// It will be reopened automatically when:
|
||||
// 1. A file transfer completes (via FileTransferManager)
|
||||
// 2. FileTransferManager starts the next queued file.
|
||||
if (!state->file_transfer_window_visible_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Position window at bottom-left of stream window
|
||||
// Adjust window size based on number of files
|
||||
float file_transfer_window_width = main_window_width_ * 0.6f;
|
||||
float file_transfer_window_height =
|
||||
main_window_height_ * 0.3f; // Dynamic height
|
||||
float pos_x = file_transfer_window_width * 0.05f;
|
||||
float pos_y = stream_window_height_ - file_transfer_window_height -
|
||||
file_transfer_window_width * 0.05;
|
||||
|
||||
const ImVec2 mouse_pos = ImGui::GetMousePos();
|
||||
const bool mouse_in_window_rect =
|
||||
mouse_pos.x >= pos_x &&
|
||||
mouse_pos.x <= pos_x + file_transfer_window_width &&
|
||||
mouse_pos.y >= pos_y &&
|
||||
mouse_pos.y <= pos_y + file_transfer_window_height;
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(pos_x, pos_y), ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(file_transfer_window_width, file_transfer_window_height),
|
||||
ImGuiCond_Always);
|
||||
if (mouse_in_window_rect) {
|
||||
ImGui::SetNextWindowFocus();
|
||||
}
|
||||
|
||||
// Set Chinese font for proper display
|
||||
const bool has_chinese_font = stream_windows_system_chinese_font_ != nullptr;
|
||||
if (has_chinese_font) {
|
||||
ImGui::PushFont(stream_windows_system_chinese_font_);
|
||||
}
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_ * 0.5f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 0.9f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.0f, 0.0f, 0.0f, 0.3f));
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
bool window_opened = true;
|
||||
const bool show_contents = ImGui::Begin(
|
||||
localization::file_transfer_progress[localization_language_index_]
|
||||
.c_str(),
|
||||
&window_opened,
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoScrollbar);
|
||||
ImGui::PopStyleColor(4);
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
state->file_transfer_window_hovered_ =
|
||||
mouse_in_window_rect ||
|
||||
ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
|
||||
|
||||
if (!window_opened) {
|
||||
state->file_transfer_window_visible_ = false;
|
||||
}
|
||||
|
||||
if (show_contents && window_opened) {
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
|
||||
// Display file list
|
||||
if (file_list.empty()) {
|
||||
ImGui::Text("No files in transfer queue");
|
||||
} else {
|
||||
// Use a scrollable child window for the file list
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::BeginChild(
|
||||
"FileList", ImVec2(0, file_transfer_window_height * 0.75f),
|
||||
ImGuiChildFlags_Borders, ImGuiWindowFlags_HorizontalScrollbar);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
|
||||
for (size_t i = 0; i < file_list.size(); ++i) {
|
||||
const auto &info = file_list[i];
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
|
||||
// Status icon and file name
|
||||
const char *status_icon = "";
|
||||
ImVec4 status_color(0.5f, 0.5f, 0.5f, 1.0f);
|
||||
const char *status_text = "";
|
||||
|
||||
switch (info.status) {
|
||||
case RemoteSession::FileTransferStatus::Queued:
|
||||
status_icon = ICON_FA_CLOCK;
|
||||
status_color =
|
||||
ImVec4(0.5f, 0.6f, 0.7f, 1.0f); // Common blue-gray for queued
|
||||
status_text =
|
||||
localization::queued[localization_language_index_].c_str();
|
||||
break;
|
||||
case RemoteSession::FileTransferStatus::Sending:
|
||||
status_icon = ICON_FA_ARROW_UP;
|
||||
status_color = ImVec4(0.2f, 0.6f, 1.0f, 1.0f);
|
||||
status_text =
|
||||
localization::sending[localization_language_index_].c_str();
|
||||
break;
|
||||
case RemoteSession::FileTransferStatus::Completed:
|
||||
status_icon = ICON_FA_CHECK;
|
||||
status_color = ImVec4(0.0f, 0.8f, 0.0f, 1.0f);
|
||||
status_text =
|
||||
localization::completed[localization_language_index_].c_str();
|
||||
break;
|
||||
case RemoteSession::FileTransferStatus::Failed:
|
||||
status_icon = ICON_FA_XMARK;
|
||||
status_color = ImVec4(1.0f, 0.2f, 0.2f, 1.0f);
|
||||
status_text =
|
||||
localization::failed[localization_language_index_].c_str();
|
||||
break;
|
||||
}
|
||||
|
||||
ImGui::TextColored(status_color, "%s", status_icon);
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", info.file_name.c_str());
|
||||
ImGui::SameLine();
|
||||
ImGui::TextColored(status_color, "%s", status_text);
|
||||
|
||||
// Progress bar for sending files
|
||||
if (info.status ==
|
||||
RemoteSession::FileTransferStatus::Sending &&
|
||||
info.file_size > 0) {
|
||||
float progress = static_cast<float>(info.sent_bytes) /
|
||||
static_cast<float>(info.file_size);
|
||||
progress = (std::max)(0.0f, (std::min)(1.0f, progress));
|
||||
|
||||
float text_height = ImGui::GetTextLineHeight();
|
||||
ImGui::ProgressBar(
|
||||
progress, ImVec2(file_transfer_window_width * 0.5f, text_height),
|
||||
"");
|
||||
ImGui::SameLine();
|
||||
|
||||
ImGui::Text("%.1f%%", progress * 100.0f);
|
||||
ImGui::SameLine();
|
||||
|
||||
float speed_x_pos = file_transfer_window_width * 0.65f;
|
||||
ImGui::SetCursorPosX(speed_x_pos);
|
||||
BitrateDisplay(static_cast<int>(info.rate_bps));
|
||||
} else if (info.status ==
|
||||
RemoteSession::FileTransferStatus::Completed) {
|
||||
// Show completed size
|
||||
char size_str[64];
|
||||
if (info.file_size < 1024) {
|
||||
snprintf(size_str, sizeof(size_str), "%llu B",
|
||||
(unsigned long long)info.file_size);
|
||||
} else if (info.file_size < 1024 * 1024) {
|
||||
snprintf(size_str, sizeof(size_str), "%.2f KB",
|
||||
info.file_size / 1024.0f);
|
||||
} else {
|
||||
snprintf(size_str, sizeof(size_str), "%.2f MB",
|
||||
info.file_size / (1024.0f * 1024.0f));
|
||||
}
|
||||
ImGui::Text("Size: %s", size_str);
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::EndChild();
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
}
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
if (has_chinese_font) {
|
||||
ImGui::PopFont();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace crossdesk
|
||||
@@ -1,643 +0,0 @@
|
||||
#include "application/gui_application.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "tinyfiledialogs.h"
|
||||
|
||||
#if _WIN32 && CROSSDESK_PORTABLE
|
||||
#include "service_host.h"
|
||||
#endif
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
int GuiApplication::SettingWindow() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
float portable_y_padding = 0.0f;
|
||||
#if _WIN32 && CROSSDESK_PORTABLE
|
||||
portable_y_padding = 0.05f;
|
||||
#endif
|
||||
|
||||
if (show_settings_window_) {
|
||||
if (settings_window_pos_reset_) {
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
#if (((defined(_WIN32) || defined(__linux__)) && !defined(__aarch64__) && \
|
||||
!defined(__arm__) && USE_CUDA) || \
|
||||
defined(__APPLE__))
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.343f, io.DisplaySize.y * 0.05f));
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(io.DisplaySize.x * 0.315f,
|
||||
io.DisplaySize.y * (0.9f + portable_y_padding)));
|
||||
#else
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.343f, io.DisplaySize.y * 0.08f));
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(io.DisplaySize.x * 0.315f,
|
||||
io.DisplaySize.y * (0.85f + portable_y_padding)));
|
||||
#endif
|
||||
} else {
|
||||
#if (((defined(_WIN32) || defined(__linux__)) && !defined(__aarch64__) && \
|
||||
!defined(__arm__) && USE_CUDA) || \
|
||||
defined(__APPLE__))
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.297f, io.DisplaySize.y * 0.05f));
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(io.DisplaySize.x * 0.42f,
|
||||
io.DisplaySize.y * (0.9f + portable_y_padding)));
|
||||
#else
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.297f, io.DisplaySize.y * 0.08f));
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(io.DisplaySize.x * 0.42f,
|
||||
io.DisplaySize.y * (0.85f + portable_y_padding)));
|
||||
#endif
|
||||
}
|
||||
|
||||
settings_window_pos_reset_ = false;
|
||||
}
|
||||
|
||||
// Settings
|
||||
{
|
||||
static int settings_items_padding = title_bar_button_width_ * 0.75f;
|
||||
int settings_items_offset = 0;
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_ * 0.5f);
|
||||
|
||||
ImGui::Begin(localization::settings[localization_language_index_].c_str(),
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
{
|
||||
const auto &supported_languages = localization::GetSupportedLanguages();
|
||||
language_button_value_ =
|
||||
localization::detail::ClampLanguageIndex(language_button_value_);
|
||||
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(
|
||||
"%s", localization::language[localization_language_index_].c_str());
|
||||
ImGui::SameLine();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 3.0f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 4.7f);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(title_bar_button_width_ * 1.8f);
|
||||
if (ImGui::BeginCombo("##language",
|
||||
localization::GetSupportedLanguages()
|
||||
[localization::detail::ClampLanguageIndex(
|
||||
language_button_value_)]
|
||||
.display_name.c_str())) {
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
for (int i = 0; i < static_cast<int>(supported_languages.size());
|
||||
++i) {
|
||||
bool selected = (i == language_button_value_);
|
||||
if (ImGui::Selectable(supported_languages[i].display_name.c_str(),
|
||||
selected))
|
||||
language_button_value_ = i;
|
||||
if (selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
if (stream_window_inited_) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
{
|
||||
const char *video_quality_items[] = {
|
||||
localization::video_quality_low[localization_language_index_]
|
||||
.c_str(),
|
||||
localization::video_quality_medium[localization_language_index_]
|
||||
.c_str(),
|
||||
localization::video_quality_high[localization_language_index_]
|
||||
.c_str()};
|
||||
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::video_quality[localization_language_index_].c_str());
|
||||
ImGui::SameLine();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 3.0f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 4.7f);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(title_bar_button_width_ * 1.8f);
|
||||
if (ImGui::BeginCombo(
|
||||
"##video_quality",
|
||||
video_quality_items[video_quality_button_value_])) {
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
for (int i = 0; i < IM_ARRAYSIZE(video_quality_items); i++) {
|
||||
bool selected = (i == video_quality_button_value_);
|
||||
if (ImGui::Selectable(video_quality_items[i], selected))
|
||||
video_quality_button_value_ = i;
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
const char *video_frame_rate_items[] = {"30 fps", "60 fps"};
|
||||
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%s",
|
||||
localization::video_frame_rate[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SameLine();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 3.0f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 4.7f);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(title_bar_button_width_ * 1.8f);
|
||||
if (ImGui::BeginCombo(
|
||||
"##video_frame_rate",
|
||||
video_frame_rate_items[video_frame_rate_button_value_])) {
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
for (int i = 0; i < IM_ARRAYSIZE(video_frame_rate_items); i++) {
|
||||
bool selected = (i == video_frame_rate_button_value_);
|
||||
if (ImGui::Selectable(video_frame_rate_items[i], selected))
|
||||
video_frame_rate_button_value_ = i;
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
const char *video_encode_format_items[] = {
|
||||
localization::h264[localization_language_index_].c_str(),
|
||||
localization::av1[localization_language_index_].c_str()};
|
||||
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::video_encode_format[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SameLine();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 3.0f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 4.7f);
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(title_bar_button_width_ * 1.8f);
|
||||
if (ImGui::BeginCombo(
|
||||
"##video_encode_format",
|
||||
video_encode_format_items[video_encode_format_button_value_])) {
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
for (int i = 0; i < IM_ARRAYSIZE(video_encode_format_items); i++) {
|
||||
bool selected = (i == video_encode_format_button_value_);
|
||||
if (ImGui::Selectable(video_encode_format_items[i], selected))
|
||||
video_encode_format_button_value_ = i;
|
||||
}
|
||||
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
|
||||
#if (((defined(_WIN32) || defined(__linux__)) && !defined(__aarch64__) && \
|
||||
!defined(__arm__) && USE_CUDA) || \
|
||||
defined(__APPLE__))
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%s", localization::enable_hardware_video_codec
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SameLine();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 4.275f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 5.955f);
|
||||
}
|
||||
|
||||
ImGui::Checkbox("##enable_hardware_video_codec",
|
||||
&enable_hardware_video_codec_);
|
||||
}
|
||||
#endif
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::enable_turn[localization_language_index_].c_str());
|
||||
ImGui::SameLine();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 4.275f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 5.955f);
|
||||
}
|
||||
|
||||
ImGui::Checkbox("##enable_turn", &enable_turn_);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 1);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
if (ImGui::Button(localization::self_hosted_server_config
|
||||
[localization_language_index_]
|
||||
.c_str())) {
|
||||
show_self_hosted_server_config_window_ = true;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 4.275f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 5.955f);
|
||||
}
|
||||
|
||||
ImGui::Checkbox("##enable_self_hosted", &enable_self_hosted_);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%s",
|
||||
localization::enable_autostart[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SameLine();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 4.275f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 5.955f);
|
||||
}
|
||||
|
||||
ImGui::Checkbox("##enable_autostart_", &enable_autostart_);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::enable_daemon[localization_language_index_].c_str());
|
||||
ImGui::SameLine();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 4.275f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 5.955f);
|
||||
}
|
||||
|
||||
ImGui::Checkbox("##enable_daemon_", &enable_daemon_);
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::Text("%s", localization::takes_effect_after_restart
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::file_transfer_save_path[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SameLine();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 2.82f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 4.5f);
|
||||
}
|
||||
|
||||
std::string display_path =
|
||||
strlen(file_transfer_save_path_buf_) > 0
|
||||
? file_transfer_save_path_buf_
|
||||
: localization::default_desktop[localization_language_index_];
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
|
||||
ImVec4(0.95f, 0.95f, 0.95f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
|
||||
ImVec4(0.9f, 0.9f, 0.9f, 1.0f));
|
||||
ImGui::PushFont(main_windows_system_chinese_font_);
|
||||
if (ImGui::Button(display_path.c_str(),
|
||||
ImVec2(title_bar_button_width_ * 2.0f, 0))) {
|
||||
const char *folder =
|
||||
tinyfd_selectFolderDialog(localization::file_transfer_save_path
|
||||
[localization_language_index_]
|
||||
.c_str(),
|
||||
strlen(file_transfer_save_path_buf_) > 0
|
||||
? file_transfer_save_path_buf_
|
||||
: nullptr);
|
||||
if (folder) {
|
||||
strncpy(file_transfer_save_path_buf_, folder,
|
||||
sizeof(file_transfer_save_path_buf_) - 1);
|
||||
file_transfer_save_path_buf_[sizeof(file_transfer_save_path_buf_) -
|
||||
1] = '\0';
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemHovered() &&
|
||||
strlen(file_transfer_save_path_buf_) > 0) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::Text("%s", file_transfer_save_path_buf_);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
ImGui::PopFont();
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
|
||||
if (stream_window_inited_) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
#if _WIN32 && CROSSDESK_PORTABLE
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%s", localization::windows_service_settings_label
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SameLine();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 4.0f);
|
||||
} else if (ConfigCenter::LANGUAGE::ENGLISH == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 5.42f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 4.6f);
|
||||
}
|
||||
|
||||
const PortableServiceInstallState state =
|
||||
portable_service_install_state_.load(std::memory_order_acquire);
|
||||
const bool service_installed =
|
||||
IsCrossDeskServiceInstalled() ||
|
||||
state == PortableServiceInstallState::succeeded;
|
||||
if (service_installed) {
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 3.9f);
|
||||
} else if (ConfigCenter::LANGUAGE::ENGLISH ==
|
||||
localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 5.32f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 4.6f);
|
||||
}
|
||||
ImGui::Text("%s", localization::windows_service_installed
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
} else {
|
||||
if (state == PortableServiceInstallState::installing) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
if (ImGui::Button(localization::install_windows_service
|
||||
[localization_language_index_]
|
||||
.c_str())) {
|
||||
StartPortableWindowsServiceInstall();
|
||||
}
|
||||
|
||||
if (state == PortableServiceInstallState::installing) {
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
ImGui::Text("%s", localization::installing_windows_service
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
} else if (state == PortableServiceInstallState::failed) {
|
||||
ImGui::SameLine();
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::failed[localization_language_index_].c_str());
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::BeginTooltip();
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PushTextWrapPos(title_bar_button_width_ * 10.0f);
|
||||
ImGui::TextWrapped("%s",
|
||||
localization::windows_service_install_failed
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 1.59f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 2.22f);
|
||||
}
|
||||
|
||||
settings_items_offset +=
|
||||
settings_items_padding + title_bar_button_width_ * 0.15f;
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
// OK
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str())) {
|
||||
show_settings_window_ = false;
|
||||
show_self_hosted_server_config_window_ = false;
|
||||
|
||||
// Language
|
||||
language_button_value_ =
|
||||
localization::detail::ClampLanguageIndex(language_button_value_);
|
||||
if (language_button_value_ == 0) {
|
||||
localization_language_ = ConfigCenter::LANGUAGE::CHINESE;
|
||||
} else if (language_button_value_ == 1) {
|
||||
localization_language_ = ConfigCenter::LANGUAGE::ENGLISH;
|
||||
} else {
|
||||
localization_language_ = ConfigCenter::LANGUAGE::RUSSIAN;
|
||||
}
|
||||
config_center_->SetLanguage(localization_language_);
|
||||
language_button_value_last_ = language_button_value_;
|
||||
localization_language_index_ = language_button_value_;
|
||||
LOG_INFO("Set localization language: {}",
|
||||
localization::GetSupportedLanguages()
|
||||
[localization::detail::ClampLanguageIndex(
|
||||
localization_language_index_)]
|
||||
.code.c_str());
|
||||
|
||||
// Video quality
|
||||
if (video_quality_button_value_ == 0) {
|
||||
config_center_->SetVideoQuality(ConfigCenter::VIDEO_QUALITY::LOW);
|
||||
} else if (video_quality_button_value_ == 1) {
|
||||
config_center_->SetVideoQuality(ConfigCenter::VIDEO_QUALITY::MEDIUM);
|
||||
} else {
|
||||
config_center_->SetVideoQuality(ConfigCenter::VIDEO_QUALITY::HIGH);
|
||||
}
|
||||
video_quality_button_value_last_ = video_quality_button_value_;
|
||||
|
||||
if (video_frame_rate_button_value_ == 0) {
|
||||
config_center_->SetVideoFrameRate(
|
||||
ConfigCenter::VIDEO_FRAME_RATE::FPS_30);
|
||||
} else if (video_frame_rate_button_value_ == 1) {
|
||||
config_center_->SetVideoFrameRate(
|
||||
ConfigCenter::VIDEO_FRAME_RATE::FPS_60);
|
||||
}
|
||||
video_frame_rate_button_value_last_ = video_frame_rate_button_value_;
|
||||
|
||||
// Video encode format
|
||||
if (video_encode_format_button_value_ == 0) {
|
||||
config_center_->SetVideoEncodeFormat(
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT::H264);
|
||||
} else if (video_encode_format_button_value_ == 1) {
|
||||
config_center_->SetVideoEncodeFormat(
|
||||
ConfigCenter::VIDEO_ENCODE_FORMAT::AV1);
|
||||
}
|
||||
video_encode_format_button_value_last_ =
|
||||
video_encode_format_button_value_;
|
||||
|
||||
// Hardware video codec
|
||||
if (enable_hardware_video_codec_) {
|
||||
config_center_->SetHardwareVideoCodec(true);
|
||||
} else {
|
||||
config_center_->SetHardwareVideoCodec(false);
|
||||
}
|
||||
enable_hardware_video_codec_last_ = enable_hardware_video_codec_;
|
||||
|
||||
// TURN mode
|
||||
if (enable_turn_) {
|
||||
config_center_->SetTurn(true);
|
||||
} else {
|
||||
config_center_->SetTurn(false);
|
||||
}
|
||||
enable_turn_last_ = enable_turn_;
|
||||
|
||||
if (enable_self_hosted_) {
|
||||
config_center_->SetSelfHosted(true);
|
||||
} else {
|
||||
config_center_->SetSelfHosted(false);
|
||||
}
|
||||
enable_self_hosted_last_ = enable_self_hosted_;
|
||||
|
||||
if (enable_autostart_) {
|
||||
config_center_->SetAutostart(true);
|
||||
} else {
|
||||
config_center_->SetAutostart(false);
|
||||
}
|
||||
enable_autostart_last_ = enable_autostart_;
|
||||
|
||||
if (enable_daemon_) {
|
||||
config_center_->SetDaemon(true);
|
||||
} else {
|
||||
config_center_->SetDaemon(false);
|
||||
}
|
||||
enable_daemon_last_ = enable_daemon_;
|
||||
|
||||
// File transfer save path
|
||||
config_center_->SetFileTransferSavePath(file_transfer_save_path_buf_);
|
||||
file_transfer_save_path_last_ = file_transfer_save_path_buf_;
|
||||
|
||||
settings_window_pos_reset_ = true;
|
||||
|
||||
// Recreate peer instance
|
||||
settings_.Load();
|
||||
|
||||
// Recreate peer instance
|
||||
if (!stream_window_inited_) {
|
||||
LOG_INFO("Recreate peer instance");
|
||||
CloseAllRemoteSessions();
|
||||
CreateConnectionPeer();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// Cancel
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str())) {
|
||||
show_settings_window_ = false;
|
||||
show_self_hosted_server_config_window_ = false;
|
||||
|
||||
if (language_button_value_ != language_button_value_last_) {
|
||||
language_button_value_ = language_button_value_last_;
|
||||
}
|
||||
|
||||
if (video_quality_button_value_ != video_quality_button_value_last_) {
|
||||
video_quality_button_value_ = video_quality_button_value_last_;
|
||||
}
|
||||
|
||||
if (video_frame_rate_button_value_ !=
|
||||
video_frame_rate_button_value_last_) {
|
||||
video_frame_rate_button_value_ = video_frame_rate_button_value_last_;
|
||||
}
|
||||
|
||||
if (video_encode_format_button_value_ !=
|
||||
video_encode_format_button_value_last_) {
|
||||
video_encode_format_button_value_ =
|
||||
video_encode_format_button_value_last_;
|
||||
}
|
||||
|
||||
if (enable_hardware_video_codec_ != enable_hardware_video_codec_last_) {
|
||||
enable_hardware_video_codec_ = enable_hardware_video_codec_last_;
|
||||
}
|
||||
|
||||
if (enable_turn_ != enable_turn_last_) {
|
||||
enable_turn_ = enable_turn_last_;
|
||||
}
|
||||
|
||||
// Restore file transfer save path
|
||||
strncpy(file_transfer_save_path_buf_,
|
||||
file_transfer_save_path_last_.c_str(),
|
||||
sizeof(file_transfer_save_path_buf_) - 1);
|
||||
file_transfer_save_path_buf_[sizeof(file_transfer_save_path_buf_) - 1] =
|
||||
'\0';
|
||||
|
||||
settings_window_pos_reset_ = true;
|
||||
}
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,60 +0,0 @@
|
||||
#include "application/gui_application.h"
|
||||
#include "layout_relative.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
int GuiApplication::MainWindow() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
float local_remote_window_width = io.DisplaySize.x;
|
||||
float local_remote_window_height =
|
||||
io.DisplaySize.y * (0.56f - TITLE_BAR_HEIGHT);
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2(0.0f, io.DisplaySize.y * (TITLE_BAR_HEIGHT)),
|
||||
ImGuiCond_Always);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
|
||||
ImGui::BeginChild(
|
||||
"DeskWindow",
|
||||
ImVec2(local_remote_window_width, local_remote_window_height),
|
||||
ImGuiChildFlags_Borders,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor(2);
|
||||
|
||||
LocalWindow();
|
||||
|
||||
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
||||
draw_list->AddLine(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.1f),
|
||||
ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.53f),
|
||||
IM_COL32(0, 0, 0, 122), 1.0f);
|
||||
|
||||
RemoteWindow();
|
||||
ImGui::EndChild();
|
||||
|
||||
RecentConnectionsWindow();
|
||||
StatusBar();
|
||||
|
||||
if (show_connection_status_window_) {
|
||||
// std::unique_lock lock(remote_sessions_mutex_);
|
||||
for (auto it = remote_sessions_.begin();
|
||||
it != remote_sessions_.end();) {
|
||||
auto &props = it->second;
|
||||
if (focused_remote_id_ == props->remote_id_) {
|
||||
if (ConnectionStatusWindow(props)) {
|
||||
it = remote_sessions_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,359 +0,0 @@
|
||||
#include "application/gui_application.h"
|
||||
|
||||
#if _WIN32 && CROSSDESK_PORTABLE
|
||||
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "service_host.h"
|
||||
|
||||
namespace crossdesk {
|
||||
namespace {
|
||||
|
||||
std::filesystem::path GetCurrentExecutablePath() {
|
||||
std::vector<wchar_t> buffer(MAX_PATH);
|
||||
while (true) {
|
||||
DWORD length = GetModuleFileNameW(nullptr, buffer.data(),
|
||||
static_cast<DWORD>(buffer.size()));
|
||||
if (length == 0) {
|
||||
return {};
|
||||
}
|
||||
if (length < buffer.size()) {
|
||||
return std::filesystem::path(buffer.data(), buffer.data() + length);
|
||||
}
|
||||
if (buffer.size() >= 32768) {
|
||||
return {};
|
||||
}
|
||||
buffer.resize(buffer.size() * 2);
|
||||
}
|
||||
}
|
||||
|
||||
bool InstallServiceWithElevation() {
|
||||
const std::filesystem::path executable_path = GetCurrentExecutablePath();
|
||||
if (executable_path.empty()) {
|
||||
LOG_ERROR("Portable service install failed: current executable not found");
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::filesystem::path service_path =
|
||||
executable_path.parent_path() / L"crossdesk_service.exe";
|
||||
const std::filesystem::path helper_path =
|
||||
executable_path.parent_path() / L"crossdesk_session_helper.exe";
|
||||
if (!std::filesystem::exists(service_path) ||
|
||||
!std::filesystem::exists(helper_path)) {
|
||||
LOG_ERROR("Portable service install failed: service binaries missing, "
|
||||
"service={}, "
|
||||
"helper={}",
|
||||
service_path.string(), helper_path.string());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring executable = executable_path.wstring();
|
||||
std::wstring working_dir = executable_path.parent_path().wstring();
|
||||
std::wstring parameters = L"--service-install";
|
||||
|
||||
SHELLEXECUTEINFOW execute_info{};
|
||||
execute_info.cbSize = sizeof(execute_info);
|
||||
execute_info.fMask = SEE_MASK_NOCLOSEPROCESS;
|
||||
execute_info.hwnd = nullptr;
|
||||
execute_info.lpVerb = L"runas";
|
||||
execute_info.lpFile = executable.c_str();
|
||||
execute_info.lpParameters = parameters.c_str();
|
||||
execute_info.lpDirectory = working_dir.c_str();
|
||||
execute_info.nShow = SW_HIDE;
|
||||
|
||||
if (!ShellExecuteExW(&execute_info)) {
|
||||
LOG_ERROR("Portable service install failed: ShellExecuteExW error={}",
|
||||
GetLastError());
|
||||
return false;
|
||||
}
|
||||
|
||||
DWORD wait_result = WaitForSingleObject(execute_info.hProcess, INFINITE);
|
||||
DWORD exit_code = 1;
|
||||
if (wait_result == WAIT_OBJECT_0) {
|
||||
GetExitCodeProcess(execute_info.hProcess, &exit_code);
|
||||
} else {
|
||||
LOG_ERROR("Portable service install wait failed, result={}", wait_result);
|
||||
}
|
||||
CloseHandle(execute_info.hProcess);
|
||||
|
||||
if (exit_code != 0) {
|
||||
LOG_ERROR("Portable service install command failed, exit_code={}",
|
||||
exit_code);
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool started = StartCrossDeskService();
|
||||
if (!started) {
|
||||
LOG_WARN("Portable service installed but start failed");
|
||||
}
|
||||
return IsCrossDeskServiceInstalled() && started;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void GuiApplication::CheckPortableWindowsService() {
|
||||
if (portable_service_prompt_checked_) {
|
||||
return;
|
||||
}
|
||||
portable_service_prompt_checked_ = true;
|
||||
|
||||
if (IsCrossDeskServiceInstalled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (portable_service_prompt_suppressed_) {
|
||||
return;
|
||||
}
|
||||
|
||||
portable_service_install_state_.store(PortableServiceInstallState::idle,
|
||||
std::memory_order_relaxed);
|
||||
show_portable_service_install_window_ = true;
|
||||
}
|
||||
|
||||
void GuiApplication::StartPortableWindowsServiceInstall() {
|
||||
portable_service_do_not_remind_ = false;
|
||||
PortableServiceInstallState expected = PortableServiceInstallState::idle;
|
||||
if (!portable_service_install_state_.compare_exchange_strong(
|
||||
expected, PortableServiceInstallState::installing,
|
||||
std::memory_order_acq_rel)) {
|
||||
if (expected != PortableServiceInstallState::failed) {
|
||||
return;
|
||||
}
|
||||
portable_service_install_state_.store(
|
||||
PortableServiceInstallState::installing, std::memory_order_release);
|
||||
}
|
||||
|
||||
JoinPortableWindowsServiceInstallThread();
|
||||
portable_service_install_thread_ = std::thread([this]() {
|
||||
const bool installed = InstallServiceWithElevation();
|
||||
portable_service_install_state_.store(
|
||||
installed ? PortableServiceInstallState::succeeded
|
||||
: PortableServiceInstallState::failed,
|
||||
std::memory_order_release);
|
||||
});
|
||||
}
|
||||
|
||||
void GuiApplication::JoinPortableWindowsServiceInstallThread() {
|
||||
if (portable_service_install_thread_.joinable()) {
|
||||
portable_service_install_thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
int GuiApplication::PortableServiceInstallWindow() {
|
||||
if (!show_portable_service_install_window_ &&
|
||||
!show_portable_service_prompt_suppressed_window_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
const float window_width =
|
||||
(std::min)(viewport->WorkSize.x * 0.6f, title_bar_button_width_ * 18.0f);
|
||||
const float window_height =
|
||||
(std::min)(viewport->WorkSize.y * 0.5f, title_bar_button_width_ * 8.0f);
|
||||
|
||||
if (show_portable_service_prompt_suppressed_window_) {
|
||||
const float notice_width = window_width;
|
||||
const float notice_height = (std::min)(viewport->WorkSize.y * 0.35f,
|
||||
title_bar_button_width_ * 4.6f);
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(viewport->WorkPos.x +
|
||||
(viewport->WorkSize.x - notice_width) / 2.0f,
|
||||
viewport->WorkPos.y +
|
||||
(viewport->WorkSize.y - notice_height) / 2.0f),
|
||||
ImGuiCond_Appearing);
|
||||
ImGui::SetNextWindowSize(ImVec2(notice_width, notice_height),
|
||||
ImGuiCond_Always);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_ * 0.5f);
|
||||
|
||||
ImGui::Begin(
|
||||
localization::notification[localization_language_index_].c_str(),
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoTitleBar);
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::SetWindowFontScale(0.55f);
|
||||
ImGui::SetCursorPosX(notice_width * 0.08f);
|
||||
ImGui::Text(
|
||||
"%s", localization::notification[localization_language_index_].c_str());
|
||||
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::SetCursorPosX(notice_width * 0.06f);
|
||||
ImGui::SetCursorPosY(notice_height * 0.28f);
|
||||
ImGui::PushTextWrapPos(notice_width * 0.88f);
|
||||
ImGui::TextWrapped("%s",
|
||||
localization::windows_service_prompt_suppressed_message
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
const std::string ok_label = localization::ok[localization_language_index_];
|
||||
const ImGuiStyle &style = ImGui::GetStyle();
|
||||
const float ok_width =
|
||||
ImGui::CalcTextSize(ok_label.c_str()).x + style.FramePadding.x * 2.0f;
|
||||
ImGui::SetCursorPosX((notice_width - ok_width) * 0.5f);
|
||||
ImGui::SetCursorPosY(notice_height * 0.75f);
|
||||
if (ImGui::Button(ok_label.c_str())) {
|
||||
show_portable_service_prompt_suppressed_window_ = false;
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
if (!show_portable_service_install_window_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(viewport->WorkPos.x + (viewport->WorkSize.x - window_width) / 2.0f,
|
||||
viewport->WorkPos.y +
|
||||
(viewport->WorkSize.y - window_height) / 2.0f),
|
||||
ImGuiCond_Appearing);
|
||||
ImGui::SetNextWindowSize(ImVec2(window_width, window_height),
|
||||
ImGuiCond_Always);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_ * 0.5f);
|
||||
|
||||
ImGui::Begin(
|
||||
localization::windows_service_setup_title[localization_language_index_]
|
||||
.c_str(),
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings |
|
||||
ImGuiWindowFlags_NoTitleBar);
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::SetWindowFontScale(0.55f);
|
||||
ImGui::SetCursorPosX(window_width * 0.08f);
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::windows_service_setup_title[localization_language_index_]
|
||||
.c_str());
|
||||
|
||||
const PortableServiceInstallState state =
|
||||
portable_service_install_state_.load(std::memory_order_acquire);
|
||||
const char *status_text = nullptr;
|
||||
if (state == PortableServiceInstallState::installing ||
|
||||
state == PortableServiceInstallState::succeeded ||
|
||||
state == PortableServiceInstallState::failed) {
|
||||
status_text =
|
||||
localization::installing_windows_service[localization_language_index_]
|
||||
.c_str();
|
||||
if (state == PortableServiceInstallState::succeeded) {
|
||||
status_text = localization::windows_service_install_success
|
||||
[localization_language_index_]
|
||||
.c_str();
|
||||
} else if (state == PortableServiceInstallState::failed) {
|
||||
status_text = localization::windows_service_install_failed
|
||||
[localization_language_index_]
|
||||
.c_str();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(0.45f);
|
||||
ImGui::SetCursorPosX(window_width * 0.04f);
|
||||
ImGui::SetCursorPosY(window_height * 0.22f);
|
||||
ImGui::BeginChild("PortableServiceInstallContent",
|
||||
ImVec2(window_width * 0.92f, window_height * 0.45f),
|
||||
ImGuiChildFlags_Borders, ImGuiWindowFlags_None);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
const float wrap_pos = ImGui::GetContentRegionAvail().x;
|
||||
ImGui::PushTextWrapPos(wrap_pos);
|
||||
ImGui::TextWrapped(
|
||||
"%s",
|
||||
localization::windows_service_setup_message[localization_language_index_]
|
||||
.c_str());
|
||||
if (status_text != nullptr) {
|
||||
ImGui::Spacing();
|
||||
ImGui::TextWrapped("%s", status_text);
|
||||
}
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::SetCursorPosX(window_width * 0.08f);
|
||||
ImGui::SetCursorPosY(window_height * 0.71f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
ImGui::Checkbox(
|
||||
localization::do_not_remind_again[localization_language_index_].c_str(),
|
||||
&portable_service_do_not_remind_);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
const float button_y = window_height * 0.84f;
|
||||
const ImGuiStyle &style = ImGui::GetStyle();
|
||||
const auto default_button_width = [&style](const std::string &label) {
|
||||
return ImGui::CalcTextSize(label.c_str()).x + style.FramePadding.x * 2.0f;
|
||||
};
|
||||
const std::string install_label =
|
||||
localization::install_windows_service[localization_language_index_];
|
||||
const std::string cancel_label =
|
||||
localization::cancel[localization_language_index_];
|
||||
const std::string ok_label = localization::ok[localization_language_index_];
|
||||
const float buttons_width = state == PortableServiceInstallState::succeeded
|
||||
? default_button_width(ok_label)
|
||||
: default_button_width(install_label) +
|
||||
style.ItemSpacing.x +
|
||||
default_button_width(cancel_label);
|
||||
ImGui::SetCursorPosX((window_width - buttons_width) * 0.5f);
|
||||
ImGui::SetCursorPosY(button_y);
|
||||
|
||||
if (state == PortableServiceInstallState::succeeded) {
|
||||
if (ImGui::Button(ok_label.c_str())) {
|
||||
show_portable_service_install_window_ = false;
|
||||
JoinPortableWindowsServiceInstallThread();
|
||||
}
|
||||
} else {
|
||||
if (state == PortableServiceInstallState::installing) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
if (ImGui::Button(install_label.c_str())) {
|
||||
StartPortableWindowsServiceInstall();
|
||||
}
|
||||
if (state == PortableServiceInstallState::installing) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (state == PortableServiceInstallState::installing) {
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
if (ImGui::Button(cancel_label.c_str())) {
|
||||
if (portable_service_do_not_remind_) {
|
||||
portable_service_prompt_suppressed_ = true;
|
||||
config_center_->SetPortableServicePromptSuppressed(true);
|
||||
show_portable_service_prompt_suppressed_window_ = true;
|
||||
}
|
||||
show_portable_service_install_window_ = false;
|
||||
}
|
||||
if (state == PortableServiceInstallState::installing) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace crossdesk
|
||||
|
||||
#endif
|
||||
@@ -1,165 +0,0 @@
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include "application/gui_application.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
std::vector<std::string> GetRootEntries() {
|
||||
std::vector<std::string> roots;
|
||||
#ifdef _WIN32
|
||||
DWORD mask = GetLogicalDrives();
|
||||
for (char letter = 'A'; letter <= 'Z'; ++letter) {
|
||||
if (mask & 1) {
|
||||
roots.push_back(std::string(1, letter) + ":\\");
|
||||
}
|
||||
mask >>= 1;
|
||||
}
|
||||
#else
|
||||
roots.push_back("/");
|
||||
#endif
|
||||
return roots;
|
||||
}
|
||||
|
||||
int GuiApplication::SelfHostedServerWindow() {
|
||||
ImGuiIO &io = ImGui::GetIO();
|
||||
if (show_self_hosted_server_config_window_) {
|
||||
if (self_hosted_server_config_window_pos_reset_) {
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.298f, io.DisplaySize.y * 0.25f));
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(io.DisplaySize.x * 0.407f, io.DisplaySize.y * 0.29f));
|
||||
} else {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(io.DisplaySize.x * 0.27f, io.DisplaySize.y * 0.3f));
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(io.DisplaySize.x * 0.465f, io.DisplaySize.y * 0.29f));
|
||||
}
|
||||
|
||||
self_hosted_server_config_window_pos_reset_ = false;
|
||||
}
|
||||
|
||||
// Settings
|
||||
{
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_ * 0.5f);
|
||||
|
||||
ImGui::Begin(localization::self_hosted_server_settings
|
||||
[localization_language_index_]
|
||||
.c_str(),
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 1.0f);
|
||||
{
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%s", localization::self_hosted_server_address
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SameLine();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 2.5f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 3.43f);
|
||||
}
|
||||
ImGui::SetNextItemWidth(title_bar_button_width_ * 3.8f);
|
||||
|
||||
ImGui::InputText("##signal_server_ip_self_", signal_server_ip_self_,
|
||||
IM_ARRAYSIZE(signal_server_ip_self_),
|
||||
ImGuiInputTextFlags_AlwaysOverwrite);
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::self_hosted_server_port[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SameLine();
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 2.5f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 3.43f);
|
||||
}
|
||||
ImGui::SetNextItemWidth(title_bar_button_width_ * 3.8f);
|
||||
|
||||
ImGui::InputText("##signal_server_port_self_", signal_server_port_self_,
|
||||
IM_ARRAYSIZE(signal_server_port_self_));
|
||||
}
|
||||
|
||||
if (stream_window_inited_) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0.0f, title_bar_button_width_ * 0.25f));
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 2.32f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(title_bar_button_width_ * 2.7f);
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
// OK
|
||||
if (ImGui::Button(
|
||||
localization::ok[localization_language_index_].c_str())) {
|
||||
show_self_hosted_server_config_window_ = false;
|
||||
|
||||
config_center_->SetServerHost(signal_server_ip_self_);
|
||||
config_center_->SetServerPort(atoi(signal_server_port_self_));
|
||||
strncpy(signal_server_ip_, signal_server_ip_self_,
|
||||
sizeof(signal_server_ip_) - 1);
|
||||
signal_server_ip_[sizeof(signal_server_ip_) - 1] = '\0';
|
||||
strncpy(signal_server_port_, signal_server_port_self_,
|
||||
sizeof(signal_server_port_) - 1);
|
||||
signal_server_port_[sizeof(signal_server_port_) - 1] = '\0';
|
||||
self_hosted_server_config_window_pos_reset_ = true;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
// Cancel
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str())) {
|
||||
show_self_hosted_server_config_window_ = false;
|
||||
self_hosted_server_config_window_pos_reset_ = true;
|
||||
strncpy(signal_server_ip_self_,
|
||||
config_center_->GetSignalServerHost().c_str(),
|
||||
sizeof(signal_server_ip_self_) - 1);
|
||||
signal_server_ip_self_[sizeof(signal_server_ip_self_) - 1] = '\0';
|
||||
int signal_port = config_center_->GetSignalServerPort();
|
||||
if (signal_port > 0) {
|
||||
strncpy(signal_server_port_self_, std::to_string(signal_port).c_str(),
|
||||
sizeof(signal_server_port_self_) - 1);
|
||||
signal_server_port_self_[sizeof(signal_server_port_self_) - 1] = '\0';
|
||||
} else {
|
||||
signal_server_port_self_[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,388 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <shared_mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "application/gui_application.h"
|
||||
#include "layout_relative.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
#include "rounded_corner_button.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
namespace {
|
||||
int CountDigits(int number) {
|
||||
if (number == 0)
|
||||
return 1;
|
||||
return (int)std::floor(std::log10(std::abs(number))) + 1;
|
||||
}
|
||||
|
||||
void BitrateDisplay(uint32_t bitrate) {
|
||||
const int num_of_digits = CountDigits(static_cast<int>(bitrate));
|
||||
if (num_of_digits <= 3) {
|
||||
ImGui::Text("%u bps", bitrate);
|
||||
} else if (num_of_digits > 3 && num_of_digits <= 6) {
|
||||
ImGui::Text("%u kbps", bitrate / 1000);
|
||||
} else {
|
||||
ImGui::Text("%.1f mbps", bitrate / 1000000.0f);
|
||||
}
|
||||
}
|
||||
|
||||
std::string FormatBytes(uint64_t bytes) {
|
||||
char buf[64];
|
||||
if (bytes < 1024ULL) {
|
||||
std::snprintf(buf, sizeof(buf), "%llu B", (unsigned long long)bytes);
|
||||
} else if (bytes < 1024ULL * 1024ULL) {
|
||||
std::snprintf(buf, sizeof(buf), "%.2f KB", bytes / 1024.0);
|
||||
} else if (bytes < 1024ULL * 1024ULL * 1024ULL) {
|
||||
std::snprintf(buf, sizeof(buf), "%.2f MB", bytes / (1024.0 * 1024.0));
|
||||
} else {
|
||||
std::snprintf(buf, sizeof(buf), "%.2f GB",
|
||||
bytes / (1024.0 * 1024.0 * 1024.0));
|
||||
}
|
||||
return std::string(buf);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int GuiApplication::ServerWindow() {
|
||||
ImGui::SetNextWindowSize(ImVec2(server_window_width_, server_window_height_),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
ImGui::Begin("##server_window", nullptr,
|
||||
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoScrollbar |
|
||||
ImGuiWindowFlags_NoScrollWithMouse);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
server_window_title_bar_height_ = title_bar_height_;
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||
ImGui::BeginChild(
|
||||
"ServerTitleBar",
|
||||
ImVec2(server_window_width_, server_window_title_bar_height_),
|
||||
ImGuiChildFlags_Borders,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
|
||||
float server_title_bar_button_width = server_window_title_bar_height_;
|
||||
float server_title_bar_button_height = server_window_title_bar_height_;
|
||||
|
||||
// Collapse/expand toggle button (FontAwesome icon).
|
||||
{
|
||||
ImGui::SetCursorPos(ImVec2(0.0f, 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));
|
||||
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
const char *icon =
|
||||
server_window_collapsed_ ? ICON_FA_ANGLE_DOWN : ICON_FA_ANGLE_UP;
|
||||
std::string toggle_label = std::string(icon) + "##server_toggle";
|
||||
|
||||
bool toggle_clicked = RoundedCornerButton(
|
||||
toggle_label.c_str(),
|
||||
ImVec2(server_title_bar_button_width, server_title_bar_button_height),
|
||||
8.5f, ImDrawFlags_RoundCornersTopLeft, true, IM_COL32(0, 0, 0, 0),
|
||||
IM_COL32(0, 0, 0, 25), IM_COL32(255, 255, 255, 255));
|
||||
if (toggle_clicked) {
|
||||
if (server_window_) {
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
SDL_GetWindowSize(server_window_, &w, &h);
|
||||
SDL_GetWindowPosition(server_window_, &x, &y);
|
||||
|
||||
if (server_window_collapsed_) {
|
||||
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;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::PopStyleVar(2);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
RemoteClientInfoWindow();
|
||||
|
||||
ImGui::End();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GuiApplication::RemoteClientInfoWindow() {
|
||||
float remote_client_info_window_width = server_window_width_ * 0.8f;
|
||||
float remote_client_info_window_height =
|
||||
(server_window_height_ - server_window_title_bar_height_) * 0.9f;
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);
|
||||
ImGui::BeginChild(
|
||||
"RemoteClientInfoWindow",
|
||||
ImVec2(remote_client_info_window_width, remote_client_info_window_height),
|
||||
ImGuiChildFlags_Borders,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
float font_scale = localization_language_index_ == 0 ? 0.5f : 0.45f;
|
||||
|
||||
std::vector<std::pair<std::string, std::string>> remote_entries;
|
||||
{
|
||||
std::shared_lock lock(connection_status_mutex_);
|
||||
remote_entries.reserve(connection_status_.size());
|
||||
for (const auto &kv : connection_status_) {
|
||||
const auto host_it = connection_host_names_.find(kv.first);
|
||||
const std::string display_name =
|
||||
(host_it != connection_host_names_.end() && !host_it->second.empty())
|
||||
? host_it->second
|
||||
: kv.first;
|
||||
remote_entries.emplace_back(kv.first, display_name);
|
||||
}
|
||||
}
|
||||
|
||||
auto find_display_name_by_remote_id =
|
||||
[&remote_entries](const std::string &remote_id) -> std::string {
|
||||
for (const auto &entry : remote_entries) {
|
||||
if (entry.first == remote_id) {
|
||||
return entry.second;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
if (!selected_server_remote_id_.empty() &&
|
||||
find_display_name_by_remote_id(selected_server_remote_id_).empty()) {
|
||||
selected_server_remote_id_.clear();
|
||||
selected_server_remote_hostname_.clear();
|
||||
}
|
||||
if (selected_server_remote_id_.empty() && !remote_entries.empty()) {
|
||||
selected_server_remote_id_ = remote_entries.front().first;
|
||||
}
|
||||
if (!selected_server_remote_id_.empty()) {
|
||||
selected_server_remote_hostname_ =
|
||||
find_display_name_by_remote_id(selected_server_remote_id_);
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(font_scale);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text("%s",
|
||||
localization::controller[localization_language_index_].c_str());
|
||||
ImGui::SameLine();
|
||||
|
||||
const char *selected_preview = "-";
|
||||
if (!selected_server_remote_hostname_.empty()) {
|
||||
selected_preview = selected_server_remote_hostname_.c_str();
|
||||
} else if (!remote_client_id_.empty()) {
|
||||
selected_preview = remote_client_id_.c_str();
|
||||
}
|
||||
|
||||
ImGui::SetNextItemWidth(remote_client_info_window_width *
|
||||
(localization_language_index_ == 0 ? 0.68f : 0.62f));
|
||||
ImGui::SetWindowFontScale(localization_language_index_ == 0 ? 0.45f : 0.4f);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
if (ImGui::BeginCombo("##server_remote_id", selected_preview)) {
|
||||
ImGui::SetWindowFontScale(localization_language_index_ == 0 ? 0.45f : 0.4f);
|
||||
for (int i = 0; i < static_cast<int>(remote_entries.size()); i++) {
|
||||
const bool selected =
|
||||
(remote_entries[i].first == selected_server_remote_id_);
|
||||
if (ImGui::Selectable(remote_entries[i].second.c_str(), selected)) {
|
||||
selected_server_remote_id_ = remote_entries[i].first;
|
||||
selected_server_remote_hostname_ = remote_entries[i].second;
|
||||
}
|
||||
if (selected) {
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::SetWindowFontScale(font_scale);
|
||||
|
||||
if (!selected_server_remote_id_.empty()) {
|
||||
ConnectionStatus status = ConnectionStatus::Closed;
|
||||
{
|
||||
std::shared_lock lock(connection_status_mutex_);
|
||||
auto it = connection_status_.find(selected_server_remote_id_);
|
||||
if (it != connection_status_.end()) {
|
||||
status = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::connection_status[localization_language_index_].c_str());
|
||||
ImGui::SameLine();
|
||||
|
||||
switch (status) {
|
||||
case ConnectionStatus::Connected:
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::p2p_connected[localization_language_index_].c_str());
|
||||
break;
|
||||
case ConnectionStatus::Connecting:
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::p2p_connecting[localization_language_index_].c_str());
|
||||
break;
|
||||
case ConnectionStatus::Disconnected:
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::p2p_disconnected[localization_language_index_].c_str());
|
||||
break;
|
||||
case ConnectionStatus::Failed:
|
||||
ImGui::Text(
|
||||
"%s", localization::p2p_failed[localization_language_index_].c_str());
|
||||
break;
|
||||
case ConnectionStatus::Closed:
|
||||
ImGui::Text(
|
||||
"%s", localization::p2p_closed[localization_language_index_].c_str());
|
||||
break;
|
||||
default:
|
||||
ImGui::Text(
|
||||
"%s", localization::p2p_failed[localization_language_index_].c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(
|
||||
"%s", localization::file_transfer[localization_language_index_].c_str());
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(
|
||||
localization::select_file[localization_language_index_].c_str())) {
|
||||
std::string title = localization::select_file[localization_language_index_];
|
||||
std::string path = OpenFileDialog(title);
|
||||
LOG_INFO("Selected file path: {}", path.c_str());
|
||||
|
||||
transfers_.ProcessSelectedFile(path, nullptr, file_label_,
|
||||
selected_server_remote_id_);
|
||||
}
|
||||
|
||||
auto &file_transfer = transfers_.global_state();
|
||||
if (file_transfer.file_transfer_window_visible_) {
|
||||
ImGui::SameLine();
|
||||
const bool is_sending = file_transfer.file_sending_.load();
|
||||
|
||||
if (is_sending) {
|
||||
// Simple animation: cycle icon every 0.5s while sending.
|
||||
static const char *kFileTransferIcons[] = {ICON_FA_ANGLE_UP,
|
||||
ICON_FA_ANGLES_UP};
|
||||
const int icon_index = static_cast<int>(ImGui::GetTime() / 0.5) %
|
||||
(static_cast<int>(sizeof(kFileTransferIcons) /
|
||||
sizeof(kFileTransferIcons[0])));
|
||||
ImGui::Text("%s", kFileTransferIcons[icon_index]);
|
||||
} else {
|
||||
// Completed.
|
||||
ImGui::Text("%s", ICON_FA_CHECK);
|
||||
}
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
const uint64_t sent_bytes = file_transfer.file_sent_bytes_.load();
|
||||
const uint64_t total_bytes = file_transfer.file_total_bytes_.load();
|
||||
const uint32_t rate_bps = file_transfer.file_send_rate_bps_.load();
|
||||
|
||||
float progress = 0.0f;
|
||||
if (total_bytes > 0) {
|
||||
progress =
|
||||
static_cast<float>(sent_bytes) / static_cast<float>(total_bytes);
|
||||
progress = (std::max)(0.0f, (std::min)(1.0f, progress));
|
||||
}
|
||||
|
||||
std::string current_file_name;
|
||||
const uint32_t current_file_id = file_transfer.current_file_id_.load();
|
||||
if (current_file_id != 0) {
|
||||
std::lock_guard<std::mutex> lock(
|
||||
file_transfer.file_transfer_list_mutex_);
|
||||
for (const auto &info : file_transfer.file_transfer_list_) {
|
||||
if (info.file_id == current_file_id) {
|
||||
current_file_name = info.file_name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::BeginTooltip();
|
||||
if (server_windows_system_chinese_font_) {
|
||||
ImGui::PushFont(server_windows_system_chinese_font_);
|
||||
}
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
if (!current_file_name.empty()) {
|
||||
ImGui::Text("%s", current_file_name.c_str());
|
||||
}
|
||||
if (total_bytes > 0) {
|
||||
const std::string sent_str = FormatBytes(sent_bytes);
|
||||
const std::string total_str = FormatBytes(total_bytes);
|
||||
ImGui::Text("%s / %s", sent_str.c_str(), total_str.c_str());
|
||||
}
|
||||
|
||||
const float text_height = ImGui::GetTextLineHeight();
|
||||
char overlay[32];
|
||||
std::snprintf(overlay, sizeof(overlay), "%.1f%%", progress * 100.0f);
|
||||
ImGui::ProgressBar(progress, ImVec2(180.0f, text_height), overlay);
|
||||
BitrateDisplay(rate_bps);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
if (server_windows_system_chinese_font_) {
|
||||
ImGui::PopFont();
|
||||
}
|
||||
ImGui::EndTooltip();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
float close_connection_button_width = server_window_width_ * 0.1f;
|
||||
float close_connection_button_height = remote_client_info_window_height;
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 0.0f, 0.0f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0.3f, 0.3f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 0.5f, 0.5f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_);
|
||||
ImGui::SetWindowFontScale(font_scale);
|
||||
if (ImGui::Button(ICON_FA_XMARK, ImVec2(close_connection_button_width,
|
||||
close_connection_button_height))) {
|
||||
if (peer_ && !selected_server_remote_id_.empty()) {
|
||||
LeaveConnection(peer_, selected_server_remote_id_.c_str());
|
||||
}
|
||||
}
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PopStyleColor(3);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,308 +0,0 @@
|
||||
#include "application/gui_application.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
void GuiApplication::DrawConnectionStatusText(
|
||||
std::shared_ptr<RemoteSession> &props) {
|
||||
std::string text;
|
||||
switch (props->connection_status_.load()) {
|
||||
case ConnectionStatus::Disconnected:
|
||||
text = localization::p2p_disconnected[localization_language_index_];
|
||||
break;
|
||||
case ConnectionStatus::Failed:
|
||||
text = localization::p2p_failed[localization_language_index_];
|
||||
break;
|
||||
case ConnectionStatus::Closed:
|
||||
text = localization::p2p_closed[localization_language_index_];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!text.empty()) {
|
||||
ImVec2 size = ImGui::GetWindowSize();
|
||||
ImVec2 text_size = ImGui::CalcTextSize(text.c_str());
|
||||
ImGui::SetCursorPos(
|
||||
ImVec2((size.x - text_size.x) * 0.5f,
|
||||
(size.y - text_size.y - title_bar_height_) * 0.5f));
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 1.0f), "%s", text.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void GuiApplication::DrawReceivingScreenText(
|
||||
std::shared_ptr<RemoteSession> &props) {
|
||||
if (!props->connection_established_ ||
|
||||
props->connection_status_.load() != ConnectionStatus::Connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool has_valid_frame = false;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(props->video_frame_mutex_);
|
||||
has_valid_frame = props->stream_texture_ != nullptr &&
|
||||
props->video_width_ > 0 && props->video_height_ > 0 &&
|
||||
props->front_frame_ && !props->front_frame_->empty();
|
||||
}
|
||||
|
||||
if (has_valid_frame) {
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string &text =
|
||||
localization::receiving_screen[localization_language_index_];
|
||||
ImVec2 size = ImGui::GetWindowSize();
|
||||
ImVec2 text_size = ImGui::CalcTextSize(text.c_str());
|
||||
ImGui::SetCursorPos(
|
||||
ImVec2((size.x - text_size.x) * 0.5f, (size.y - text_size.y) * 0.5f));
|
||||
ImGui::TextColored(ImVec4(1.0f, 1.0f, 1.0f, 0.92f), "%s", text.c_str());
|
||||
}
|
||||
|
||||
void GuiApplication::CloseTab(decltype(remote_sessions_)::iterator &it) {
|
||||
// std::unique_lock lock(remote_sessions_mutex_);
|
||||
if (it != remote_sessions_.end()) {
|
||||
CloseRemoteSession(it->second);
|
||||
it = remote_sessions_.erase(it);
|
||||
if (remote_sessions_.empty()) {
|
||||
SDL_Event event;
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int GuiApplication::StreamWindow() {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(0, fullscreen_button_pressed_ ? 0 : title_bar_height_),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(stream_window_width_, stream_window_height_),
|
||||
ImGuiCond_Always);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0));
|
||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0, 0, 0, 0));
|
||||
bool video_bg_opened = ImGui::Begin(
|
||||
"VideoBg", nullptr,
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoDocking);
|
||||
ImGui::PopStyleColor(2);
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
if (!video_bg_opened) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ImGuiWindowFlags stream_window_flag =
|
||||
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoMove;
|
||||
|
||||
if (!fullscreen_button_pressed_) {
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(title_bar_button_width_ * 0.8f, title_bar_button_width_ * 0.1f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowSize(ImVec2(0, title_bar_button_width_ * 0.8f),
|
||||
ImGuiCond_Always);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0.0f));
|
||||
ImGui::Begin("TabBar", nullptr,
|
||||
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
|
||||
ImGuiWindowFlags_NoBringToFrontOnFocus |
|
||||
ImGuiWindowFlags_NoDocking);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
if (ImGui::BeginTabBar("StreamTabBar",
|
||||
ImGuiTabBarFlags_Reorderable |
|
||||
ImGuiTabBarFlags_AutoSelectNewTabs)) {
|
||||
is_tab_bar_hovered_ = ImGui::IsWindowHovered();
|
||||
|
||||
// std::shared_lock lock(remote_sessions_mutex_);
|
||||
for (auto it = remote_sessions_.begin();
|
||||
it != remote_sessions_.end();) {
|
||||
auto &props = it->second;
|
||||
if (!props->tab_opened_) {
|
||||
std::string remote_id_to_close = props->remote_id_;
|
||||
// lock.unlock();
|
||||
{
|
||||
// std::unique_lock unique_lock(remote_sessions_mutex_);
|
||||
auto close_it = remote_sessions_.find(remote_id_to_close);
|
||||
if (close_it != remote_sessions_.end()) {
|
||||
CloseTab(close_it);
|
||||
}
|
||||
}
|
||||
// lock.lock();
|
||||
it = remote_sessions_.begin();
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string& tab_label = props->remote_id_;
|
||||
if (ImGui::BeginTabItem(tab_label.c_str(), &props->tab_opened_)) {
|
||||
props->tab_selected_ = true;
|
||||
ImGui::SetWindowFontScale(0.6f);
|
||||
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(stream_window_width_,
|
||||
stream_window_height_ -
|
||||
(fullscreen_button_pressed_ ? 0 : title_bar_height_)),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowPos(
|
||||
ImVec2(0, fullscreen_button_pressed_ ? 0 : title_bar_height_),
|
||||
ImGuiCond_Always);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0.0f));
|
||||
ImGui::Begin(props->remote_id_.c_str(), nullptr, stream_window_flag);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
ImVec2 pos = ImGui::GetWindowPos();
|
||||
ImVec2 size = ImGui::GetWindowSize();
|
||||
props->render_window_x_ = pos.x;
|
||||
props->render_window_y_ = pos.y;
|
||||
props->render_window_width_ = size.x;
|
||||
props->render_window_height_ = size.y;
|
||||
UpdateRenderRect();
|
||||
|
||||
ControlWindow(props);
|
||||
|
||||
// Show file transfer window if needed
|
||||
FileTransferWindow(props);
|
||||
|
||||
DrawReceivingScreenText(props);
|
||||
|
||||
focused_remote_id_ = props->remote_id_;
|
||||
|
||||
if (!props->peer_) {
|
||||
std::string remote_id_to_erase = props->remote_id_;
|
||||
// lock.unlock();
|
||||
{
|
||||
// std::unique_lock unique_lock(remote_sessions_mutex_);
|
||||
auto erase_it = remote_sessions_.find(remote_id_to_erase);
|
||||
if (erase_it != remote_sessions_.end()) {
|
||||
// Ensure we flush pending STREAM_REFRESH_EVENT events and
|
||||
// clean up peer resources before erasing the entry, otherwise
|
||||
// SDL events may still hold raw pointers to freed
|
||||
// RemoteSession (including video_frame_mutex_),
|
||||
// leading to std::system_error when locking.
|
||||
CloseTab(erase_it);
|
||||
}
|
||||
}
|
||||
// lock.lock();
|
||||
ImGui::End();
|
||||
ImGui::EndTabItem();
|
||||
it = remote_sessions_.begin();
|
||||
continue;
|
||||
} else {
|
||||
DrawConnectionStatusText(props);
|
||||
++it;
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
ImGui::EndTabItem();
|
||||
} else {
|
||||
props->tab_selected_ = false;
|
||||
if (!props->tab_opened_) {
|
||||
std::string remote_id_to_close = props->remote_id_;
|
||||
// lock.unlock();
|
||||
{
|
||||
// std::unique_lock unique_lock(remote_sessions_mutex_);
|
||||
auto close_it = remote_sessions_.find(remote_id_to_close);
|
||||
if (close_it != remote_sessions_.end()) {
|
||||
CloseTab(close_it);
|
||||
}
|
||||
}
|
||||
// lock.lock();
|
||||
it = remote_sessions_.begin();
|
||||
continue;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTabBar();
|
||||
}
|
||||
|
||||
ImGui::End(); // End TabBar
|
||||
} else {
|
||||
// std::shared_lock lock(remote_sessions_mutex_);
|
||||
for (auto it = remote_sessions_.begin();
|
||||
it != remote_sessions_.end();) {
|
||||
auto &props = it->second;
|
||||
if (!props->tab_opened_) {
|
||||
std::string remote_id_to_close = props->remote_id_;
|
||||
// lock.unlock();
|
||||
{
|
||||
// std::unique_lock unique_lock(remote_sessions_mutex_);
|
||||
auto close_it = remote_sessions_.find(remote_id_to_close);
|
||||
if (close_it != remote_sessions_.end()) {
|
||||
CloseTab(close_it);
|
||||
}
|
||||
}
|
||||
// lock.lock();
|
||||
it = remote_sessions_.begin();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (props->tab_selected_) {
|
||||
ImGui::SetNextWindowSize(
|
||||
ImVec2(stream_window_width_,
|
||||
stream_window_height_ -
|
||||
(fullscreen_button_pressed_ ? 0 : title_bar_height_)),
|
||||
ImGuiCond_Always);
|
||||
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0.0f));
|
||||
ImGui::Begin(props->remote_id_.c_str(), nullptr, stream_window_flag);
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::PopStyleVar(2);
|
||||
|
||||
ImVec2 pos = ImGui::GetWindowPos();
|
||||
ImVec2 size = ImGui::GetWindowSize();
|
||||
props->render_window_x_ = pos.x;
|
||||
props->render_window_y_ = pos.y;
|
||||
props->render_window_width_ = size.x;
|
||||
props->render_window_height_ = size.y;
|
||||
UpdateRenderRect();
|
||||
|
||||
ControlWindow(props);
|
||||
|
||||
// Show file transfer window if needed
|
||||
FileTransferWindow(props);
|
||||
|
||||
DrawReceivingScreenText(props);
|
||||
|
||||
ImGui::End();
|
||||
|
||||
if (!props->peer_) {
|
||||
fullscreen_button_pressed_ = false;
|
||||
SDL_SetWindowFullscreen(stream_window_, false);
|
||||
std::string remote_id_to_erase = props->remote_id_;
|
||||
// lock.unlock();
|
||||
{
|
||||
// std::unique_lock unique_lock(remote_sessions_mutex_);
|
||||
auto erase_it = remote_sessions_.find(remote_id_to_erase);
|
||||
if (erase_it != remote_sessions_.end()) {
|
||||
CloseTab(erase_it);
|
||||
}
|
||||
}
|
||||
// lock.lock();
|
||||
it = remote_sessions_.begin();
|
||||
continue;
|
||||
} else {
|
||||
DrawConnectionStatusText(props);
|
||||
++it;
|
||||
}
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateRenderRect();
|
||||
ImGui::End(); // End VideoBg
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,163 +0,0 @@
|
||||
#include <string>
|
||||
|
||||
#include "application/gui_application.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
#include "rd_log.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
int GuiApplication::UpdateNotificationWindow() {
|
||||
if (show_update_notification_window_ && update_available_) {
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
|
||||
float update_notification_window_width = title_bar_button_width_ * 10.0f;
|
||||
float update_notification_window_height = title_bar_button_width_ * 8.0f;
|
||||
|
||||
// #ifdef __APPLE__
|
||||
// float font_scale = 0.3f;
|
||||
// #else
|
||||
// float font_scale = 0.5f;
|
||||
// #endif
|
||||
|
||||
ImGui::SetNextWindowPos(ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
|
||||
update_notification_window_width) /
|
||||
2,
|
||||
(viewport->WorkSize.y - viewport->WorkPos.y -
|
||||
update_notification_window_height) /
|
||||
2),
|
||||
ImGuiCond_FirstUseEver);
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(update_notification_window_width,
|
||||
update_notification_window_height));
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_ * 0.5f);
|
||||
ImGui::Begin(
|
||||
localization::notification[localization_language_index_].c_str(),
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoTitleBar);
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() +
|
||||
update_notification_window_height * 0.05f);
|
||||
|
||||
// title: new version available
|
||||
ImGui::SetCursorPosX(update_notification_window_width * 0.1f);
|
||||
ImGui::SetWindowFontScale(0.55f);
|
||||
std::string title =
|
||||
localization::new_version_available[localization_language_index_] +
|
||||
": " + latest_version_;
|
||||
ImGui::Text("%s", title.c_str());
|
||||
ImGui::SetWindowFontScale(0.1f);
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// website link
|
||||
std::string download_text =
|
||||
localization::access_website[localization_language_index_] +
|
||||
"https://crossdesk.cn";
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
ImGui::SetCursorPosX(update_notification_window_width * 0.1f);
|
||||
Hyperlink(download_text, "https://crossdesk.cn",
|
||||
update_notification_window_width);
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
float scrollable_height =
|
||||
update_notification_window_height - UPDATE_NOTIFICATION_RESERVED_HEIGHT;
|
||||
|
||||
if (main_windows_system_chinese_font_ != nullptr) {
|
||||
ImGui::PushFont(main_windows_system_chinese_font_);
|
||||
}
|
||||
// scrollable content area
|
||||
ImGui::SetCursorPosX(update_notification_window_width * 0.05f);
|
||||
ImGui::BeginChild(
|
||||
"ScrollableContent",
|
||||
ImVec2(update_notification_window_width * 0.9f, scrollable_height),
|
||||
ImGuiChildFlags_Borders, ImGuiWindowFlags_None);
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
// set text wrap position to current available width (accounts for
|
||||
// scrollbar)
|
||||
float wrap_pos = ImGui::GetContentRegionAvail().x;
|
||||
ImGui::PushTextWrapPos(wrap_pos);
|
||||
|
||||
// release name
|
||||
if (latest_version_info_.contains("releaseName") &&
|
||||
latest_version_info_["releaseName"].is_string() &&
|
||||
!latest_version_info_["releaseName"].empty()) {
|
||||
ImGui::SetCursorPosX(update_notification_window_width * 0.05f);
|
||||
std::string release_name =
|
||||
latest_version_info_["releaseName"].get<std::string>();
|
||||
ImGui::TextWrapped("%s", release_name.c_str());
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
// release notes
|
||||
if (!release_notes_.empty()) {
|
||||
ImGui::SetCursorPosX(update_notification_window_width * 0.05f);
|
||||
ImGui::TextWrapped("%s", release_notes_.c_str());
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
// release date
|
||||
if (latest_version_info_.contains("releaseDate") &&
|
||||
latest_version_info_["releaseDate"].is_string() &&
|
||||
!latest_version_info_["releaseDate"].empty()) {
|
||||
ImGui::SetCursorPosX(update_notification_window_width * 0.05f);
|
||||
std::string date_label =
|
||||
localization::release_date[localization_language_index_];
|
||||
std::string release_date = latest_version_info_["releaseDate"];
|
||||
std::string date_text = date_label + release_date;
|
||||
ImGui::Text("%s", date_text.c_str());
|
||||
ImGui::Spacing();
|
||||
}
|
||||
|
||||
// pop text wrap position
|
||||
ImGui::PopTextWrapPos();
|
||||
|
||||
ImGui::EndChild();
|
||||
|
||||
// pop system font
|
||||
if (main_windows_system_chinese_font_ != nullptr) {
|
||||
ImGui::PopFont();
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(update_notification_window_width * 0.407f);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(update_notification_window_width * 0.367f);
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(0.5f);
|
||||
// update button
|
||||
if (ImGui::Button(
|
||||
localization::update[localization_language_index_].c_str())) {
|
||||
// open download page
|
||||
std::string url = "https://crossdesk.cn";
|
||||
OpenUrl(url);
|
||||
show_update_notification_window_ = false;
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button(
|
||||
localization::cancel[localization_language_index_].c_str())) {
|
||||
show_update_notification_window_ = false;
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
|
||||
ImGui::End();
|
||||
ImGui::PopStyleVar(3);
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace crossdesk
|
||||
@@ -1,187 +0,0 @@
|
||||
#include "application/gui_application.h"
|
||||
#include "layout.h"
|
||||
#include "localization.h"
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
bool GuiApplication::DrawToggleSwitch(const char *id, bool active,
|
||||
bool enabled) {
|
||||
const float TRACK_HEIGHT = ImGui::GetFrameHeight();
|
||||
const float TRACK_WIDTH = TRACK_HEIGHT * 1.8f;
|
||||
const float TRACK_RADIUS = TRACK_HEIGHT * 0.5f;
|
||||
const float KNOB_PADDING = 2.0f;
|
||||
const float KNOB_HEIGHT = TRACK_HEIGHT - 4.0f;
|
||||
const float KNOB_WIDTH = KNOB_HEIGHT * 1.2f;
|
||||
const float KNOB_RADIUS = KNOB_HEIGHT * 0.5f;
|
||||
const float DISABLED_ALPHA = 0.6f;
|
||||
const float KNOB_ALPHA_DISABLED = 0.9f;
|
||||
|
||||
const ImVec4 COLOR_ACTIVE = ImVec4(0.0f, 0.0f, 1.0f, 1.0f);
|
||||
const ImVec4 COLOR_ACTIVE_HOVER = ImVec4(0.26f, 0.59f, 0.98f, 1.0f);
|
||||
const ImVec4 COLOR_INACTIVE = ImVec4(0.60f, 0.60f, 0.60f, 1.0f);
|
||||
const ImVec4 COLOR_INACTIVE_HOVER = ImVec4(0.70f, 0.70f, 0.70f, 1.0f);
|
||||
const ImVec4 COLOR_KNOB = ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||
|
||||
ImDrawList *draw_list = ImGui::GetWindowDrawList();
|
||||
ImVec2 track_pos = ImGui::GetCursorScreenPos();
|
||||
|
||||
ImGui::InvisibleButton(id, ImVec2(TRACK_WIDTH, TRACK_HEIGHT));
|
||||
bool hovered = ImGui::IsItemHovered();
|
||||
bool clicked = ImGui::IsItemClicked() && enabled;
|
||||
|
||||
ImVec4 track_color =
|
||||
active ? (hovered && enabled ? COLOR_ACTIVE_HOVER : COLOR_ACTIVE)
|
||||
: (hovered && enabled ? COLOR_INACTIVE_HOVER : COLOR_INACTIVE);
|
||||
|
||||
if (!enabled) {
|
||||
track_color.w *= DISABLED_ALPHA;
|
||||
}
|
||||
|
||||
ImVec2 track_min = ImVec2(track_pos.x, track_pos.y + 0.5f);
|
||||
ImVec2 track_max =
|
||||
ImVec2(track_pos.x + TRACK_WIDTH, track_pos.y + TRACK_HEIGHT - 0.5f);
|
||||
draw_list->AddRectFilled(track_min, track_max,
|
||||
ImGui::GetColorU32(track_color), TRACK_RADIUS);
|
||||
|
||||
float knob_position = active ? 1.0f : 0.0f;
|
||||
float knob_min_x = track_pos.x + KNOB_PADDING;
|
||||
float knob_max_x = track_pos.x + TRACK_WIDTH - KNOB_WIDTH - KNOB_PADDING;
|
||||
float knob_x = knob_min_x + knob_position * (knob_max_x - knob_min_x);
|
||||
float knob_y = track_pos.y + (TRACK_HEIGHT - KNOB_HEIGHT) * 0.5f;
|
||||
|
||||
ImVec4 knob_color = COLOR_KNOB;
|
||||
if (!enabled) {
|
||||
knob_color.w = KNOB_ALPHA_DISABLED;
|
||||
}
|
||||
|
||||
ImVec2 knob_min = ImVec2(knob_x, knob_y);
|
||||
ImVec2 knob_max = ImVec2(knob_x + KNOB_WIDTH, knob_y + KNOB_HEIGHT);
|
||||
draw_list->AddRectFilled(knob_min, knob_max, ImGui::GetColorU32(knob_color),
|
||||
KNOB_RADIUS);
|
||||
|
||||
return clicked;
|
||||
}
|
||||
|
||||
int GuiApplication::RequestPermissionWindow() {
|
||||
RefreshMacPermissionStatus(false);
|
||||
|
||||
const bool screen_recording_granted =
|
||||
mac_screen_recording_permission_granted_;
|
||||
const bool accessibility_granted = mac_accessibility_permission_granted_;
|
||||
|
||||
show_request_permission_window_ =
|
||||
!screen_recording_granted || !accessibility_granted;
|
||||
|
||||
if (!show_request_permission_window_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const ImGuiViewport *viewport = ImGui::GetMainViewport();
|
||||
float window_width = localization_language_index_ == 0
|
||||
? REQUEST_PERMISSION_WINDOW_WIDTH_CN
|
||||
: REQUEST_PERMISSION_WINDOW_WIDTH_EN;
|
||||
float window_height = localization_language_index_ == 0
|
||||
? REQUEST_PERMISSION_WINDOW_HEIGHT_CN
|
||||
: REQUEST_PERMISSION_WINDOW_HEIGHT_EN;
|
||||
|
||||
float checkbox_padding = localization_language_index_ == 0
|
||||
? REQUEST_PERMISSION_WINDOW_CHECKBOX_PADDING_CN
|
||||
: REQUEST_PERMISSION_WINDOW_CHECKBOX_PADDING_EN;
|
||||
|
||||
ImVec2 center_pos = ImVec2(
|
||||
(viewport->WorkSize.x - window_width) * 0.5f + viewport->WorkPos.x,
|
||||
(viewport->WorkSize.y - window_height) * 0.5f + viewport->WorkPos.y);
|
||||
ImGui::SetNextWindowPos(center_pos, ImGuiCond_Once);
|
||||
|
||||
ImGui::SetNextWindowSize(ImVec2(window_width, window_height),
|
||||
ImGuiCond_Always);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, window_rounding_);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_ * 0.5f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||
|
||||
ImGui::Begin(
|
||||
localization::request_permissions[localization_language_index_].c_str(),
|
||||
nullptr,
|
||||
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
|
||||
ImGuiWindowFlags_NoSavedSettings);
|
||||
|
||||
ImGui::SetWindowFontScale(0.3f);
|
||||
|
||||
// use system font
|
||||
if (main_windows_system_chinese_font_ != nullptr) {
|
||||
ImGui::PushFont(main_windows_system_chinese_font_);
|
||||
}
|
||||
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + ImGui::GetTextLineHeight() +
|
||||
5.0f);
|
||||
ImGui::SetCursorPosX(10.0f);
|
||||
ImGui::TextWrapped(
|
||||
"%s",
|
||||
localization::permission_required_message[localization_language_index_]
|
||||
.c_str());
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Spacing();
|
||||
ImGui::Spacing();
|
||||
|
||||
// accessibility permission
|
||||
ImGui::SetCursorPosX(10.0f);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(
|
||||
"1. %s:",
|
||||
localization::accessibility_permission[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SameLine();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::SetCursorPosX(checkbox_padding);
|
||||
if (accessibility_granted) {
|
||||
DrawToggleSwitch("accessibility_toggle_on", true, false);
|
||||
} else {
|
||||
if (DrawToggleSwitch("accessibility_toggle", false, true)) {
|
||||
OpenAccessibilityPreferences();
|
||||
mac_accessibility_permission_requested_ = true;
|
||||
RefreshMacPermissionStatus(true);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// screen recording permission
|
||||
ImGui::SetCursorPosX(10.0f);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::Text(
|
||||
"2. %s:",
|
||||
localization::screen_recording_permission[localization_language_index_]
|
||||
.c_str());
|
||||
ImGui::SameLine();
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::SetCursorPosX(checkbox_padding);
|
||||
if (screen_recording_granted) {
|
||||
DrawToggleSwitch("screen_recording_toggle_on", true, false);
|
||||
} else {
|
||||
if (DrawToggleSwitch("screen_recording_toggle", false, true)) {
|
||||
OpenScreenRecordingPreferences();
|
||||
mac_screen_recording_permission_requested_ = true;
|
||||
RefreshMacPermissionStatus(true);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::SetWindowFontScale(0.45f);
|
||||
|
||||
// pop system font
|
||||
if (main_windows_system_chinese_font_ != nullptr) {
|
||||
ImGui::PopFont();
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
ImGui::SetWindowFontScale(1.0f);
|
||||
ImGui::PopStyleVar(4);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
return 0;
|
||||
}
|
||||
} // namespace crossdesk
|
||||
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2026-01-20
|
||||
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _WINDOW_UTIL_MAC_H_
|
||||
#define _WINDOW_UTIL_MAC_H_
|
||||
|
||||
struct SDL_Window;
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
// Best-effort: keep an SDL window above normal windows on macOS.
|
||||
// No-op on non-macOS builds.
|
||||
void MacSetWindowAlwaysOnTop(::SDL_Window* window, bool always_on_top);
|
||||
|
||||
// Best-effort: exclude an SDL window from the Window menu and window cycling.
|
||||
// Note: Cmd-Tab switches apps (not individual windows), so this primarily
|
||||
// affects the Window menu and Cmd-` window cycling.
|
||||
void MacSetWindowExcludedFromWindowMenu(::SDL_Window* window, bool excluded);
|
||||
|
||||
} // namespace crossdesk
|
||||
|
||||
#endif
|
||||
@@ -1,64 +0,0 @@
|
||||
#include "window_util_mac.h"
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
namespace crossdesk {
|
||||
|
||||
static NSWindow* GetNSWindowFromSDL(::SDL_Window* window) {
|
||||
if (!window) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
#if !defined(SDL_PROP_WINDOW_COCOA_WINDOW_POINTER)
|
||||
return nil;
|
||||
#else
|
||||
SDL_PropertiesID props = SDL_GetWindowProperties(window);
|
||||
void* cocoa_window_ptr =
|
||||
SDL_GetPointerProperty(props, SDL_PROP_WINDOW_COCOA_WINDOW_POINTER, NULL);
|
||||
if (!cocoa_window_ptr) {
|
||||
return nil;
|
||||
}
|
||||
return (__bridge NSWindow*)cocoa_window_ptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
void MacSetWindowAlwaysOnTop(::SDL_Window* window, bool always_on_top) {
|
||||
NSWindow* ns_window = GetNSWindowFromSDL(window);
|
||||
if (!ns_window) {
|
||||
(void)always_on_top;
|
||||
return;
|
||||
}
|
||||
|
||||
// Keep above normal windows.
|
||||
const NSInteger level = always_on_top ? NSFloatingWindowLevel : NSNormalWindowLevel;
|
||||
[ns_window setLevel:level];
|
||||
|
||||
// Optional: keep visible across Spaces/fullscreen. Safe as best-effort.
|
||||
NSWindowCollectionBehavior behavior = [ns_window collectionBehavior];
|
||||
behavior |= NSWindowCollectionBehaviorCanJoinAllSpaces;
|
||||
behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
|
||||
[ns_window setCollectionBehavior:behavior];
|
||||
}
|
||||
|
||||
void MacSetWindowExcludedFromWindowMenu(::SDL_Window* window, bool excluded) {
|
||||
NSWindow* ns_window = GetNSWindowFromSDL(window);
|
||||
if (!ns_window) {
|
||||
(void)excluded;
|
||||
return;
|
||||
}
|
||||
|
||||
[ns_window setExcludedFromWindowsMenu:excluded];
|
||||
|
||||
NSWindowCollectionBehavior behavior = [ns_window collectionBehavior];
|
||||
behavior |= NSWindowCollectionBehaviorIgnoresCycle;
|
||||
behavior |= NSWindowCollectionBehaviorTransient;
|
||||
[ns_window setCollectionBehavior:behavior];
|
||||
}
|
||||
|
||||
} // namespace crossdesk
|
||||
|
||||
#endif // __APPLE__
|
||||
@@ -60,7 +60,6 @@ function setup_targets()
|
||||
add_packages("libyuv")
|
||||
add_packages("concurrentqueue", {public = true})
|
||||
add_files("apps/desktop/src/common/*.cpp")
|
||||
remove_files("apps/desktop/src/common/rounded_corner_button.cpp")
|
||||
if is_os("windows") then
|
||||
add_files("apps/desktop/src/platform/windows/system_info.cpp")
|
||||
elseif is_os("macosx") then
|
||||
@@ -335,9 +334,7 @@ function setup_targets()
|
||||
target("assets")
|
||||
set_kind("headeronly")
|
||||
add_includedirs("apps/desktop/src/gui/assets/localization",
|
||||
"apps/desktop/src/gui/assets/fonts",
|
||||
"apps/desktop/src/gui/assets/icons",
|
||||
"apps/desktop/src/gui/assets/layouts", {public = true})
|
||||
"apps/desktop/src/gui/assets/fonts", {public = true})
|
||||
|
||||
target("version_checker")
|
||||
set_kind("object")
|
||||
|
||||
+45
-32
@@ -8,14 +8,14 @@
|
||||
apps/desktop/src/gui/
|
||||
├── render.h # 对外稳定入口 Render
|
||||
├── render.cpp # 创建 GuiApplication 并转发 Run
|
||||
├── application/ # SDL/ImGui 应用外壳
|
||||
├── application/ # Slint 应用外壳
|
||||
│ ├── gui_application.h # GuiApplication 声明
|
||||
│ ├── gui_application.cpp # 初始化、主循环和清理
|
||||
│ ├── gui_application.cpp # 初始化、UI 回调、状态同步和清理
|
||||
│ ├── application_state.h # 窗口、交互和 UI 状态
|
||||
│ ├── sdl_event_dispatch.cpp # SDL 窗口及应用事件分发
|
||||
│ ├── sdl_events.cpp # 键盘和鼠标事件转换
|
||||
│ ├── window_lifecycle.cpp # 原生窗口及 ImGui 上下文生命周期
|
||||
│ └── window_rendering.cpp # 三类窗口的渲染流程
|
||||
│ ├── server_window_state.h # 被控端窗口的控制端选择逻辑
|
||||
│ └── window_geometry.h # 窗口定位计算
|
||||
├── rendering/ # Slint 与原生视频渲染器的衔接
|
||||
│ └── slint_video_presenter.h/.cpp # 视频纹理呈现与渲染通知
|
||||
├── runtime/ # 非界面的 GUI 运行时
|
||||
│ ├── gui_runtime.h/.cpp # 运行时协调接口与公共实现
|
||||
│ ├── connection_runtime.cpp # 连接、在线探测、超时及会话清理
|
||||
@@ -33,11 +33,15 @@ apps/desktop/src/gui/
|
||||
│ ├── file_transfer/ # 文件队列、发送、确认和进度
|
||||
│ ├── input/ # 键盘状态、命令和超时处理
|
||||
│ └── settings/ # 配置缓存和最近连接别名
|
||||
├── views/ # ImGui 视图实现
|
||||
│ ├── panels/ # 主窗口内嵌面板
|
||||
│ ├── toolbars/ # 标题栏、状态栏和控制栏
|
||||
│ └── windows/ # 独立窗口和模态对话框
|
||||
└── assets/ # 字体、图标、布局和本地化资源
|
||||
├── ui/ # Slint 声明式界面
|
||||
│ ├── crossdesk_ui.slint # 界面组件导出入口
|
||||
│ ├── common.slint # 共享控件、字体尺寸、颜色和图标字形
|
||||
│ ├── main_window.slint # 主窗口、面板、设置及对话框
|
||||
│ ├── stream_window.slint # 串流窗口、标签页和控制栏
|
||||
│ ├── server_window.slint # 被控端窗口
|
||||
│ ├── ui_localization.h # 本地化数据到 Slint 属性的映射
|
||||
│ └── icons/ # SVG 图标资源
|
||||
└── assets/ # 嵌入字体和本地化数据
|
||||
```
|
||||
|
||||
平台私有 GUI 实现统一位于 `apps/desktop/src/platform/<os>/gui/`,包括托盘、macOS Metal 渲染与权限、Windows 服务运行时等;Windows 与 Linux 共用的 OpenGL 渲染器位于 `apps/desktop/src/platform/common/gui/`。
|
||||
@@ -47,8 +51,9 @@ apps/desktop/src/gui/
|
||||
```text
|
||||
Render
|
||||
└── GuiApplication
|
||||
├── SDL/ImGui 生命周期
|
||||
├── views/panels / views/toolbars / views/windows
|
||||
├── Slint 窗口、事件循环和定时器
|
||||
├── ui/ 声明式组件与回调
|
||||
├── SlintVideoPresenter → 平台 VideoRenderer
|
||||
└── GuiRuntime
|
||||
├── SessionDeviceManager
|
||||
├── KeyboardController
|
||||
@@ -83,17 +88,19 @@ Render
|
||||
|
||||
### GuiApplication
|
||||
|
||||
`GuiApplication` 是 SDL/ImGui 应用外壳,负责:
|
||||
`GuiApplication` 是 Slint 应用外壳,负责:
|
||||
|
||||
- 初始化日志、配置、SDL 和功能模块;
|
||||
- 创建、销毁原生窗口及 ImGui 上下文;
|
||||
- 执行主事件循环;
|
||||
- 分发 SDL 事件;
|
||||
- 调用面板、工具栏和窗口的绘制方法;
|
||||
- 创建、销毁 Slint 主窗口、串流窗口和被控端窗口;
|
||||
- 运行 Slint 事件循环,通过定时器执行 `Tick()` 和视频帧调度;
|
||||
- 将 Slint 回调转换为连接、键盘和鼠标操作,并处理 SDL 退出及剪贴板事件;
|
||||
- 将运行时状态同步到 Slint 属性和模型,并管理原生视频呈现;
|
||||
- 在程序退出时按顺序清理资源。
|
||||
|
||||
它不应实现连接协议、文件传输、剪贴板或设备控制细节。
|
||||
|
||||
SDL 仍用于音频、显示器信息、剪贴板等平台能力。旧 ImGui 视图、窗口事件处理和专用资源已移除;`ui/common.slint` 中的 `ImGuiLineStyle`、`ImGuiFontStyle` 和 `ImGuiCheckBox` 是当前界面使用的 Slint 组件,名称表示沿用的视觉风格,不依赖 ImGui 库。`assets/fonts/fa_solid_900.h` 仍为 Slint 窗口提供 Font Awesome 图标字体。
|
||||
|
||||
### GuiRuntime
|
||||
|
||||
`GuiRuntime` 是 GUI 进程的非界面协调层,负责:
|
||||
@@ -125,7 +132,8 @@ Render
|
||||
|
||||
| 文件 | 状态范围 |
|
||||
| --- | --- |
|
||||
| `application/application_state.h` | SDL 窗口、渲染上下文、交互标志和 UI 显示状态 |
|
||||
| `application/application_state.h` | 应用退出、交互、更新、窗口生命周期请求和共享 UI 状态 |
|
||||
| `GuiApplication::SlintUi` | Slint 窗口句柄、定时器、视频呈现器、托盘和界面同步缓存 |
|
||||
| `runtime/runtime_state.h` | 配置、Peer、连接表、在线探测和平台集成状态 |
|
||||
| `runtime/remote_session.h` | 单个远端会话独占的连接、媒体、控制和文件状态 |
|
||||
| `runtime/gui_state.h` | 仅组合 `ApplicationState` 与 `RuntimeState` |
|
||||
@@ -147,20 +155,23 @@ Render::Run
|
||||
→ 初始化路径、日志和配置
|
||||
→ 初始化 SDL 和功能模块
|
||||
→ GuiRuntime::CreateConnectionPeer
|
||||
→ 创建主窗口
|
||||
→ GuiApplication::MainLoop
|
||||
→ GuiApplication::InitializeUi 创建主窗口并绑定回调
|
||||
→ 启动 Tick 定时器和视频帧调度
|
||||
→ slint::run_event_loop
|
||||
→ Tick 协调连接、处理后台事件并同步窗口状态
|
||||
```
|
||||
|
||||
### 建立远端连接
|
||||
|
||||
```text
|
||||
remote_peer_panel
|
||||
MainWindow::connect_requested
|
||||
→ GuiApplication::ConnectFromUi
|
||||
→ GuiRuntime::ConnectTo
|
||||
→ 在线状态探测
|
||||
→ 创建或复用 RemoteSession
|
||||
→ MiniRTC JoinConnection
|
||||
→ PeerEventHandler 接收连接状态
|
||||
→ 创建串流窗口并开始渲染
|
||||
→ SyncStreamWindow 创建串流窗口并开始呈现视频
|
||||
```
|
||||
|
||||
### 接收远端数据
|
||||
@@ -178,12 +189,11 @@ MiniRTC callback
|
||||
### 退出和清理
|
||||
|
||||
```text
|
||||
SDL quit / tray exit
|
||||
Slint quit / SDL quit / tray exit
|
||||
→ GuiApplication::Cleanup
|
||||
→ CloseAllRemoteSessions
|
||||
→ 停止设备及后台任务
|
||||
→ 销毁 Peer
|
||||
→ 销毁 ImGui 上下文和 SDL 窗口
|
||||
→ CloseAllRemoteSessions 并等待会话清理
|
||||
→ 分离视频呈现器并释放 Slint 窗口和托盘
|
||||
→ SDL_Quit
|
||||
```
|
||||
|
||||
@@ -193,7 +203,8 @@ SDL quit / tray exit
|
||||
|
||||
| 功能 | 目录 |
|
||||
| --- | --- |
|
||||
| SDL 初始化、事件循环、窗口生命周期 | `application/` |
|
||||
| Slint/SDL 初始化、UI 回调、状态同步和窗口生命周期 | `application/` |
|
||||
| Slint 与原生视频渲染器的衔接 | `rendering/` |
|
||||
| 连接、会话、Peer 回调和平台运行时 | `runtime/` |
|
||||
| 视频、音频、鼠标、键盘设备生命周期 | `features/devices/` |
|
||||
| 键盘状态与桌面输入处理 | `features/input/` |
|
||||
@@ -201,8 +212,10 @@ SDL quit / tray exit
|
||||
| 剪贴板同步 | `features/clipboard/` |
|
||||
| 文件传输 | `features/file_transfer/` |
|
||||
| 配置持久化 | `features/settings/` |
|
||||
| 主窗口内嵌区域 | `views/panels/` |
|
||||
| 标题栏、状态栏、控制栏 | `views/toolbars/` |
|
||||
| 独立窗口和对话框 | `views/windows/` |
|
||||
| 主窗口内嵌区域、设置和对话框 | `ui/main_window.slint` |
|
||||
| 串流标签页、控制栏和统计面板 | `ui/stream_window.slint` |
|
||||
| 被控端窗口 | `ui/server_window.slint` |
|
||||
| 共享控件、字体尺寸、颜色和图标字形 | `ui/common.slint` |
|
||||
| 系统托盘 | `apps/desktop/src/platform/<os>/gui/tray/` |
|
||||
| 字体、图标、布局和本地化数据 | `assets/` |
|
||||
| 嵌入字体和本地化数据 | `assets/` |
|
||||
| SVG 图标资源 | `ui/icons/` |
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# **更新内容**
|
||||
|
||||
1. 新增原生iOS/iPadOS客户端v0.0.1,支持远程画面与音频、触控鼠标和键盘输入、多显示器切换、双向文件传输及接收远端文本剪贴板。
|
||||
2. 完善iOS远程操作体验,提供相对位置与绝对位置鼠标模式、捏合缩放及悬浮控制菜单,支持查看连接状态、P2P/中继模式、分辨率与码率;修复切回前台后画面无法恢复、相对鼠标跳动及灵动岛遮挡控件等问题。
|
||||
3. 新增帧率优先、画质优先和平衡三种画面偏好,优化带宽变化时的分辨率、帧率与码率调节,提升远程桌面的清晰度和流畅度。
|
||||
4. 优化Windows与macOS原生视频采集、硬件编码和渲染流程,减少画面拷贝;Linux新增CUDA/OpenGL视频渲染,提升硬件解码画面的显示效率。
|
||||
5. 优化P2P连接建立与UPnP支持,支持连接从中继升级为P2P,并改进ICE连接、视频恢复及音视频时间戳处理,提升弱网和网络变化时的连接稳定性。
|
||||
6. 修复Windows锁屏界面无法远程控制,以及部分设备因OpenGL初始化失败导致启动崩溃的问题,优化锁屏画面采集与恢复。
|
||||
7. 修复桌面端与iOS端设备在线状态更新不及时、桌面端启动后托盘图标未显示的问题,并将连接清理移出界面线程,减少断开连接时的卡顿。
|
||||
8. 完善iOS与桌面端跨平台构建和打包流程,更新界面截图、构建指南、自托管说明及常见问题文档。
|
||||
|
||||
PC客户端版本为v1.5.0,iOS/iPadOS客户端使用独立版本号,本次为v0.0.1。iOS/iPadOS客户端要求iOS 16.0及以上版本和arm64真机,提供的构建包为未签名应用,需要自行签名后安装;目前仅支持作为控制端连接电脑。
|
||||
@@ -1,10 +0,0 @@
|
||||
# **更新内容**
|
||||
|
||||
1. 新增Windows、macOS与Linux X11隐私屏,支持遮蔽被控端本地画面、屏蔽本地键鼠输入,并在断开连接或异常时自动恢复;远程会话保持正常操作。
|
||||
2. 增强桌面端与iOS端P2P穿透能力,优化UDP打洞、ICE连接建立与带宽恢复,支持使用服务器下发的连接配置,并改善加密传输与旧版客户端的兼容性。
|
||||
3. 新增Windows屏幕采集方式设置,支持DXGI、WGC与GDI;修复锁屏、隐私屏及未连接物理鼠标时远程光标不可见、位置异常和拖动窗口时出现重复光标的问题。
|
||||
4. 优化画面缩小时的显示清晰度、帧率与分辨率恢复,修复Windows、macOS与Linux系统音频采集可能卡住的问题,提升远程画面与音频的稳定性。
|
||||
5. 修复Linux X11远程键盘输入与macOS窗口缩放光标识别,改进鼠标操作和光标状态传输的可靠性;优化控制栏提示、网络状态布局与加密状态显示,并修复部分平台的Dock及任务栏图标显示问题。
|
||||
6. 完善iOS/iPadOS连接失败提示,保留密码错误、设备不可用和网络中断等具体原因,简化服务器配置并优化原生客户端构建流程。
|
||||
|
||||
PC客户端版本为v1.5.1,iOS/iPadOS客户端暂未上架App Store,目前仅支持作为控制端连接远端电脑。隐私屏需控制端与被控端均支持,暂不支持Web及旧版客户端;macOS要求14.0及以上版本,Linux仅支持X11会话。
|
||||
Reference in New Issue
Block a user