[feat] add support for self-hosted server configuration

This commit is contained in:
dijunkun
2025-10-18 23:41:10 +08:00
parent adfe14809f
commit 5590aaeb1e
32 changed files with 4030 additions and 242 deletions

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

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
/*
* @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
#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
#define SETTINGS_WINDOW_HEIGHT_CN 315
#define SETTINGS_WINDOW_HEIGHT_EN 315
#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 165
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN 165
#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_SRTP_CHECKBOX_PADDING_CN 171
#define ENABLE_SRTP_CHECKBOX_PADDING_EN 218
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_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
#endif

View File

@@ -0,0 +1,160 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-05-29
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _LOCALIZATION_H_
#define _LOCALIZATION_H_
#include <string>
#include <vector>
namespace localization {
static std::vector<std::string> local_desktop = {
reinterpret_cast<const char*>(u8"本桌面"), "Local Desktop"};
static std::vector<std::string> local_id = {
reinterpret_cast<const char*>(u8"本机ID"), "Local ID"};
static std::vector<std::string> local_id_copied_to_clipboard = {
reinterpret_cast<const char*>(u8"已复制到剪贴板"), "Copied to clipboard"};
static std::vector<std::string> password = {
reinterpret_cast<const char*>(u8"密码"), "Password"};
static std::vector<std::string> max_password_len = {
reinterpret_cast<const char*>(u8"最大6个字符"), "Max 6 chars"};
static std::vector<std::string> remote_desktop = {
reinterpret_cast<const char*>(u8"控制远程桌面"), "Control Remote Desktop"};
static std::vector<std::string> remote_id = {
reinterpret_cast<const char*>(u8"对端ID"), "Remote ID"};
static std::vector<std::string> connect = {
reinterpret_cast<const char*>(u8"连接"), "Connect"};
static std::vector<std::string> recent_connections = {
reinterpret_cast<const char*>(u8"近期连接"), "Recent Connections"};
static std::vector<std::string> disconnect = {
reinterpret_cast<const char*>(u8"断开连接"), "Disconnect"};
static std::vector<std::string> fullscreen = {
reinterpret_cast<const char*>(u8"全屏"), " Fullscreen"};
static std::vector<std::string> show_net_traffic_stats = {
reinterpret_cast<const char*>(u8"显示流量统计"), "Show Net Traffic Stats"};
static std::vector<std::string> hide_net_traffic_stats = {
reinterpret_cast<const char*>(u8"隐藏流量统计"), "Hide Net Traffic Stats"};
static std::vector<std::string> video = {
reinterpret_cast<const char*>(u8"视频"), "Video"};
static std::vector<std::string> audio = {
reinterpret_cast<const char*>(u8"音频"), "Audio"};
static std::vector<std::string> data = {reinterpret_cast<const char*>(u8"数据"),
"Data"};
static std::vector<std::string> total = {
reinterpret_cast<const char*>(u8"总计"), "Total"};
static std::vector<std::string> in = {reinterpret_cast<const char*>(u8"输入"),
"In"};
static std::vector<std::string> out = {reinterpret_cast<const char*>(u8"输出"),
"Out"};
static std::vector<std::string> loss_rate = {
reinterpret_cast<const char*>(u8"丢包率"), "Loss Rate"};
static std::vector<std::string> exit_fullscreen = {
reinterpret_cast<const char*>(u8"退出全屏"), "Exit fullscreen"};
static std::vector<std::string> control_mouse = {
reinterpret_cast<const char*>(u8"控制"), "Control"};
static std::vector<std::string> release_mouse = {
reinterpret_cast<const char*>(u8"释放"), "Release"};
static std::vector<std::string> audio_capture = {
reinterpret_cast<const char*>(u8"声音"), "Audio"};
static std::vector<std::string> mute = {
reinterpret_cast<const char*>(u8" 静音"), " Mute"};
static std::vector<std::string> settings = {
reinterpret_cast<const char*>(u8"设置"), "Settings"};
static std::vector<std::string> language = {
reinterpret_cast<const char*>(u8"语言:"), "Language:"};
static std::vector<std::string> language_zh = {
reinterpret_cast<const char*>(u8"中文"), "Chinese"};
static std::vector<std::string> language_en = {
reinterpret_cast<const char*>(u8"英文"), "English"};
static std::vector<std::string> video_quality = {
reinterpret_cast<const char*>(u8"视频质量:"), "Video Quality:"};
static std::vector<std::string> video_frame_rate = {
reinterpret_cast<const char*>(u8"画面采集帧率:"),
"Video Capture Frame Rate:"};
static std::vector<std::string> video_quality_high = {
reinterpret_cast<const char*>(u8""), "High"};
static std::vector<std::string> video_quality_medium = {
reinterpret_cast<const char*>(u8""), "Medium"};
static std::vector<std::string> video_quality_low = {
reinterpret_cast<const char*>(u8""), "Low"};
static std::vector<std::string> video_encode_format = {
reinterpret_cast<const char*>(u8"视频编码格式:"), "Video Encode Format:"};
static std::vector<std::string> av1 = {reinterpret_cast<const char*>(u8"AV1"),
"AV1"};
static std::vector<std::string> h264 = {
reinterpret_cast<const char*>(u8"H.264"), "H.264"};
static std::vector<std::string> enable_hardware_video_codec = {
reinterpret_cast<const char*>(u8"启用硬件编解码器:"),
"Enable Hardware Video Codec:"};
static std::vector<std::string> enable_turn = {
reinterpret_cast<const char*>(u8"启用中继服务:"), "Enable TURN Service:"};
static std::vector<std::string> enable_srtp = {
reinterpret_cast<const char*>(u8"启用SRTP:"), "Enable SRTP:"};
static std::vector<std::string> self_hosted_server_config = {
reinterpret_cast<const char*>(u8"自托管服务器配置"),
"Self-Hosted Server Config"};
static std::vector<std::string> self_hosted_server_settings = {
reinterpret_cast<const char*>(u8"自托管服务器设置"),
"Self-Hosted Server Settings"};
static std::vector<std::string> self_hosted_server_address = {
reinterpret_cast<const char*>(u8"服务器地址:"), "Server Address:"};
static std::vector<std::string> self_hosted_server_port = {
reinterpret_cast<const char*>(u8"服务器端口:"), "Server Port:"};
static std::vector<std::string> self_hosted_server_certificate_path = {
reinterpret_cast<const char*>(u8"证书文件路径:"), "Certificate File Path:"};
static std::vector<std::string> select_a_file = {
reinterpret_cast<const char*>(u8"请选择文件"), "Please select a file"};
static std::vector<std::string> ok = {reinterpret_cast<const char*>(u8"确认"),
"OK"};
static std::vector<std::string> cancel = {
reinterpret_cast<const char*>(u8"取消"), "Cancel"};
static std::vector<std::string> new_password = {
reinterpret_cast<const char*>(u8"请输入六位密码:"),
"Please input a six-char password:"};
static std::vector<std::string> input_password = {
reinterpret_cast<const char*>(u8"请输入密码:"), "Please input password:"};
static std::vector<std::string> validate_password = {
reinterpret_cast<const char*>(u8"验证密码中..."), "Validate password ..."};
static std::vector<std::string> reinput_password = {
reinterpret_cast<const char*>(u8"请重新输入密码"),
"Please input password again"};
static std::vector<std::string> remember_password = {
reinterpret_cast<const char*>(u8"记住密码"), "Remember password"};
static std::vector<std::string> signal_connected = {
reinterpret_cast<const char*>(u8"已连接服务器"), "Connected"};
static std::vector<std::string> signal_disconnected = {
reinterpret_cast<const char*>(u8"未连接服务器"), "Disconnected"};
static std::vector<std::string> p2p_connected = {
reinterpret_cast<const char*>(u8"对等连接已建立"), "P2P Connected"};
static std::vector<std::string> p2p_disconnected = {
reinterpret_cast<const char*>(u8"对等连接已断开"), "P2P Disconnected"};
static std::vector<std::string> p2p_connecting = {
reinterpret_cast<const char*>(u8"正在建立对等连接..."),
"P2P Connecting ..."};
static std::vector<std::string> p2p_failed = {
reinterpret_cast<const char*>(u8"对等连接失败"), "P2P Failed"};
static std::vector<std::string> p2p_closed = {
reinterpret_cast<const char*>(u8"对等连接已关闭"), "P2P closed"};
static std::vector<std::string> no_such_id = {
reinterpret_cast<const char*>(u8"无此ID"), "No such ID"};
static std::vector<std::string> about = {
reinterpret_cast<const char*>(u8"关于"), "About"};
static std::vector<std::string> version = {
reinterpret_cast<const char*>(u8"版本"), "Version"};
static std::vector<std::string> confirm_delete_connection = {
reinterpret_cast<const char*>(u8"确认删除此连接"),
"Confirm to delete this connection"};
} // namespace localization
#endif

View File

@@ -1,17 +0,0 @@
#ifdef _WIN32
#ifdef DESK_PORT_DEBUG
#pragma comment(linker, "/subsystem:\"console\"")
#else
#pragma comment(linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"")
#endif
#endif
#include "rd_log.h"
#include "render.h"
int main([[maybe_unused]] int argc, [[maybe_unused]] char *argv[]) {
Render render;
render.Run();
return 0;
}

View File

@@ -0,0 +1,290 @@
#include <random>
#include "layout.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
int Render::LocalWindow() {
ImGui::SetNextWindowPos(ImVec2(-1.0f, title_bar_height_), ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::BeginChild("LocalDesktopWindow",
ImVec2(local_window_width_, local_window_height_),
ImGuiChildFlags_None,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleColor();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + main_window_text_y_padding_);
ImGui::Indent(main_child_window_x_padding_);
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(main_child_window_x_padding_,
title_bar_height_ + main_child_window_y_padding_),
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(
"LocalDesktopWindow_1",
ImVec2(local_child_window_width_, local_child_window_height_),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoTitleBar |
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(IPUT_WINDOW_WIDTH);
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(22, 38))) {
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) {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
notification_window_width_) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
notification_window_height_) /
2));
ImGui::SetNextWindowSize(
ImVec2(notification_window_width_, notification_window_height_));
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, 5.0f);
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.5f);
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(IPUT_WINDOW_WIDTH);
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(ICON_FA_EYE, ImVec2(22, 38))) {
show_password_ = !show_password_;
}
if (!show_password_) {
ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->AddLine(ImVec2(l_x + 3.0f, l_y + 12.5f),
ImVec2(l_x + 20.3f, l_y + 26.5f),
IM_COL32(239, 240, 242, 255), 2.0f);
draw_list->AddLine(ImVec2(l_x + 3.0f, l_y + 11.0f),
ImVec2(l_x + 20.3f, l_y + 25.0f),
IM_COL32(0, 0, 0, 255), 1.5f);
}
ImGui::SameLine();
if (ImGui::Button(ICON_FA_PEN, ImVec2(22, 38))) {
show_reset_password_window_ = true;
}
ImGui::SetWindowFontScale(1.0f);
ImGui::PopStyleColor(3);
if (show_reset_password_window_) {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
connection_status_window_width_) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
connection_status_window_height_) /
2));
ImGui::SetNextWindowSize(ImVec2(connection_status_window_width_,
connection_status_window_height_));
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::Begin("ResetPasswordWindow", nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
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 - IPUT_WINDOW_WIDTH / 2) * 0.5f);
ImGui::SetCursorPosY(window_height * 0.4f);
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH / 2);
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';
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';
SaveSettingsIntoCacheFile();
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;
}

View File

@@ -0,0 +1,286 @@
#include "localization.h"
#include "rd_log.h"
#include "render.h"
int Render::RecentConnectionsWindow() {
ImGui::SetNextWindowPos(
ImVec2(0, title_bar_height_ + local_window_height_ - 1.0f),
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(main_window_width_default_,
main_window_height_default_ - title_bar_height_ -
local_window_height_ - status_bar_height_ + 1.0f),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleVar();
ImGui::PopStyleColor();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + main_window_text_y_padding_);
ImGui::Indent(main_child_window_x_padding_);
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 Render::ShowRecentConnections() {
ImGui::SetCursorPosX(25.0f);
ImVec2 sub_window_pos = ImGui::GetCursorPos();
std::map<std::string, ImVec2> sub_containers_pos;
float recent_connection_sub_container_width =
recent_connection_image_width_ + 16.0f;
float recent_connection_sub_container_height =
recent_connection_image_height_ + 36.0f;
ImGui::PushStyleColor(ImGuiCol_ChildBg,
ImVec4(239.0f / 255, 240.0f / 255, 242.0f / 255, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
ImGui::BeginChild("RecentConnectionsContainer",
ImVec2(main_window_width_default_ - 50.0f, 145.0f),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_AlwaysHorizontalScrollbar |
ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse);
ImGui::PopStyleVar();
ImGui::PopStyleColor();
size_t recent_connections_count = recent_connections_.size();
int count = 0;
float button_width = 22;
float button_height = 22;
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_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoScrollbar);
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
if ('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");
continue;
}
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 ('N' == connection_info[9] && connection_info.size() >= 10) {
size_t pos_n = connection_info.find('N');
size_t pos_at = connection_info.find('@');
if (pos_n == std::string::npos) {
LOG_ERROR("Invalid filename");
continue;
}
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 {
it.second.remote_host_name = "unknown";
}
ImVec2 image_screen_pos = ImVec2(ImGui::GetCursorScreenPos().x + 5.0f,
ImGui::GetCursorScreenPos().y + 5.0f);
ImVec2 image_pos =
ImVec2(ImGui::GetCursorPosX() + 5.0f, ImGui::GetCursorPosY() + 5.0f);
ImGui::SetCursorPos(image_pos);
ImGui::Image((ImTextureID)(intptr_t)it.second.texture,
ImVec2((float)recent_connection_image_width_,
(float)recent_connection_image_height_));
// remote id display button
{
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0.2f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0.2f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0, 0, 0, 0.2f));
ImVec2 dummy_button_pos =
ImVec2(image_pos.x, image_pos.y + recent_connection_image_height_);
std::string dummy_button_name = "##DummyButton" + it.second.remote_id;
ImGui::SetCursorPos(dummy_button_pos);
ImGui::SetWindowFontScale(0.6f);
ImGui::Button(dummy_button_name.c_str(),
ImVec2(recent_connection_image_width_ - 2 * button_width,
button_height));
ImGui::SetWindowFontScale(1.0f);
ImGui::SetCursorPos(
ImVec2(dummy_button_pos.x + 2.0f, dummy_button_pos.y + 1.0f));
ImGui::SetWindowFontScale(0.65f);
ImGui::Text("%s", it.second.remote_id.c_str());
ImGui::SetWindowFontScale(1.0f);
ImGui::PopStyleColor(3);
if (ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::SetWindowFontScale(0.5f);
ImGui::Text("%s", it.second.remote_host_name.c_str());
ImGui::SetWindowFontScale(1.0f);
ImGui::EndTooltip();
}
}
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0.2f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered,
ImVec4(0.1f, 0.4f, 0.8f, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
ImVec4(1.0f, 1.0f, 1.0f, 0.7f));
ImGui::SetWindowFontScale(0.5f);
// trash button
{
ImVec2 trash_can_button_pos = ImVec2(
image_pos.x + recent_connection_image_width_ - 2 * button_width,
image_pos.y + recent_connection_image_height_);
ImGui::SetCursorPos(trash_can_button_pos);
std::string trash_can = ICON_FA_TRASH_CAN;
std::string recent_connection_delete_button_name =
trash_can + "##RecentConnectionDelete" +
std::to_string(trash_can_button_pos.x);
if (ImGui::Button(recent_connection_delete_button_name.c_str(),
ImVec2(button_width, button_height))) {
show_confirm_delete_connection_ = true;
delete_connection_name_ = it.first;
}
if (delete_connection_ && delete_connection_name_ == it.first) {
if (!thumbnail_->DeleteThumbnail(it.first)) {
reload_recent_connections_ = true;
delete_connection_ = false;
}
}
}
// connect button
{
ImVec2 connect_button_pos =
ImVec2(image_pos.x + recent_connection_image_width_ - button_width,
image_pos.y + recent_connection_image_height_);
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(button_width, button_height))) {
ConnectTo(it.second.remote_id, it.second.password.c_str(),
it.second.remember_password);
}
}
ImGui::SetWindowFontScale(1.0f);
ImGui::PopStyleColor(3);
ImGui::EndChild();
if (count != recent_connections_count - 1) {
ImVec2 line_start =
ImVec2(image_screen_pos.x + recent_connection_image_width_ + 20.0f,
image_screen_pos.y);
ImVec2 line_end = ImVec2(
image_screen_pos.x + recent_connection_image_width_ + 20.0f,
image_screen_pos.y + recent_connection_image_height_ + button_height);
ImGui::GetWindowDrawList()->AddLine(line_start, line_end,
IM_COL32(0, 0, 0, 122), 1.0f);
}
count++;
ImGui::SameLine(0, count != recent_connections_count ? 26.0f : 0.0f);
}
ImGui::EndChild();
if (show_confirm_delete_connection_) {
ConfirmDeleteConnection();
}
return 0;
}
int Render::ConfirmDeleteConnection() {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
ImGui::SetNextWindowPos(ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
connection_status_window_width_) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
connection_status_window_height_) /
2));
ImGui::SetNextWindowSize(ImVec2(connection_status_window_width_,
connection_status_window_height_));
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::Begin("ConfirmDeleteConnectionWindow", nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoSavedSettings);
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
std::string text =
localization::confirm_delete_connection[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 6 / 19);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
// 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 window_width = ImGui::GetWindowSize().x;
auto window_height = ImGui::GetWindowSize().y;
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::SetWindowFontScale(1.0f);
ImGui::End();
ImGui::PopStyleVar();
return 0;
}

View File

@@ -0,0 +1,181 @@
#include "layout.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
static int InputTextCallback(ImGuiInputTextCallbackData *data);
int Render::RemoteWindow() {
ImGui::SetNextWindowPos(ImVec2(local_window_width_ + 1.0f, title_bar_height_),
ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::BeginChild("RemoteDesktopWindow",
ImVec2(remote_window_width_, remote_window_height_),
ImGuiChildFlags_None,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleColor();
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + main_window_text_y_padding_);
ImGui::Indent(main_child_window_x_padding_ - 1.0f);
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(local_window_width_ + main_child_window_x_padding_ - 1.0f,
title_bar_height_ + main_child_window_y_padding_),
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_child_window_width_, remote_child_window_height_),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
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(IPUT_WINDOW_WIDTH);
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(55, 38)) ||
enter_pressed) {
connect_button_pressed_ = true;
bool found = false;
for (auto &[id, props] : recent_connections_) {
if (id.find(remote_id) != std::string::npos) {
found = true;
if (client_properties_.find(remote_id) !=
client_properties_.end()) {
if (!client_properties_[remote_id]->connection_established_) {
ConnectTo(props.remote_id, props.password.c_str(), false);
} else {
// todo: show warning message
LOG_INFO("Already connected to [{}]", remote_id);
}
} else {
ConnectTo(props.remote_id, props.password.c_str(), false);
}
}
}
if (!found) {
ConnectTo(remote_id, "", false);
}
}
if (need_to_rejoin_) {
need_to_rejoin_ = false;
for (const auto &[_, props] : client_properties_) {
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;
}
int Render::ConnectTo(const std::string &remote_id, const char *password,
bool remember_password) {
LOG_INFO("Connect to [{}]", remote_id);
focused_remote_id_ = remote_id;
if (client_properties_.find(remote_id) == client_properties_.end()) {
client_properties_[remote_id] =
std::make_shared<SubStreamWindowProperties>();
auto props = client_properties_[remote_id];
props->local_id_ = "C-" + std::string(client_id_);
props->remote_id_ = remote_id;
memcpy(&props->params_, &params_, sizeof(Params));
props->params_.user_id = props->local_id_.c_str();
props->peer_ = CreatePeer(&props->params_);
AddAudioStream(props->peer_, props->audio_label_.c_str());
AddDataStream(props->peer_, props->data_label_.c_str());
if (props->peer_) {
LOG_INFO("[{}] Create peer instance successful", props->local_id_);
Init(props->peer_);
LOG_INFO("[{}] Peer init finish", props->local_id_);
} else {
LOG_INFO("Create peer [{}] instance failed", props->local_id_);
}
props->connection_status_ = ConnectionStatus::Connecting;
}
int ret = -1;
auto props = client_properties_[remote_id];
if (!props->connection_established_) {
props->remember_password_ = remember_password;
if (strcmp(password, "") != 0 &&
strcmp(password, props->remote_password_) != 0) {
strncpy(props->remote_password_, password,
sizeof(props->remote_password_) - 1);
props->remote_password_[sizeof(props->remote_password_) - 1] = '\0';
}
std::string remote_id_with_pwd = remote_id + "@" + password;
ret = JoinConnection(props->peer_, remote_id_with_pwd.c_str());
if (0 == ret) {
props->rejoin_ = false;
} else {
props->rejoin_ = true;
need_to_rejoin_ = true;
}
}
return 0;
}

1396
src/gui/render.cpp Normal file

File diff suppressed because it is too large Load Diff

462
src/gui/render.h Normal file
View File

@@ -0,0 +1,462 @@
/*
* @Author: DI JUNKUN
* @Date: 2024-05-29
* Copyright (c) 2024 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _MAIN_WINDOW_H_
#define _MAIN_WINDOW_H_
#include <SDL3/SDL.h>
#include <atomic>
#include <chrono>
#include <fstream>
#include <mutex>
#include <optional>
#include <string>
#include <unordered_map>
#include "IconsFontAwesome6.h"
#include "config_center.h"
#include "device_controller_factory.h"
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_sdlrenderer3.h"
#include "imgui_internal.h"
#include "minirtc.h"
#include "path_manager.h"
#include "screen_capturer_factory.h"
#include "speaker_capturer_factory.h"
#include "thumbnail.h"
class Render {
public:
struct SubStreamWindowProperties {
Params params_;
PeerPtr* peer_ = nullptr;
std::string audio_label_ = "control_audio";
std::string data_label_ = "control_data";
std::string local_id_ = "";
std::string remote_id_ = "";
bool exit_ = false;
bool signal_connected_ = false;
SignalStatus signal_status_ = SignalStatus::SignalClosed;
bool connection_established_ = false;
bool rejoin_ = false;
bool net_traffic_stats_button_pressed_ = false;
bool mouse_control_button_pressed_ = false;
bool mouse_controller_is_started_ = false;
bool audio_capture_button_pressed_ = false;
bool control_mouse_ = false;
bool streaming_ = false;
bool is_control_bar_in_left_ = true;
bool control_bar_hovered_ = false;
bool display_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_ = 230;
float control_window_min_height_ = 40;
float control_window_max_height_ = 170;
float control_window_width_ = 230;
float control_window_height_ = 40;
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;
unsigned char* dst_buffer_ = nullptr;
size_t dst_buffer_capacity_ = 0;
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;
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_ = "";
std::vector<DisplayInfo> display_info_list_;
SDL_Texture* stream_texture_ = nullptr;
uint8_t* argb_buffer_ = nullptr;
int argb_buffer_size_ = 0;
SDL_Rect stream_render_rect_;
SDL_Rect stream_render_rect_last_;
ImVec2 control_window_pos_;
ConnectionStatus connection_status_ = ConnectionStatus::Closed;
TraversalMode traversal_mode_ = TraversalMode::UnknownMode;
int fps_ = 0;
int frame_count_ = 0;
std::chrono::steady_clock::time_point last_time_;
XNetTrafficStats net_traffic_stats_;
};
public:
Render();
~Render();
public:
int Run();
private:
void InitializeLogger();
void InitializeSettings();
void InitializeSDL();
void InitializeModules();
void InitializeMainWindow();
void MainLoop();
void UpdateLabels();
void UpdateInteractions();
void HandleRecentConnections();
void HandleStreamWindow();
void Cleanup();
void CleanupFactories();
void CleanupPeer(std::shared_ptr<SubStreamWindowProperties> props);
void CleanupPeers();
void CleanSubStreamWindowProperties(
std::shared_ptr<SubStreamWindowProperties> props);
void UpdateRenderRect();
void ProcessSdlEvent(const SDL_Event& event);
private:
int CreateStreamRenderWindow();
int TitleBar(bool main_window);
int MainWindow();
int StreamWindow();
int LocalWindow();
int RemoteWindow();
int RecentConnectionsWindow();
int SettingWindow();
int SelfHostedServerWindow();
int ShowSimpleFileBrowser();
int ControlWindow(std::shared_ptr<SubStreamWindowProperties>& props);
int ControlBar(std::shared_ptr<SubStreamWindowProperties>& props);
int AboutWindow();
int StatusBar();
bool ConnectionStatusWindow(
std::shared_ptr<SubStreamWindowProperties>& props);
int ShowRecentConnections();
private:
int ConnectTo(const std::string& remote_id, const char* password,
bool remember_password);
int CreateMainWindow();
int DestroyMainWindow();
int CreateStreamWindow();
int DestroyStreamWindow();
int SetupFontAndStyle();
int SetupMainWindow();
int DestroyMainWindowContext();
int SetupStreamWindow();
int DestroyStreamWindowContext();
int DrawMainWindow();
int DrawStreamWindow();
int ConfirmDeleteConnection();
int NetTrafficStats(std::shared_ptr<SubStreamWindowProperties>& props);
void DrawConnectionStatusText(
std::shared_ptr<SubStreamWindowProperties>& props);
public:
static void OnReceiveVideoBufferCb(const XVideoFrame* video_frame,
const char* user_id, size_t user_id_size,
void* user_data);
static void OnReceiveAudioBufferCb(const char* data, size_t size,
const char* user_id, size_t user_id_size,
void* user_data);
static void OnReceiveDataBufferCb(const char* data, size_t size,
const char* user_id, size_t user_id_size,
void* user_data);
static void OnSignalStatusCb(SignalStatus status, const char* user_id,
size_t user_id_size, void* user_data);
static void OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
size_t user_id_size, void* user_data);
static void NetStatusReport(const char* client_id, size_t client_id_size,
TraversalMode mode,
const XNetTrafficStats* net_traffic_stats,
const char* user_id, const size_t user_id_size,
void* user_data);
static SDL_HitTestResult HitTestCallback(SDL_Window* window,
const SDL_Point* area, void* data);
static std::vector<char> SerializeRemoteAction(const RemoteAction& action);
static bool DeserializeRemoteAction(const char* data, size_t size,
RemoteAction& out);
static void FreeRemoteAction(RemoteAction& action);
private:
int SendKeyCommand(int key_code, bool is_down);
int ProcessMouseEvent(const SDL_Event& event);
static void SdlCaptureAudioIn(void* userdata, Uint8* stream, int len);
static void SdlCaptureAudioOut(void* userdata, Uint8* stream, int len);
private:
int SaveSettingsIntoCacheFile();
int LoadSettingsFromCacheFile();
int ScreenCapturerInit();
int StartScreenCapturer();
int StopScreenCapturer();
int StartSpeakerCapturer();
int StopSpeakerCapturer();
int StartMouseController();
int StopMouseController();
int StartKeyboardCapturer();
int StopKeyboardCapturer();
int CreateConnectionPeer();
int AudioDeviceInit();
int AudioDeviceDestroy();
private:
struct CDCache {
char client_id_with_password[17];
int language;
int video_quality;
int video_frame_rate;
int video_encode_format;
bool enable_hardware_video_codec;
bool enable_turn;
bool enable_srtp;
unsigned char key[16];
unsigned char iv[16];
};
private:
CDCache cd_cache_;
std::mutex cd_cache_mutex_;
std::unique_ptr<ConfigCenter> config_center_;
ConfigCenter::LANGUAGE localization_language_ =
ConfigCenter::LANGUAGE::CHINESE;
std::unique_ptr<PathManager> path_manager_;
std::string cert_path_;
std::string exec_log_path_;
std::string dll_log_path_;
std::string cache_path_;
int localization_language_index_ = -1;
int localization_language_index_last_ = -1;
bool modules_inited_ = false;
/* ------ all windows property start ------ */
float title_bar_width_ = 640;
float title_bar_height_ = 30;
/* ------ all windows property end ------ */
/* ------ main window property start ------ */
// thumbnail
unsigned char aes128_key_[16];
unsigned char aes128_iv_[16];
std::unique_ptr<Thumbnail> thumbnail_;
// recent connections
std::vector<std::pair<std::string, Thumbnail::RecentConnection>>
recent_connections_;
int recent_connection_image_width_ = 160;
int recent_connection_image_height_ = 90;
uint32_t recent_connection_image_save_time_ = 0;
// main window render
SDL_Window* main_window_ = nullptr;
SDL_Renderer* main_renderer_ = nullptr;
ImGuiContext* main_ctx_ = nullptr;
bool exit_ = false;
const int sdl_refresh_ms_ = 16; // ~60 FPS
// main window properties
bool start_mouse_controller_ = false;
bool mouse_controller_is_started_ = false;
bool start_screen_capturer_ = false;
bool screen_capturer_is_started_ = false;
bool start_keyboard_capturer_ = false;
bool keyboard_capturer_is_started_ = false;
bool foucs_on_main_window_ = false;
bool foucs_on_stream_window_ = false;
bool audio_capture_ = 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 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_ = 200;
float about_window_height_ = 150;
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;
bool audio_buffer_fresh_ = false;
bool need_to_rejoin_ = false;
bool just_created_ = false;
std::string controlled_remote_id_ = "";
std::string focused_remote_id_ = "";
bool need_to_send_host_info_ = false;
SDL_Event last_mouse_event;
SDL_AudioStream* output_stream_;
uint32_t STREAM_REFRESH_EVENT = 0;
// stream window render
SDL_Window* stream_window_ = nullptr;
SDL_Renderer* stream_renderer_ = nullptr;
ImGuiContext* stream_ctx_ = nullptr;
// stream window properties
bool need_to_create_stream_window_ = false;
bool stream_window_created_ = false;
bool stream_window_inited_ = false;
bool window_maximized_ = false;
bool stream_window_grabbed_ = 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;
SDL_PixelFormat stream_pixformat_ = SDL_PIXELFORMAT_NV12;
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;
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 fullscreen_button_pressed_ = false;
bool focus_on_input_widget_ = true;
bool is_client_mode_ = false;
bool reload_recent_connections_ = true;
bool show_confirm_delete_connection_ = false;
bool delete_connection_ = false;
bool is_tab_bar_hovered_ = false;
std::string delete_connection_name_ = "";
bool re_enter_remote_id_ = false;
double copy_start_time_ = 0;
SignalStatus signal_status_ = SignalStatus::SignalClosed;
std::string signal_status_str_ = "";
bool signal_connected_ = false;
PeerPtr* peer_ = nullptr;
PeerPtr* peer_reserved_ = nullptr;
std::string video_primary_label_ = "primary_display";
std::string video_secondary_label_ = "secondary_display";
std::string audio_label_ = "audio";
std::string data_label_ = "data";
Params params_;
SDL_AudioDeviceID input_dev_;
SDL_AudioDeviceID output_dev_;
ScreenCapturerFactory* screen_capturer_factory_ = nullptr;
ScreenCapturer* screen_capturer_ = nullptr;
SpeakerCapturerFactory* speaker_capturer_factory_ = nullptr;
SpeakerCapturer* speaker_capturer_ = nullptr;
DeviceControllerFactory* device_controller_factory_ = nullptr;
MouseController* mouse_controller_ = nullptr;
KeyboardCapturer* keyboard_capturer_ = nullptr;
std::vector<DisplayInfo> display_info_list_;
uint64_t last_frame_time_;
char client_id_[10] = "";
char client_id_display_[12] = "";
char client_id_with_password_[17] = "";
char password_saved_[7] = "";
int language_button_value_ = 0;
int video_quality_button_value_ = 0;
int video_frame_rate_button_value_ = 0;
int video_encode_format_button_value_ = 0;
bool enable_hardware_video_codec_ = false;
bool enable_turn_ = false;
bool enable_srtp_ = false;
char signal_server_ip_[256] = "api.crossdesk.cn";
char signal_server_port_[6] = "9099";
char cert_file_path_[256] = "";
bool enable_self_hosted_server_ = false;
int language_button_value_last_ = 0;
int video_quality_button_value_last_ = 0;
int video_encode_format_button_value_last_ = 0;
bool enable_hardware_video_codec_last_ = false;
bool enable_turn_last_ = false;
bool enable_srtp_last_ = false;
char signal_server_ip_tmp_[256] = "api.crossdesk.cn";
char signal_server_port_tmp_[6] = "9099";
bool settings_window_pos_reset_ = true;
bool self_hosted_server_config_window_pos_reset_ = true;
std::string selected_current_file_path_ = "";
std::string selected_file_ = "";
/* ------ main window property end ------ */
/* ------ sub stream window property start ------ */
std::unordered_map<std::string, std::shared_ptr<SubStreamWindowProperties>>
client_properties_;
void CloseTab(decltype(client_properties_)::iterator& it);
/* ------ stream window property end ------ */
};
#endif

