|
|
@@ -1,6 +1,8 @@
|
|
|
#include "pch.h"
|
|
|
#include "CShuangpingTool.h"
|
|
|
|
|
|
+#include <vector>
|
|
|
+
|
|
|
CShuangpingTool::CShuangpingTool()
|
|
|
{
|
|
|
}
|
|
|
@@ -10,75 +12,103 @@ bool CShuangpingTool::IsShuangping()
|
|
|
return m_is_shuangping;
|
|
|
}
|
|
|
|
|
|
-void CShuangpingTool::Init()
|
|
|
+// EnumDisplayMonitors 的回调:把所有可见显示器的屏幕矩形收进来
|
|
|
+static BOOL CALLBACK ShuangpingMonitorEnumProc(HMONITOR /*hMonitor*/, HDC /*hdcMonitor*/, LPRECT lprcMonitor, LPARAM dwData)
|
|
|
{
|
|
|
- INT iNumber = GetSystemMetrics(SM_CMONITORS);
|
|
|
+ std::vector<RECT>* pRects = reinterpret_cast<std::vector<RECT>*>(dwData);
|
|
|
+ pRects->push_back(*lprcMonitor);
|
|
|
+ return TRUE;
|
|
|
+}
|
|
|
|
|
|
- if (iNumber > 1)
|
|
|
+void CShuangpingTool::Detect()
|
|
|
+{
|
|
|
+ m_is_shuangping = false;
|
|
|
+ ::SetRectEmpty(&m_fuping_rect);
|
|
|
+
|
|
|
+ std::vector<RECT> rects;
|
|
|
+ if (!::EnumDisplayMonitors(NULL, NULL, ShuangpingMonitorEnumProc, reinterpret_cast<LPARAM>(&rects)))
|
|
|
{
|
|
|
- m_is_shuangping = true;
|
|
|
+ LOG_INFO("副屏检测:EnumDisplayMonitors 失败,LastError=" << GetLastError());
|
|
|
+ return;
|
|
|
}
|
|
|
- else
|
|
|
- {
|
|
|
- //这种情况说明没有副屏
|
|
|
- m_is_shuangping = false;
|
|
|
|
|
|
+ if (rects.size() <= 1)
|
|
|
+ {
|
|
|
+ //只有一个显示器,不是双屏
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- //确定副屏显示区域
|
|
|
- RECT rect;
|
|
|
- rect.left = 0;
|
|
|
- rect.top = 0;
|
|
|
- rect.right = 0;
|
|
|
- rect.bottom = 0;
|
|
|
-
|
|
|
- for (int i = 0; i < iNumber; i++)
|
|
|
+ //遍历所有显示器,找到“非主屏”的那一块作为副屏
|
|
|
+ for (const RECT& rc : rects)
|
|
|
{
|
|
|
- //初始化
|
|
|
- BOOL bFlag;
|
|
|
-
|
|
|
- DISPLAY_DEVICE dd;
|
|
|
- ZeroMemory(&dd, sizeof(dd));
|
|
|
- dd.cb = sizeof(dd);
|
|
|
-
|
|
|
- DEVMODE devMode;
|
|
|
- ZeroMemory(&devMode, sizeof(devMode));
|
|
|
- devMode.dmSize = sizeof(devMode);
|
|
|
-
|
|
|
- bFlag = EnumDisplayDevices(NULL, i, &dd, 0);//如果有该显示器,再去获得它的设置
|
|
|
- if (bFlag == FALSE)
|
|
|
+ HMONITOR hMon = ::MonitorFromRect(&rc, MONITOR_DEFAULTTONULL);
|
|
|
+ if (hMon == NULL)
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- bFlag = EnumDisplaySettings(dd.DeviceName, ENUM_CURRENT_SETTINGS, &devMode);
|
|
|
- if (bFlag == FALSE)
|
|
|
+ MONITORINFO mi = { sizeof(mi) };
|
|
|
+ if (!::GetMonitorInfo(hMon, &mi))
|
|
|
{
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- if (devMode.dmPosition.x == 0 && devMode.dmPosition.y == 0)
|
|
|
+ if ((mi.dwFlags & MONITORINFOF_PRIMARY) == 0)
|
|
|
{
|
|
|
- //主显示器
|
|
|
- continue;
|
|
|
+ //非主屏:用 rcMonitor(含任务栏区域的整块屏幕)铺满副屏
|
|
|
+ m_fuping_rect = mi.rcMonitor;
|
|
|
+ m_is_shuangping = true;
|
|
|
+ LOG_INFO("副屏检测:找到副屏,区域=(" << m_fuping_rect.left << "," << m_fuping_rect.top
|
|
|
+ << "," << m_fuping_rect.right << "," << m_fuping_rect.bottom << ")");
|
|
|
+ return;
|
|
|
}
|
|
|
- else
|
|
|
- {
|
|
|
- //副显示器
|
|
|
- rect.left = devMode.dmPosition.x;
|
|
|
- rect.top = devMode.dmPosition.y;
|
|
|
+ }
|
|
|
|
|
|
- rect.right = rect.left + devMode.dmPelsWidth;
|
|
|
- rect.bottom = rect.top + devMode.dmPelsHeight;
|
|
|
+ LOG_INFO("副屏检测:检测到多块显示器但未找到有效的非主屏,不创建副屏窗口");
|
|
|
+}
|
|
|
|
|
|
- m_fuping_rect = rect;
|
|
|
- }
|
|
|
+void CShuangpingTool::EnsureDetected()
|
|
|
+{
|
|
|
+ std::lock_guard<std::mutex> lock(m_detectMutex);
|
|
|
+ if (!m_bDetected)
|
|
|
+ {
|
|
|
+ Detect();
|
|
|
+ m_bDetected = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void CShuangpingTool::Init()
|
|
|
+{
|
|
|
+ //程序启动时(可能在异步线程里)先做一次检测;InitWnd 还会再 EnsureDetected 兜底
|
|
|
+ EnsureDetected();
|
|
|
+}
|
|
|
+
|
|
|
void CShuangpingTool::InitWnd(HWND hwnd)
|
|
|
{
|
|
|
+ m_hMainWnd = hwnd;
|
|
|
+
|
|
|
+ //兜底检测:不依赖异步 Init 的完成时机,避免拿到未初始化/全零的副屏区域
|
|
|
+ EnsureDetected();
|
|
|
+
|
|
|
+ if (!m_is_shuangping)
|
|
|
+ {
|
|
|
+ LOG_INFO("非双屏模式,不创建副屏窗口");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //区域有效性校验:宽高必须大于 0,否则 MoveWindow 会变成零尺寸不可见窗口
|
|
|
+ if (m_fuping_rect.right <= m_fuping_rect.left || m_fuping_rect.bottom <= m_fuping_rect.top)
|
|
|
+ {
|
|
|
+ LOG_INFO("副屏区域无效(零尺寸),不创建副屏窗口");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (m_pFuping != NULL)
|
|
|
+ {
|
|
|
+ //已经创建过,避免重复创建
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
//创建并初始化副屏窗口
|
|
|
m_pFuping = new CFupingWnd();
|
|
|
if (m_pFuping == NULL)
|
|
|
@@ -91,10 +121,46 @@ void CShuangpingTool::InitWnd(HWND hwnd)
|
|
|
m_pFuping->Create(hwnd, _T("智铺子收银系统副屏"), UI_WNDSTYLE_DIALOG, 0, 0, 0, 0, 0, NULL);
|
|
|
m_pFuping->SetIcon(IDI_ICON_DUILIB);
|
|
|
|
|
|
- ::MoveWindow(m_pFuping->GetHWND(), m_fuping_rect.left, m_fuping_rect.top, m_fuping_rect.right - m_fuping_rect.left, m_fuping_rect.bottom - m_fuping_rect.top, true);
|
|
|
+ ::MoveWindow(m_pFuping->GetHWND(), m_fuping_rect.left, m_fuping_rect.top,
|
|
|
+ m_fuping_rect.right - m_fuping_rect.left, m_fuping_rect.bottom - m_fuping_rect.top, TRUE);
|
|
|
m_pFuping->ShowWindow(true);
|
|
|
}
|
|
|
|
|
|
+void CShuangpingTool::OnDisplayChange()
|
|
|
+{
|
|
|
+ bool bOldShuang = m_is_shuangping;
|
|
|
+ RECT oldRect = m_fuping_rect;
|
|
|
+
|
|
|
+ //强制重新检测当前显示器拓扑(加锁,与异步 Init / InitWnd 互斥)
|
|
|
+ {
|
|
|
+ std::lock_guard<std::mutex> lock(m_detectMutex);
|
|
|
+ m_bDetected = false;
|
|
|
+ Detect();
|
|
|
+ m_bDetected = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (bOldShuang && !m_is_shuangping)
|
|
|
+ {
|
|
|
+ LOG_INFO("显示器拓扑变化:副屏丢失,销毁副屏窗口");
|
|
|
+ DestoryWnd();
|
|
|
+ }
|
|
|
+ else if (!bOldShuang && m_is_shuangping)
|
|
|
+ {
|
|
|
+ LOG_INFO("显示器拓扑变化:检测到副屏,创建副屏窗口");
|
|
|
+ InitWnd(m_hMainWnd);
|
|
|
+ }
|
|
|
+ else if (m_is_shuangping && (oldRect.left != m_fuping_rect.left || oldRect.top != m_fuping_rect.top
|
|
|
+ || oldRect.right != m_fuping_rect.right || oldRect.bottom != m_fuping_rect.bottom))
|
|
|
+ {
|
|
|
+ LOG_INFO("显示器拓扑变化:副屏区域变化,移动副屏窗口");
|
|
|
+ if (m_pFuping != NULL)
|
|
|
+ {
|
|
|
+ ::MoveWindow(m_pFuping->GetHWND(), m_fuping_rect.left, m_fuping_rect.top,
|
|
|
+ m_fuping_rect.right - m_fuping_rect.left, m_fuping_rect.bottom - m_fuping_rect.top, TRUE);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void CShuangpingTool::DestoryWnd()
|
|
|
{
|
|
|
if (m_pFuping == NULL)
|
|
|
@@ -102,8 +168,14 @@ void CShuangpingTool::DestoryWnd()
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ //先停止副屏内部的工作线程并杀掉定时器
|
|
|
m_pFuping->Stop();
|
|
|
|
|
|
+ //销毁窗口(会触发 CFupingWnd::OnFinalMessage,但 OnFinalMessage 里不再 delete this)
|
|
|
+ ::DestroyWindow(m_pFuping->GetHWND());
|
|
|
+
|
|
|
+ //统一由 CShuangpingTool 持有并释放副屏对象,避免二次释放/悬空指针
|
|
|
+ delete m_pFuping;
|
|
|
m_pFuping = NULL;
|
|
|
}
|
|
|
|