[feat] add online status indicators for recent connections

This commit is contained in:
dijunkun
2026-02-28 17:06:44 +08:00
parent 11b5f87841
commit 9223bf9d2d
7 changed files with 2295 additions and 2085 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -216,6 +216,11 @@ static std::vector<std::string> connection_mode_direct = {
reinterpret_cast<const char*>(u8"直连"), "Direct"};
static std::vector<std::string> connection_mode_relay = {
reinterpret_cast<const char*>(u8"中继"), "Relay"};
static std::vector<std::string> online = {
reinterpret_cast<const char*>(u8"在线"), "Online"};
static std::vector<std::string> offline = {
reinterpret_cast<const char*>(u8"离线"), "Offline"};
#if _WIN32
static std::vector<LPCWSTR> exit_program = {L"退出", L"Exit"};
#endif

36
src/gui/device_presence.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* @Author: DI JUNKUN
* @Date: 2026-02-28
* Copyright (c) 2026 by DI JUNKUN, All Rights Reserved.
*/
#ifndef _DEVICE_PRESENCE_H_
#define _DEVICE_PRESENCE_H_
#include <mutex>
#include <string>
#include <unordered_map>
class DevicePresence {
public:
void SetOnline(const std::string& device_id, bool online) {
std::lock_guard<std::mutex> lock(mutex_);
cache_[device_id] = online;
}
bool IsOnline(const std::string& device_id) const {
std::lock_guard<std::mutex> lock(mutex_);
return cache_.count(device_id) > 0 && cache_.at(device_id);
}
void Clear() {
std::lock_guard<std::mutex> lock(mutex_);
cache_.clear();
}
private:
std::unordered_map<std::string, bool> cache_;
mutable std::mutex mutex_;
};
#endif

View File

@@ -122,6 +122,8 @@ int Render::ShowRecentConnections() {
it.second.remote_host_name = "unknown";
}
bool online = device_presence_.IsOnline(it.second.remote_id);
ImVec2 image_screen_pos = ImVec2(
ImGui::GetCursorScreenPos().x + recent_connection_image_width * 0.04f,
ImGui::GetCursorScreenPos().y + recent_connection_image_height * 0.08f);
@@ -133,6 +135,26 @@ int Render::ShowRecentConnections() {
(ImTextureID)(intptr_t)it.second.texture,
ImVec2(recent_connection_image_width, recent_connection_image_height));
ImDrawList* draw_list = ImGui::GetWindowDrawList();
ImVec2 circle_pos =
ImVec2(image_screen_pos.x + recent_connection_image_width * 0.07f,
image_screen_pos.y + recent_connection_image_height * 0.13f);
ImU32 fill_color =
online ? IM_COL32(0, 255, 0, 255) : IM_COL32(140, 140, 140, 255);
ImU32 border_color = IM_COL32(255, 255, 255, 230);
draw_list->AddCircleFilled(circle_pos, 6.0f, fill_color);
draw_list->AddCircle(circle_pos, 6.0f, border_color, 100, 2.0f);
if (ImGui::IsMouseHoveringRect(
ImVec2(circle_pos.x - 6.0f, circle_pos.y - 6.0f),
ImVec2(circle_pos.x + 6.0f, circle_pos.y + 6.0f))) {
ImGui::BeginTooltip();
ImGui::SetWindowFontScale(0.5f);
ImGui::Text("%s", online ? localization::online[0].c_str()
: localization::offline[0].c_str());
ImGui::SetWindowFontScale(1.0f);
ImGui::EndTooltip();
}
// remote id display button
{
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0.2f));

View File