545
src/gui/render_callback.cpp Normal file
View File

@@ -0,0 +1,545 @@
#include "device_controller.h"
#include "localization.h"
#include "platform.h"
#include "rd_log.h"
#include "render.h"
#define NV12_BUFFER_SIZE 1280 * 720 * 3 / 2
#ifdef DESK_PORT_DEBUG
#else
#define MOUSE_CONTROL 1
#endif
int Render::SendKeyCommand(int key_code, bool is_down) {
RemoteAction remote_action;
remote_action.type = ControlType::keyboard;
if (is_down) {
remote_action.k.flag = KeyFlag::key_down;
} else {
remote_action.k.flag = KeyFlag::key_up;
}
remote_action.k.key_value = key_code;
if (!controlled_remote_id_.empty()) {
if (client_properties_.find(controlled_remote_id_) !=
client_properties_.end()) {
auto props = client_properties_[controlled_remote_id_];
if (props->connection_status_ == ConnectionStatus::Connected) {
SendDataFrame(props->peer_, (const char*)&remote_action,
sizeof(remote_action), props->data_label_.c_str());
}
}
}
return 0;
}
int Render::ProcessMouseEvent(const SDL_Event& event) {
controlled_remote_id_ = "";
int video_width, video_height = 0;
int render_width, render_height = 0;
float ratio_x, ratio_y = 0;
RemoteAction remote_action;
for (auto& it : client_properties_) {
auto props = it.second;
if (!props->control_mouse_) {
continue;
}
if (event.button.x >= props->stream_render_rect_.x &&
event.button.x <=
props->stream_render_rect_.x + props->stream_render_rect_.w &&
event.button.y >= props->stream_render_rect_.y &&
event.button.y <=
props->stream_render_rect_.y + props->stream_render_rect_.h) {
controlled_remote_id_ = it.first;
render_width = props->stream_render_rect_.w;
render_height = props->stream_render_rect_.h;
last_mouse_event.button.x = event.button.x;
last_mouse_event.button.y = event.button.y;
remote_action.m.x =
(float)(event.button.x - props->stream_render_rect_.x) / render_width;
remote_action.m.y =
(float)(event.button.y - props->stream_render_rect_.y) /
render_height;
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 (props->control_bar_hovered_ || props->display_selectable_hovered_) {
remote_action.m.flag = MouseFlag::move;
}
SendDataFrame(props->peer_, (const char*)&remote_action,
sizeof(remote_action), props->data_label_.c_str());
} else if (SDL_EVENT_MOUSE_WHEEL == event.type &&
last_mouse_event.button.x >= props->stream_render_rect_.x &&
last_mouse_event.button.x <= props->stream_render_rect_.x +
props->stream_render_rect_.w &&
last_mouse_event.button.y >= props->stream_render_rect_.y &&
last_mouse_event.button.y <= props->stream_render_rect_.y +
props->stream_render_rect_.h) {
int scroll_x = event.wheel.x;
int 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;
if (scroll_x == 0) {
remote_action.m.flag = MouseFlag::wheel_vertical;
remote_action.m.s = scroll_y;
} else if (scroll_y == 0) {
remote_action.m.flag = MouseFlag::wheel_horizontal;
remote_action.m.s = scroll_x;
}
render_width = props->stream_render_rect_.w;
render_height = props->stream_render_rect_.h;
remote_action.m.x =
(float)(event.button.x - props->stream_render_rect_.x) / render_width;
remote_action.m.y =
(float)(event.button.y - props->stream_render_rect_.y) /
render_height;
SendDataFrame(props->peer_, (const char*)&remote_action,
sizeof(remote_action), props->data_label_.c_str());
}
}
return 0;
}
void Render::SdlCaptureAudioIn(void* userdata, Uint8* stream, int len) {
Render* render = (Render*)userdata;
if (!render) {
return;
}
if (1) {
for (auto it : render->client_properties_) {
auto props = it.second;
if (props->connection_status_ == ConnectionStatus::Connected) {
SendAudioFrame(props->peer_, (const char*)stream, len,
render->audio_label_.c_str());
}
}
} else {
memcpy(render->audio_buffer_, stream, len);
render->audio_len_ = len;
SDL_Delay(10);
render->audio_buffer_fresh_ = true;
}
}
void Render::SdlCaptureAudioOut([[maybe_unused]] void* userdata,
[[maybe_unused]] Uint8* stream,
[[maybe_unused]] int len) {
// Render *render = (Render *)userdata;
// for (auto it : render->client_properties_) {
// auto props = it.second;
// if (props->connection_status_ == SignalStatus::SignalConnected) {
// SendAudioFrame(props->peer_, (const char *)stream, len);
// }
// }
// if (!render->audio_buffer_fresh_) {
// return;
// }
// SDL_memset(stream, 0, len);
// if (render->audio_len_ == 0) {
// return;
// } else {
// }
// len = (len > render->audio_len_ ? render->audio_len_ : len);
// SDL_MixAudioFormat(stream, render->audio_buffer_, AUDIO_S16LSB, len,
// SDL_MIX_MAXVOLUME);
// render->audio_buffer_fresh_ = false;
}
void Render::OnReceiveVideoBufferCb(const XVideoFrame* video_frame,
const char* user_id, size_t user_id_size,
void* user_data) {
Render* render = (Render*)user_data;
if (!render) {
return;
}
std::string remote_id(user_id, user_id_size);
if (render->client_properties_.find(remote_id) ==
render->client_properties_.end()) {
return;
}
SubStreamWindowProperties* props =
render->client_properties_.find(remote_id)->second.get();
if (props->connection_established_) {
if (!props->dst_buffer_) {
props->dst_buffer_capacity_ = video_frame->size;
props->dst_buffer_ = new unsigned char[video_frame->size];
}
if (props->dst_buffer_capacity_ < video_frame->size) {
delete props->dst_buffer_;
props->dst_buffer_capacity_ = video_frame->size;
props->dst_buffer_ = new unsigned char[video_frame->size];
}
memcpy(props->dst_buffer_, video_frame->data, video_frame->size);
bool need_to_update_render_rect = false;
if (props->video_width_ != props->video_width_last_ ||
props->video_height_ != props->video_height_last_) {
need_to_update_render_rect = true;
props->video_width_last_ = props->video_width_;
props->video_height_last_ = props->video_height_;
}
props->video_width_ = video_frame->width;
props->video_height_ = video_frame->height;
props->video_size_ = video_frame->size;
if (need_to_update_render_rect) {
render->UpdateRenderRect();
}
SDL_Event event;
event.type = render->STREAM_REFRESH_EVENT;
event.user.data1 = props;
SDL_PushEvent(&event);
props->streaming_ = true;
if (props->net_traffic_stats_button_pressed_) {
props->frame_count_++;
auto now = std::chrono::steady_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
now - props->last_time_)
.count();
if (elapsed >= 1000) {
props->fps_ = props->frame_count_ * 1000 / elapsed;
props->frame_count_ = 0;
props->last_time_ = now;
}
}
}
}
void Render::OnReceiveAudioBufferCb(const char* data, size_t size,
const char* user_id, size_t user_id_size,
void* user_data) {
Render* render = (Render*)user_data;
if (!render) {
return;
}
render->audio_buffer_fresh_ = true;
if (render->output_stream_) {
int pushed = SDL_PutAudioStreamData(
render->output_stream_, (const Uint8*)data, static_cast<int>(size));
if (pushed < 0) {
LOG_ERROR("Failed to push audio data: {}", SDL_GetError());
}
}
}
void Render::OnReceiveDataBufferCb(const char* data, size_t size,
const char* user_id, size_t user_id_size,
void* user_data) {
Render* render = (Render*)user_data;
if (!render) {
return;
}
RemoteAction remote_action;
memcpy(&remote_action, data, size);
std::string remote_id(user_id, user_id_size);
if (render->client_properties_.find(remote_id) !=
render->client_properties_.end()) {
// local
auto props = render->client_properties_.find(remote_id)->second;
RemoteAction host_info;
if (DeserializeRemoteAction(data, size, host_info)) {
if (ControlType::host_infomation == host_info.type &&
props->remote_host_name_.empty()) {
props->remote_host_name_ =
std::string(host_info.i.host_name, host_info.i.host_name_size);
LOG_INFO("Remote hostname: [{}]", props->remote_host_name_);
for (int i = 0; i < host_info.i.display_num; i++) {
props->display_info_list_.push_back(DisplayInfo(
std::string(host_info.i.display_list[i]), host_info.i.left[i],
host_info.i.top[i], host_info.i.right[i], host_info.i.bottom[i]));
LOG_INFO("Remote display [{}:{}], bound [({}, {}) ({}, {})]", i + 1,
props->display_info_list_[i].name,
props->display_info_list_[i].left,
props->display_info_list_[i].top,
props->display_info_list_[i].right,
props->display_info_list_[i].bottom);
}
}
} else {
props->remote_host_name_ = std::string(remote_action.i.host_name,
remote_action.i.host_name_size);
LOG_INFO("Remote hostname: [{}]", props->remote_host_name_);
LOG_ERROR("No remote display detected");
}
FreeRemoteAction(host_info);
} else {
// remote
if (ControlType::mouse == remote_action.type && render->mouse_controller_) {
render->mouse_controller_->SendMouseCommand(remote_action,
render->selected_display_);
} else if (ControlType::audio_capture == remote_action.type) {
if (remote_action.a) {
render->StartSpeakerCapturer();
render->audio_capture_ = true;
} else {
render->StopSpeakerCapturer();
render->audio_capture_ = false;
}
} else if (ControlType::keyboard == remote_action.type &&
render->keyboard_capturer_) {
render->keyboard_capturer_->SendKeyboardCommand(
(int)remote_action.k.key_value,
remote_action.k.flag == KeyFlag::key_down);
} else if (ControlType::display_id == remote_action.type) {
if (render->screen_capturer_) {
render->selected_display_ = remote_action.d;
render->screen_capturer_->SwitchTo(remote_action.d);
}
}
}
}
void Render::OnSignalStatusCb(SignalStatus status, const char* user_id,
size_t user_id_size, void* user_data) {
Render* render = (Render*)user_data;
if (!render) {
return;
}
std::string client_id(user_id, user_id_size);
if (client_id == render->client_id_) {
render->signal_status_ = status;
if (SignalStatus::SignalConnecting == status) {
render->signal_connected_ = false;
} else if (SignalStatus::SignalConnected == status) {
render->signal_connected_ = true;
LOG_INFO("[{}] connected to signal server", client_id);
} else if (SignalStatus::SignalFailed == status) {
render->signal_connected_ = false;
} else if (SignalStatus::SignalClosed == status) {
render->signal_connected_ = false;
} else if (SignalStatus::SignalReconnecting == status) {
render->signal_connected_ = false;
} else if (SignalStatus::SignalServerClosed == status) {
render->signal_connected_ = false;
}
} else {
if (client_id.rfind("C-", 0) != 0) {
return;
}
std::string remote_id(client_id.begin() + 2, client_id.end());
if (render->client_properties_.find(remote_id) ==
render->client_properties_.end()) {
return;
}
auto props = render->client_properties_.find(remote_id)->second;
props->signal_status_ = status;
if (SignalStatus::SignalConnecting == status) {
props->signal_connected_ = false;
} else if (SignalStatus::SignalConnected == status) {
props->signal_connected_ = true;
LOG_INFO("[{}] connected to signal server", remote_id);
} else if (SignalStatus::SignalFailed == status) {
props->signal_connected_ = false;
} else if (SignalStatus::SignalClosed == status) {
props->signal_connected_ = false;
} else if (SignalStatus::SignalReconnecting == status) {
props->signal_connected_ = false;
} else if (SignalStatus::SignalServerClosed == status) {
props->signal_connected_ = false;
}
}
}
void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
const size_t user_id_size, void* user_data) {
Render* render = (Render*)user_data;
if (!render) return;
std::string remote_id(user_id, user_id_size);
auto it = render->client_properties_.find(remote_id);
auto props = (it != render->client_properties_.end()) ? it->second : nullptr;
if (props) {
render->is_client_mode_ = true;
render->show_connection_status_window_ = true;
props->connection_status_ = status;
switch (status) {
case ConnectionStatus::Connected:
if (!render->need_to_create_stream_window_ &&
!render->client_properties_.empty()) {
render->need_to_create_stream_window_ = true;
}
props->connection_established_ = true;
props->stream_render_rect_ = {
0, (int)render->title_bar_height_,
(int)render->stream_window_width_,
(int)(render->stream_window_height_ - render->title_bar_height_)};
break;
case ConnectionStatus::Disconnected:
case ConnectionStatus::Failed:
case ConnectionStatus::Closed:
render->password_validating_time_ = 0;
render->start_screen_capturer_ = false;
render->start_mouse_controller_ = false;
render->start_keyboard_capturer_ = false;
render->control_mouse_ = false;
props->connection_established_ = false;
props->mouse_control_button_pressed_ = false;
if (props->dst_buffer_) {
memset(props->dst_buffer_, 0, props->dst_buffer_capacity_);
SDL_UpdateTexture(props->stream_texture_, NULL, props->dst_buffer_,
props->texture_width_);
}
render->CleanSubStreamWindowProperties(props);
break;
case ConnectionStatus::IncorrectPassword:
render->password_validating_ = false;
render->password_validating_time_++;
if (render->connect_button_pressed_) {
render->connect_button_pressed_ = false;
props->connection_established_ = false;
render->connect_button_label_ =
localization::connect[render->localization_language_index_];
}
break;
case ConnectionStatus::NoSuchTransmissionId:
if (render->connect_button_pressed_) {
props->connection_established_ = false;
render->connect_button_label_ =
localization::connect[render->localization_language_index_];
}
break;
default:
break;
}
} else {
render->is_client_mode_ = false;
render->show_connection_status_window_ = true;
switch (status) {
case ConnectionStatus::Connected:
render->need_to_send_host_info_ = true;
render->start_screen_capturer_ = true;
render->start_mouse_controller_ = true;
break;
case ConnectionStatus::Closed:
render->start_screen_capturer_ = false;
render->start_mouse_controller_ = false;
render->start_keyboard_capturer_ = false;
render->need_to_send_host_info_ = false;
if (props) props->connection_established_ = false;
if (render->audio_capture_) {
render->StopSpeakerCapturer();
render->audio_capture_ = false;
}
break;
default:
break;
}
}
}
void Render::NetStatusReport(const char* client_id, size_t client_id_size,
TraversalMode mode,
const XNetTrafficStats* net_traffic_stats,
const char* user_id, const size_t user_id_size,
void* user_data) {
Render* render = (Render*)user_data;
if (!render) {
return;
}
if (strchr(client_id, '@') != nullptr && strchr(user_id, '-') == nullptr) {
std::string id, password;
const char* at_pos = strchr(client_id, '@');
if (at_pos == nullptr) {
id = client_id;
password.clear();
} else {
id.assign(client_id, at_pos - client_id);
password = at_pos + 1;
}
memset(&render->client_id_, 0, sizeof(render->client_id_));
strncpy(render->client_id_, id.c_str(), sizeof(render->client_id_) - 1);
render->client_id_[sizeof(render->client_id_) - 1] = '\0';
memset(&render->password_saved_, 0, sizeof(render->password_saved_));
strncpy(render->password_saved_, password.c_str(),
sizeof(render->password_saved_) - 1);
render->password_saved_[sizeof(render->password_saved_) - 1] = '\0';
memset(&render->client_id_with_password_, 0,
sizeof(render->client_id_with_password_));
strncpy(render->client_id_with_password_, client_id,
sizeof(render->client_id_with_password_) - 1);
render->client_id_with_password_[sizeof(render->client_id_with_password_) -
1] = '\0';
LOG_INFO("Use client id [{}] and save id into cache file", id);
render->SaveSettingsIntoCacheFile();
}
std::string remote_id(user_id, user_id_size);
if (render->client_properties_.find(remote_id) ==
render->client_properties_.end()) {
return;
}
auto props = render->client_properties_.find(remote_id)->second;
if (props->traversal_mode_ != mode) {
props->traversal_mode_ = mode;
LOG_INFO("Net mode: [{}]", int(props->traversal_mode_));
}
if (!net_traffic_stats) {
return;
}
// only display client side net status if connected to itself
if (!(render->peer_reserved_ && !strstr(client_id, "C-"))) {
props->net_traffic_stats_ = *net_traffic_stats;
}
}

