mirror of
https://github.com/kunkundi/crossdesk.git
synced 2026-04-07 02:15:17 +08:00
[feat] add online status indicators for recent connections
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -216,6 +216,11 @@ static std::vector<std::string> connection_mode_direct = {
|
|||||||
reinterpret_cast<const char*>(u8"直连"), "Direct"};
|
reinterpret_cast<const char*>(u8"直连"), "Direct"};
|
||||||
static std::vector<std::string> connection_mode_relay = {
|
static std::vector<std::string> connection_mode_relay = {
|
||||||
reinterpret_cast<const char*>(u8"中继"), "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
|
#if _WIN32
|
||||||
static std::vector<LPCWSTR> exit_program = {L"退出", L"Exit"};
|
static std::vector<LPCWSTR> exit_program = {L"退出", L"Exit"};
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
36
src/gui/device_presence.h
Normal file
36
src/gui/device_presence.h
Normal 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
|
||||||
@@ -122,6 +122,8 @@ int Render::ShowRecentConnections() {
|
|||||||
it.second.remote_host_name = "unknown";
|
it.second.remote_host_name = "unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool online = device_presence_.IsOnline(it.second.remote_id);
|
||||||
|
|
||||||
ImVec2 image_screen_pos = ImVec2(
|
ImVec2 image_screen_pos = ImVec2(
|
||||||
ImGui::GetCursorScreenPos().x + recent_connection_image_width * 0.04f,
|
ImGui::GetCursorScreenPos().x + recent_connection_image_width * 0.04f,
|
||||||
ImGui::GetCursorScreenPos().y + recent_connection_image_height * 0.08f);
|
ImGui::GetCursorScreenPos().y + recent_connection_image_height * 0.08f);
|
||||||
@@ -133,6 +135,26 @@ int Render::ShowRecentConnections() {
|
|||||||
(ImTextureID)(intptr_t)it.second.texture,
|
(ImTextureID)(intptr_t)it.second.texture,
|
||||||
ImVec2(recent_connection_image_width, recent_connection_image_height));
|
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
|
// remote id display button
|
||||||
{
|
{
|
||||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0.2f));
|
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0, 0, 0, 0.2f));
|
||||||
|
|||||||
@@ -810,6 +810,7 @@ int Render::CreateConnectionPeer() {
|
|||||||
params_.on_receive_video_frame = OnReceiveVideoBufferCb;
|
params_.on_receive_video_frame = OnReceiveVideoBufferCb;
|
||||||
|
|
||||||
params_.on_signal_status = OnSignalStatusCb;
|
params_.on_signal_status = OnSignalStatusCb;
|
||||||
|
params_.on_signal_message = OnSignalMessageCb;
|
||||||
params_.on_connection_status = OnConnectionStatusCb;
|
params_.on_connection_status = OnConnectionStatusCb;
|
||||||
params_.on_net_status_report = OnNetStatusReport;
|
params_.on_net_status_report = OnNetStatusReport;
|
||||||
|
|
||||||
@@ -1593,6 +1594,7 @@ void Render::MainLoop() {
|
|||||||
|
|
||||||
UpdateLabels();
|
UpdateLabels();
|
||||||
HandleRecentConnections();
|
HandleRecentConnections();
|
||||||
|
HandleConnectionStatusChange();
|
||||||
HandleStreamWindow();
|
HandleStreamWindow();
|
||||||
HandleServerWindow();
|
HandleServerWindow();
|
||||||
|
|
||||||
@@ -1632,10 +1634,46 @@ void Render::HandleRecentConnections() {
|
|||||||
LOG_INFO("Load recent connection thumbnails");
|
LOG_INFO("Load recent connection thumbnails");
|
||||||
}
|
}
|
||||||
reload_recent_connections_ = false;
|
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() {
|
void Render::HandleStreamWindow() {
|
||||||
if (need_to_create_stream_window_) {
|
if (need_to_create_stream_window_) {
|
||||||
CreateStreamWindow();
|
CreateStreamWindow();
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#include "IconsFontAwesome6.h"
|
#include "IconsFontAwesome6.h"
|
||||||
#include "config_center.h"
|
#include "config_center.h"
|
||||||
#include "device_controller_factory.h"
|
#include "device_controller_factory.h"
|
||||||
|
#include "device_presence.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include "imgui_impl_sdl3.h"
|
#include "imgui_impl_sdl3.h"
|
||||||
#include "imgui_impl_sdlrenderer3.h"
|
#include "imgui_impl_sdlrenderer3.h"
|
||||||
@@ -192,6 +193,7 @@ class Render {
|
|||||||
void UpdateLabels();
|
void UpdateLabels();
|
||||||
void UpdateInteractions();
|
void UpdateInteractions();
|
||||||
void HandleRecentConnections();
|
void HandleRecentConnections();
|
||||||
|
void HandleConnectionStatusChange();
|
||||||
void HandleStreamWindow();
|
void HandleStreamWindow();
|
||||||
void HandleServerWindow();
|
void HandleServerWindow();
|
||||||
void Cleanup();
|
void Cleanup();
|
||||||
@@ -286,6 +288,9 @@ class Render {
|
|||||||
static void OnSignalStatusCb(SignalStatus status, const char* user_id,
|
static void OnSignalStatusCb(SignalStatus status, const char* user_id,
|
||||||
size_t user_id_size, void* user_data);
|
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,
|
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);
|
||||||
|
|
||||||
@@ -402,9 +407,12 @@ class Render {
|
|||||||
// recent connections
|
// recent connections
|
||||||
std::vector<std::pair<std::string, Thumbnail::RecentConnection>>
|
std::vector<std::pair<std::string, Thumbnail::RecentConnection>>
|
||||||
recent_connections_;
|
recent_connections_;
|
||||||
|
std::vector<std::string> recent_connection_ids_;
|
||||||
int recent_connection_image_width_ = 160;
|
int recent_connection_image_width_ = 160;
|
||||||
int recent_connection_image_height_ = 90;
|
int recent_connection_image_height_ = 90;
|
||||||
uint32_t recent_connection_image_save_time_ = 0;
|
uint32_t recent_connection_image_save_time_ = 0;
|
||||||
|
DevicePresence device_presence_;
|
||||||
|
bool need_to_send_recent_connections_ = true;
|
||||||
|
|
||||||
// main window render
|
// main window render
|
||||||
SDL_Window* main_window_ = nullptr;
|
SDL_Window* main_window_ = nullptr;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
#include "clipboard.h"
|
#include "clipboard.h"
|
||||||
#include "device_controller.h"
|
#include "device_controller.h"
|
||||||
@@ -20,6 +21,47 @@
|
|||||||
|
|
||||||
namespace crossdesk {
|
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) {
|
int Render::SendKeyCommand(int key_code, bool is_down) {
|
||||||
RemoteAction remote_action;
|
RemoteAction remote_action;
|
||||||
remote_action.type = ControlType::keyboard;
|
remote_action.type = ControlType::keyboard;
|
||||||
|
|||||||
Reference in New Issue
Block a user