@@ -810,6 +810,7 @@ int Render::CreateConnectionPeer() {
params_.on_receive_video_frame = OnReceiveVideoBufferCb;
params_.on_signal_status = OnSignalStatusCb;
params_.on_signal_message = OnSignalMessageCb;
params_.on_connection_status = OnConnectionStatusCb;
params_.on_net_status_report = OnNetStatusReport;
@@ -1593,6 +1594,7 @@ void Render::MainLoop() {
UpdateLabels();
HandleRecentConnections();
HandleConnectionStatusChange();
HandleStreamWindow();
HandleServerWindow();
@@ -1632,10 +1634,46 @@ void Render::HandleRecentConnections() {
LOG_INFO("Load recent connection thumbnails");
}
reload_recent_connections_ = false;
recent_connection_ids_.clear();
for (const auto& conn : recent_connections_) {
recent_connection_ids_.push_back(conn.first);
}
need_to_send_recent_connections_ = true;
}
}
}
void Render::HandleConnectionStatusChange() {
if (signal_connected_ && peer_ && need_to_send_recent_connections_) {
if (!recent_connection_ids_.empty()) {
nlohmann::json j;
j["type"] = "recent_connections_presence";
j["user_id"] = client_id_;
j["devices"] = nlohmann::json::array();
for (const auto& id : recent_connection_ids_) {
std::string pure_id = id;
size_t pos_y = pure_id.find('Y');
size_t pos_n = pure_id.find('N');
size_t pos = std::string::npos;
if (pos_y != std::string::npos &&
(pos_n == std::string::npos || pos_y < pos_n)) {
pos = pos_y;
} else if (pos_n != std::string::npos) {
pos = pos_n;
}
if (pos != std::string::npos) {
pure_id = pure_id.substr(0, pos);
}
j["devices"].push_back(pure_id);
}
auto s = j.dump();
SendSignalMessage(peer_, s.data(), s.size());
}
}
need_to_send_recent_connections_ = false;
}
void Render::HandleStreamWindow() {
if (need_to_create_stream_window_) {
CreateStreamWindow();

View File

@@ -25,6 +25,7 @@
#include "IconsFontAwesome6.h"
#include "config_center.h"
#include "device_controller_factory.h"
#include "device_presence.h"
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_sdlrenderer3.h"
@@ -192,6 +193,7 @@ class Render {
void UpdateLabels();
void UpdateInteractions();
void HandleRecentConnections();
void HandleConnectionStatusChange();
void HandleStreamWindow();
void HandleServerWindow();
void Cleanup();
@@ -286,6 +288,9 @@ class Render {
static void OnSignalStatusCb(SignalStatus status, const char* user_id,
size_t user_id_size, void* user_data);
static void OnSignalMessageCb(const char* message, size_t size,
void* user_data);
static void OnConnectionStatusCb(ConnectionStatus status, const char* user_id,
size_t user_id_size, void* user_data);
@@ -402,9 +407,12 @@ class Render {
// recent connections
std::vector<std::pair<std::string, Thumbnail::RecentConnection>>
recent_connections_;
std::vector<std::string> recent_connection_ids_;
int recent_connection_image_width_ = 160;
int recent_connection_image_height_ = 90;
uint32_t recent_connection_image_save_time_ = 0;
DevicePresence device_presence_;
bool need_to_send_recent_connections_ = true;
// main window render
SDL_Window* main_window_ = nullptr;

View File

@@ -6,6 +6,7 @@
#include <fstream>
#include <limits>
#include <unordered_map>
#include <unordered_set>
#include "clipboard.h"
#include "device_controller.h"
@@ -20,6 +21,47 @@
namespace crossdesk {
void Render::OnSignalMessageCb(const char* message, size_t size,
void* user_data) {
Render* render = (Render*)user_data;
if (!render || !message || size == 0) {
return;
}
std::string s(message, size);
auto j = nlohmann::json::parse(s, nullptr, false);
if (j.is_discarded() || !j.contains("type") || !j["type"].is_string()) {
return;
}
std::string type = j["type"].get<std::string>();
if (type == "presence") {
if (j.contains("devices") && j["devices"].is_array()) {
for (auto& dev : j["devices"]) {
if (!dev.is_object()) {
continue;
}
if (!dev.contains("id") || !dev["id"].is_string()) {
continue;
}
if (!dev.contains("online") || !dev["online"].is_boolean()) {
continue;
}
std::string id = dev["id"].get<std::string>();
bool online = dev["online"].get<bool>();
render->device_presence_.SetOnline(id, online);
}
}
} else if (type == "presence_update") {
if (j.contains("id") && j["id"].is_string() && j.contains("online") &&
j["online"].is_boolean()) {
std::string id = j["id"].get<std::string>();
bool online = j["online"].get<bool>();
if (!id.empty()) {
render->device_presence_.SetOnline(id, online);
}
}
}
}
int Render::SendKeyCommand(int key_code, bool is_down) {
RemoteAction remote_action;
remote_action.type = ControlType::keyboard;