mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-26 12:15:34 +08:00
Merge branch 'run-in-bg' into self-hosted-server
This commit is contained in:
@@ -44,6 +44,9 @@ int ConfigCenter::Load() {
|
||||
enable_self_hosted_ =
|
||||
ini_.GetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);
|
||||
|
||||
enable_minimize_to_tray_ = ini_.GetBoolValue(
|
||||
section_, "enable_minimize_to_tray", enable_minimize_to_tray_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -62,6 +65,8 @@ int ConfigCenter::Save() {
|
||||
ini_.SetLongValue(section_, "server_port", static_cast<long>(server_port_));
|
||||
ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str());
|
||||
ini_.SetBoolValue(section_, "enable_self_hosted", enable_self_hosted_);
|
||||
ini_.SetBoolValue(section_, "enable_minimize_to_tray",
|
||||
enable_minimize_to_tray_);
|
||||
|
||||
SI_Error rc = ini_.SaveFile(config_path_.c_str());
|
||||
if (rc < 0) {
|
||||
@@ -186,6 +191,11 @@ int ConfigCenter::SetSelfHosted(bool enable_self_hosted) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ConfigCenter::SetMinimizeToTray(bool enable_minimize_to_tray) {
|
||||
enable_minimize_to_tray_ = enable_minimize_to_tray;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// getters
|
||||
|
||||
ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() const { return language_; }
|
||||
@@ -227,3 +237,5 @@ std::string ConfigCenter::GetDefaultCertFilePath() const {
|
||||
}
|
||||
|
||||
bool ConfigCenter::IsSelfHosted() const { return enable_self_hosted_; }
|
||||
|
||||
bool ConfigCenter::IsMinimizeToTray() const { return enable_minimize_to_tray_; }
|
||||
@@ -36,6 +36,7 @@ class ConfigCenter {
|
||||
int SetServerPort(int server_port);
|
||||
int SetCertFilePath(const std::string& cert_file_path);
|
||||
int SetSelfHosted(bool enable_self_hosted);
|
||||
int SetMinimizeToTray(bool enable_minimize_to_tray);
|
||||
|
||||
// read config
|
||||
|
||||
@@ -53,6 +54,7 @@ class ConfigCenter {
|
||||
int GetDefaultServerPort() const;
|
||||
std::string GetDefaultCertFilePath() const;
|
||||
bool IsSelfHosted() const;
|
||||
bool IsMinimizeToTray() const;
|
||||
|
||||
int Load();
|
||||
int Save();
|
||||
@@ -76,6 +78,7 @@ class ConfigCenter {
|
||||
int server_port_default_ = 9099;
|
||||
std::string cert_file_path_default_ = "";
|
||||
bool enable_self_hosted_ = false;
|
||||
bool enable_minimize_to_tray_ = false;
|
||||
};
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -20,8 +20,8 @@
|
||||
#define INPUT_WINDOW_PADDING_EN 96
|
||||
#define SETTINGS_WINDOW_WIDTH_CN 202
|
||||
#define SETTINGS_WINDOW_WIDTH_EN 248
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 315
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 315
|
||||
#define SETTINGS_WINDOW_HEIGHT_CN 345
|
||||
#define SETTINGS_WINDOW_HEIGHT_EN 345
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN 228
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN 275
|
||||
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN 165
|
||||
@@ -42,6 +42,8 @@
|
||||
#define ENABLE_SRTP_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171
|
||||
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN 218
|
||||
#define ENABLE_MINIZE_TO_TRAY_PADDING_CN 171
|
||||
#define ENABLE_MINIZE_TO_TRAY_PADDING_EN 218
|
||||
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_CN 90
|
||||
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_EN 137
|
||||
#define SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN 90
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#if _WIN32
|
||||
#include <Windows.h>
|
||||
#endif
|
||||
namespace localization {
|
||||
|
||||
static std::vector<std::string> local_desktop = {
|
||||
@@ -155,6 +159,12 @@ static std::vector<std::string> version = {
|
||||
static std::vector<std::string> confirm_delete_connection = {
|
||||
reinterpret_cast<const char*>(u8"确认删除此连接"),
|
||||
"Confirm to delete this connection"};
|
||||
} // namespace localization
|
||||
#if _WIN32
|
||||
|
||||
static std::vector<std::string> minimize_to_tray = {
|
||||
reinterpret_cast<const char*>(u8"退出时最小化到系统托盘:"),
|
||||
"Minimize to system tray when exit:"};
|
||||
static std::vector<LPCWSTR> exit_program = {L"退出", L"Exit"};
|
||||
#endif
|
||||
} // namespace localization
|
||||
#endif
|
||||
@@ -588,6 +588,17 @@ int Render::CreateMainWindow() {
|
||||
// for window region action
|
||||
SDL_SetWindowHitTest(main_window_, HitTestCallback, this);
|
||||
|
||||
#if _WIN32
|
||||
SDL_PropertiesID props = SDL_GetWindowProperties(main_window_);
|
||||
HWND main_hwnd = (HWND)SDL_GetPointerProperty(
|
||||
props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);
|
||||
|
||||
HICON tray_icon = (HICON)LoadImageW(NULL, L"crossdesk.ico", IMAGE_ICON, 0, 0,
|
||||
LR_LOADFROMFILE | LR_DEFAULTSIZE);
|
||||
tray_ = std::make_unique<WinTray>(main_hwnd, tray_icon, L"CrossDesk",
|
||||
localization_language_index_);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -968,6 +979,14 @@ void Render::MainLoop() {
|
||||
ProcessSdlEvent(event);
|
||||
}
|
||||
|
||||
#if _WIN32
|
||||
MSG msg;
|
||||
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
#endif
|
||||
|
||||
UpdateLabels();
|
||||
HandleRecentConnections();
|
||||
HandleStreamWindow();
|
||||
|
||||
@@ -29,6 +29,9 @@
|
||||
#include "screen_capturer_factory.h"
|
||||
#include "speaker_capturer_factory.h"
|
||||
#include "thumbnail.h"
|
||||
#if _WIN32
|
||||
#include "win_tray.h"
|
||||
#endif
|
||||
|
||||
class Render {
|
||||
public:
|
||||
@@ -298,6 +301,9 @@ class Render {
|
||||
ImGuiContext* main_ctx_ = nullptr;
|
||||
bool exit_ = false;
|
||||
const int sdl_refresh_ms_ = 16; // ~60 FPS
|
||||
#if _WIN32
|
||||
std::unique_ptr<WinTray> tray_;
|
||||
#endif
|
||||
|
||||
// main window properties
|
||||
bool start_mouse_controller_ = false;
|
||||
@@ -444,6 +450,8 @@ class Render {
|
||||
bool enable_hardware_video_codec_last_ = false;
|
||||
bool enable_turn_last_ = false;
|
||||
bool enable_srtp_last_ = false;
|
||||
bool enable_minimize_to_tray_ = false;
|
||||
bool enable_minimize_to_tray_last_ = false;
|
||||
char signal_server_ip_tmp_[256] = "api.crossdesk.cn";
|
||||
char signal_server_port_tmp_[6] = "9099";
|
||||
bool settings_window_pos_reset_ = true;
|
||||
|
||||
@@ -139,9 +139,17 @@ int Render::TitleBar(bool main_window) {
|
||||
float xmark_size = 12.0f;
|
||||
std::string close_button = "##xmark"; // ICON_FA_XMARK;
|
||||
if (ImGui::Button(close_button.c_str(), ImVec2(BUTTON_PADDING, 30))) {
|
||||
#if _WIN32
|
||||
if (enable_minimize_to_tray_) {
|
||||
tray_->MinimizeToTray();
|
||||
} else {
|
||||
#endif
|
||||
SDL_Event event;
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
#if _WIN32
|
||||
}
|
||||
#endif
|
||||
}
|
||||
draw_list->AddLine(ImVec2(xmark_pos_x - xmark_size / 2 - 0.25f,
|
||||
xmark_pos_y - xmark_size / 2 + 0.75f),
|
||||
|
||||
112
src/gui/tray/win_tray.cpp
Normal file
112
src/gui/tray/win_tray.cpp
Normal file
@@ -0,0 +1,112 @@
|
||||
#include "win_tray.h"
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "localization.h"
|
||||
|
||||
// callback for the message-only window that handles tray icon messages
|
||||
static LRESULT CALLBACK MsgWndProc(HWND hwnd, UINT msg, WPARAM wParam,
|
||||
LPARAM lParam) {
|
||||
WinTray* tray =
|
||||
reinterpret_cast<WinTray*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
|
||||
if (!tray) {
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
if (msg == WM_TRAY_CALLBACK) {
|
||||
MSG tmpMsg = {};
|
||||
tmpMsg.message = msg;
|
||||
tmpMsg.wParam = wParam;
|
||||
tmpMsg.lParam = lParam;
|
||||
tray->HandleTrayMessage(&tmpMsg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
|
||||
WinTray::WinTray(HWND app_hwnd, HICON icon, const std::wstring& tooltip,
|
||||
int language_index)
|
||||
: app_hwnd_(app_hwnd),
|
||||
icon_(icon),
|
||||
tip_(tooltip),
|
||||
hwnd_message_only_(nullptr),
|
||||
language_index_(language_index) {
|
||||
WNDCLASS wc = {};
|
||||
wc.lpfnWndProc = MsgWndProc;
|
||||
wc.hInstance = GetModuleHandle(nullptr);
|
||||
wc.lpszClassName = L"TrayMessageWindow";
|
||||
RegisterClass(&wc);
|
||||
|
||||
// create a message-only window to receive tray messages
|
||||
hwnd_message_only_ =
|
||||
CreateWindowEx(0, wc.lpszClassName, L"TrayMsg", 0, 0, 0, 0, 0,
|
||||
HWND_MESSAGE, nullptr, wc.hInstance, nullptr);
|
||||
|
||||
// store pointer to this WinTray instance in window data
|
||||
SetWindowLongPtr(hwnd_message_only_, GWLP_USERDATA,
|
||||
reinterpret_cast<LONG_PTR>(this));
|
||||
|
||||
// initialize NOTIFYICONDATA structure
|
||||
ZeroMemory(&nid_, sizeof(nid_));
|
||||
nid_.cbSize = sizeof(nid_);
|
||||
nid_.hWnd = hwnd_message_only_;
|
||||
nid_.uID = 1;
|
||||
nid_.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
|
||||
nid_.uCallbackMessage = WM_TRAY_CALLBACK;
|
||||
nid_.hIcon = icon_;
|
||||
wcsncpy_s(nid_.szTip, tip_.c_str(), _TRUNCATE);
|
||||
}
|
||||
|
||||
WinTray::~WinTray() {
|
||||
RemoveTrayIcon();
|
||||
if (hwnd_message_only_) DestroyWindow(hwnd_message_only_);
|
||||
}
|
||||
|
||||
void WinTray::MinimizeToTray() {
|
||||
Shell_NotifyIcon(NIM_ADD, &nid_);
|
||||
// hide application window
|
||||
ShowWindow(app_hwnd_, SW_HIDE);
|
||||
}
|
||||
|
||||
void WinTray::RemoveTrayIcon() { Shell_NotifyIcon(NIM_DELETE, &nid_); }
|
||||
|
||||
bool WinTray::HandleTrayMessage(MSG* msg) {
|
||||
if (!msg || msg->message != WM_TRAY_CALLBACK) return false;
|
||||
|
||||
switch (LOWORD(msg->lParam)) {
|
||||
case WM_LBUTTONDBLCLK:
|
||||
case WM_LBUTTONUP: {
|
||||
ShowWindow(app_hwnd_, SW_SHOW);
|
||||
SetForegroundWindow(app_hwnd_);
|
||||
break;
|
||||
}
|
||||
|
||||
case WM_RBUTTONUP: {
|
||||
POINT pt;
|
||||
GetCursorPos(&pt);
|
||||
HMENU menu = CreatePopupMenu();
|
||||
AppendMenuW(menu, MF_STRING, 1001,
|
||||
localization::exit_program[language_index_]);
|
||||
|
||||
SetForegroundWindow(hwnd_message_only_);
|
||||
int cmd =
|
||||
TrackPopupMenu(menu, TPM_RETURNCMD | TPM_NONOTIFY | TPM_LEFTALIGN,
|
||||
pt.x, pt.y, 0, hwnd_message_only_, nullptr);
|
||||
DestroyMenu(menu);
|
||||
|
||||
// handle menu command
|
||||
if (cmd == 1001) {
|
||||
// exit application
|
||||
SDL_Event event;
|
||||
event.type = SDL_EVENT_QUIT;
|
||||
SDL_PushEvent(&event);
|
||||
} else if (cmd == 1002) {
|
||||
ShowWindow(app_hwnd_, SW_SHOW); // show main window
|
||||
SetForegroundWindow(app_hwnd_);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
36
src/gui/tray/win_tray.h
Normal file
36
src/gui/tray/win_tray.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2025-10-22
|
||||
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _WIN_TRAY_H_
|
||||
#define _WIN_TRAY_H_
|
||||
|
||||
#include <Windows.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#define WM_TRAY_CALLBACK (WM_USER + 1)
|
||||
|
||||
class WinTray {
|
||||
public:
|
||||
WinTray(HWND app_hwnd, HICON icon, const std::wstring& tooltip,
|
||||
int language_index);
|
||||
~WinTray();
|
||||
|
||||
void MinimizeToTray();
|
||||
void RemoveTrayIcon();
|
||||
bool HandleTrayMessage(MSG* msg);
|
||||
|
||||
private:
|
||||
HWND app_hwnd_;
|
||||
HWND hwnd_message_only_;
|
||||
HICON icon_;
|
||||
std::wstring tip_;
|
||||
int language_index_;
|
||||
NOTIFYICONDATA nid_;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -57,7 +57,7 @@ int Render::SettingWindow() {
|
||||
localization::language_en[localization_language_index_].c_str()};
|
||||
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
ImGui::Text(
|
||||
"%s", localization::language[localization_language_index_].c_str());
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
@@ -88,7 +88,7 @@ int Render::SettingWindow() {
|
||||
.c_str()};
|
||||
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::video_quality[localization_language_index_].c_str());
|
||||
@@ -111,7 +111,7 @@ int Render::SettingWindow() {
|
||||
const char* video_frame_rate_items[] = {"30 fps", "60 fps"};
|
||||
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
ImGui::Text("%s",
|
||||
localization::video_frame_rate[localization_language_index_]
|
||||
.c_str());
|
||||
@@ -137,7 +137,7 @@ int Render::SettingWindow() {
|
||||
localization::av1[localization_language_index_].c_str()};
|
||||
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::video_encode_format[localization_language_index_]
|
||||
@@ -160,7 +160,7 @@ int Render::SettingWindow() {
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
ImGui::Text("%s", localization::enable_hardware_video_codec
|
||||
[localization_language_index_]
|
||||
.c_str());
|
||||
@@ -179,7 +179,7 @@ int Render::SettingWindow() {
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::enable_turn[localization_language_index_].c_str());
|
||||
@@ -197,7 +197,7 @@ int Render::SettingWindow() {
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
ImGui::Text(
|
||||
"%s",
|
||||
localization::enable_srtp[localization_language_index_].c_str());
|
||||
@@ -215,7 +215,7 @@ int Render::SettingWindow() {
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 2);
|
||||
ImGui::SetCursorPosY(settings_items_offset + 1);
|
||||
|
||||
if (ImGui::Button(localization::self_hosted_server_config
|
||||
[localization_language_index_]
|
||||
@@ -232,7 +232,27 @@ int Render::SettingWindow() {
|
||||
ImGui::Checkbox("##enable_self_hosted_server",
|
||||
&enable_self_hosted_server_);
|
||||
}
|
||||
#if _WIN32
|
||||
ImGui::Separator();
|
||||
|
||||
{
|
||||
settings_items_offset += settings_items_padding;
|
||||
ImGui::SetCursorPosY(settings_items_offset + 4);
|
||||
|
||||
ImGui::Text("%s",
|
||||
localization::minimize_to_tray[localization_language_index_]
|
||||
.c_str());
|
||||
|
||||
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
|
||||
ImGui::SetCursorPosX(ENABLE_MINIZE_TO_TRAY_PADDING_CN);
|
||||
} else {
|
||||
ImGui::SetCursorPosX(ENABLE_MINIZE_TO_TRAY_PADDING_EN);
|
||||
}
|
||||
ImGui::SetCursorPosY(settings_items_offset);
|
||||
ImGui::Checkbox("##enable_minimize_to_tray_",
|
||||
&enable_minimize_to_tray_);
|
||||
}
|
||||
#endif
|
||||
if (stream_window_inited_) {
|
||||
ImGui::EndDisabled();
|
||||
}
|
||||
|
||||
@@ -150,6 +150,10 @@ target("gui")
|
||||
"src/gui/windows/*.cpp")
|
||||
add_includedirs("src/gui", "src/gui/panels", "src/gui/toolbars",
|
||||
"src/gui/windows", {public = true})
|
||||
if is_os("windows") then
|
||||
add_files("src/gui/tray/*.cpp")
|
||||
add_includedirs("src/gui/tray", {public = true})
|
||||
end
|
||||
|
||||
target("crossdesk")
|
||||
set_kind("binary")
|
||||
|
||||
Reference in New Issue
Block a user