View File

@@ -0,0 +1,326 @@
#include "layout.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
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;
}
int Render::ControlBar(std::shared_ptr<SubStreamWindowProperties>& props) {
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
if (props->control_bar_expand_) {
ImGui::SetCursorPosX(props->is_control_bar_in_left_
? (props->control_window_width_ + 5.0f)
: 38.0f);
// mouse control button
ImDrawList* draw_list = ImGui::GetWindowDrawList();
if (props->is_control_bar_in_left_) {
draw_list->AddLine(ImVec2(ImGui::GetCursorScreenPos().x - 5.0f,
ImGui::GetCursorScreenPos().y - 7.0f),
ImVec2(ImGui::GetCursorScreenPos().x - 5.0f,
ImGui::GetCursorScreenPos().y - 7.0f +
props->control_window_height_),
IM_COL32(178, 178, 178, 255), 1.0f);
}
std::string display = ICON_FA_DISPLAY;
if (ImGui::Button(display.c_str(), ImVec2(25, 25))) {
ImGui::OpenPopup("display");
}
ImVec2 btn_min = ImGui::GetItemRectMin();
ImVec2 btn_size_actual = ImGui::GetItemRectSize();
if (ImGui::BeginPopup("display")) {
ImGui::SetWindowFontScale(0.5f);
for (int i = 0; i < props->display_info_list_.size(); i++) {
if (ImGui::Selectable(props->display_info_list_[i].name.c_str())) {
props->selected_display_ = i;
RemoteAction remote_action;
remote_action.type = ControlType::display_id;
remote_action.d = i;
if (props->connection_status_ == ConnectionStatus::Connected) {
SendDataFrame(props->peer_, (const char*)&remote_action,
sizeof(remote_action), props->data_label_.c_str());
}
}
props->display_selectable_hovered_ = ImGui::IsWindowHovered();
}
ImGui::SetWindowFontScale(1.0f);
ImGui::EndPopup();
}
ImGui::SetWindowFontScale(0.6f);
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.5f,
btn_min.y + (btn_size_actual.y - text_size.y) * 0.5f - 2.0f);
ImGui::GetWindowDrawList()->AddText(
text_pos, IM_COL32(0, 0, 0, 255),
std::to_string(props->selected_display_ + 1).c_str());
ImGui::SetWindowFontScale(1.0f);
ImGui::SameLine();
float disable_mouse_x = ImGui::GetCursorScreenPos().x + 4.0f;
float disable_mouse_y = ImGui::GetCursorScreenPos().y + 4.0f;
std::string mouse = props->mouse_control_button_pressed_
? ICON_FA_COMPUTER_MOUSE
: ICON_FA_COMPUTER_MOUSE;
if (ImGui::Button(mouse.c_str(), ImVec2(25, 25))) {
if (props->connection_established_) {
start_keyboard_capturer_ = !start_keyboard_capturer_;
props->control_mouse_ = !props->control_mouse_;
props->mouse_control_button_pressed_ =
!props->mouse_control_button_pressed_;
props->mouse_control_button_label_ =
props->mouse_control_button_pressed_
? localization::release_mouse[localization_language_index_]
: localization::control_mouse[localization_language_index_];
}
}
if (!props->mouse_control_button_pressed_) {
draw_list->AddLine(
ImVec2(disable_mouse_x, disable_mouse_y),
ImVec2(disable_mouse_x + 16.0f, disable_mouse_y + 14.2f),
IM_COL32(0, 0, 0, 255), 2.0f);
draw_list->AddLine(
ImVec2(disable_mouse_x - 1.2f, disable_mouse_y + 1.2f),
ImVec2(disable_mouse_x + 15.3f, disable_mouse_y + 15.4f),
ImGui::IsItemHovered() ? IM_COL32(66, 150, 250, 255)
: IM_COL32(179, 213, 253, 255),
2.0f);
}
ImGui::SameLine();
// audio capture button
float disable_audio_x = ImGui::GetCursorScreenPos().x + 4;
float disable_audio_y = ImGui::GetCursorScreenPos().y + 4.0f;
// std::string audio = audio_capture_button_pressed_ ? ICON_FA_VOLUME_HIGH
// :
// ICON_FA_VOLUME_XMARK;
std::string audio = props->audio_capture_button_pressed_
? ICON_FA_VOLUME_HIGH
: ICON_FA_VOLUME_HIGH;
if (ImGui::Button(audio.c_str(), ImVec2(25, 25))) {
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_;
SendDataFrame(props->peer_, (const char*)&remote_action,
sizeof(remote_action), props->data_label_.c_str());
}
}
if (!props->audio_capture_button_pressed_) {
draw_list->AddLine(
ImVec2(disable_audio_x, disable_audio_y),
ImVec2(disable_audio_x + 16.0f, disable_audio_y + 14.2f),
IM_COL32(0, 0, 0, 255), 2.0f);
draw_list->AddLine(
ImVec2(disable_audio_x - 1.2f, disable_audio_y + 1.2f),
ImVec2(disable_audio_x + 15.3f, disable_audio_y + 15.4f),
ImGui::IsItemHovered() ? IM_COL32(66, 150, 250, 255)
: IM_COL32(179, 213, 253, 255),
2.0f);
}
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;
if (ImGui::Button(net_traffic_stats.c_str(), ImVec2(25, 25))) {
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_];
}
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;
if (ImGui::Button(fullscreen.c_str(), ImVec2(25, 25))) {
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;
}
ImGui::SameLine();
// close button
std::string close_button = ICON_FA_XMARK;
if (ImGui::Button(close_button.c_str(), ImVec2(25, 25))) {
CleanupPeer(props);
}
ImGui::SameLine();
if (!props->is_control_bar_in_left_) {
draw_list->AddLine(ImVec2(ImGui::GetCursorScreenPos().x - 3.0f,
ImGui::GetCursorScreenPos().y - 7.0f),
ImVec2(ImGui::GetCursorScreenPos().x - 3.0f,
ImGui::GetCursorScreenPos().y - 7.0f +
props->control_window_height_),
IM_COL32(178, 178, 178, 255), 1.0f);
}
}
ImGui::SetCursorPosX(props->is_control_bar_in_left_
? (props->control_window_width_ * 2 - 20.0f)
: 5.0f);
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);
if (ImGui::Button(control_bar.c_str(), ImVec2(15, 25))) {
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_ = 40;
props->net_traffic_stats_button_pressed_ = false;
}
}
if (props->net_traffic_stats_button_pressed_ && props->control_bar_expand_) {
NetTrafficStats(props);
}
ImGui::PopStyleVar();
return 0;
}
int Render::NetTrafficStats(std::shared_ptr<SubStreamWindowProperties>& props) {
ImGui::SetCursorPos(ImVec2(props->is_control_bar_in_left_
? (props->control_window_width_ + 5.0f)
: 5.0f,
40.0f));
if (ImGui::BeginTable("NetTrafficStats", 4, ImGuiTableFlags_BordersH,
ImVec2(props->control_window_max_width_ - 10.0f,
props->control_window_max_height_ - 60.0f))) {
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::EndTable();
}
return 0;
}

