mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-03-24 09:07:31 +08:00
[feat] add support for displaying multiple controller info and file transfer to controllers
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -201,6 +201,8 @@ static std::vector<std::string> controller = {
|
|||||||
reinterpret_cast<const char*>(u8"控制端:"), "Controller:"};
|
reinterpret_cast<const char*>(u8"控制端:"), "Controller:"};
|
||||||
static std::vector<std::string> file_transfer = {
|
static std::vector<std::string> file_transfer = {
|
||||||
reinterpret_cast<const char*>(u8"文件传输:"), "File Transfer:"};
|
reinterpret_cast<const char*>(u8"文件传输:"), "File Transfer:"};
|
||||||
|
static std::vector<std::string> connection_status = {
|
||||||
|
reinterpret_cast<const char*>(u8"连接状态:"), "Connection Status:"};
|
||||||
|
|
||||||
#if _WIN32
|
#if _WIN32
|
||||||
static std::vector<std::string> minimize_to_tray = {
|
static std::vector<std::string> minimize_to_tray = {
|
||||||
|
|||||||
@@ -829,7 +829,7 @@ int Render::CreateConnectionPeer() {
|
|||||||
|
|
||||||
params_.on_signal_status = OnSignalStatusCb;
|
params_.on_signal_status = OnSignalStatusCb;
|
||||||
params_.on_connection_status = OnConnectionStatusCb;
|
params_.on_connection_status = OnConnectionStatusCb;
|
||||||
params_.net_status_report = NetStatusReport;
|
params_.on_net_status_report = OnNetStatusReport;
|
||||||
|
|
||||||
params_.user_data = this;
|
params_.user_data = this;
|
||||||
|
|
||||||
@@ -1873,16 +1873,35 @@ void Render::CleanSubStreamWindowProperties(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Render::SubStreamWindowProperties>
|
||||||
|
Render::GetSubStreamWindowPropertiesByRemoteId(const std::string& remote_id) {
|
||||||
|
if (remote_id.empty()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_lock lock(client_properties_mutex_);
|
||||||
|
auto it = client_properties_.find(remote_id);
|
||||||
|
if (it == client_properties_.end()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
void Render::StartFileTransfer(std::shared_ptr<SubStreamWindowProperties> props,
|
void Render::StartFileTransfer(std::shared_ptr<SubStreamWindowProperties> props,
|
||||||
const std::filesystem::path& file_path,
|
const std::filesystem::path& file_path,
|
||||||
const std::string& file_label) {
|
const std::string& file_label,
|
||||||
if (!props || !props->peer_) {
|
const std::string& remote_id) {
|
||||||
LOG_ERROR("StartFileTransfer: invalid props or peer");
|
const bool is_global = (props == nullptr);
|
||||||
|
PeerPtr* peer = is_global ? peer_ : props->peer_;
|
||||||
|
if (!peer) {
|
||||||
|
LOG_ERROR("StartFileTransfer: invalid peer");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool expected = false;
|
bool expected = false;
|
||||||
if (!props->file_sending_.compare_exchange_strong(expected, true)) {
|
if (!(is_global ? file_transfer_.file_sending_
|
||||||
|
: props->file_transfer_.file_sending_)
|
||||||
|
.compare_exchange_strong(expected, true)) {
|
||||||
// Already sending, this should not happen if called correctly
|
// Already sending, this should not happen if called correctly
|
||||||
LOG_WARN(
|
LOG_WARN(
|
||||||
"StartFileTransfer called but file_sending_ is already true, "
|
"StartFileTransfer called but file_sending_ is already true, "
|
||||||
@@ -1891,13 +1910,19 @@ void Render::StartFileTransfer(std::shared_ptr<SubStreamWindowProperties> props,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto peer = props->peer_;
|
|
||||||
auto props_weak = std::weak_ptr<SubStreamWindowProperties>(props);
|
auto props_weak = std::weak_ptr<SubStreamWindowProperties>(props);
|
||||||
Render* render_ptr = this;
|
Render* render_ptr = this;
|
||||||
|
|
||||||
std::thread([peer, file_path, file_label, props_weak, render_ptr]() {
|
std::thread([peer, file_path, file_label, props_weak, render_ptr, remote_id,
|
||||||
|
is_global]() {
|
||||||
auto props_locked = props_weak.lock();
|
auto props_locked = props_weak.lock();
|
||||||
if (!props_locked) {
|
|
||||||
|
FileTransferState* state = nullptr;
|
||||||
|
if (props_locked) {
|
||||||
|
state = &props_locked->file_transfer_;
|
||||||
|
} else if (is_global) {
|
||||||
|
state = &render_ptr->file_transfer_;
|
||||||
|
} else {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1905,49 +1930,50 @@ void Render::StartFileTransfer(std::shared_ptr<SubStreamWindowProperties> props,
|
|||||||
uint64_t total_size = std::filesystem::file_size(file_path, ec);
|
uint64_t total_size = std::filesystem::file_size(file_path, ec);
|
||||||
if (ec) {
|
if (ec) {
|
||||||
LOG_ERROR("Failed to get file size: {}", ec.message().c_str());
|
LOG_ERROR("Failed to get file size: {}", ec.message().c_str());
|
||||||
props_locked->file_sending_ = false;
|
state->file_sending_ = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
props_locked->file_sent_bytes_ = 0;
|
state->file_sent_bytes_ = 0;
|
||||||
props_locked->file_total_bytes_ = total_size;
|
state->file_total_bytes_ = total_size;
|
||||||
props_locked->file_send_rate_bps_ = 0;
|
state->file_send_rate_bps_ = 0;
|
||||||
props_locked->file_transfer_window_visible_ = true;
|
state->file_transfer_window_visible_ = true;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(props_locked->file_transfer_mutex_);
|
std::lock_guard<std::mutex> lock(state->file_transfer_mutex_);
|
||||||
props_locked->file_send_start_time_ = std::chrono::steady_clock::now();
|
state->file_send_start_time_ = std::chrono::steady_clock::now();
|
||||||
props_locked->file_send_last_update_time_ =
|
state->file_send_last_update_time_ = state->file_send_start_time_;
|
||||||
props_locked->file_send_start_time_;
|
state->file_send_last_bytes_ = 0;
|
||||||
props_locked->file_send_last_bytes_ = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO(
|
LOG_INFO(
|
||||||
"File transfer started: {} ({} bytes), file_sending_={}, "
|
"File transfer started: {} ({} bytes), file_sending_={}, "
|
||||||
"total_bytes_={}",
|
"total_bytes_={}",
|
||||||
file_path.filename().string(), total_size,
|
file_path.filename().string(), total_size, state->file_sending_.load(),
|
||||||
props_locked->file_sending_.load(),
|
state->file_total_bytes_.load());
|
||||||
props_locked->file_total_bytes_.load());
|
|
||||||
|
|
||||||
FileSender sender;
|
FileSender sender;
|
||||||
uint32_t file_id = FileSender::NextFileId();
|
uint32_t file_id = FileSender::NextFileId();
|
||||||
|
|
||||||
{
|
if (props_locked) {
|
||||||
std::lock_guard<std::shared_mutex> lock(
|
std::lock_guard<std::shared_mutex> lock(
|
||||||
render_ptr->file_id_to_props_mutex_);
|
render_ptr->file_id_to_props_mutex_);
|
||||||
render_ptr->file_id_to_props_[file_id] = props_weak;
|
render_ptr->file_id_to_props_[file_id] = props_weak;
|
||||||
|
} else {
|
||||||
|
std::lock_guard<std::shared_mutex> lock(
|
||||||
|
render_ptr->file_id_to_transfer_state_mutex_);
|
||||||
|
render_ptr->file_id_to_transfer_state_[file_id] = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
props_locked->current_file_id_ = file_id;
|
state->current_file_id_ = file_id;
|
||||||
|
|
||||||
// Update file transfer list: mark as sending
|
// Update file transfer list: mark as sending
|
||||||
// Find the queued file that matches the exact file path
|
// Find the queued file that matches the exact file path
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(props_locked->file_transfer_list_mutex_);
|
std::lock_guard<std::mutex> lock(state->file_transfer_list_mutex_);
|
||||||
for (auto& info : props_locked->file_transfer_list_) {
|
for (auto& info : state->file_transfer_list_) {
|
||||||
if (info.file_path == file_path &&
|
if (info.file_path == file_path &&
|
||||||
info.status ==
|
info.status == FileTransferState::FileTransferStatus::Queued) {
|
||||||
SubStreamWindowProperties::FileTransferStatus::Queued) {
|
info.status = FileTransferState::FileTransferStatus::Sending;
|
||||||
info.status = SubStreamWindowProperties::FileTransferStatus::Sending;
|
|
||||||
info.file_id = file_id;
|
info.file_id = file_id;
|
||||||
info.file_size = total_size;
|
info.file_size = total_size;
|
||||||
info.sent_bytes = 0;
|
info.sent_bytes = 0;
|
||||||
@@ -1956,81 +1982,88 @@ void Render::StartFileTransfer(std::shared_ptr<SubStreamWindowProperties> props,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
props_locked->file_transfer_window_visible_ = true;
|
state->file_transfer_window_visible_ = true;
|
||||||
|
|
||||||
// Progress will be updated via ACK from receiver
|
// Progress will be updated via ACK from receiver
|
||||||
int ret = sender.SendFile(
|
int ret = sender.SendFile(
|
||||||
file_path, file_path.filename().string(),
|
file_path, file_path.filename().string(),
|
||||||
[peer, file_label](const char* buf, size_t sz) -> int {
|
[peer, file_label, remote_id](const char* buf, size_t sz) -> int {
|
||||||
return SendReliableDataFrame(peer, buf, sz, file_label.c_str());
|
if (remote_id.empty()) {
|
||||||
|
return SendReliableDataFrame(peer, buf, sz, file_label.c_str());
|
||||||
|
} else {
|
||||||
|
return SendReliableDataFrameToPeer(
|
||||||
|
peer, buf, sz, file_label.c_str(), remote_id.c_str(),
|
||||||
|
remote_id.size());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
64 * 1024, file_id);
|
64 * 1024, file_id);
|
||||||
|
|
||||||
// file_sending_ should remain true until we receive the final ACK from
|
// file_sending_ should remain true until we receive the final ACK from
|
||||||
// receiver
|
// receiver
|
||||||
auto props_locked_final = props_weak.lock();
|
// On error, set file_sending_ to false immediately to allow next file
|
||||||
if (props_locked_final) {
|
if (ret != 0) {
|
||||||
// On error, set file_sending_ to false immediately to allow next file
|
state->file_sending_ = false;
|
||||||
if (ret != 0) {
|
state->file_transfer_window_visible_ = false;
|
||||||
props_locked_final->file_sending_ = false;
|
state->file_sent_bytes_ = 0;
|
||||||
props_locked_final->file_transfer_window_visible_ = false;
|
state->file_total_bytes_ = 0;
|
||||||
props_locked_final->file_sent_bytes_ = 0;
|
state->file_send_rate_bps_ = 0;
|
||||||
props_locked_final->file_total_bytes_ = 0;
|
state->current_file_id_ = 0;
|
||||||
props_locked_final->file_send_rate_bps_ = 0;
|
|
||||||
props_locked_final->current_file_id_ = 0;
|
|
||||||
|
|
||||||
// Unregister file_id mapping on error
|
// Unregister file_id mapping on error
|
||||||
{
|
if (props_locked) {
|
||||||
std::lock_guard<std::shared_mutex> lock(
|
std::lock_guard<std::shared_mutex> lock(
|
||||||
render_ptr->file_id_to_props_mutex_);
|
render_ptr->file_id_to_props_mutex_);
|
||||||
render_ptr->file_id_to_props_.erase(file_id);
|
render_ptr->file_id_to_props_.erase(file_id);
|
||||||
}
|
} else {
|
||||||
|
std::lock_guard<std::shared_mutex> lock(
|
||||||
|
render_ptr->file_id_to_transfer_state_mutex_);
|
||||||
|
render_ptr->file_id_to_transfer_state_.erase(file_id);
|
||||||
|
}
|
||||||
|
|
||||||
// Update file transfer list: mark as failed
|
// Update file transfer list: mark as failed
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(
|
std::lock_guard<std::mutex> lock(state->file_transfer_list_mutex_);
|
||||||
props_locked_final->file_transfer_list_mutex_);
|
for (auto& info : state->file_transfer_list_) {
|
||||||
for (auto& info : props_locked_final->file_transfer_list_) {
|
if (info.file_id == file_id) {
|
||||||
if (info.file_id == file_id) {
|
info.status = FileTransferState::FileTransferStatus::Failed;
|
||||||
info.status =
|
break;
|
||||||
SubStreamWindowProperties::FileTransferStatus::Failed;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_ERROR("FileSender::SendFile failed for [{}], ret={}",
|
|
||||||
file_path.string().c_str(), ret);
|
|
||||||
|
|
||||||
render_ptr->ProcessFileQueue(props_locked_final);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_ERROR("FileSender::SendFile failed for [{}], ret={}",
|
||||||
|
file_path.string().c_str(), ret);
|
||||||
|
|
||||||
|
render_ptr->ProcessFileQueue(props_locked);
|
||||||
}
|
}
|
||||||
}).detach();
|
}).detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Render::ProcessFileQueue(
|
void Render::ProcessFileQueue(
|
||||||
std::shared_ptr<SubStreamWindowProperties> props) {
|
std::shared_ptr<SubStreamWindowProperties> props) {
|
||||||
if (!props) {
|
FileTransferState* state = props ? &props->file_transfer_ : &file_transfer_;
|
||||||
|
if (!state) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props->file_sending_.load()) {
|
if (state->file_sending_.load()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SubStreamWindowProperties::QueuedFile queued_file;
|
FileTransferState::QueuedFile queued_file;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(props->file_queue_mutex_);
|
std::lock_guard<std::mutex> lock(state->file_queue_mutex_);
|
||||||
if (props->file_send_queue_.empty()) {
|
if (state->file_send_queue_.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
queued_file = props->file_send_queue_.front();
|
queued_file = state->file_send_queue_.front();
|
||||||
props->file_send_queue_.pop();
|
state->file_send_queue_.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_INFO("Processing next file in queue: {}",
|
LOG_INFO("Processing next file in queue: {}",
|
||||||
queued_file.file_path.string().c_str());
|
queued_file.file_path.string().c_str());
|
||||||
StartFileTransfer(props, queued_file.file_path, queued_file.file_label);
|
StartFileTransfer(props, queued_file.file_path, queued_file.file_label,
|
||||||
|
queued_file.remote_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Render::UpdateRenderRect() {
|
void Render::UpdateRenderRect() {
|
||||||
|
|||||||
100
src/gui/render.h
100
src/gui/render.h
@@ -42,6 +42,41 @@
|
|||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
class Render {
|
class Render {
|
||||||
public:
|
public:
|
||||||
|
struct FileTransferState {
|
||||||
|
std::atomic<bool> file_sending_ = false;
|
||||||
|
std::atomic<uint64_t> file_sent_bytes_ = 0;
|
||||||
|
std::atomic<uint64_t> file_total_bytes_ = 0;
|
||||||
|
std::atomic<uint32_t> file_send_rate_bps_ = 0;
|
||||||
|
std::mutex file_transfer_mutex_;
|
||||||
|
std::chrono::steady_clock::time_point file_send_start_time_;
|
||||||
|
std::chrono::steady_clock::time_point file_send_last_update_time_;
|
||||||
|
uint64_t file_send_last_bytes_ = 0;
|
||||||
|
bool file_transfer_window_visible_ = false;
|
||||||
|
std::atomic<uint32_t> current_file_id_{0};
|
||||||
|
|
||||||
|
struct QueuedFile {
|
||||||
|
std::filesystem::path file_path;
|
||||||
|
std::string file_label;
|
||||||
|
std::string remote_id;
|
||||||
|
};
|
||||||
|
std::queue<QueuedFile> file_send_queue_;
|
||||||
|
std::mutex file_queue_mutex_;
|
||||||
|
|
||||||
|
enum class FileTransferStatus { Queued, Sending, Completed, Failed };
|
||||||
|
|
||||||
|
struct FileTransferInfo {
|
||||||
|
std::string file_name;
|
||||||
|
std::filesystem::path file_path;
|
||||||
|
uint64_t file_size = 0;
|
||||||
|
FileTransferStatus status = FileTransferStatus::Queued;
|
||||||
|
uint64_t sent_bytes = 0;
|
||||||
|
uint32_t file_id = 0;
|
||||||
|
uint32_t rate_bps = 0;
|
||||||
|
};
|
||||||
|
std::vector<FileTransferInfo> file_transfer_list_;
|
||||||
|
std::mutex file_transfer_list_mutex_;
|
||||||
|
};
|
||||||
|
|
||||||
struct SubStreamWindowProperties {
|
struct SubStreamWindowProperties {
|
||||||
Params params_;
|
Params params_;
|
||||||
PeerPtr* peer_ = nullptr;
|
PeerPtr* peer_ = nullptr;
|
||||||
@@ -129,38 +164,10 @@ class Render {
|
|||||||
std::chrono::steady_clock::time_point last_time_;
|
std::chrono::steady_clock::time_point last_time_;
|
||||||
XNetTrafficStats net_traffic_stats_;
|
XNetTrafficStats net_traffic_stats_;
|
||||||
|
|
||||||
// File transfer progress
|
using QueuedFile = FileTransferState::QueuedFile;
|
||||||
std::atomic<bool> file_sending_ = false;
|
using FileTransferStatus = FileTransferState::FileTransferStatus;
|
||||||
std::atomic<uint64_t> file_sent_bytes_ = 0;
|
using FileTransferInfo = FileTransferState::FileTransferInfo;
|
||||||
std::atomic<uint64_t> file_total_bytes_ = 0;
|
FileTransferState file_transfer_;
|
||||||
std::atomic<uint32_t> file_send_rate_bps_ = 0;
|
|
||||||
std::mutex file_transfer_mutex_;
|
|
||||||
std::chrono::steady_clock::time_point file_send_start_time_;
|
|
||||||
std::chrono::steady_clock::time_point file_send_last_update_time_;
|
|
||||||
uint64_t file_send_last_bytes_ = 0;
|
|
||||||
bool file_transfer_window_visible_ = false;
|
|
||||||
std::atomic<uint32_t> current_file_id_{0};
|
|
||||||
|
|
||||||
struct QueuedFile {
|
|
||||||
std::filesystem::path file_path;
|
|
||||||
std::string file_label;
|
|
||||||
};
|
|
||||||
std::queue<QueuedFile> file_send_queue_;
|
|
||||||
std::mutex file_queue_mutex_;
|
|
||||||
|
|
||||||
enum class FileTransferStatus { Queued, Sending, Completed, Failed };
|
|
||||||
|
|
||||||
struct FileTransferInfo {
|
|
||||||
std::string file_name;
|
|
||||||
std::filesystem::path file_path;
|
|
||||||
uint64_t file_size = 0;
|
|
||||||
FileTransferStatus status = FileTransferStatus::Queued;
|
|
||||||
uint64_t sent_bytes = 0;
|
|
||||||
uint32_t file_id = 0;
|
|
||||||
uint32_t rate_bps = 0;
|
|
||||||
};
|
|
||||||
std::vector<FileTransferInfo> file_transfer_list_;
|
|
||||||
std::mutex file_transfer_list_mutex_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -193,9 +200,13 @@ class Render {
|
|||||||
|
|
||||||
void ProcessFileDropEvent(const SDL_Event& event);
|
void ProcessFileDropEvent(const SDL_Event& event);
|
||||||
|
|
||||||
void ProcessSelectedFile(const std::string& path,
|
void ProcessSelectedFile(
|
||||||
std::shared_ptr<SubStreamWindowProperties>& props,
|
const std::string& path,
|
||||||
const std::string& file_label);
|
const std::shared_ptr<SubStreamWindowProperties>& props,
|
||||||
|
const std::string& file_label, const std::string& remote_id = "");
|
||||||
|
|
||||||
|
std::shared_ptr<SubStreamWindowProperties>
|
||||||
|
GetSubStreamWindowPropertiesByRemoteId(const std::string& remote_id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int CreateStreamRenderWindow();
|
int CreateStreamRenderWindow();
|
||||||
@@ -274,11 +285,11 @@ class Render {
|
|||||||
static void OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
|
static void OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
|
||||||
size_t user_id_size, void* user_data);
|
size_t user_id_size, void* user_data);
|
||||||
|
|
||||||
static void NetStatusReport(const char* client_id, size_t client_id_size,
|
static void OnNetStatusReport(const char* client_id, size_t client_id_size,
|
||||||
TraversalMode mode,
|
TraversalMode mode,
|
||||||
const XNetTrafficStats* net_traffic_stats,
|
const XNetTrafficStats* net_traffic_stats,
|
||||||
const char* user_id, const size_t user_id_size,
|
const char* user_id, const size_t user_id_size,
|
||||||
void* user_data);
|
void* user_data);
|
||||||
|
|
||||||
static SDL_HitTestResult HitTestCallback(SDL_Window* window,
|
static SDL_HitTestResult HitTestCallback(SDL_Window* window,
|
||||||
const SDL_Point* area, void* data);
|
const SDL_Point* area, void* data);
|
||||||
@@ -319,7 +330,8 @@ class Render {
|
|||||||
// File transfer helper functions
|
// File transfer helper functions
|
||||||
void StartFileTransfer(std::shared_ptr<SubStreamWindowProperties> props,
|
void StartFileTransfer(std::shared_ptr<SubStreamWindowProperties> props,
|
||||||
const std::filesystem::path& file_path,
|
const std::filesystem::path& file_path,
|
||||||
const std::string& file_label);
|
const std::string& file_label,
|
||||||
|
const std::string& remote_id = "");
|
||||||
void ProcessFileQueue(std::shared_ptr<SubStreamWindowProperties> props);
|
void ProcessFileQueue(std::shared_ptr<SubStreamWindowProperties> props);
|
||||||
|
|
||||||
int AudioDeviceInit();
|
int AudioDeviceInit();
|
||||||
@@ -579,6 +591,10 @@ class Render {
|
|||||||
std::unordered_map<uint32_t, std::weak_ptr<SubStreamWindowProperties>>
|
std::unordered_map<uint32_t, std::weak_ptr<SubStreamWindowProperties>>
|
||||||
file_id_to_props_;
|
file_id_to_props_;
|
||||||
std::shared_mutex file_id_to_props_mutex_;
|
std::shared_mutex file_id_to_props_mutex_;
|
||||||
|
|
||||||
|
// Map file_id to FileTransferState for global file transfer (props == null)
|
||||||
|
std::unordered_map<uint32_t, FileTransferState*> file_id_to_transfer_state_;
|
||||||
|
std::shared_mutex file_id_to_transfer_state_mutex_;
|
||||||
SDL_AudioDeviceID input_dev_;
|
SDL_AudioDeviceID input_dev_;
|
||||||
SDL_AudioDeviceID output_dev_;
|
SDL_AudioDeviceID output_dev_;
|
||||||
ScreenCapturerFactory* screen_capturer_factory_ = nullptr;
|
ScreenCapturerFactory* screen_capturer_factory_ = nullptr;
|
||||||
@@ -653,6 +669,8 @@ class Render {
|
|||||||
|
|
||||||
/* ------ server mode ------ */
|
/* ------ server mode ------ */
|
||||||
std::unordered_map<std::string, ConnectionStatus> connection_status_;
|
std::unordered_map<std::string, ConnectionStatus> connection_status_;
|
||||||
|
std::string selected_server_remote_id_ = "";
|
||||||
|
FileTransferState file_transfer_;
|
||||||
};
|
};
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
#endif
|
#endif
|
||||||
@@ -361,42 +361,62 @@ void Render::OnReceiveDataBufferCb(const char* data, size_t size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Render::FileTransferState* state = nullptr;
|
||||||
if (!props) {
|
if (!props) {
|
||||||
LOG_WARN("FileTransferAck: no props found for file_id={}", ack.file_id);
|
{
|
||||||
return;
|
std::shared_lock lock(render->file_id_to_transfer_state_mutex_);
|
||||||
|
auto it = render->file_id_to_transfer_state_.find(ack.file_id);
|
||||||
|
if (it != render->file_id_to_transfer_state_.end()) {
|
||||||
|
state = it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
LOG_WARN("FileTransferAck: no props/state found for file_id={}",
|
||||||
|
ack.file_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
state = &props->file_transfer_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update progress based on ACK
|
// Update progress based on ACK
|
||||||
props->file_sent_bytes_ = ack.acked_offset;
|
state->file_sent_bytes_ = ack.acked_offset;
|
||||||
props->file_total_bytes_ = ack.total_size;
|
state->file_total_bytes_ = ack.total_size;
|
||||||
|
|
||||||
uint32_t rate_bps = 0;
|
uint32_t rate_bps = 0;
|
||||||
{
|
{
|
||||||
uint32_t data_channel_bitrate =
|
if (props) {
|
||||||
props->net_traffic_stats_.data_outbound_stats.bitrate;
|
uint32_t data_channel_bitrate =
|
||||||
|
props->net_traffic_stats_.data_outbound_stats.bitrate;
|
||||||
|
|
||||||
if (data_channel_bitrate > 0 && props->file_sending_.load()) {
|
if (data_channel_bitrate > 0 && state->file_sending_.load()) {
|
||||||
rate_bps = static_cast<uint32_t>(data_channel_bitrate * 0.99f);
|
rate_bps = static_cast<uint32_t>(data_channel_bitrate * 0.99f);
|
||||||
|
|
||||||
uint32_t current_rate = props->file_send_rate_bps_.load();
|
uint32_t current_rate = state->file_send_rate_bps_.load();
|
||||||
if (current_rate > 0) {
|
if (current_rate > 0) {
|
||||||
// 70% old + 30% new for smoother display
|
// 70% old + 30% new for smoother display
|
||||||
rate_bps = static_cast<uint32_t>(current_rate * 0.7 + rate_bps * 0.3);
|
rate_bps =
|
||||||
|
static_cast<uint32_t>(current_rate * 0.7 + rate_bps * 0.3);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
rate_bps = state->file_send_rate_bps_.load();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rate_bps = props->file_send_rate_bps_.load();
|
// Global transfer: no per-connection bitrate, keep existing value.
|
||||||
|
rate_bps = state->file_send_rate_bps_.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
props->file_send_rate_bps_ = rate_bps;
|
state->file_send_rate_bps_ = rate_bps;
|
||||||
props->file_send_last_bytes_ = ack.acked_offset;
|
state->file_send_last_bytes_ = ack.acked_offset;
|
||||||
auto now = std::chrono::steady_clock::now();
|
auto now = std::chrono::steady_clock::now();
|
||||||
props->file_send_last_update_time_ = now;
|
state->file_send_last_update_time_ = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update file transfer list: update progress and rate
|
// Update file transfer list: update progress and rate
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(props->file_transfer_list_mutex_);
|
std::lock_guard<std::mutex> lock(state->file_transfer_list_mutex_);
|
||||||
for (auto& info : props->file_transfer_list_) {
|
for (auto& info : state->file_transfer_list_) {
|
||||||
if (info.file_id == ack.file_id) {
|
if (info.file_id == ack.file_id) {
|
||||||
info.sent_bytes = ack.acked_offset;
|
info.sent_bytes = ack.acked_offset;
|
||||||
info.file_size = ack.total_size;
|
info.file_size = ack.total_size;
|
||||||
@@ -410,8 +430,8 @@ void Render::OnReceiveDataBufferCb(const char* data, size_t size,
|
|||||||
if ((ack.flags & 0x01) != 0) {
|
if ((ack.flags & 0x01) != 0) {
|
||||||
// Transfer completed - receiver has finished receiving the file
|
// Transfer completed - receiver has finished receiving the file
|
||||||
// Reopen window if it was closed by user
|
// Reopen window if it was closed by user
|
||||||
props->file_transfer_window_visible_ = true;
|
state->file_transfer_window_visible_ = true;
|
||||||
props->file_sending_ = false; // Mark sending as finished
|
state->file_sending_ = false; // Mark sending as finished
|
||||||
LOG_INFO(
|
LOG_INFO(
|
||||||
"File transfer completed via ACK, file_id={}, total_size={}, "
|
"File transfer completed via ACK, file_id={}, total_size={}, "
|
||||||
"acked_offset={}",
|
"acked_offset={}",
|
||||||
@@ -419,11 +439,11 @@ void Render::OnReceiveDataBufferCb(const char* data, size_t size,
|
|||||||
|
|
||||||
// Update file transfer list: mark as completed
|
// Update file transfer list: mark as completed
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(props->file_transfer_list_mutex_);
|
std::lock_guard<std::mutex> lock(state->file_transfer_list_mutex_);
|
||||||
for (auto& info : props->file_transfer_list_) {
|
for (auto& info : state->file_transfer_list_) {
|
||||||
if (info.file_id == ack.file_id) {
|
if (info.file_id == ack.file_id) {
|
||||||
info.status =
|
info.status =
|
||||||
SubStreamWindowProperties::FileTransferStatus::Completed;
|
Render::FileTransferState::FileTransferStatus::Completed;
|
||||||
info.sent_bytes = ack.total_size;
|
info.sent_bytes = ack.total_size;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -432,9 +452,15 @@ void Render::OnReceiveDataBufferCb(const char* data, size_t size,
|
|||||||
|
|
||||||
// Unregister file_id mapping after completion
|
// Unregister file_id mapping after completion
|
||||||
{
|
{
|
||||||
std::lock_guard<std::shared_mutex> lock(
|
if (props) {
|
||||||
render->file_id_to_props_mutex_);
|
std::lock_guard<std::shared_mutex> lock(
|
||||||
render->file_id_to_props_.erase(ack.file_id);
|
render->file_id_to_props_mutex_);
|
||||||
|
render->file_id_to_props_.erase(ack.file_id);
|
||||||
|
} else {
|
||||||
|
std::lock_guard<std::shared_mutex> lock(
|
||||||
|
render->file_id_to_transfer_state_mutex_);
|
||||||
|
render->file_id_to_transfer_state_.erase(ack.file_id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process next file in queue
|
// Process next file in queue
|
||||||
@@ -683,11 +709,11 @@ void Render::OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Render::NetStatusReport(const char* client_id, size_t client_id_size,
|
void Render::OnNetStatusReport(const char* client_id, size_t client_id_size,
|
||||||
TraversalMode mode,
|
TraversalMode mode,
|
||||||
const XNetTrafficStats* net_traffic_stats,
|
const XNetTrafficStats* net_traffic_stats,
|
||||||
const char* user_id, const size_t user_id_size,
|
const char* user_id, const size_t user_id_size,
|
||||||
void* user_data) {
|
void* user_data) {
|
||||||
Render* render = (Render*)user_data;
|
Render* render = (Render*)user_data;
|
||||||
if (!render) {
|
if (!render) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -54,12 +54,16 @@ std::string Render::OpenFileDialog(std::string title) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Render::ProcessSelectedFile(
|
void Render::ProcessSelectedFile(
|
||||||
const std::string& path, std::shared_ptr<SubStreamWindowProperties>& props,
|
const std::string& path,
|
||||||
const std::string& file_label) {
|
const std::shared_ptr<SubStreamWindowProperties>& props,
|
||||||
|
const std::string& file_label, const std::string& remote_id) {
|
||||||
if (path.empty()) {
|
if (path.empty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FileTransferState* file_transfer_state =
|
||||||
|
props ? &props->file_transfer_ : &file_transfer_;
|
||||||
|
|
||||||
LOG_INFO("Selected file: {}", path.c_str());
|
LOG_INFO("Selected file: {}", path.c_str());
|
||||||
|
|
||||||
std::filesystem::path file_path = std::filesystem::u8path(path);
|
std::filesystem::path file_path = std::filesystem::u8path(path);
|
||||||
@@ -74,47 +78,51 @@ void Render::ProcessSelectedFile(
|
|||||||
|
|
||||||
// Add file to transfer list
|
// Add file to transfer list
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(props->file_transfer_list_mutex_);
|
std::lock_guard<std::mutex> lock(
|
||||||
SubStreamWindowProperties::FileTransferInfo info;
|
file_transfer_state->file_transfer_list_mutex_);
|
||||||
|
FileTransferState::FileTransferInfo info;
|
||||||
info.file_name = file_path.filename().u8string();
|
info.file_name = file_path.filename().u8string();
|
||||||
info.file_path = file_path; // Store full path for precise matching
|
info.file_path = file_path; // Store full path for precise matching
|
||||||
info.file_size = file_size;
|
info.file_size = file_size;
|
||||||
info.status = SubStreamWindowProperties::FileTransferStatus::Queued;
|
info.status = FileTransferState::FileTransferStatus::Queued;
|
||||||
info.sent_bytes = 0;
|
info.sent_bytes = 0;
|
||||||
info.file_id = 0;
|
info.file_id = 0;
|
||||||
info.rate_bps = 0;
|
info.rate_bps = 0;
|
||||||
props->file_transfer_list_.push_back(info);
|
file_transfer_state->file_transfer_list_.push_back(info);
|
||||||
}
|
}
|
||||||
props->file_transfer_window_visible_ = true;
|
file_transfer_state->file_transfer_window_visible_ = true;
|
||||||
|
|
||||||
if (props->file_sending_.load()) {
|
if (file_transfer_state->file_sending_.load()) {
|
||||||
// Add to queue
|
// Add to queue
|
||||||
size_t queue_size = 0;
|
size_t queue_size = 0;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(props->file_queue_mutex_);
|
std::lock_guard<std::mutex> lock(file_transfer_state->file_queue_mutex_);
|
||||||
SubStreamWindowProperties::QueuedFile queued_file;
|
FileTransferState::QueuedFile queued_file;
|
||||||
queued_file.file_path = file_path;
|
queued_file.file_path = file_path;
|
||||||
queued_file.file_label = file_label;
|
queued_file.file_label = file_label;
|
||||||
props->file_send_queue_.push(queued_file);
|
queued_file.remote_id = remote_id;
|
||||||
queue_size = props->file_send_queue_.size();
|
file_transfer_state->file_send_queue_.push(queued_file);
|
||||||
|
queue_size = file_transfer_state->file_send_queue_.size();
|
||||||
}
|
}
|
||||||
LOG_INFO("File added to queue: {} ({} files in queue)",
|
LOG_INFO("File added to queue: {} ({} files in queue)",
|
||||||
file_path.filename().string().c_str(), queue_size);
|
file_path.filename().string().c_str(), queue_size);
|
||||||
} else {
|
} else {
|
||||||
StartFileTransfer(props, file_path, file_label);
|
StartFileTransfer(props, file_path, file_label, remote_id);
|
||||||
|
|
||||||
if (props->file_sending_.load()) {
|
if (file_transfer_state->file_sending_.load()) {
|
||||||
} else {
|
} else {
|
||||||
// Failed to start (race condition: another file started between
|
// Failed to start (race condition: another file started between
|
||||||
// check and call) Add to queue
|
// check and call) Add to queue
|
||||||
size_t queue_size = 0;
|
size_t queue_size = 0;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(props->file_queue_mutex_);
|
std::lock_guard<std::mutex> lock(
|
||||||
SubStreamWindowProperties::QueuedFile queued_file;
|
file_transfer_state->file_queue_mutex_);
|
||||||
|
FileTransferState::QueuedFile queued_file;
|
||||||
queued_file.file_path = file_path;
|
queued_file.file_path = file_path;
|
||||||
queued_file.file_label = file_label;
|
queued_file.file_label = file_label;
|
||||||
props->file_send_queue_.push(queued_file);
|
queued_file.remote_id = remote_id;
|
||||||
queue_size = props->file_send_queue_.size();
|
file_transfer_state->file_send_queue_.push(queued_file);
|
||||||
|
queue_size = file_transfer_state->file_send_queue_.size();
|
||||||
}
|
}
|
||||||
LOG_INFO(
|
LOG_INFO(
|
||||||
"File added to queue after race condition: {} ({} files in "
|
"File added to queue after race condition: {} ({} files in "
|
||||||
@@ -275,7 +283,7 @@ int Render::ControlBar(std::shared_ptr<SubStreamWindowProperties>& props) {
|
|||||||
std::string title =
|
std::string title =
|
||||||
localization::select_file[localization_language_index_];
|
localization::select_file[localization_language_index_];
|
||||||
std::string path = OpenFileDialog(title);
|
std::string path = OpenFileDialog(title);
|
||||||
this->ProcessSelectedFile(path, props, file_label_);
|
ProcessSelectedFile(path, props, file_label_);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|||||||
@@ -30,12 +30,14 @@ int BitrateDisplay(int bitrate) {
|
|||||||
|
|
||||||
int Render::FileTransferWindow(
|
int Render::FileTransferWindow(
|
||||||
std::shared_ptr<SubStreamWindowProperties>& props) {
|
std::shared_ptr<SubStreamWindowProperties>& props) {
|
||||||
|
FileTransferState* state = props ? &props->file_transfer_ : &file_transfer_;
|
||||||
|
|
||||||
// Only show window if there are files in transfer list or currently
|
// Only show window if there are files in transfer list or currently
|
||||||
// transferring
|
// transferring
|
||||||
std::vector<SubStreamWindowProperties::FileTransferInfo> file_list;
|
std::vector<SubStreamWindowProperties::FileTransferInfo> file_list;
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(props->file_transfer_list_mutex_);
|
std::lock_guard<std::mutex> lock(state->file_transfer_list_mutex_);
|
||||||
file_list = props->file_transfer_list_;
|
file_list = state->file_transfer_list_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort file list: Sending first, then Completed, then Queued, then Failed
|
// Sort file list: Sending first, then Completed, then Queued, then Failed
|
||||||
@@ -66,7 +68,7 @@ int Render::FileTransferWindow(
|
|||||||
// It will be reopened automatically when:
|
// It will be reopened automatically when:
|
||||||
// 1. A file transfer completes (in render_callback.cpp)
|
// 1. A file transfer completes (in render_callback.cpp)
|
||||||
// 2. A new file starts sending from queue (in render.cpp)
|
// 2. A new file starts sending from queue (in render.cpp)
|
||||||
if (!props->file_transfer_window_visible_) {
|
if (!state->file_transfer_window_visible_) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +117,7 @@ int Render::FileTransferWindow(
|
|||||||
|
|
||||||
// Close button handling
|
// Close button handling
|
||||||
if (!window_opened) {
|
if (!window_opened) {
|
||||||
props->file_transfer_window_visible_ = false;
|
state->file_transfer_window_visible_ = false;
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "layout_relative.h"
|
#include "layout_relative.h"
|
||||||
#include "localization.h"
|
#include "localization.h"
|
||||||
#include "rd_log.h"
|
#include "rd_log.h"
|
||||||
@@ -5,8 +9,6 @@
|
|||||||
|
|
||||||
namespace crossdesk {
|
namespace crossdesk {
|
||||||
|
|
||||||
namespace {} // namespace
|
|
||||||
|
|
||||||
int Render::ServerWindow() {
|
int Render::ServerWindow() {
|
||||||
ImGui::SetNextWindowSize(ImVec2(server_window_width_, server_window_height_),
|
ImGui::SetNextWindowSize(ImVec2(server_window_width_, server_window_height_),
|
||||||
ImGuiCond_Always);
|
ImGuiCond_Always);
|
||||||
@@ -87,9 +89,9 @@ int Render::ServerWindow() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
int Render::RemoteClientInfoWindow() {
|
int Render::RemoteClientInfoWindow() {
|
||||||
float remote_client_info_window_width = server_window_width_ * 0.75f;
|
float remote_client_info_window_width = server_window_width_ * 0.8f;
|
||||||
float remote_client_info_window_height =
|
float remote_client_info_window_height =
|
||||||
(server_window_height_ - server_window_title_bar_height_) * 0.3f;
|
(server_window_height_ - server_window_title_bar_height_) * 0.9f;
|
||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);
|
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f);
|
||||||
@@ -102,44 +104,105 @@ int Render::RemoteClientInfoWindow() {
|
|||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
|
|
||||||
ImGui::SetWindowFontScale(0.6f);
|
float font_scale = localization_language_index_ == 0 ? 0.5f : 0.45f;
|
||||||
|
|
||||||
|
ImGui::SetWindowFontScale(localization_language_index_ == 0 ? 0.5f : 0.48f);
|
||||||
|
|
||||||
|
std::vector<std::string> remote_ids;
|
||||||
|
remote_ids.reserve(connection_status_.size());
|
||||||
|
for (const auto& kv : connection_status_) {
|
||||||
|
remote_ids.push_back(kv.first);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!selected_server_remote_id_.empty()) {
|
||||||
|
if (std::find(remote_ids.begin(), remote_ids.end(),
|
||||||
|
selected_server_remote_id_) == remote_ids.end()) {
|
||||||
|
selected_server_remote_id_.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (selected_server_remote_id_.empty() && !remote_ids.empty()) {
|
||||||
|
selected_server_remote_id_ = remote_ids.front();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::Text("%s",
|
ImGui::Text("%s",
|
||||||
localization::controller[localization_language_index_].c_str());
|
localization::controller[localization_language_index_].c_str());
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Text("%s", remote_client_id_.c_str());
|
|
||||||
|
|
||||||
ImGui::SetWindowFontScale(1.0f);
|
const char* selected_preview = "-";
|
||||||
|
if (!selected_server_remote_id_.empty()) {
|
||||||
ImGui::EndChild();
|
selected_preview = selected_server_remote_id_.c_str();
|
||||||
|
} else if (!remote_client_id_.empty()) {
|
||||||
ImGui::SameLine();
|
selected_preview = remote_client_id_.c_str();
|
||||||
|
|
||||||
float close_connection_button_width = server_window_width_ * 0.15f;
|
|
||||||
float close_connection_button_height = remote_client_info_window_height;
|
|
||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 0.0f, 0.0f, 1.0f));
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0.3f, 0.3f, 1.0f));
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 0.5f, 0.5f, 1.0f));
|
|
||||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 5.0f);
|
|
||||||
ImGui::SetWindowFontScale(0.6f);
|
|
||||||
if (ImGui::Button(ICON_FA_XMARK, ImVec2(close_connection_button_width,
|
|
||||||
close_connection_button_height))) {
|
|
||||||
LeaveConnection(peer_, self_hosted_id_);
|
|
||||||
}
|
}
|
||||||
ImGui::SetWindowFontScale(1.0f);
|
|
||||||
ImGui::PopStyleColor(3);
|
|
||||||
ImGui::PopStyleVar();
|
|
||||||
|
|
||||||
ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(1.0f, 1.0f, 1.0f, 0.0f));
|
ImGui::SetNextItemWidth(remote_client_info_window_width *
|
||||||
ImGui::BeginChild(
|
(localization_language_index_ == 0 ? 0.65f : 0.6f));
|
||||||
"RemoteClientInfoFileTransferWindow",
|
if (ImGui::BeginCombo("##server_remote_id", selected_preview)) {
|
||||||
ImVec2(remote_client_info_window_width, remote_client_info_window_height),
|
ImGui::SetWindowFontScale(font_scale);
|
||||||
ImGuiChildFlags_Border,
|
for (int i = 0; i < static_cast<int>(remote_ids.size()); i++) {
|
||||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove |
|
const bool selected = (remote_ids[i] == selected_server_remote_id_);
|
||||||
ImGuiWindowFlags_NoBringToFrontOnFocus);
|
if (ImGui::Selectable(remote_ids[i].c_str(), selected)) {
|
||||||
ImGui::PopStyleColor();
|
selected_server_remote_id_ = remote_ids[i];
|
||||||
|
}
|
||||||
|
if (selected) {
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
|
ImGui::SetWindowFontScale(font_scale);
|
||||||
|
|
||||||
|
if (!selected_server_remote_id_.empty()) {
|
||||||
|
auto it = connection_status_.find(selected_server_remote_id_);
|
||||||
|
const ConnectionStatus status = (it == connection_status_.end())
|
||||||
|
? ConnectionStatus::Closed
|
||||||
|
: it->second;
|
||||||
|
|
||||||
|
ImGui::Text(
|
||||||
|
"%s",
|
||||||
|
localization::connection_status[localization_language_index_].c_str());
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
switch (connection_status_[selected_server_remote_id_]) {
|
||||||
|
case ConnectionStatus::Connected:
|
||||||
|
ImGui::Text(
|
||||||
|
"%s",
|
||||||
|
localization::p2p_connected[localization_language_index_].c_str());
|
||||||
|
break;
|
||||||
|
case ConnectionStatus::Connecting:
|
||||||
|
ImGui::Text(
|
||||||
|
"%s",
|
||||||
|
localization::p2p_connecting[localization_language_index_].c_str());
|
||||||
|
break;
|
||||||
|
case ConnectionStatus::Disconnected:
|
||||||
|
ImGui::Text("%s",
|
||||||
|
localization::p2p_disconnected[localization_language_index_]
|
||||||
|
.c_str());
|
||||||
|
break;
|
||||||
|
case ConnectionStatus::Failed:
|
||||||
|
ImGui::Text(
|
||||||
|
"%s",
|
||||||
|
localization::p2p_failed[localization_language_index_].c_str());
|
||||||
|
break;
|
||||||
|
case ConnectionStatus::Closed:
|
||||||
|
ImGui::Text(
|
||||||
|
"%s",
|
||||||
|
localization::p2p_closed[localization_language_index_].c_str());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ImGui::Text(
|
||||||
|
"%s",
|
||||||
|
localization::p2p_failed[localization_language_index_].c_str());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
|
||||||
ImGui::SetWindowFontScale(0.6f);
|
|
||||||
ImGui::AlignTextToFramePadding();
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::Text(
|
ImGui::Text(
|
||||||
"%s", localization::file_transfer[localization_language_index_].c_str());
|
"%s", localization::file_transfer[localization_language_index_].c_str());
|
||||||
@@ -151,11 +214,34 @@ int Render::RemoteClientInfoWindow() {
|
|||||||
std::string title = localization::select_file[localization_language_index_];
|
std::string title = localization::select_file[localization_language_index_];
|
||||||
std::string path = OpenFileDialog(title);
|
std::string path = OpenFileDialog(title);
|
||||||
LOG_INFO("Selected file path: {}", path.c_str());
|
LOG_INFO("Selected file path: {}", path.c_str());
|
||||||
|
|
||||||
|
ProcessSelectedFile(path, nullptr, file_label_, selected_server_remote_id_);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SetWindowFontScale(1.0f);
|
ImGui::SetWindowFontScale(1.0f);
|
||||||
|
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
float close_connection_button_width = server_window_width_ * 0.1f;
|
||||||
|
float close_connection_button_height = remote_client_info_window_height;
|
||||||
|
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(1.0f, 0.0f, 0.0f, 1.0f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.0f, 0.3f, 0.3f, 1.0f));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(1.0f, 0.5f, 0.5f, 1.0f));
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 5.0f);
|
||||||
|
ImGui::SetWindowFontScale(font_scale);
|
||||||
|
if (ImGui::Button(ICON_FA_XMARK, ImVec2(close_connection_button_width,
|
||||||
|
close_connection_button_height))) {
|
||||||
|
if (peer_ && !selected_server_remote_id_.empty()) {
|
||||||
|
LeaveConnection(peer_, selected_server_remote_id_.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::SetWindowFontScale(1.0f);
|
||||||
|
ImGui::PopStyleColor(3);
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace crossdesk
|
} // namespace crossdesk
|
||||||
Submodule submodules/minirtc updated: a282340f8b...e2623281d6
Reference in New Issue
Block a user