2 Commits

Author SHA1 Message Date
dijunkun
b2ab940f20 [feat] use no close select table 2025-10-23 17:55:30 +08:00
dijunkun
2f0b0ffc22 [feat] add configuration to minimize to system tray when clicking the close button, refs #4 2025-10-22 17:21:47 +08:00
19 changed files with 2568 additions and 2743 deletions

View File

@@ -44,6 +44,9 @@ int ConfigCenter::Load() {
enable_self_hosted_ = enable_self_hosted_ =
ini_.GetBoolValue(section_, "enable_self_hosted", 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; return 0;
} }
@@ -62,6 +65,8 @@ int ConfigCenter::Save() {
ini_.SetLongValue(section_, "server_port", static_cast<long>(server_port_)); ini_.SetLongValue(section_, "server_port", static_cast<long>(server_port_));
ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str()); ini_.SetValue(section_, "cert_file_path", cert_file_path_.c_str());
ini_.SetBoolValue(section_, "enable_self_hosted", enable_self_hosted_); 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()); SI_Error rc = ini_.SaveFile(config_path_.c_str());
if (rc < 0) { if (rc < 0) {
@@ -186,6 +191,11 @@ int ConfigCenter::SetSelfHosted(bool enable_self_hosted) {
return 0; return 0;
} }
int ConfigCenter::SetMinimizeToTray(bool enable_minimize_to_tray) {
enable_minimize_to_tray_ = enable_minimize_to_tray;
return 0;
}
// getters // getters
ConfigCenter::LANGUAGE ConfigCenter::GetLanguage() const { return language_; } 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::IsSelfHosted() const { return enable_self_hosted_; }
bool ConfigCenter::IsMinimizeToTray() const { return enable_minimize_to_tray_; }

View File

@@ -36,6 +36,7 @@ class ConfigCenter {
int SetServerPort(int server_port); int SetServerPort(int server_port);
int SetCertFilePath(const std::string& cert_file_path); int SetCertFilePath(const std::string& cert_file_path);
int SetSelfHosted(bool enable_self_hosted); int SetSelfHosted(bool enable_self_hosted);
int SetMinimizeToTray(bool enable_minimize_to_tray);
// read config // read config
@@ -53,6 +54,7 @@ class ConfigCenter {
int GetDefaultServerPort() const; int GetDefaultServerPort() const;
std::string GetDefaultCertFilePath() const; std::string GetDefaultCertFilePath() const;
bool IsSelfHosted() const; bool IsSelfHosted() const;
bool IsMinimizeToTray() const;
int Load(); int Load();
int Save(); int Save();
@@ -76,6 +78,7 @@ class ConfigCenter {
int server_port_default_ = 9099; int server_port_default_ = 9099;
std::string cert_file_path_default_ = ""; std::string cert_file_path_default_ = "";
bool enable_self_hosted_ = false; bool enable_self_hosted_ = false;
bool enable_minimize_to_tray_ = false;
}; };
#endif #endif

File diff suppressed because it is too large Load Diff

View File

@@ -20,8 +20,8 @@
#define INPUT_WINDOW_PADDING_EN 96 #define INPUT_WINDOW_PADDING_EN 96
#define SETTINGS_WINDOW_WIDTH_CN 202 #define SETTINGS_WINDOW_WIDTH_CN 202
#define SETTINGS_WINDOW_WIDTH_EN 248 #define SETTINGS_WINDOW_WIDTH_EN 248
#define SETTINGS_WINDOW_HEIGHT_CN 315 #define SETTINGS_WINDOW_HEIGHT_CN 345
#define SETTINGS_WINDOW_HEIGHT_EN 315 #define SETTINGS_WINDOW_HEIGHT_EN 345
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN 228 #define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_CN 228
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN 275 #define SELF_HOSTED_SERVER_CONFIG_WINDOW_WIDTH_EN 275
#define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN 165 #define SELF_HOSTED_SERVER_CONFIG_WINDOW_HEIGHT_CN 165
@@ -42,6 +42,8 @@
#define ENABLE_SRTP_CHECKBOX_PADDING_EN 218 #define ENABLE_SRTP_CHECKBOX_PADDING_EN 218
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171 #define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_CN 171
#define ENABLE_SELF_HOSTED_SERVER_CHECKBOX_PADDING_EN 218 #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_CN 90
#define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_EN 137 #define SELF_HOSTED_SERVER_HOST_INPUT_BOX_PADDING_EN 137
#define SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN 90 #define SELF_HOSTED_SERVER_PORT_INPUT_BOX_PADDING_CN 90

View File

@@ -8,6 +8,10 @@
#include <string> #include <string>
#include <vector> #include <vector>
#if _WIN32
#include <Windows.h>
#endif
namespace localization { namespace localization {
static std::vector<std::string> local_desktop = { 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 = { static std::vector<std::string> confirm_delete_connection = {
reinterpret_cast<const char*>(u8"确认删除此连接"), reinterpret_cast<const char*>(u8"确认删除此连接"),
"Confirm to delete this connection"}; "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 #endif

View File

@@ -588,6 +588,17 @@ int Render::CreateMainWindow() {
// for window region action // for window region action
SDL_SetWindowHitTest(main_window_, HitTestCallback, this); 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; return 0;
} }
@@ -968,6 +979,14 @@ void Render::MainLoop() {
ProcessSdlEvent(event); ProcessSdlEvent(event);
} }
#if _WIN32
MSG msg;
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
#endif
UpdateLabels(); UpdateLabels();
HandleRecentConnections(); HandleRecentConnections();
HandleStreamWindow(); HandleStreamWindow();

View File

@@ -29,6 +29,9 @@
#include "screen_capturer_factory.h" #include "screen_capturer_factory.h"
#include "speaker_capturer_factory.h" #include "speaker_capturer_factory.h"
#include "thumbnail.h" #include "thumbnail.h"
#if _WIN32
#include "win_tray.h"
#endif
class Render { class Render {
public: public:
@@ -298,6 +301,9 @@ class Render {
ImGuiContext* main_ctx_ = nullptr; ImGuiContext* main_ctx_ = nullptr;
bool exit_ = false; bool exit_ = false;
const int sdl_refresh_ms_ = 16; // ~60 FPS const int sdl_refresh_ms_ = 16; // ~60 FPS
#if _WIN32
std::unique_ptr<WinTray> tray_;
#endif
// main window properties // main window properties
bool start_mouse_controller_ = false; bool start_mouse_controller_ = false;
@@ -444,6 +450,8 @@ class Render {
bool enable_hardware_video_codec_last_ = false; bool enable_hardware_video_codec_last_ = false;
bool enable_turn_last_ = false; bool enable_turn_last_ = false;
bool enable_srtp_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_ip_tmp_[256] = "api.crossdesk.cn";
char signal_server_port_tmp_[6] = "9099"; char signal_server_port_tmp_[6] = "9099";
bool settings_window_pos_reset_ = true; bool settings_window_pos_reset_ = true;

View File

@@ -139,9 +139,17 @@ int Render::TitleBar(bool main_window) {
float xmark_size = 12.0f; float xmark_size = 12.0f;
std::string close_button = "##xmark"; // ICON_FA_XMARK; std::string close_button = "##xmark"; // ICON_FA_XMARK;
if (ImGui::Button(close_button.c_str(), ImVec2(BUTTON_PADDING, 30))) { if (ImGui::Button(close_button.c_str(), ImVec2(BUTTON_PADDING, 30))) {
SDL_Event event; #if _WIN32
event.type = SDL_EVENT_QUIT; if (enable_minimize_to_tray_) {
SDL_PushEvent(&event); 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, draw_list->AddLine(ImVec2(xmark_pos_x - xmark_size / 2 - 0.25f,
xmark_pos_y - xmark_size / 2 + 0.75f), xmark_pos_y - xmark_size / 2 + 0.75f),

112
src/gui/tray/win_tray.cpp Normal file
View 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
View 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

View File

@@ -57,7 +57,7 @@ int Render::SettingWindow() {
localization::language_en[localization_language_index_].c_str()}; localization::language_en[localization_language_index_].c_str()};
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2); ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::Text( ImGui::Text(
"%s", localization::language[localization_language_index_].c_str()); "%s", localization::language[localization_language_index_].c_str());
if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) { if (ConfigCenter::LANGUAGE::CHINESE == localization_language_) {
@@ -88,7 +88,7 @@ int Render::SettingWindow() {
.c_str()}; .c_str()};
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2); ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::Text( ImGui::Text(
"%s", "%s",
localization::video_quality[localization_language_index_].c_str()); 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"}; const char* video_frame_rate_items[] = {"30 fps", "60 fps"};
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2); ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::Text("%s", ImGui::Text("%s",
localization::video_frame_rate[localization_language_index_] localization::video_frame_rate[localization_language_index_]
.c_str()); .c_str());
@@ -137,7 +137,7 @@ int Render::SettingWindow() {
localization::av1[localization_language_index_].c_str()}; localization::av1[localization_language_index_].c_str()};
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2); ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::Text( ImGui::Text(
"%s", "%s",
localization::video_encode_format[localization_language_index_] localization::video_encode_format[localization_language_index_]
@@ -160,7 +160,7 @@ int Render::SettingWindow() {
{ {
settings_items_offset += settings_items_padding; 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 ImGui::Text("%s", localization::enable_hardware_video_codec
[localization_language_index_] [localization_language_index_]
.c_str()); .c_str());
@@ -179,7 +179,7 @@ int Render::SettingWindow() {
{ {
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2); ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::Text( ImGui::Text(
"%s", "%s",
localization::enable_turn[localization_language_index_].c_str()); localization::enable_turn[localization_language_index_].c_str());
@@ -197,7 +197,7 @@ int Render::SettingWindow() {
{ {
settings_items_offset += settings_items_padding; settings_items_offset += settings_items_padding;
ImGui::SetCursorPosY(settings_items_offset + 2); ImGui::SetCursorPosY(settings_items_offset + 4);
ImGui::Text( ImGui::Text(
"%s", "%s",
localization::enable_srtp[localization_language_index_].c_str()); localization::enable_srtp[localization_language_index_].c_str());
@@ -215,7 +215,7 @@ int Render::SettingWindow() {
{ {
settings_items_offset += settings_items_padding; 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 if (ImGui::Button(localization::self_hosted_server_config
[localization_language_index_] [localization_language_index_]
@@ -232,7 +232,27 @@ int Render::SettingWindow() {
ImGui::Checkbox("##enable_self_hosted_server", ImGui::Checkbox("##enable_self_hosted_server",
&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_) { if (stream_window_inited_) {
ImGui::EndDisabled(); ImGui::EndDisabled();
} }

View File

@@ -43,7 +43,12 @@ int Render::ShowSimpleFileBrowser() {
localization::select_a_file[localization_language_index_].c_str(); localization::select_a_file[localization_language_index_].c_str();
} }
// 设置固定宽度
// ImGui::SetNextItemWidth(SELF_HOSTED_SERVER_INPUT_WINDOW_WIDTH);
ImGui::PushItemFlag(ImGuiItemFlags_AutoClosePopups, false); // 禁用自动关闭
if (ImGui::BeginCombo("##select_a_file", display_text.c_str())) { if (ImGui::BeginCombo("##select_a_file", display_text.c_str())) {
bool file_selected = false;
if (selected_current_file_path_ == "Root" || if (selected_current_file_path_ == "Root" ||
!std::filesystem::exists(selected_current_file_path_) || !std::filesystem::exists(selected_current_file_path_) ||
!std::filesystem::is_directory(selected_current_file_path_)) { !std::filesystem::is_directory(selected_current_file_path_)) {
@@ -77,6 +82,7 @@ int Render::ShowSimpleFileBrowser() {
} else { } else {
if (ImGui::Selectable(name.c_str())) { if (ImGui::Selectable(name.c_str())) {
selected_file_ = entry.path().string(); selected_file_ = entry.path().string();
file_selected = true; // 记录选中文件
} }
} }
} }
@@ -85,8 +91,14 @@ int Render::ShowSimpleFileBrowser() {
} }
} }
ImGui::EndCombo(); // 如果选中了文件,则自动关闭下拉框
if (file_selected) {
ImGui::EndCombo(); // 关闭下拉框
} else {
ImGui::EndCombo(); // 保持下拉框开启
}
} }
ImGui::PopItemFlag(); // 恢复默认行为
return 0; return 0;
} }

View File

@@ -12,7 +12,7 @@
static std::vector<DisplayInfo> gs_display_list; static std::vector<DisplayInfo> gs_display_list;
std::string WideToUtf8(const wchar_t* wideStr) { std::string WideToUtf8(const wchar_t *wideStr) {
int size_needed = WideCharToMultiByte(CP_UTF8, 0, wideStr, -1, nullptr, 0, int size_needed = WideCharToMultiByte(CP_UTF8, 0, wideStr, -1, nullptr, 0,
nullptr, nullptr); nullptr, nullptr);
std::string result(size_needed, 0); std::string result(size_needed, 0);
@@ -31,14 +31,14 @@ BOOL WINAPI EnumMonitorProc(HMONITOR hmonitor, [[maybe_unused]] HDC hdc,
if (monitor_info_.dwFlags & MONITORINFOF_PRIMARY) { if (monitor_info_.dwFlags & MONITORINFOF_PRIMARY) {
gs_display_list.insert( gs_display_list.insert(
gs_display_list.begin(), gs_display_list.begin(),
{(void*)hmonitor, WideToUtf8(monitor_info_.szDevice), {(void *)hmonitor, WideToUtf8(monitor_info_.szDevice),
(monitor_info_.dwFlags & MONITORINFOF_PRIMARY) ? true : false, (monitor_info_.dwFlags & MONITORINFOF_PRIMARY) ? true : false,
monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top, monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top,
monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom}); monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom});
*(HMONITOR*)data = hmonitor; *(HMONITOR *)data = hmonitor;
} else { } else {
gs_display_list.push_back(DisplayInfo( gs_display_list.push_back(DisplayInfo(
(void*)hmonitor, WideToUtf8(monitor_info_.szDevice), (void *)hmonitor, WideToUtf8(monitor_info_.szDevice),
(monitor_info_.dwFlags & MONITORINFOF_PRIMARY) ? true : false, (monitor_info_.dwFlags & MONITORINFOF_PRIMARY) ? true : false,
monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top, monitor_info_.rcMonitor.left, monitor_info_.rcMonitor.top,
monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom)); monitor_info_.rcMonitor.right, monitor_info_.rcMonitor.bottom));
@@ -81,7 +81,7 @@ bool ScreenCapturerWgc::IsWgcSupported() {
/* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */ /* no contract for IGraphicsCaptureItemInterop, verify 10.0.18362.0 */
return winrt::Windows::Foundation::Metadata::ApiInformation:: return winrt::Windows::Foundation::Metadata::ApiInformation::
IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 8); IsApiContractPresent(L"Windows.Foundation.UniversalApiContract", 8);
} catch (const winrt::hresult_error&) { } catch (const winrt::hresult_error &) {
return false; return false;
} catch (...) { } catch (...) {
return false; return false;
@@ -115,7 +115,7 @@ int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) {
} }
for (int i = 0; i < display_info_list_.size(); i++) { for (int i = 0; i < display_info_list_.size(); i++) {
const auto& display = display_info_list_[i]; const auto &display = display_info_list_[i];
LOG_INFO( LOG_INFO(
"index: {}, display name: {}, is primary: {}, bounds: ({}, {}) - " "index: {}, display name: {}, is primary: {}, bounds: ({}, {}) - "
"({}, {})", "({}, {})",
@@ -243,28 +243,26 @@ int ScreenCapturerWgc::SwitchTo(int monitor_index) {
return 0; return 0;
} }
void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame& frame, void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame &frame,
int id) { int id) {
if (!on_data_) { if (on_data_) {
return; if (!nv12_frame_) {
nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2];
}
libyuv::ARGBToNV12((const uint8_t *)frame.data, frame.width * 4,
(uint8_t *)nv12_frame_, frame.width,
(uint8_t *)(nv12_frame_ + frame.width * frame.height),
frame.width, frame.width, frame.height);
on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width,
frame.height, display_info_list_[id].name.c_str());
} }
if (!nv12_frame_) {
nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2];
}
libyuv::ARGBToNV12((const uint8_t*)frame.data, frame.width * 4,
(uint8_t*)nv12_frame_, frame.width,
(uint8_t*)(nv12_frame_ + frame.width * frame.height),
frame.width, frame.width, frame.height);
on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width,
frame.height, display_info_list_[id].name.c_str());
} }
void ScreenCapturerWgc::CleanUp() { void ScreenCapturerWgc::CleanUp() {
if (inited_) { if (inited_) {
for (auto& session : sessions_) { for (auto &session : sessions_) {
if (session.session_) { if (session.session_) {
session.session_->Stop(); session.session_->Stop();
} }

View File

@@ -43,8 +43,6 @@ class ScreenCapturerWgc : public ScreenCapturer,
std::vector<DisplayInfo> display_info_list_; std::vector<DisplayInfo> display_info_list_;
int monitor_index_ = 0; int monitor_index_ = 0;
HWND hwnd_ = nullptr;
private: private:
class WgcSessionInfo { class WgcSessionInfo {
public: public:
@@ -65,9 +63,6 @@ class ScreenCapturerWgc : public ScreenCapturer,
unsigned char* nv12_frame_ = nullptr; unsigned char* nv12_frame_ = nullptr;
unsigned char* nv12_frame_scaled_ = nullptr; unsigned char* nv12_frame_scaled_ = nullptr;
private:
bool CreateHiddenWindow();
}; };
#endif #endif

View File

@@ -1,68 +0,0 @@
#ifndef _SCREEN_CAPTURER_WGC_H_
#define _SCREEN_CAPTURER_WGC_H_
#include <atomic>
#include <functional>
#include <string>
#include <thread>
#include <vector>
#include "screen_capturer.h"
#include "wgc_session.h"
#include "wgc_session_impl.h"
class ScreenCapturerWgc : public ScreenCapturer,
public WgcSession::wgc_session_observer {
public:
ScreenCapturerWgc();
~ScreenCapturerWgc();
public:
bool IsWgcSupported();
int Init(const int fps, cb_desktop_data cb) override;
int Destroy() override;
int Start() override;
int Stop() override;
int Pause(int monitor_index) override;
int Resume(int monitor_index) override;
std::vector<DisplayInfo> GetDisplayInfoList() { return display_info_list_; }
int SwitchTo(int monitor_index);
void OnFrame(const WgcSession::wgc_session_frame& frame, int id);
protected:
void CleanUp();
private:
HMONITOR monitor_;
MONITORINFOEX monitor_info_;
std::vector<DisplayInfo> display_info_list_;
int monitor_index_ = 0;
private:
class WgcSessionInfo {
public:
std::unique_ptr<WgcSession> session_;
bool inited_ = false;
bool running_ = false;
bool paused_ = false;
};
std::vector<WgcSessionInfo> sessions_;
std::atomic_bool running_;
std::atomic_bool inited_;
int fps_ = 60;
cb_desktop_data on_data_ = nullptr;
unsigned char* nv12_frame_ = nullptr;
unsigned char* nv12_frame_scaled_ = nullptr;
};
#endif

View File

@@ -1,187 +0,0 @@
#include <Windows.h>
#include <d3d11_4.h>
#include <iostream>
#include <thread>
#include "libyuv.h"
#include "rd_log.h"
#include "screen_capturer_wgc.h"
// Dummy window proc for hidden window
static LRESULT CALLBACK DummyWndProc(HWND hwnd, UINT msg, WPARAM wParam,
LPARAM lParam) {
return DefWindowProc(hwnd, msg, wParam, lParam);
}
// ======================= 构造函数 / 析构函数 =======================
ScreenCapturerWgc::ScreenCapturerWgc()
: monitor_(nullptr),
hwnd_(nullptr),
monitor_index_(0),
running_(false),
inited_(false),
fps_(60),
on_data_(nullptr),
nv12_frame_(nullptr),
nv12_frame_scaled_(nullptr) {}
ScreenCapturerWgc::~ScreenCapturerWgc() {
Stop();
CleanUp();
if (nv12_frame_) {
delete[] nv12_frame_;
nv12_frame_ = nullptr;
}
if (nv12_frame_scaled_) {
delete[] nv12_frame_scaled_;
nv12_frame_scaled_ = nullptr;
}
}
// ======================= 隐藏窗口创建 =======================
bool ScreenCapturerWgc::CreateHiddenWindow() {
const wchar_t kClassName[] = L"ScreenCapturerHiddenWindow";
WNDCLASSW wc = {};
wc.lpfnWndProc = DummyWndProc;
wc.hInstance = GetModuleHandle(nullptr);
wc.lpszClassName = kClassName;
if (!RegisterClassW(&wc)) {
std::cerr << "Failed to register dummy window class\n";
return false;
}
hwnd_ = CreateWindowW(kClassName, L"", WS_OVERLAPPEDWINDOW, 0, 0, 1, 1,
nullptr, nullptr, wc.hInstance, nullptr);
if (!hwnd_) {
std::cerr << "Failed to create dummy window\n";
return false;
}
ShowWindow(hwnd_, SW_HIDE);
return true;
}
// ======================= 初始化 =======================
int ScreenCapturerWgc::Init(const int fps, cb_desktop_data cb) {
if (inited_) return 0;
fps_ = fps;
on_data_ = cb;
// 创建隐藏窗口
if (!CreateHiddenWindow()) {
return -1;
}
// 初始化 WGC Session
sessions_.push_back(
{std::make_unique<WgcSessionImpl>(0), false, false, false});
sessions_.back().session_->RegisterObserver(this);
int error = sessions_.back().session_->Initialize(hwnd_);
if (error != 0) {
std::cerr << "WGC session init failed\n";
return error;
}
sessions_[0].inited_ = true;
inited_ = true;
return 0;
}
int ScreenCapturerWgc::Destroy() {
Stop();
CleanUp();
return 0;
}
int ScreenCapturerWgc::Pause(int monitor_index) {
// 目前只支持隐藏窗口,所以忽略 monitor_index
if (!running_) return -1;
sessions_[0].session_->Pause();
sessions_[0].paused_ = true;
return 0;
}
int ScreenCapturerWgc::Resume(int monitor_index) {
if (!running_) return -1;
sessions_[0].session_->Resume();
sessions_[0].paused_ = false;
return 0;
}
int ScreenCapturerWgc::SwitchTo(int monitor_index) {
// 单隐藏窗口模式,不支持切换
return 0;
}
// ======================= 开始 =======================
int ScreenCapturerWgc::Start() {
if (!inited_) return -1;
if (running_) return 0;
if (sessions_.empty()) return -1;
sessions_[0].session_->Start();
sessions_[0].running_ = true;
running_ = true;
return 0;
}
// ======================= 停止 =======================
int ScreenCapturerWgc::Stop() {
if (!running_) return 0;
if (!sessions_.empty()) {
sessions_[0].session_->Stop();
sessions_[0].running_ = false;
}
running_ = false;
if (hwnd_) {
DestroyWindow(hwnd_);
hwnd_ = nullptr;
}
inited_ = false;
sessions_.clear();
return 0;
}
// ======================= 帧回调 =======================
void ScreenCapturerWgc::OnFrame(const WgcSession::wgc_session_frame& frame,
int id) {
if (!on_data_) {
return;
}
if (!nv12_frame_) {
nv12_frame_ = new unsigned char[frame.width * frame.height * 3 / 2];
}
libyuv::ARGBToNV12((const uint8_t*)frame.data, frame.width * 4,
(uint8_t*)nv12_frame_, frame.width,
(uint8_t*)(nv12_frame_ + frame.width * frame.height),
frame.width, frame.width, frame.height);
on_data_(nv12_frame_, frame.width * frame.height * 3 / 2, frame.width,
frame.height, "hidden_window");
}
// ======================= 清理 =======================
void ScreenCapturerWgc::CleanUp() {
if (inited_) {
for (auto& session : sessions_) {
if (session.session_) {
session.session_->Stop();
}
}
sessions_.clear();
}
}

View File

@@ -7,8 +7,6 @@
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include "rd_log.h"
#define CHECK_INIT \ #define CHECK_INIT \
if (!is_initialized_) { \ if (!is_initialized_) { \
std::cout << "AE_NEED_INIT" << std::endl; \ std::cout << "AE_NEED_INIT" << std::endl; \
@@ -22,118 +20,122 @@
extern "C" { extern "C" {
HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice( HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(
::IDXGIDevice* dxgiDevice, ::IInspectable** graphicsDevice); ::IDXGIDevice *dxgiDevice, ::IInspectable **graphicsDevice);
} }
WgcSessionImpl::WgcSessionImpl(int id) : id_(id) {} WgcSessionImpl::WgcSessionImpl(int id) : id_(id) {}
WgcSessionImpl::~WgcSessionImpl() { WgcSessionImpl::~WgcSessionImpl() {
try { Stop();
Stop(); CleanUp();
CleanUp();
} catch (...) {
}
} }
void WgcSessionImpl::Release() { delete this; } void WgcSessionImpl::Release() { delete this; }
int WgcSessionImpl::Initialize(HWND hwnd) { int WgcSessionImpl::Initialize(HWND hwnd) {
std::scoped_lock locker(lock_); std::lock_guard locker(lock_);
target_.hwnd = hwnd; target_.hwnd = hwnd;
target_.is_window = true; target_.is_window = true;
return Initialize(); return Initialize();
} }
int WgcSessionImpl::Initialize(HMONITOR hmonitor) { int WgcSessionImpl::Initialize(HMONITOR hmonitor) {
std::scoped_lock locker(lock_); std::lock_guard locker(lock_);
target_.hmonitor = hmonitor; target_.hmonitor = hmonitor;
target_.is_window = false; target_.is_window = false;
return Initialize(); return Initialize();
} }
void WgcSessionImpl::RegisterObserver(wgc_session_observer* observer) { void WgcSessionImpl::RegisterObserver(wgc_session_observer *observer) {
std::scoped_lock locker(lock_); std::lock_guard locker(lock_);
observer_ = observer; observer_ = observer;
} }
int WgcSessionImpl::Start() { int WgcSessionImpl::Start() {
std::scoped_lock locker(lock_); std::lock_guard locker(lock_);
CHECK_INIT;
if (is_running_) return 0; if (is_running_) return 0;
int error = 1;
CHECK_INIT;
try { try {
if (!capture_item_) { if (!capture_session_) {
std::cout << "AE_NO_CAPTURE_ITEM" << std::endl; auto current_size = capture_item_.Size();
LOG_ERROR("No capture item"); capture_framepool_ =
return 2; winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
CreateFreeThreaded(d3d11_direct_device_,
winrt::Windows::Graphics::DirectX::
DirectXPixelFormat::B8G8R8A8UIntNormalized,
2, current_size);
capture_session_ = capture_framepool_.CreateCaptureSession(capture_item_);
capture_frame_size_ = current_size;
capture_framepool_trigger_ = capture_framepool_.FrameArrived(
winrt::auto_revoke, {this, &WgcSessionImpl::OnFrame});
capture_close_trigger_ = capture_item_.Closed(
winrt::auto_revoke, {this, &WgcSessionImpl::OnClosed});
} }
auto current_size = capture_item_.Size(); if (!capture_framepool_) throw std::exception();
capture_framepool_ =
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
CreateFreeThreaded(d3d11_direct_device_,
winrt::Windows::Graphics::DirectX::
DirectXPixelFormat::B8G8R8A8UIntNormalized,
2, current_size);
capture_session_ = capture_framepool_.CreateCaptureSession(capture_item_);
capture_frame_size_ = current_size;
capture_framepool_trigger_ = capture_framepool_.FrameArrived(
winrt::auto_revoke, {this, &WgcSessionImpl::OnFrame});
capture_close_trigger_ = capture_item_.Closed(
winrt::auto_revoke, {this, &WgcSessionImpl::OnClosed});
capture_session_.IsCursorCaptureEnabled(false);
capture_session_.StartCapture();
is_running_ = true; is_running_ = true;
is_paused_ = false;
return 0; // we do not need to crate a thread to enter a message loop coz we use
} catch (winrt::hresult_error const& e) { // CreateFreeThreaded instead of Create to create a capture frame pool,
LOG_ERROR("Create WGC Capture Failed: {}", winrt::to_string(e.message())); // we need to test the performance later
// loop_ = std::thread(std::bind(&WgcSessionImpl::message_func, this));
capture_session_.StartCapture();
capture_session_.IsCursorCaptureEnabled(false);
error = 0;
} catch (winrt::hresult_error) {
std::cout << "AE_WGC_CREATE_CAPTURER_FAILED" << std::endl;
return 86; return 86;
} catch (...) { } catch (...) {
return 86; return 86;
} }
return error;
} }
int WgcSessionImpl::Stop() { int WgcSessionImpl::Stop() {
std::scoped_lock locker(lock_); std::lock_guard locker(lock_);
CHECK_INIT; CHECK_INIT;
if (!is_running_) return 0;
is_running_ = false; is_running_ = false;
try { // if (loop_.joinable()) loop_.join();
if (capture_framepool_trigger_) capture_framepool_trigger_.revoke();
if (capture_close_trigger_) capture_close_trigger_.revoke(); if (capture_framepool_trigger_) capture_framepool_trigger_.revoke();
if (capture_session_) {
capture_session_.Close(); if (capture_session_) {
capture_session_ = nullptr; capture_session_.Close();
} capture_session_ = nullptr;
if (capture_framepool_) {
capture_framepool_.Close();
capture_framepool_ = nullptr;
}
} catch (...) {
} }
return 0; return 0;
} }
int WgcSessionImpl::Pause() { int WgcSessionImpl::Pause() {
std::scoped_lock locker(lock_); std::lock_guard locker(lock_);
CHECK_INIT;
is_paused_ = true; is_paused_ = true;
CHECK_INIT;
return 0; return 0;
} }
int WgcSessionImpl::Resume() { int WgcSessionImpl::Resume() {
std::scoped_lock locker(lock_); std::lock_guard locker(lock_);
CHECK_INIT;
is_paused_ = false; is_paused_ = false;
CHECK_INIT;
return 0; return 0;
} }
@@ -141,15 +143,19 @@ auto WgcSessionImpl::CreateD3D11Device() {
winrt::com_ptr<ID3D11Device> d3d_device; winrt::com_ptr<ID3D11Device> d3d_device;
HRESULT hr; HRESULT hr;
WINRT_ASSERT(!d3d_device);
D3D_DRIVER_TYPE type = D3D_DRIVER_TYPE_HARDWARE;
UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT; UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, flags, hr = D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0,
nullptr, 0, D3D11_SDK_VERSION, d3d_device.put(), D3D11_SDK_VERSION, d3d_device.put(), nullptr, nullptr);
nullptr, nullptr);
if (DXGI_ERROR_UNSUPPORTED == hr) { if (DXGI_ERROR_UNSUPPORTED == hr) {
hr = D3D11CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, flags, // change D3D_DRIVER_TYPE
nullptr, 0, D3D11_SDK_VERSION, d3d_device.put(), type = D3D_DRIVER_TYPE_WARP;
nullptr, nullptr); hr = D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0,
D3D11_SDK_VERSION, d3d_device.put(), nullptr,
nullptr);
} }
winrt::check_hresult(hr); winrt::check_hresult(hr);
@@ -162,28 +168,26 @@ auto WgcSessionImpl::CreateD3D11Device() {
} }
auto WgcSessionImpl::CreateCaptureItemForWindow(HWND hwnd) { auto WgcSessionImpl::CreateCaptureItemForWindow(HWND hwnd) {
auto interop_factory = auto activation_factory = winrt::get_activation_factory<
winrt::get_activation_factory< winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>() auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
.as<IGraphicsCaptureItemInterop>(); winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item{nullptr};
interop_factory->CreateForWindow( interop_factory->CreateForWindow(
hwnd, hwnd,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(), winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
reinterpret_cast<void**>(winrt::put_abi(item))); reinterpret_cast<void **>(winrt::put_abi(item)));
return item; return item;
} }
auto WgcSessionImpl::CreateCaptureItemForMonitor(HMONITOR hmonitor) { auto WgcSessionImpl::CreateCaptureItemForMonitor(HMONITOR hmonitor) {
auto interop_factory = auto activation_factory = winrt::get_activation_factory<
winrt::get_activation_factory< winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>() auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
.as<IGraphicsCaptureItemInterop>(); winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item{nullptr};
interop_factory->CreateForMonitor( interop_factory->CreateForMonitor(
hmonitor, hmonitor,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(), winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
reinterpret_cast<void**>(winrt::put_abi(item))); reinterpret_cast<void **>(winrt::put_abi(item)));
return item; return item;
} }
@@ -192,10 +196,13 @@ HRESULT WgcSessionImpl::CreateMappedTexture(
unsigned int height) { unsigned int height) {
D3D11_TEXTURE2D_DESC src_desc; D3D11_TEXTURE2D_DESC src_desc;
src_texture->GetDesc(&src_desc); src_texture->GetDesc(&src_desc);
D3D11_TEXTURE2D_DESC map_desc;
D3D11_TEXTURE2D_DESC map_desc = src_desc; map_desc.Width = width == 0 ? src_desc.Width : width;
map_desc.Width = width ? width : src_desc.Width; map_desc.Height = height == 0 ? src_desc.Height : height;
map_desc.Height = height ? height : src_desc.Height; map_desc.MipLevels = src_desc.MipLevels;
map_desc.ArraySize = src_desc.ArraySize;
map_desc.Format = src_desc.Format;
map_desc.SampleDesc = src_desc.SampleDesc;
map_desc.Usage = D3D11_USAGE_STAGING; map_desc.Usage = D3D11_USAGE_STAGING;
map_desc.BindFlags = 0; map_desc.BindFlags = 0;
map_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; map_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
@@ -209,101 +216,163 @@ HRESULT WgcSessionImpl::CreateMappedTexture(
} }
void WgcSessionImpl::OnFrame( void WgcSessionImpl::OnFrame(
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const& sender, winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const &sender,
[[maybe_unused]] winrt::Windows::Foundation::IInspectable const& args) { [[maybe_unused]] winrt::Windows::Foundation::IInspectable const &args) {
if (!is_running_ || is_paused_) return; std::lock_guard locker(lock_);
std::scoped_lock locker(lock_);
if (!observer_) return; auto is_new_size = false;
auto frame = sender.TryGetNextFrame(); {
if (!frame) return; auto frame = sender.TryGetNextFrame();
auto frame_size = frame.ContentSize();
auto frame_size = frame.ContentSize(); if (frame_size.Width != capture_frame_size_.Width ||
bool size_changed = (frame_size.Width != capture_frame_size_.Width || frame_size.Height != capture_frame_size_.Height) {
frame_size.Height != capture_frame_size_.Height); // The thing we have been capturing has changed size.
if (size_changed) { // We need to resize our swap chain first, then blit the pixels.
capture_frame_size_ = frame_size; // After we do that, retire the frame and then recreate our frame pool.
is_new_size = true;
capture_frame_size_ = frame_size;
}
// copy to mapped texture
{
if (is_paused_) {
return;
}
auto frame_captured =
GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
if (!d3d11_texture_mapped_ || is_new_size)
CreateMappedTexture(frame_captured);
d3d11_device_context_->CopyResource(d3d11_texture_mapped_.get(),
frame_captured.get());
D3D11_MAPPED_SUBRESOURCE map_result;
HRESULT hr = d3d11_device_context_->Map(
d3d11_texture_mapped_.get(), 0, D3D11_MAP_READ,
0 /*coz we use CreateFreeThreaded, so we cant use flags
D3D11_MAP_FLAG_DO_NOT_WAIT*/
,
&map_result);
if (FAILED(hr)) {
OutputDebugStringW(
(L"map resource failed: " + std::to_wstring(hr)).c_str());
}
// copy data from map_result.pData
if (map_result.pData && observer_) {
observer_->OnFrame(
wgc_session_frame{static_cast<unsigned int>(frame_size.Width),
static_cast<unsigned int>(frame_size.Height),
map_result.RowPitch,
const_cast<const unsigned char *>(
(unsigned char *)map_result.pData)},
id_);
}
d3d11_device_context_->Unmap(d3d11_texture_mapped_.get(), 0);
}
}
if (is_new_size) {
capture_framepool_.Recreate(d3d11_direct_device_, capture_framepool_.Recreate(d3d11_direct_device_,
winrt::Windows::Graphics::DirectX:: winrt::Windows::Graphics::DirectX::
DirectXPixelFormat::B8G8R8A8UIntNormalized, DirectXPixelFormat::B8G8R8A8UIntNormalized,
2, capture_frame_size_); 2, capture_frame_size_);
} }
auto frame_surface =
GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
if (!d3d11_texture_mapped_ || size_changed)
CreateMappedTexture(frame_surface, frame_size.Width, frame_size.Height);
d3d11_device_context_->CopyResource(d3d11_texture_mapped_.get(),
frame_surface.get());
D3D11_MAPPED_SUBRESOURCE map_result;
HRESULT hr = d3d11_device_context_->Map(d3d11_texture_mapped_.get(), 0,
D3D11_MAP_READ, 0, &map_result);
if (SUCCEEDED(hr)) {
wgc_session_frame frame_info{
static_cast<unsigned int>(frame_size.Width),
static_cast<unsigned int>(frame_size.Height), map_result.RowPitch,
reinterpret_cast<const unsigned char*>(map_result.pData)};
observer_->OnFrame(frame_info, id_);
d3d11_device_context_->Unmap(d3d11_texture_mapped_.get(), 0);
}
} }
void WgcSessionImpl::OnClosed( void WgcSessionImpl::OnClosed(
winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&, winrt::Windows::Graphics::Capture::GraphicsCaptureItem const &,
winrt::Windows::Foundation::IInspectable const&) { winrt::Windows::Foundation::IInspectable const &) {
OutputDebugStringW(L"WgcSessionImpl::OnClosed"); OutputDebugStringW(L"WgcSessionImpl::OnClosed");
Stop();
} }
int WgcSessionImpl::Initialize() { int WgcSessionImpl::Initialize() {
if (is_initialized_) return 0; if (is_initialized_) return 0;
d3d11_direct_device_ = CreateD3D11Device(); if (!(d3d11_direct_device_ = CreateD3D11Device())) {
if (!d3d11_direct_device_) {
std::cout << "AE_D3D_CREATE_DEVICE_FAILED" << std::endl; std::cout << "AE_D3D_CREATE_DEVICE_FAILED" << std::endl;
return 1; return 1;
} }
try { try {
capture_item_ = target_.is_window if (target_.is_window)
? CreateCaptureItemForWindow(target_.hwnd) capture_item_ = CreateCaptureItemForWindow(target_.hwnd);
: CreateCaptureItemForMonitor(target_.hmonitor); else
capture_item_ = CreateCaptureItemForMonitor(target_.hmonitor);
auto d3d_device = // Set up
auto d3d11_device =
GetDXGIInterfaceFromObject<ID3D11Device>(d3d11_direct_device_); GetDXGIInterfaceFromObject<ID3D11Device>(d3d11_direct_device_);
d3d_device->GetImmediateContext(d3d11_device_context_.put()); d3d11_device->GetImmediateContext(d3d11_device_context_.put());
} catch (winrt::hresult_error) {
std::cout << "AE_WGC_CREATE_CAPTURER_FAILED" << std::endl;
return 86;
} catch (...) { } catch (...) {
LOG_ERROR("AE_WGC_CREATE_CAPTURER_FAILED");
return 86; return 86;
} }
is_initialized_ = true; is_initialized_ = true;
return 0; return 0;
} }
void WgcSessionImpl::CleanUp() { void WgcSessionImpl::CleanUp() {
std::scoped_lock locker(lock_); std::lock_guard locker(lock_);
if (cleaned_.exchange(true)) return;
auto expected = false;
if (cleaned_.compare_exchange_strong(expected, true)) {
capture_close_trigger_.revoke();
capture_framepool_trigger_.revoke();
try {
if (capture_framepool_trigger_) capture_framepool_trigger_.revoke();
if (capture_close_trigger_) capture_close_trigger_.revoke();
if (capture_framepool_) capture_framepool_.Close(); if (capture_framepool_) capture_framepool_.Close();
if (capture_session_) capture_session_.Close(); if (capture_session_) capture_session_.Close();
} catch (...) {
capture_framepool_ = nullptr;
capture_session_ = nullptr;
capture_item_ = nullptr;
is_initialized_ = false;
} }
capture_framepool_ = nullptr;
capture_session_ = nullptr;
capture_item_ = nullptr;
d3d11_texture_mapped_ = nullptr;
d3d11_device_context_ = nullptr;
d3d11_direct_device_ = nullptr;
is_initialized_ = false;
is_running_ = false;
} }
LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param,
LPARAM l_param) {
return DefWindowProc(window, message, w_param, l_param);
}
// void WgcSessionImpl::message_func() {
// const std::wstring kClassName = L"am_fake_window";
// WNDCLASS wc = {};
// wc.style = CS_HREDRAW | CS_VREDRAW;
// wc.lpfnWndProc = DefWindowProc;
// wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
// wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW);
// wc.lpszClassName = kClassName.c_str();
// if (!::RegisterClassW(&wc)) return;
// hwnd_ = ::CreateWindowW(kClassName.c_str(), nullptr, WS_OVERLAPPEDWINDOW,
// 0,
// 0, 0, 0, nullptr, nullptr, nullptr, nullptr);
// MSG msg;
// while (is_running_) {
// while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
// if (!is_running_) break;
// TranslateMessage(&msg);
// DispatchMessage(&msg);
// }
// Sleep(10);
// }
// ::CloseWindow(hwnd_);
// ::DestroyWindow(hwnd_);
// }

View File

@@ -1,380 +0,0 @@
#include "wgc_session_impl.h"
#include <Windows.Graphics.Capture.Interop.h>
#include <atomic>
#include <functional>
#include <iostream>
#include <memory>
#include "rd_log.h"
#define CHECK_INIT \
if (!is_initialized_) { \
std::cout << "AE_NEED_INIT" << std::endl; \
return 4; \
}
#define CHECK_CLOSED \
if (cleaned_.load() == true) { \
throw winrt::hresult_error(RO_E_CLOSED); \
}
extern "C" {
HRESULT __stdcall CreateDirect3D11DeviceFromDXGIDevice(
::IDXGIDevice* dxgiDevice, ::IInspectable** graphicsDevice);
}
WgcSessionImpl::WgcSessionImpl(int id) : id_(id) {}
WgcSessionImpl::~WgcSessionImpl() {
Stop();
CleanUp();
}
void WgcSessionImpl::Release() { delete this; }
int WgcSessionImpl::Initialize(HWND hwnd) {
std::lock_guard locker(lock_);
target_.hwnd = hwnd;
target_.is_window = true;
return Initialize();
}
int WgcSessionImpl::Initialize(HMONITOR hmonitor) {
std::lock_guard locker(lock_);
target_.hmonitor = hmonitor;
target_.is_window = false;
return Initialize();
}
void WgcSessionImpl::RegisterObserver(wgc_session_observer* observer) {
std::lock_guard locker(lock_);
observer_ = observer;
}
int WgcSessionImpl::Start() {
std::lock_guard locker(lock_);
if (is_running_) return 0;
int error = 1;
CHECK_INIT;
try {
if (!capture_session_) {
auto current_size = capture_item_.Size();
capture_framepool_ =
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool::
CreateFreeThreaded(d3d11_direct_device_,
winrt::Windows::Graphics::DirectX::
DirectXPixelFormat::B8G8R8A8UIntNormalized,
2, current_size);
capture_session_ = capture_framepool_.CreateCaptureSession(capture_item_);
capture_frame_size_ = current_size;
capture_framepool_trigger_ = capture_framepool_.FrameArrived(
winrt::auto_revoke, {this, &WgcSessionImpl::OnFrame});
capture_close_trigger_ = capture_item_.Closed(
winrt::auto_revoke, {this, &WgcSessionImpl::OnClosed});
}
if (!capture_framepool_) throw std::exception();
is_running_ = true;
// we do not need to crate a thread to enter a message loop coz we use
// CreateFreeThreaded instead of Create to create a capture frame pool,
// we need to test the performance later
// loop_ = std::thread(std::bind(&WgcSessionImpl::message_func, this));
capture_session_.StartCapture();
capture_session_.IsCursorCaptureEnabled(false);
error = 0;
} catch (winrt::hresult_error) {
LOG_ERROR("Create WGC Capture Failed");
return 86;
} catch (...) {
return 86;
}
return error;
}
int WgcSessionImpl::Stop() {
std::lock_guard locker(lock_);
CHECK_INIT;
is_running_ = false;
// if (loop_.joinable()) loop_.join();
if (capture_framepool_trigger_) capture_framepool_trigger_.revoke();
if (capture_session_) {
capture_session_.Close();
capture_session_ = nullptr;
}
return 0;
}
int WgcSessionImpl::Pause() {
std::lock_guard locker(lock_);
is_paused_ = true;
CHECK_INIT;
return 0;
}
int WgcSessionImpl::Resume() {
std::lock_guard locker(lock_);
is_paused_ = false;
CHECK_INIT;
return 0;
}
auto WgcSessionImpl::CreateD3D11Device() {
winrt::com_ptr<ID3D11Device> d3d_device;
HRESULT hr;
WINRT_ASSERT(!d3d_device);
D3D_DRIVER_TYPE type = D3D_DRIVER_TYPE_HARDWARE;
UINT flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
hr = D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0,
D3D11_SDK_VERSION, d3d_device.put(), nullptr, nullptr);
if (DXGI_ERROR_UNSUPPORTED == hr) {
// change D3D_DRIVER_TYPE
type = D3D_DRIVER_TYPE_WARP;
hr = D3D11CreateDevice(nullptr, type, nullptr, flags, nullptr, 0,
D3D11_SDK_VERSION, d3d_device.put(), nullptr,
nullptr);
}
winrt::check_hresult(hr);
winrt::com_ptr<::IInspectable> d3d11_device;
winrt::check_hresult(CreateDirect3D11DeviceFromDXGIDevice(
d3d_device.as<IDXGIDevice>().get(), d3d11_device.put()));
return d3d11_device
.as<winrt::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>();
}
auto WgcSessionImpl::CreateCaptureItemForWindow(HWND hwnd) {
auto activation_factory = winrt::get_activation_factory<
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
interop_factory->CreateForWindow(
hwnd,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
reinterpret_cast<void**>(winrt::put_abi(item)));
return item;
}
auto WgcSessionImpl::CreateCaptureItemForMonitor(HMONITOR hmonitor) {
auto activation_factory = winrt::get_activation_factory<
winrt::Windows::Graphics::Capture::GraphicsCaptureItem>();
auto interop_factory = activation_factory.as<IGraphicsCaptureItemInterop>();
winrt::Windows::Graphics::Capture::GraphicsCaptureItem item = {nullptr};
interop_factory->CreateForMonitor(
hmonitor,
winrt::guid_of<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>(),
reinterpret_cast<void**>(winrt::put_abi(item)));
return item;
}
HRESULT WgcSessionImpl::CreateMappedTexture(
winrt::com_ptr<ID3D11Texture2D> src_texture, unsigned int width,
unsigned int height) {
D3D11_TEXTURE2D_DESC src_desc;
src_texture->GetDesc(&src_desc);
D3D11_TEXTURE2D_DESC map_desc;
map_desc.Width = width == 0 ? src_desc.Width : width;
map_desc.Height = height == 0 ? src_desc.Height : height;
map_desc.MipLevels = src_desc.MipLevels;
map_desc.ArraySize = src_desc.ArraySize;
map_desc.Format = src_desc.Format;
map_desc.SampleDesc = src_desc.SampleDesc;
map_desc.Usage = D3D11_USAGE_STAGING;
map_desc.BindFlags = 0;
map_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
map_desc.MiscFlags = 0;
auto d3dDevice =
GetDXGIInterfaceFromObject<ID3D11Device>(d3d11_direct_device_);
return d3dDevice->CreateTexture2D(&map_desc, nullptr,
d3d11_texture_mapped_.put());
}
void WgcSessionImpl::OnFrame(
winrt::Windows::Graphics::Capture::Direct3D11CaptureFramePool const& sender,
[[maybe_unused]] winrt::Windows::Foundation::IInspectable const& args) {
std::lock_guard locker(lock_);
auto is_new_size = false;
{
auto frame = sender.TryGetNextFrame();
auto frame_size = frame.ContentSize();
if (frame_size.Width != capture_frame_size_.Width ||
frame_size.Height != capture_frame_size_.Height) {
// The thing we have been capturing has changed size.
// We need to resize our swap chain first, then blit the pixels.
// After we do that, retire the frame and then recreate our frame pool.
is_new_size = true;
capture_frame_size_ = frame_size;
}
// copy to mapped texture
{
if (is_paused_) {
return;
}
auto frame_captured =
GetDXGIInterfaceFromObject<ID3D11Texture2D>(frame.Surface());
if (!d3d11_texture_mapped_ || is_new_size)
CreateMappedTexture(frame_captured);
d3d11_device_context_->CopyResource(d3d11_texture_mapped_.get(),
frame_captured.get());
D3D11_MAPPED_SUBRESOURCE map_result;
HRESULT hr = d3d11_device_context_->Map(
d3d11_texture_mapped_.get(), 0, D3D11_MAP_READ,
0 /*coz we use CreateFreeThreaded, so we cant use flags
D3D11_MAP_FLAG_DO_NOT_WAIT*/
,
&map_result);
if (FAILED(hr)) {
OutputDebugStringW(
(L"map resource failed: " + std::to_wstring(hr)).c_str());
}
// copy data from map_result.pData
if (map_result.pData && observer_) {
observer_->OnFrame(
wgc_session_frame{static_cast<unsigned int>(frame_size.Width),
static_cast<unsigned int>(frame_size.Height),
map_result.RowPitch,
const_cast<const unsigned char*>(
(unsigned char*)map_result.pData)},
id_);
}
d3d11_device_context_->Unmap(d3d11_texture_mapped_.get(), 0);
}
}
if (is_new_size) {
capture_framepool_.Recreate(d3d11_direct_device_,
winrt::Windows::Graphics::DirectX::
DirectXPixelFormat::B8G8R8A8UIntNormalized,
2, capture_frame_size_);
}
}
void WgcSessionImpl::OnClosed(
winrt::Windows::Graphics::Capture::GraphicsCaptureItem const&,
winrt::Windows::Foundation::IInspectable const&) {
OutputDebugStringW(L"WgcSessionImpl::OnClosed");
}
int WgcSessionImpl::Initialize() {
if (is_initialized_) return 0;
if (!(d3d11_direct_device_ = CreateD3D11Device())) {
std::cout << "AE_D3D_CREATE_DEVICE_FAILED" << std::endl;
return 1;
}
try {
if (target_.is_window)
capture_item_ = CreateCaptureItemForWindow(target_.hwnd);
else
capture_item_ = CreateCaptureItemForMonitor(target_.hmonitor);
// Set up
auto d3d11_device =
GetDXGIInterfaceFromObject<ID3D11Device>(d3d11_direct_device_);
d3d11_device->GetImmediateContext(d3d11_device_context_.put());
} catch (winrt::hresult_error) {
LOG_ERROR("AE_WGC_CREATE_CAPTURER_FAILED");
return 86;
} catch (...) {
return 86;
}
is_initialized_ = true;
return 0;
}
void WgcSessionImpl::CleanUp() {
std::lock_guard locker(lock_);
auto expected = false;
if (cleaned_.compare_exchange_strong(expected, true)) {
capture_close_trigger_.revoke();
capture_framepool_trigger_.revoke();
if (capture_framepool_) capture_framepool_.Close();
if (capture_session_) capture_session_.Close();
capture_framepool_ = nullptr;
capture_session_ = nullptr;
capture_item_ = nullptr;
is_initialized_ = false;
}
}
LRESULT CALLBACK WindowProc(HWND window, UINT message, WPARAM w_param,
LPARAM l_param) {
return DefWindowProc(window, message, w_param, l_param);
}
// void WgcSessionImpl::message_func() {
// const std::wstring kClassName = L"am_fake_window";
// WNDCLASS wc = {};
// wc.style = CS_HREDRAW | CS_VREDRAW;
// wc.lpfnWndProc = DefWindowProc;
// wc.hCursor = LoadCursor(nullptr, IDC_ARROW);
// wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOW);
// wc.lpszClassName = kClassName.c_str();
// if (!::RegisterClassW(&wc)) return;
// hwnd_ = ::CreateWindowW(kClassName.c_str(), nullptr, WS_OVERLAPPEDWINDOW,
// 0,
// 0, 0, 0, nullptr, nullptr, nullptr, nullptr);
// MSG msg;
// while (is_running_) {
// while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
// if (!is_running_) break;
// TranslateMessage(&msg);
// DispatchMessage(&msg);
// }
// Sleep(10);
// }
// ::CloseWindow(hwnd_);
// ::DestroyWindow(hwnd_);
// }

View File

@@ -150,6 +150,10 @@ target("gui")
"src/gui/windows/*.cpp") "src/gui/windows/*.cpp")
add_includedirs("src/gui", "src/gui/panels", "src/gui/toolbars", add_includedirs("src/gui", "src/gui/panels", "src/gui/toolbars",
"src/gui/windows", {public = true}) "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") target("crossdesk")
set_kind("binary") set_kind("binary")