[refactor] reorganize platform and wire sources

This commit is contained in:
dijunkun
2026-09-01 01:29:30 +08:00
parent fb0ab4f14e
commit 90184f5251
354 changed files with 2837 additions and 2456 deletions
@@ -0,0 +1,514 @@
#include <algorithm>
#include <remote_action.h>
#include <atomic>
#include <chrono>
#include <filesystem>
#include <fstream>
#include <thread>
#include <vector>
#include "application/gui_application.h"
#include "file_transfer.h"
#include "layout.h"
#include "localization.h"
#include "rd_log.h"
#include "tinyfiledialogs.h"
namespace crossdesk {
namespace {
void ShowControlBarTooltip(const std::string &text) {
if (!ImGui::IsItemHovered() || text.empty()) {
return;
}
ImGui::BeginTooltip();
ImGui::SetWindowFontScale(0.5f);
ImGui::Text("%s", text.c_str());
ImGui::SetWindowFontScale(1.0f);
ImGui::EndTooltip();
}
} // namespace
int CountDigits(int number) {
if (number == 0)
return 1;
return (int)std::floor(std::log10(std::abs(number))) + 1;
}
int BitrateDisplay(int bitrate) {
int num_of_digits = CountDigits(bitrate);
if (num_of_digits <= 3) {
ImGui::Text("%d bps", bitrate);
} else if (num_of_digits > 3 && num_of_digits <= 6) {
ImGui::Text("%d kbps", bitrate / 1000);
} else {
ImGui::Text("%.1f mbps", bitrate / 1000000.0f);
}
return 0;
}
int LossRateDisplay(float loss_rate) {
if (loss_rate < 0.01f) {
ImGui::Text("0%%");
} else {
ImGui::Text("%.0f%%", loss_rate * 100);
}
return 0;
}
std::string GuiApplication::OpenFileDialog(std::string title) {
const char *path = tinyfd_openFileDialog(title.c_str(),
"", // default path
0, // number of filters
nullptr, // filters
nullptr, // filter description
0 // no multiple selection
);
return path ? path : "";
}
int GuiApplication::ControlBar(
std::shared_ptr<RemoteSession> &props) {
float button_width = title_bar_height_ * 0.8f;
float button_height = title_bar_height_ * 0.8f;
float line_padding = title_bar_height_ * 0.12f;
float line_thickness = title_bar_height_ * 0.07f;
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, window_rounding_ * 0.5f);
if (props->control_bar_expand_) {
ImGui::SetCursorPosX(props->is_control_bar_in_left_
? props->control_window_width_ * 0.03f
: props->control_window_width_ * 0.17f);
ImDrawList *draw_list = ImGui::GetWindowDrawList();
if (!props->is_control_bar_in_left_) {
draw_list->AddLine(
ImVec2(ImGui::GetCursorScreenPos().x - button_height * 0.5f,
ImGui::GetCursorScreenPos().y + button_height * 0.2f),
ImVec2(ImGui::GetCursorScreenPos().x - button_height * 0.5f,
ImGui::GetCursorScreenPos().y + button_height * 0.8f),
IM_COL32(178, 178, 178, 255), 2.0f);
}
std::string display = ICON_FA_DISPLAY;
ImGui::SetWindowFontScale(0.5f);
if (ImGui::Button(display.c_str(), ImVec2(button_width, button_height))) {
ImGui::OpenPopup("display");
}
ImVec2 btn_min = ImGui::GetItemRectMin();
ImVec2 btn_size_actual = ImGui::GetItemRectSize();
ShowControlBarTooltip(
localization::select_display[localization_language_index_]);
props->display_selectable_hovered_ = false;
if (ImGui::BeginPopup("display")) {
ImGui::SetWindowFontScale(0.5f);
for (int i = 0; i < props->display_info_list_.size(); i++) {
const std::string display_label = localization::FormatDisplayLabel(
static_cast<size_t>(i), props->display_info_list_[i].name,
localization_language_index_);
if (ImGui::Selectable(display_label.c_str())) {
props->selected_display_ = i;
RemoteAction remote_action;
remote_action.type = ControlType::display_id;
remote_action.d = i;
if (props->connection_status_.load() == ConnectionStatus::Connected) {
std::string msg = remote_action.to_json();
SendReliableDataFrame(props->peer_, msg.c_str(), msg.size(),
props->control_data_label_.c_str());
}
}
}
props->display_selectable_hovered_ =
ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
ImGui::EndPopup();
}
ImGui::SetWindowFontScale(0.35f);
ImVec2 text_size = ImGui::CalcTextSize(
std::to_string(props->selected_display_ + 1).c_str());
ImVec2 text_pos =
ImVec2(btn_min.x + (btn_size_actual.x - text_size.x) * 0.55f,
btn_min.y + (btn_size_actual.y - text_size.y) * 0.33f);
ImGui::GetWindowDrawList()->AddText(
text_pos, IM_COL32(0, 0, 0, 255),
std::to_string(props->selected_display_ + 1).c_str());
auto send_service_command = [&](ServiceCommandFlag flag,
const char *log_action) {
if (props->connection_status_.load() == ConnectionStatus::Connected &&
props->peer_) {
RemoteAction remote_action;
remote_action.type = ControlType::service_command;
remote_action.c.flag = flag;
std::string msg = remote_action.to_json();
int ret = SendReliableDataFrame(props->peer_, msg.c_str(), msg.size(),
props->control_data_label_.c_str());
if (ret != 0) {
LOG_WARN("Send {} command failed, remote_id={}, ret={}", log_action,
props->remote_id_, ret);
}
}
};
ImGui::SameLine();
std::string shortcut = ICON_FA_KEYBOARD;
ImGui::SetWindowFontScale(0.5f);
if (ImGui::Button(shortcut.c_str(), ImVec2(button_width, button_height))) {
ImGui::OpenPopup("shortcut");
}
ShowControlBarTooltip(
localization::send_shortcut[localization_language_index_]);
props->shortcut_selectable_hovered_ = false;
if (ImGui::BeginPopup("shortcut")) {
ImGui::SetWindowFontScale(0.5f);
std::string sas_label = "Ctrl+Alt+Del";
std::string lock_label = "Win+L";
if (ImGui::Selectable(sas_label.c_str())) {
send_service_command(ServiceCommandFlag::send_sas, "SAS");
}
if (ImGui::Selectable(lock_label.c_str())) {
send_service_command(ServiceCommandFlag::lock_workstation,
"remote lock");
}
props->shortcut_selectable_hovered_ =
ImGui::IsWindowHovered(ImGuiHoveredFlags_RootAndChildWindows);
ImGui::SetWindowFontScale(1.0f);
ImGui::EndPopup();
}
ImGui::SameLine();
float mouse_x = ImGui::GetCursorScreenPos().x;
float mouse_y = ImGui::GetCursorScreenPos().y;
float disable_mouse_x = mouse_x + line_padding;
float disable_mouse_y = mouse_y + line_padding;
std::string mouse = ICON_FA_COMPUTER_MOUSE;
ImGui::SetWindowFontScale(0.5f);
if (ImGui::Button(mouse.c_str(), ImVec2(button_width, button_height))) {
if (props->connection_established_) {
start_keyboard_capturer_ = !start_keyboard_capturer_;
props->control_mouse_ = !props->control_mouse_;
props->enable_mouse_control_ = !props->enable_mouse_control_;
props->mouse_control_button_label_ =
props->enable_mouse_control_
? localization::release_mouse[localization_language_index_]
: localization::control_mouse[localization_language_index_];
}
}
const bool mouse_button_hovered = ImGui::IsItemHovered();
const std::string mouse_tooltip =
props->enable_mouse_control_
? localization::release_mouse[localization_language_index_]
: localization::control_mouse[localization_language_index_];
ShowControlBarTooltip(mouse_tooltip);
if (!props->enable_mouse_control_) {
draw_list->AddLine(ImVec2(disable_mouse_x, disable_mouse_y),
ImVec2(mouse_x + button_width - line_padding,
mouse_y + button_height - line_padding),
IM_COL32(0, 0, 0, 255), line_thickness);
draw_list->AddLine(
ImVec2(disable_mouse_x - line_thickness * 0.7f,
disable_mouse_y + line_thickness * 0.7f),
ImVec2(mouse_x + button_width - line_padding - line_thickness * 0.7f,
mouse_y + button_height - line_padding +
line_thickness * 0.7f),
mouse_button_hovered ? IM_COL32(66, 150, 250, 255)
: IM_COL32(179, 213, 253, 255),
line_thickness);
}
ImGui::SameLine();
// audio capture button
float audio_x = ImGui::GetCursorScreenPos().x;
float audio_y = ImGui::GetCursorScreenPos().y;
float disable_audio_x = audio_x + line_padding;
float disable_audio_y = audio_y + line_padding;
std::string audio = props->audio_capture_button_pressed_
? ICON_FA_VOLUME_HIGH
: ICON_FA_VOLUME_HIGH;
ImGui::SetWindowFontScale(0.5f);
if (ImGui::Button(audio.c_str(), ImVec2(button_width, button_height))) {
if (props->connection_established_) {
props->audio_capture_button_pressed_ =
!props->audio_capture_button_pressed_;
props->audio_capture_button_label_ =
props->audio_capture_button_pressed_
? localization::audio_capture[localization_language_index_]
: localization::mute[localization_language_index_];
RemoteAction remote_action;
remote_action.type = ControlType::audio_capture;
remote_action.a = props->audio_capture_button_pressed_;
std::string msg = remote_action.to_json();
SendReliableDataFrame(props->peer_, msg.c_str(), msg.size(),
props->control_data_label_.c_str());
}
}
const bool audio_button_hovered = ImGui::IsItemHovered();
const std::string audio_tooltip =
props->audio_capture_button_pressed_
? localization::mute[localization_language_index_]
: localization::audio_capture[localization_language_index_];
ShowControlBarTooltip(audio_tooltip);
if (!props->audio_capture_button_pressed_) {
draw_list->AddLine(ImVec2(disable_audio_x, disable_audio_y),
ImVec2(audio_x + button_width - line_padding,
audio_y + button_height - line_padding),
IM_COL32(0, 0, 0, 255), line_thickness);
draw_list->AddLine(
ImVec2(disable_audio_x - line_thickness * 0.7f,
disable_audio_y + line_thickness * 0.7f),
ImVec2(audio_x + button_width - line_padding - line_thickness * 0.7f,
audio_y + button_height - line_padding +
line_thickness * 0.7f),
audio_button_hovered ? IM_COL32(66, 150, 250, 255)
: IM_COL32(179, 213, 253, 255),
line_thickness);
}
ImGui::SameLine();
std::string open_folder = ICON_FA_FOLDER_OPEN;
if (ImGui::Button(open_folder.c_str(),
ImVec2(button_width, button_height))) {
std::string title =
localization::select_file[localization_language_index_];
std::string path = OpenFileDialog(title);
transfers_.ProcessSelectedFile(path, props, file_label_);
}
ShowControlBarTooltip(
localization::select_file[localization_language_index_]);
ImGui::SameLine();
// net traffic stats button
bool button_color_style_pushed = false;
if (props->net_traffic_stats_button_pressed_) {
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(66 / 255.0f, 150 / 255.0f,
250 / 255.0f, 1.0f));
button_color_style_pushed = true;
}
std::string net_traffic_stats = ICON_FA_SIGNAL;
ImGui::SetWindowFontScale(0.5f);
if (ImGui::Button(net_traffic_stats.c_str(),
ImVec2(button_width, button_height))) {
props->net_traffic_stats_button_pressed_ =
!props->net_traffic_stats_button_pressed_;
props->control_window_height_is_changing_ = true;
props->net_traffic_stats_button_pressed_time_ = ImGui::GetTime();
props->net_traffic_stats_button_label_ =
props->net_traffic_stats_button_pressed_
? localization::hide_net_traffic_stats
[localization_language_index_]
: localization::show_net_traffic_stats
[localization_language_index_];
}
const std::string net_traffic_stats_tooltip =
props->net_traffic_stats_button_pressed_
? localization::hide_net_traffic_stats[localization_language_index_]
: localization::show_net_traffic_stats
[localization_language_index_];
ShowControlBarTooltip(net_traffic_stats_tooltip);
if (button_color_style_pushed) {
ImGui::PopStyleColor();
button_color_style_pushed = false;
}
ImGui::SameLine();
// fullscreen button
std::string fullscreen =
fullscreen_button_pressed_ ? ICON_FA_COMPRESS : ICON_FA_EXPAND;
ImGui::SetWindowFontScale(0.5f);
if (ImGui::Button(fullscreen.c_str(),
ImVec2(button_width, button_height))) {
fullscreen_button_pressed_ = !fullscreen_button_pressed_;
props->fullscreen_button_label_ =
fullscreen_button_pressed_
? localization::exit_fullscreen[localization_language_index_]
: localization::fullscreen[localization_language_index_];
if (fullscreen_button_pressed_) {
SDL_SetWindowFullscreen(stream_window_, true);
} else {
SDL_SetWindowFullscreen(stream_window_, false);
}
props->reset_control_bar_pos_ = true;
}
const std::string fullscreen_tooltip =
fullscreen_button_pressed_
? localization::exit_fullscreen[localization_language_index_]
: localization::fullscreen[localization_language_index_];
ShowControlBarTooltip(fullscreen_tooltip);
ImGui::SameLine();
// close button
std::string close_button = ICON_FA_XMARK;
ImGui::SetWindowFontScale(0.5f);
if (ImGui::Button(close_button.c_str(),
ImVec2(button_width, button_height))) {
CloseRemoteSession(props);
}
ShowControlBarTooltip(
localization::disconnect[localization_language_index_]);
ImGui::SameLine();
if (props->is_control_bar_in_left_) {
draw_list->AddLine(
ImVec2(ImGui::GetCursorScreenPos().x + button_height * 0.13f,
ImGui::GetCursorScreenPos().y + button_height * 0.2f),
ImVec2(ImGui::GetCursorScreenPos().x + button_height * 0.13f,
ImGui::GetCursorScreenPos().y + button_height * 0.8f),
IM_COL32(178, 178, 178, 255), 2.0f);
}
ImGui::SameLine();
}
float expand_button_pos_x = props->control_bar_expand_
? (props->is_control_bar_in_left_
? props->control_window_width_ * 0.917f
: props->control_window_width_ * 0.03f)
: props->control_window_width_ * 0.11f;
ImGui::SetCursorPosX(expand_button_pos_x);
std::string control_bar =
props->control_bar_expand_
? (props->is_control_bar_in_left_ ? ICON_FA_ANGLE_LEFT
: ICON_FA_ANGLE_RIGHT)
: (props->is_control_bar_in_left_ ? ICON_FA_ANGLE_RIGHT
: ICON_FA_ANGLE_LEFT);
const std::string control_bar_tooltip =
props->control_bar_expand_
? localization::collapse_control_bar[localization_language_index_]
: localization::expand_control_bar[localization_language_index_];
if (ImGui::Button(control_bar.c_str(),
ImVec2(button_height * 0.6f, button_height))) {
props->control_bar_expand_ = !props->control_bar_expand_;
props->control_bar_button_pressed_time_ = ImGui::GetTime();
props->control_window_width_is_changing_ = true;
if (!props->control_bar_expand_) {
props->control_window_height_ = props->control_window_min_height_;
props->net_traffic_stats_button_pressed_ = false;
}
}
ShowControlBarTooltip(control_bar_tooltip);
if (props->net_traffic_stats_button_pressed_ && props->control_bar_expand_) {
NetTrafficStats(props);
}
ImGui::PopStyleVar();
return 0;
}
int GuiApplication::NetTrafficStats(
std::shared_ptr<RemoteSession> &props) {
ImGui::SetCursorPos(ImVec2(props->control_window_width_ * 0.048f,
props->control_window_min_height_));
ImGui::SetWindowFontScale(0.5f);
if (ImGui::BeginTable("NetTrafficStats", 4, ImGuiTableFlags_BordersH,
ImVec2(props->control_window_max_width_ * 0.9f,
props->control_window_max_height_ - 0.9f))) {
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthStretch);
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableNextColumn();
ImGui::Text(" ");
ImGui::TableNextColumn();
ImGui::Text("%s", localization::in[localization_language_index_].c_str());
ImGui::TableNextColumn();
ImGui::Text("%s", localization::out[localization_language_index_].c_str());
ImGui::TableNextColumn();
ImGui::Text("%s",
localization::loss_rate[localization_language_index_].c_str());
ImGui::TableNextColumn();
ImGui::Text("%s",
localization::video[localization_language_index_].c_str());
ImGui::TableNextColumn();
BitrateDisplay((int)props->net_traffic_stats_.video_inbound_stats.bitrate);
ImGui::TableNextColumn();
BitrateDisplay((int)props->net_traffic_stats_.video_outbound_stats.bitrate);
ImGui::TableNextColumn();
LossRateDisplay(props->net_traffic_stats_.video_inbound_stats.loss_rate);
ImGui::TableNextColumn();
ImGui::Text("%s",
localization::audio[localization_language_index_].c_str());
ImGui::TableNextColumn();
BitrateDisplay((int)props->net_traffic_stats_.audio_inbound_stats.bitrate);
ImGui::TableNextColumn();
BitrateDisplay((int)props->net_traffic_stats_.audio_outbound_stats.bitrate);
ImGui::TableNextColumn();
LossRateDisplay(props->net_traffic_stats_.audio_inbound_stats.loss_rate);
ImGui::TableNextColumn();
ImGui::Text("%s", localization::data[localization_language_index_].c_str());
ImGui::TableNextColumn();
BitrateDisplay((int)props->net_traffic_stats_.data_inbound_stats.bitrate);
ImGui::TableNextColumn();
BitrateDisplay((int)props->net_traffic_stats_.data_outbound_stats.bitrate);
ImGui::TableNextColumn();
LossRateDisplay(props->net_traffic_stats_.data_inbound_stats.loss_rate);
ImGui::TableNextColumn();
ImGui::Text("%s",
localization::total[localization_language_index_].c_str());
ImGui::TableNextColumn();
BitrateDisplay((int)props->net_traffic_stats_.total_inbound_stats.bitrate);
ImGui::TableNextColumn();
BitrateDisplay((int)props->net_traffic_stats_.total_outbound_stats.bitrate);
ImGui::TableNextColumn();
LossRateDisplay(props->net_traffic_stats_.total_inbound_stats.loss_rate);
ImGui::TableNextColumn();
ImGui::Text("FPS:");
ImGui::TableNextColumn();
ImGui::Text("%d", props->fps_);
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::Text("%s:",
localization::resolution[localization_language_index_].c_str());
ImGui::TableNextColumn();
ImGui::Text("%dx%d", props->video_width_, props->video_height_);
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::TableNextColumn();
ImGui::Text(
"%s:",
localization::connection_mode[localization_language_index_].c_str());
ImGui::TableNextColumn();
ImGui::Text(
"%s",
props->traversal_mode_ == 0
? localization::connection_mode_direct[localization_language_index_]
.c_str()
: localization::connection_mode_relay[localization_language_index_]
.c_str());
ImGui::EndTable();
}
ImGui::SetWindowFontScale(1.0f);
return 0;
}
} // namespace crossdesk
@@ -0,0 +1,56 @@
#include "application/gui_application.h"
#include "layout_relative.h"
#include "localization.h"
namespace crossdesk {
int GuiApplication::StatusBar() {
ImGuiIO &io = ImGui::GetIO();
float status_bar_width = io.DisplaySize.x;
float status_bar_height = io.DisplaySize.y * STATUS_BAR_HEIGHT;
static bool a, b, c, d, e;
ImGui::SetNextWindowPos(ImVec2(0, io.DisplaySize.y * (1 - STATUS_BAR_HEIGHT)),
ImGuiCond_Always);
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
ImGui::BeginChild("StatusBar", ImVec2(status_bar_width, status_bar_height),
ImGuiChildFlags_Borders,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleColor(2);
ImVec2 dot_pos = ImVec2(status_bar_width * 0.025f,
io.DisplaySize.y * (1 - STATUS_BAR_HEIGHT * 0.5f));
ImDrawList *draw_list = ImGui::GetWindowDrawList();
draw_list->AddCircleFilled(dot_pos, status_bar_height * 0.25f,
ImColor(1.0f, 1.0f, 1.0f), 100);
bool tls_cert_error = signal_status_ == SignalStatus::SignalTlsCertError;
draw_list->AddCircleFilled(
dot_pos, status_bar_height * 0.2f,
tls_cert_error ? ImColor(1.0f, 0.65f, 0.0f)
: ImColor(signal_connected_ ? 0.0f : 1.0f,
signal_connected_ ? 1.0f : 0.0f, 0.0f),
100);
ImGui::SetWindowFontScale(0.6f);
const char *signal_status_text =
tls_cert_error
? localization::signal_tls_cert_error[localization_language_index_]
.c_str()
: (signal_connected_
? localization::signal_connected[localization_language_index_]
.c_str()
: localization::signal_disconnected
[localization_language_index_]
.c_str());
draw_list->AddText(ImVec2(status_bar_width * 0.045f,
io.DisplaySize.y * (1 - STATUS_BAR_HEIGHT * 0.9f)),
ImColor(0.0f, 0.0f, 0.0f), signal_status_text);
ImGui::SetWindowFontScale(1.0f);
ImGui::EndChild();
return 0;
}
} // namespace crossdesk
@@ -0,0 +1,329 @@
#include "application/gui_application.h"
#include "layout_relative.h"
#include "localization.h"
#include "rd_log.h"
#include "rounded_corner_button.h"
constexpr double kNewVersionIconBlinkIntervalSec = 2.0;
constexpr double kNewVersionIconBlinkOnTimeSec = 1.0;
namespace crossdesk {
int GuiApplication::TitleBar(bool main_window) {
ImGuiIO &io = ImGui::GetIO();
float title_bar_width = title_bar_width_;
float title_bar_height = title_bar_height_;
float title_bar_height_padding = title_bar_height_;
float title_bar_button_width = title_bar_button_width_;
float title_bar_button_height = title_bar_button_height_;
if (main_window) {
// When the main window is minimized, Dear ImGui may report DisplaySize as
// (0, 0). Do not overwrite shared title-bar metrics with zeros, otherwise
// stream/server windows (which reuse these metrics) will lose their title
// bars and appear collapsed.
if (io.DisplaySize.x > 0.0f && io.DisplaySize.y > 0.0f) {
title_bar_width = io.DisplaySize.x;
title_bar_height = io.DisplaySize.y * TITLE_BAR_HEIGHT;
title_bar_height_padding = io.DisplaySize.y * TITLE_BAR_HEIGHT;
title_bar_button_width = io.DisplaySize.x * TITLE_BAR_BUTTON_WIDTH;
title_bar_button_height = io.DisplaySize.y * TITLE_BAR_BUTTON_HEIGHT;
title_bar_height_ = title_bar_height;
title_bar_button_width_ = title_bar_button_width;
title_bar_button_height_ = title_bar_button_height;
} else {
// Keep using last known good values.
title_bar_width = title_bar_width_;
title_bar_height = title_bar_height_;
title_bar_height_padding = title_bar_height_;
title_bar_button_width = title_bar_button_width_;
title_bar_button_height = title_bar_button_height_;
}
} else {
title_bar_width = io.DisplaySize.x;
title_bar_height = title_bar_button_height_;
title_bar_button_width = title_bar_button_width_;
title_bar_button_height = title_bar_button_height_;
}
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiCond_Always);
ImGui::BeginChild(main_window ? "MainTitleBar" : "StreamTitleBar",
ImVec2(title_bar_width, title_bar_height_padding),
ImGuiChildFlags_Borders,
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoBringToFrontOnFocus);
ImGui::PopStyleVar();
ImGui::PopStyleColor();
// get draw list
ImDrawList *draw_list = ImGui::GetWindowDrawList();
ImGui::SetCursorPos(
ImVec2(title_bar_width - title_bar_button_width * 3, 0.0f));
if (main_window) {
float bar_pos_x = title_bar_width - title_bar_button_width * 3 +
title_bar_button_width * 0.33f;
float bar_pos_y = title_bar_button_height * 0.5f;
// draw menu icon
float menu_bar_line_size = title_bar_button_width * 0.33f;
draw_list->AddLine(
ImVec2(bar_pos_x, bar_pos_y - title_bar_button_height * 0.15f),
ImVec2(bar_pos_x + menu_bar_line_size,
bar_pos_y - title_bar_button_height * 0.15f),
IM_COL32(0, 0, 0, 255));
draw_list->AddLine(ImVec2(bar_pos_x, bar_pos_y),
ImVec2(bar_pos_x + menu_bar_line_size, bar_pos_y),
IM_COL32(0, 0, 0, 255));
draw_list->AddLine(
ImVec2(bar_pos_x, bar_pos_y + title_bar_button_height * 0.15f),
ImVec2(bar_pos_x + menu_bar_line_size,
bar_pos_y + title_bar_button_height * 0.15f),
IM_COL32(0, 0, 0, 255));
std::string title_bar_menu_button = "##title_bar_menu"; // ICON_FA_BARS;
std::string title_bar_menu = "##title_bar_menu";
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0.1f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
if (ImGui::Button(
title_bar_menu_button.c_str(),
ImVec2(title_bar_button_width, title_bar_button_height))) {
ImGui::OpenPopup(title_bar_menu.c_str());
}
ImGui::PopStyleColor(3);
if (ImGui::BeginPopup(title_bar_menu.c_str())) {
ImGui::SetWindowFontScale(0.6f);
if (ImGui::MenuItem(
localization::settings[localization_language_index_].c_str())) {
show_settings_window_ = true;
}
show_new_version_icon_in_menu_ = false;
std::string about_str = localization::about[localization_language_index_];
if (update_available_) {
const double now_time = ImGui::GetTime();
// every 2 seconds
if (now_time - new_version_icon_last_trigger_time_ >=
kNewVersionIconBlinkIntervalSec) {
show_new_version_icon_ = true;
new_version_icon_render_start_time_ = now_time;
new_version_icon_last_trigger_time_ = now_time;
}
// render for 1 second
if (show_new_version_icon_) {
about_str = about_str + " " + ICON_FA_CIRCLE_ARROW_UP;
if (now_time - new_version_icon_render_start_time_ >=
kNewVersionIconBlinkOnTimeSec) {
show_new_version_icon_ = false;
}
} else {
about_str = about_str + " ";
}
}
if (ImGui::MenuItem(about_str.c_str())) {
show_about_window_ = true;
}
if (update_available_ && ImGui::IsItemHovered()) {
ImGui::BeginTooltip();
ImGui::SetWindowFontScale(0.5f);
std::string new_version_available_str =
localization::new_version_available[localization_language_index_] +
": " + latest_version_;
ImGui::Text("%s", new_version_available_str.c_str());
ImGui::SetWindowFontScale(1.0f);
ImGui::EndTooltip();
}
ImGui::EndPopup();
} else {
show_new_version_icon_in_menu_ = true;
}
if (update_available_ && show_new_version_icon_in_menu_) {
const double now_time = ImGui::GetTime();
// every 2 seconds
if (now_time - new_version_icon_last_trigger_time_ >=
kNewVersionIconBlinkIntervalSec) {
show_new_version_icon_ = true;
new_version_icon_render_start_time_ = now_time;
new_version_icon_last_trigger_time_ = now_time;
}
// render for 1 second
if (show_new_version_icon_) {
ImGui::SetWindowFontScale(0.6f);
ImGui::SetCursorPos(ImVec2(bar_pos_x + title_bar_button_width * 0.21f,
bar_pos_y - title_bar_button_width * 0.24f));
ImGui::Text(ICON_FA_CIRCLE_ARROW_UP);
ImGui::SetWindowFontScale(1.0f);
if (now_time - new_version_icon_render_start_time_ >=
kNewVersionIconBlinkOnTimeSec) {
show_new_version_icon_ = false;
}
}
}
{
SettingWindow();
SelfHostedServerWindow();
AboutWindow();
}
} else {
ImGui::SetWindowFontScale(1.2f);
}
ImGui::SetCursorPos(ImVec2(
title_bar_width - title_bar_button_width * (main_window ? 2 : 3), 0.0f));
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0.1f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
float minimize_pos_x = title_bar_width -
title_bar_button_width * (main_window ? 2 : 3) +
title_bar_button_width * 0.33f;
float minimize_pos_y = title_bar_button_height * 0.5f;
std::string window_minimize_button = "##minimize"; // ICON_FA_MINUS;
if (ImGui::Button(window_minimize_button.c_str(),
ImVec2(title_bar_button_width, title_bar_button_height))) {
if (main_window) {
last_main_minimize_request_tick_ = SDL_GetTicks();
} else {
last_stream_minimize_request_tick_ = SDL_GetTicks();
}
SDL_MinimizeWindow(main_window ? main_window_ : stream_window_);
}
draw_list->AddLine(
ImVec2(minimize_pos_x, minimize_pos_y),
ImVec2(minimize_pos_x + title_bar_button_width * 0.33f, minimize_pos_y),
IM_COL32(0, 0, 0, 255));
ImGui::PopStyleColor(3);
if (!main_window) {
ImGui::SetCursorPos(
ImVec2(title_bar_width - title_bar_button_width * 2, 0.0f));
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0, 0, 0, 0.1f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive,
ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
if (window_maximized_) {
float pos_x_top = title_bar_width - title_bar_button_width * 1.65f;
float pos_y_top = title_bar_button_height * 0.36f;
float pos_x_bottom = title_bar_width - title_bar_button_width * 1.6f;
float pos_y_bottom = title_bar_button_height * 0.28f;
std::string window_restore_button =
"##restore"; // ICON_FA_WINDOW_RESTORE;
if (ImGui::Button(
window_restore_button.c_str(),
ImVec2(title_bar_button_width, title_bar_button_height))) {
SDL_RestoreWindow(stream_window_);
window_maximized_ = false;
}
draw_list->AddRect(ImVec2(pos_x_top, pos_y_top),
ImVec2(pos_x_top + title_bar_button_height * 0.33f,
pos_y_top + title_bar_button_height * 0.33f),
IM_COL32(0, 0, 0, 255));
draw_list->AddRect(ImVec2(pos_x_bottom, pos_y_bottom),
ImVec2(pos_x_bottom + title_bar_button_height * 0.33f,
pos_y_bottom + title_bar_button_height * 0.33f),
IM_COL32(0, 0, 0, 255));
if (ImGui::IsItemHovered()) {
draw_list->AddRectFilled(
ImVec2(pos_x_top + title_bar_button_height * 0.02f,
pos_y_top + title_bar_button_height * 0.01f),
ImVec2(pos_x_top + title_bar_button_height * 0.32f,
pos_y_top + title_bar_button_height * 0.31f),
IM_COL32(229, 229, 229, 255));
} else {
draw_list->AddRectFilled(
ImVec2(pos_x_top + title_bar_button_height * 0.02f,
pos_y_top + title_bar_button_height * 0.01f),
ImVec2(pos_x_top + title_bar_button_height * 0.32f,
pos_y_top + title_bar_button_height * 0.31f),
IM_COL32(255, 255, 255, 255));
}
} else {
float maximize_pos_x = title_bar_width - title_bar_button_width * 1.5f -
title_bar_button_height * 0.165f;
float maximize_pos_y = title_bar_button_height * 0.33f;
std::string window_maximize_button = "##maximize"; // ICON_FA_SQUARE_FULL;
if (ImGui::Button(
window_maximize_button.c_str(),
ImVec2(title_bar_button_width, title_bar_button_height))) {
SDL_MaximizeWindow(stream_window_);
window_maximized_ = !window_maximized_;
}
draw_list->AddRect(
ImVec2(maximize_pos_x, maximize_pos_y),
ImVec2(maximize_pos_x + title_bar_button_height * 0.33f,
maximize_pos_y + title_bar_button_height * 0.33f),
IM_COL32(0, 0, 0, 255));
}
ImGui::PopStyleColor(3);
}
float xmark_button_pos_x = title_bar_width - title_bar_button_width;
ImGui::SetCursorPos(ImVec2(xmark_button_pos_x, 0.0f));
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0, 0, 1.0f));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 0, 0, 0.5f));
float xmark_pos_x = xmark_button_pos_x + title_bar_button_width * 0.5f;
float xmark_pos_y = title_bar_button_height * 0.5f;
float xmark_size = title_bar_button_width * 0.33f;
std::string close_button = "##xmark"; // ICON_FA_XMARK;
bool close_button_clicked = false;
if (main_window) {
close_button_clicked = RoundedCornerButton(
close_button.c_str(),
ImVec2(title_bar_button_width, title_bar_button_height), 8.5f,
ImDrawFlags_RoundCornersTopRight, true, IM_COL32(0, 0, 0, 0),
IM_COL32(250, 0, 0, 255), IM_COL32(255, 0, 0, 128));
} else {
close_button_clicked =
ImGui::Button(close_button.c_str(),
ImVec2(title_bar_button_width, title_bar_button_height));
}
if (close_button_clicked) {
if (main_window) {
if (!MinimizeMainWindowToTray()) {
SDL_HideWindow(main_window_);
}
} else {
SDL_Event event;
event.type = SDL_EVENT_QUIT;
SDL_PushEvent(&event);
}
}
draw_list->AddLine(ImVec2(xmark_pos_x - xmark_size / 2 - 0.25f,
xmark_pos_y - xmark_size / 2 + 0.75f),
ImVec2(xmark_pos_x + xmark_size / 2 - 1.5f,
xmark_pos_y + xmark_size / 2 - 0.5f),
IM_COL32(0, 0, 0, 255));
draw_list->AddLine(
ImVec2(xmark_pos_x + xmark_size / 2 - 1.75f,
xmark_pos_y - xmark_size / 2 + 0.75f),
ImVec2(xmark_pos_x - xmark_size / 2, xmark_pos_y + xmark_size / 2 - 1.0f),
IM_COL32(0, 0, 0, 255));
ImGui::PopStyleColor(3);
ImGui::EndChild();
return 0;
}
} // namespace crossdesk