View File

@@ -0,0 +1,38 @@
#include "localization.h"
#include "render.h"
int Render::StatusBar() {
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
static bool a, b, c, d, e;
ImGui::SetNextWindowPos(
ImVec2(0, main_window_height_default_ - status_bar_height_ - 1),
ImGuiCond_Always);
ImGui::BeginChild(
"StatusBar", ImVec2(main_window_width_, status_bar_height_ + 1),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoBringToFrontOnFocus);
ImVec2 dot_pos =
ImVec2(13, main_window_height_default_ - status_bar_height_ + 11.0f);
ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->AddCircleFilled(dot_pos, 5.0f,
ImColor(signal_connected_ ? 0.0f : 1.0f,
signal_connected_ ? 1.0f : 0.0f, 0.0f),
100);
draw_list->AddCircle(dot_pos, 6.0f, ImColor(1.0f, 1.0f, 1.0f), 100);
ImGui::SetWindowFontScale(0.6f);
draw_list->AddText(
ImVec2(25, main_window_height_default_ - status_bar_height_ + 3.0f),
ImColor(0.0f, 0.0f, 0.0f),
signal_connected_
? localization::signal_connected[localization_language_index_].c_str()
: localization::signal_disconnected[localization_language_index_]
.c_str());
ImGui::SetWindowFontScale(1.0f);
ImGui::PopStyleColor();
ImGui::EndChild();
return 0;
}

View File

@@ -0,0 +1,166 @@
#include "localization.h"
#include "rd_log.h"
#include "render.h"
#define BUTTON_PADDING 36.0f
int Render::TitleBar(bool main_window) {
ImGui::PushStyleColor(ImGuiCol_MenuBarBg, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::SetWindowFontScale(0.8f);
ImGui::BeginChild(
main_window ? "MainTitleBar" : "StreamTitleBar",
ImVec2(main_window ? main_window_width_ : stream_window_width_,
title_bar_height_),
ImGuiChildFlags_Border,
ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::SetWindowFontScale(1.0f);
ImGui::PopStyleColor();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
if (ImGui::BeginMenuBar()) {
ImGui::SetCursorPosX(
(main_window ? main_window_width_ : stream_window_width_) -
(BUTTON_PADDING * 3 - 3));
ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0, 0, 0, 0.1f));
ImGui::PushStyleColor(ImGuiCol_HeaderActive,
ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
if (main_window) {
float bar_pos_x = ImGui::GetCursorPosX() + 6;
float bar_pos_y = ImGui::GetCursorPosY() + 15;
std::string menu_button = " "; // ICON_FA_BARS;
if (ImGui::BeginMenu(menu_button.c_str())) {
ImGui::SetWindowFontScale(0.5f);
if (ImGui::MenuItem(
localization::settings[localization_language_index_].c_str())) {
show_settings_window_ = true;
}
if (ImGui::MenuItem(
localization::about[localization_language_index_].c_str())) {
show_about_window_ = true;
}
ImGui::SetWindowFontScale(1.0f);
ImGui::EndMenu();
}
float menu_bar_line_size = 15.0f;
draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y - 6),
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y - 6),
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 + 6),
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y + 6),
IM_COL32(0, 0, 0, 255));
{
SettingWindow();
SelfHostedServerWindow();
AboutWindow();
}
}
ImGui::PopStyleColor(2);
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::SetCursorPosX(main_window
? (main_window_width_ - BUTTON_PADDING * 2)
: (stream_window_width_ - BUTTON_PADDING * 3));
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 = ImGui::GetCursorPosX() + 12;
float minimize_pos_y = ImGui::GetCursorPosY() + 15;
std::string window_minimize_button = "##minimize"; // ICON_FA_MINUS;
if (ImGui::Button(window_minimize_button.c_str(),
ImVec2(BUTTON_PADDING, 30))) {
SDL_MinimizeWindow(main_window ? main_window_ : stream_window_);
}
draw_list->AddLine(ImVec2(minimize_pos_x, minimize_pos_y),
ImVec2(minimize_pos_x + 12, minimize_pos_y),
IM_COL32(0, 0, 0, 255));
ImGui::PopStyleColor(2);
if (!main_window) {
ImGui::SetCursorPosX(stream_window_width_ - BUTTON_PADDING * 2);
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 = ImGui::GetCursorPosX() + 11;
float pos_y_top = ImGui::GetCursorPosY() + 11;
float pos_x_bottom = ImGui::GetCursorPosX() + 13;
float pos_y_bottom = ImGui::GetCursorPosY() + 9;
std::string window_restore_button =
"##restore"; // ICON_FA_WINDOW_RESTORE;
if (ImGui::Button(window_restore_button.c_str(),
ImVec2(BUTTON_PADDING, 30))) {
SDL_RestoreWindow(stream_window_);
window_maximized_ = false;
}
draw_list->AddRect(ImVec2(pos_x_top, pos_y_top),
ImVec2(pos_x_top + 12, pos_y_top + 12),
IM_COL32(0, 0, 0, 255));
draw_list->AddRect(ImVec2(pos_x_bottom, pos_y_bottom),
ImVec2(pos_x_bottom + 12, pos_y_bottom + 12),
IM_COL32(0, 0, 0, 255));
draw_list->AddRectFilled(ImVec2(pos_x_top + 1, pos_y_top + 1),
ImVec2(pos_x_top + 11, pos_y_top + 11),
IM_COL32(255, 255, 255, 255));
} else {
float maximize_pos_x = ImGui::GetCursorPosX() + 12;
float maximize_pos_y = ImGui::GetCursorPosY() + 10;
std::string window_maximize_button =
"##maximize"; // ICON_FA_SQUARE_FULL;
if (ImGui::Button(window_maximize_button.c_str(),
ImVec2(BUTTON_PADDING, 30))) {
SDL_MaximizeWindow(stream_window_);
window_maximized_ = !window_maximized_;
}
draw_list->AddRect(ImVec2(maximize_pos_x, maximize_pos_y),
ImVec2(maximize_pos_x + 12, maximize_pos_y + 12),
IM_COL32(0, 0, 0, 255));
}
ImGui::PopStyleColor(2);
}
ImGui::SetCursorPosX(
(main_window ? main_window_width_ : stream_window_width_) -
BUTTON_PADDING);
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 = ImGui::GetCursorPosX() + 18;
float xmark_pos_y = ImGui::GetCursorPosY() + 16;
float xmark_size = 12.0f;
std::string close_button = "##xmark"; // ICON_FA_XMARK;
if (ImGui::Button(close_button.c_str(), ImVec2(BUTTON_PADDING, 30))) {
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(2);
ImGui::PopStyleColor();
}
ImGui::EndMenuBar();
ImGui::EndChild();
ImGui::PopStyleColor();
return 0;
}

View File

@@ -0,0 +1,55 @@
#include "layout.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
int Render::AboutWindow() {
if (show_about_window_) {
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, 5.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
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 RD_VERSION
version = RD_VERSION;
#else
version = "Unknown";
#endif
std::string text =
localization::version[localization_language_index_] + ": " + version;
ImGui::Text("%s", text.c_str());
ImGui::SetCursorPosX(about_window_width_ * 0.42f);
ImGui::SetCursorPosY(about_window_height_ * 0.75f);
// 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;
}

View File

@@ -0,0 +1,171 @@
#include "layout.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
bool Render::ConnectionStatusWindow(
std::shared_ptr<SubStreamWindowProperties> &props) {
const ImGuiViewport *viewport = ImGui::GetMainViewport();
bool ret_flag = false;
ImGui::SetNextWindowPos(ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
connection_status_window_width_) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
connection_status_window_height_) /
2));
ImGui::SetNextWindowSize(ImVec2(connection_status_window_width_,
connection_status_window_height_));
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0, 1.0, 1.0, 1.0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 1.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::Begin("ConnectionStatusWindow", nullptr,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoSavedSettings);
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
ImGui::SetWindowFontScale(0.5f);
std::string text;
if (ConnectionStatus::Connecting == props->connection_status_) {
text = localization::p2p_connecting[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
} else if (ConnectionStatus::Connected == props->connection_status_) {
text = localization::p2p_connected[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
// 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 == props->connection_status_) {
text = localization::p2p_disconnected[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
// 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 == props->connection_status_) {
text = localization::p2p_failed[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
// 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 == props->connection_status_) {
text = localization::p2p_closed[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
// 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 == props->connection_status_) {
if (!password_validating_) {
if (password_validating_time_ == 1) {
text = localization::input_password[localization_language_index_];
} else {
text = localization::reinput_password[localization_language_index_];
}
auto window_width = ImGui::GetWindowSize().x;
auto window_height = ImGui::GetWindowSize().y;
ImGui::SetCursorPosX((window_width - IPUT_WINDOW_WIDTH / 2) * 0.5f);
ImGui::SetCursorPosY(window_height * 0.4f);
ImGui::SetNextItemWidth(IPUT_WINDOW_WIDTH / 2);
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((window_width - text_size.x) * 0.5f - 13.0f);
ImGui::Checkbox(
localization::remember_password[localization_language_index_].c_str(),
&(props->remember_password_));
ImGui::SetWindowFontScale(0.5f);
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()) ||
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_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
}
} else if (ConnectionStatus::NoSuchTransmissionId ==
props->connection_status_) {
text = localization::no_such_id[localization_language_index_];
ImGui::SetCursorPosX(connection_status_window_width_ * 3 / 7);
ImGui::SetCursorPosY(connection_status_window_height_ * 2 / 3);
// 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;
}
}
auto window_width = ImGui::GetWindowSize().x;
auto window_height = ImGui::GetWindowSize().y;
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::SetWindowFontScale(1.0f);
ImGui::End();
ImGui::PopStyleVar();
return ret_flag;
}

View File

@@ -0,0 +1,223 @@
#include "rd_log.h"
#include "render.h"
int Render::ControlWindow(std::shared_ptr<SubStreamWindowProperties> &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(1, 1, 1, 1));
ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(0, 0));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 10.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 10.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::SetNextWindowSize(
ImVec2(props->control_window_width_, props->control_window_height_),
ImGuiCond_Always);
ImGui::SetNextWindowPos(ImVec2(0, title_bar_height_ + 1), ImGuiCond_Once);
float pos_x = 0;
float pos_y = 0;
float y_boundary = fullscreen_button_pressed_ ? 0 : (title_bar_height_ + 1);
if (props->reset_control_bar_pos_) {
float new_cursor_pos_x = 0;
float new_cursor_pos_y = 0;
// set control window pos
if (props->control_window_pos_.y + props->control_window_height_ >
stream_window_height_) {
pos_y = stream_window_height_ - props->control_window_height_;
} else if (props->control_window_pos_.y < y_boundary) {
pos_y = y_boundary;
} else {
pos_y = props->control_window_pos_.y;
}
if (props->is_control_bar_in_left_) {
pos_x = 0;
} else {
pos_x = stream_window_width_ - props->control_window_width_;
}
ImGui::SetNextWindowPos(ImVec2(pos_x, pos_y), ImGuiCond_Always);
if (0 != props->mouse_diff_control_bar_pos_x_ &&
0 != props->mouse_diff_control_bar_pos_y_) {
// set cursor pos
new_cursor_pos_x = pos_x + props->mouse_diff_control_bar_pos_x_;
new_cursor_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_) {
if (props->control_window_pos_.x <= stream_window_width_ / 2) {
if (props->control_window_pos_.y + props->control_window_height_ >
stream_window_height_) {
pos_y = stream_window_height_ - props->control_window_height_;
} else {
pos_y = props->control_window_pos_.y;
}
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 (props->control_window_pos_.x > stream_window_width_ / 2) {
pos_x = 0;
pos_y =
(props->control_window_pos_.y >= y_boundary &&
props->control_window_pos_.y <=
stream_window_height_ - props->control_window_height_)
? props->control_window_pos_.y
: (props->control_window_pos_.y < (fullscreen_button_pressed_
? 0
: (title_bar_height_ + 1))
? (fullscreen_button_pressed_ ? 0
: (title_bar_height_ + 1))
: (stream_window_height_ - 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 = stream_window_width_ - props->control_window_max_width_;
} else {
props->control_window_width_is_changing_ = true;
pos_x = stream_window_width_ - 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 = stream_window_width_ - props->control_window_min_width_;
} else {
props->control_window_width_is_changing_ = true;
pos_x = stream_window_width_ - props->control_window_width_;
}
}
props->is_control_bar_in_left_ = false;
}
if (props->control_window_pos_.y + props->control_window_height_ >
stream_window_height_) {
pos_y = stream_window_height_ - props->control_window_height_;
} else if (props->control_window_pos_.y < y_boundary) {
pos_y = y_boundary;
}
ImGui::SetNextWindowPos(ImVec2(pos_x, pos_y), ImGuiCond_Always);
}
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::Begin(control_window_title.c_str(), nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoDocking);
ImGui::PopStyleVar();
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;
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
static bool a, b, c, d, e;
ImGui::SetNextWindowPos(
ImVec2(props->is_control_bar_in_left_
? props->control_window_pos_.x - props->control_window_width_
: props->control_window_pos_.x,
props->control_window_pos_.y),
ImGuiCond_Always);
ImGui::SetWindowFontScale(0.5f);
std::string control_child_window_title =
props->remote_id_ + "ControlChildWindow";
ImGui::BeginChild(
control_child_window_title.c_str(),
ImVec2(props->control_window_width_ * 2, props->control_window_height_),
ImGuiChildFlags_Border, ImGuiWindowFlags_NoDecoration);
ImGui::SetWindowFontScale(1.0f);
ImGui::PopStyleColor();
ControlBar(props);
props->control_bar_hovered_ = ImGui::IsWindowHovered();
ImGui::EndChild();
ImGui::End();
ImGui::PopStyleVar(4);
ImGui::PopStyleColor();
return 0;
}

View File

@@ -0,0 +1,373 @@
#include "layout.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
int Render::SettingWindow() {
if (show_settings_window_) {
if (settings_window_pos_reset_) {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
SETTINGS_WINDOW_WIDTH_CN) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
SETTINGS_WINDOW_HEIGHT_CN) /
2));
ImGui::SetNextWindowSize(
ImVec2(SETTINGS_WINDOW_WIDTH_CN, SETTINGS_WINDOW_HEIGHT_CN));
} else {
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
SETTINGS_WINDOW_WIDTH_EN) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
SETTINGS_WINDOW_HEIGHT_EN) /
2));
ImGui::SetNextWindowSize(
ImVec2(SETTINGS_WINDOW_WIDTH_EN, SETTINGS_WINDOW_HEIGHT_EN));
}
settings_window_pos_reset_ = false;
}
// Settings
{
static int settings_items_padding = 30;
int settings_items_offset = 0;
ImGui::SetWindowFontScale(0.5f);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
ImGui::Begin(localization::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);
{
const char* language_items[] = {
localization::language_zh[localization_language_index_].c_str(),
localization::language_en[localization_language_index_].c_str()};
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::Text(
"%s", localization::language[localization_language_index_].c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(LANGUAGE_SELECT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(LANGUAGE_SELECT_WINDOW_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
ImGui::Combo("##language", &language_button_value_, language_items,
IM_ARRAYSIZE(language_items));
}
ImGui::Separator();
if (stream_window_inited_) {
ImGui::BeginDisabled();
}
{
const char* video_quality_items[] = {
localization::video_quality_high[localization_language_index_]
.c_str(),
localization::video_quality_medium[localization_language_index_]
.c_str(),
localization::video_quality_low[localization_language_index_]
.c_str()};
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::Text(
"%s",
localization::video_quality[localization_language_index_].c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(VIDEO_QUALITY_SELECT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(VIDEO_QUALITY_SELECT_WINDOW_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
ImGui::Combo("##video_quality", &video_quality_button_value_,
video_quality_items, IM_ARRAYSIZE(video_quality_items));
}
ImGui::Separator();
{
const char* video_frame_rate_items[] = {"30 fps", "60 fps"};
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::Text("%s",
localization::video_frame_rate[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(VIDEO_FRAME_RATE_SELECT_WINDOW_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
ImGui::Combo("##video_frame_rate", &video_frame_rate_button_value_,
video_frame_rate_items,
IM_ARRAYSIZE(video_frame_rate_items));
}
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 + 2);
ImGui::Text(
"%s",
localization::video_encode_format[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_CN);
} else {
ImGui::SetCursorPosX(VIDEO_ENCODE_FORMAT_SELECT_WINDOW_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SETTINGS_SELECT_WINDOW_WIDTH);
ImGui::Combo(
"##video_encode_format", &video_encode_format_button_value_,
video_encode_format_items, IM_ARRAYSIZE(video_encode_format_items));
}
ImGui::Separator();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::Text("%s", localization::enable_hardware_video_codec
[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_CN);
} else {
ImGui::SetCursorPosX(ENABLE_HARDWARE_VIDEO_CODEC_CHECKBOX_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::Checkbox("##enable_hardware_video_codec",
&enable_hardware_video_codec_);
}
ImGui::Separator();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::Text(
"%s",
localization::enable_turn[localization_language_index_].c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(ENABLE_TURN_CHECKBOX_PADDING_CN);
} else {
ImGui::SetCursorPosX(ENABLE_TURN_CHECKBOX_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::Checkbox("##enable_turn", &enable_turn_);
}
ImGui::Separator();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::Text(
"%s",
localization::enable_srtp[localization_language_index_].c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(ENABLE_SRTP_CHECKBOX_PADDING_CN);
} else {
ImGui::SetCursorPosX(ENABLE_SRTP_CHECKBOX_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::Checkbox("##enable_srtp", &enable_srtp_);
}
ImGui::Separator();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
if (ImGui::Button(localization::self_hosted_server_config
[localization_language_index_]
.c_str())) {
show_self_hosted_server_config_window_ = true;
}
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN);
} else {
ImGui::SetCursorPosX(ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::Checkbox("##enable_self_hosted_server",
&enable_self_hosted_server_);
}
if (stream_window_inited_) {
ImGui::EndDisabled();
}
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_CN);
} else {
ImGui::SetCursorPosX(SETTINGS_OK_BUTTON_PADDING_EN);
}
settings_items_offset += settings_items_padding + 10;
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
if (language_button_value_ == 0) {
config_center_->SetLanguage(ConfigCenter::LANGUAGE::CHINESE);
} else {
config_center_->SetLanguage(ConfigCenter::LANGUAGE::ENGLISH);
}
language_button_value_last_ = language_button_value_;
localization_language_ = (ConfigCenter::LANGUAGE)language_button_value_;
localization_language_index_ = language_button_value_;
LOG_INFO("Set localization language: {}",
localization_language_index_ == 0 ? "zh" : "en");
// Video quality
if (video_quality_button_value_ == 0) {
config_center_->SetVideoQuality(ConfigCenter::VIDEO_QUALITY::HIGH);
} else if (video_quality_button_value_ == 1) {
config_center_->SetVideoQuality(ConfigCenter::VIDEO_QUALITY::MEDIUM);
} else {
config_center_->SetVideoQuality(ConfigCenter::VIDEO_QUALITY::LOW);
}
video_quality_button_value_last_ = video_quality_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_;
// SRTP
if (enable_srtp_) {
config_center_->SetSrtp(true);
} else {
config_center_->SetSrtp(false);
}
enable_srtp_last_ = enable_srtp_;
if (enable_self_hosted_server_) {
config_center_->SetSelfHosted(true);
} else {
config_center_->SetSelfHosted(false);
}
settings_window_pos_reset_ = true;
// Recreate peer instance
LoadSettingsFromCacheFile();
// Recreate peer instance
if (!stream_window_inited_) {
LOG_INFO("Recreate peer instance");
CleanupPeers();
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_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_;
}
settings_window_pos_reset_ = true;
}
ImGui::SetWindowFontScale(1.0f);
ImGui::SetWindowFontScale(0.5f);
ImGui::End();
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
ImGui::SetWindowFontScale(1.0f);
}
}
return 0;
}

View File

@@ -0,0 +1,49 @@
#include "localization.h"
#include "rd_log.h"
#include "render.h"
int Render::MainWindow() {
ImGui::SetNextWindowPos(ImVec2(0, 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, 1.0f));
ImGui::BeginChild("DeskWindow",
ImVec2(main_window_width_default_, local_window_height_),
ImGuiChildFlags_Border,
ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleVar();
ImGui::PopStyleColor();
LocalWindow();
ImDrawList* draw_list = ImGui::GetWindowDrawList();
draw_list->AddLine(
ImVec2(main_window_width_default_ / 2, title_bar_height_ + 15.0f),
ImVec2(main_window_width_default_ / 2, title_bar_height_ + 225.0f),
IM_COL32(0, 0, 0, 122), 1.0f);
RemoteWindow();
ImGui::EndChild();
RecentConnectionsWindow();
StatusBar();
if (show_connection_status_window_) {
for (auto it = client_properties_.begin();
it != client_properties_.end();) {
auto& props = it->second;
if (focused_remote_id_ == props->remote_id_) {
if (ConnectionStatusWindow(props)) {
it = client_properties_.erase(it);
} else {
++it;
}
} else {
++it;
}
}
}
return 0;
}

View File

@@ -0,0 +1,270 @@
#include <filesystem>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#endif
#include "layout.h"
#include "localization.h"
#include "rd_log.h"
#include "render.h"
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 Render::ShowSimpleFileBrowser() {
std::string display_text;
if (!selected_file_.empty()) {
display_text = std::filesystem::path(selected_file_).filename().string();
} else if (selected_current_file_path_ != "Root") {
display_text =
std::filesystem::path(selected_current_file_path_).filename().string();
if (display_text.empty()) {
display_text = selected_current_file_path_;
}
}
if (display_text.empty()) {
display_text =
localization::select_a_file[localization_language_index_].c_str();
}
if (ImGui::BeginCombo("##select_a_file", display_text.c_str())) {
if (selected_current_file_path_ == "Root" ||
!std::filesystem::exists(selected_current_file_path_) ||
!std::filesystem::is_directory(selected_current_file_path_)) {
auto roots = GetRootEntries();
for (const auto& root : roots) {
if (ImGui::Selectable(root.c_str())) {
selected_current_file_path_ = root;
selected_file_.clear();
}
}
} else {
std::filesystem::path p(selected_current_file_path_);
if (ImGui::Selectable("..")) {
if (p.has_parent_path() && p != p.root_path())
selected_current_file_path_ = p.parent_path().string();
else
selected_current_file_path_ = "Root";
selected_file_.clear();
}
try {
for (const auto& entry :
std::filesystem::directory_iterator(selected_current_file_path_)) {
std::string name = entry.path().filename().string();
if (entry.is_directory()) {
if (ImGui::Selectable(name.c_str())) {
selected_current_file_path_ = entry.path().string();
selected_file_.clear();
}
} else {
if (ImGui::Selectable(name.c_str())) {
selected_file_ = entry.path().string();
}
}
}
} catch (const std::exception& e) {
ImGui::TextColored(ImVec4(1, 0, 0, 1), "Error: %s", e.what());
}
}
ImGui::EndCombo();
}
return 0;
}
int Render::SelfHostedServerWindow() {
if (show_self_hosted_server_config_window_) {
if (self_hosted_server_config_window_pos_reset_) {
const ImGuiViewport* viewport = ImGui::GetMainViewport();
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN) /
2));
ImGui::SetNextWindowSize(
ImVec2(SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN,
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN));
} else {
ImGui::SetNextWindowPos(
ImVec2((viewport->WorkSize.x - viewport->WorkPos.x -
SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN) /
2,
(viewport->WorkSize.y - viewport->WorkPos.y -
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN) /
2));
ImGui::SetNextWindowSize(
ImVec2(SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN,
SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_EN));
}
self_hosted_server_config_window_pos_reset_ = false;
}
// Settings
{
static int settings_items_padding = 30;
int settings_items_offset = 0;
ImGui::SetWindowFontScale(0.5f);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 5.0f);
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 3.0f);
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);
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::Text("%s", localization::self_hosted_server_address
[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_CN);
} else {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
ImGui::InputText("##signal_server_ip_tmp_", signal_server_ip_tmp_,
IM_ARRAYSIZE(signal_server_ip_tmp_),
ImGuiInputTextFlags_AlwaysOverwrite);
}
ImGui::Separator();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::Text(
"%s",
localization::self_hosted_server_port[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN);
} else {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
ImGui::InputText("##signal_server_port_tmp_", signal_server_port_tmp_,
IM_ARRAYSIZE(signal_server_port_tmp_));
}
ImGui::Separator();
{
settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2);
ImGui::Text("%s", localization::self_hosted_server_certificate_path
[localization_language_index_]
.c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN);
} else {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_EN);
}
ImGui::SetCursorPosY(settings_items_offset);
ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
ShowSimpleFileBrowser();
}
if (stream_window_inited_) {
ImGui::EndDisabled();
}
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_CN);
} else {
ImGui::SetCursorPosX(SELF_HOSTED_SERVER_CONFIG_OK_BUTTON_PADDING_EN);
}
settings_items_offset += settings_items_padding + 10;
ImGui::SetCursorPosY(settings_items_offset);
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_tmp_);
config_center_->SetServerPort(atoi(signal_server_port_tmp_));
config_center_->SetCertFilePath(selected_file_);
strncpy(signal_server_ip_, signal_server_ip_tmp_,
sizeof(signal_server_ip_) - 1);
signal_server_ip_[sizeof(signal_server_ip_) - 1] = '\0';
strncpy(signal_server_port_, signal_server_port_tmp_,
sizeof(signal_server_port_) - 1);
signal_server_port_[sizeof(signal_server_port_) - 1] = '\0';
strncpy(cert_file_path_, selected_file_.c_str(),
sizeof(cert_file_path_) - 1);
cert_file_path_[sizeof(cert_file_path_) - 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_tmp_, signal_server_ip_,
sizeof(signal_server_ip_tmp_) - 1);
signal_server_ip_tmp_[sizeof(signal_server_ip_tmp_) - 1] = '\0';
strncpy(signal_server_port_tmp_, signal_server_port_,
sizeof(signal_server_port_tmp_) - 1);
signal_server_port_tmp_[sizeof(signal_server_port_tmp_) - 1] = '\0';
config_center_->SetServerHost(signal_server_ip_tmp_);
config_center_->SetServerPort(atoi(signal_server_port_tmp_));
selected_file_.clear();
}
ImGui::SetWindowFontScale(1.0f);
ImGui::SetWindowFontScale(0.5f);
ImGui::End();
ImGui::PopStyleVar(2);
ImGui::PopStyleColor();
ImGui::SetWindowFontScale(1.0f);
}
}
return 0;
}

View File

@@ -0,0 +1,202 @@
#include "localization.h"
#include "rd_log.h"
#include "render.h"
void Render::DrawConnectionStatusText(
std::shared_ptr<SubStreamWindowProperties>& props) {
std::string text;
switch (props->connection_status_) {
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 Render::CloseTab(decltype(client_properties_)::iterator& it) {
CleanupPeer(it->second);
it = client_properties_.erase(it);
if (client_properties_.empty()) {
SDL_Event event;
event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event);
}
}
int Render::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));
ImGui::Begin("VideoBg", nullptr,
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoDocking);
ImGui::PopStyleColor(2);
ImGui::PopStyleVar();
ImGuiWindowFlags stream_window_flag =
ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoDecoration |
ImGuiWindowFlags_NoBringToFrontOnFocus | ImGuiWindowFlags_NoMove;
if (!fullscreen_button_pressed_) {
ImGui::SetNextWindowPos(ImVec2(20, 0), ImGuiCond_Always);
ImGui::SetNextWindowSize(ImVec2(0, 20), ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 8.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();
for (auto it = client_properties_.begin();
it != client_properties_.end();) {
auto& props = it->second;
if (!props->tab_opened_) {
CloseTab(it);
continue;
}
ImGui::SetWindowFontScale(0.6f);
std::string tab_label =
enable_srtp_
? std::string(ICON_FA_SHIELD_HALVED) + " " + props->remote_id_
: props->remote_id_;
if (ImGui::BeginTabItem(tab_label.c_str(), &props->tab_opened_)) {
props->tab_selected_ = true;
ImGui::SetWindowFontScale(1.0f);
ImGui::SetNextWindowSize(
ImVec2(stream_window_width_, stream_window_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);
focused_remote_id_ = props->remote_id_;
if (!props->peer_) {
it = client_properties_.erase(it);
if (client_properties_.empty()) {
SDL_Event event;
event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event);
}
} else {
DrawConnectionStatusText(props);
++it;
}
ImGui::End();
ImGui::EndTabItem();
} else {
props->tab_selected_ = false;
ImGui::SetWindowFontScale(1.0f);
++it;
}
}
ImGui::EndTabBar();
}
ImGui::End(); // End TabBar
} else {
for (auto it = client_properties_.begin();
it != client_properties_.end();) {
auto& props = it->second;
if (!props->tab_opened_) {
CloseTab(it);
continue;
}
if (props->tab_selected_) {
ImGui::SetNextWindowSize(
ImVec2(stream_window_width_, stream_window_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);
ImGui::End();
if (!props->peer_) {
fullscreen_button_pressed_ = false;
SDL_SetWindowFullscreen(stream_window_, false);
it = client_properties_.erase(it);
if (client_properties_.empty()) {
SDL_Event event;
event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event);
}
} else {
DrawConnectionStatusText(props);
++it;
}
} else {
++it;
}
}
}
// UpdateRenderRect();
ImGui::End(); // End VideoBg
return 0;
}