Skip to content

Commit 63279a6

Browse files
fix: [record] allow recording across compositor switch and fix camera in 2D
Unify 3D/2D handling: migrate camera to RecorderRegionShow when switching to 2D so it stays visible; add ensureRecorderRegionAndMigrateCameraFor2D() and fix initCameraInfo re-init leak. Port of the v20 (release/eagle) fix to v25. Log: 录屏中 3D/2D 合成器切换不再停止录屏,2D 下摄像头迁移至录制框保持可见,并修复 initCameraInfo 重复初始化泄漏 Bug: https://pms.uniontech.com/bug-view-349319.html Influence: 录屏过程中切换合成器(3D/2D)不再强制停止录屏;切到 2D 时摄像头迁移至录制框以保持可见;修复 initCameraInfo 重复初始化导致的资源泄漏
1 parent a9f7395 commit 63279a6

4 files changed

Lines changed: 159 additions & 20 deletions

File tree

src/RecorderRegionShow.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,14 @@ RecorderRegionShow::~RecorderRegionShow()
6666
void RecorderRegionShow::initCameraInfo(const CameraWidget::Position position, const QSize size)
6767
{
6868
qCDebug(dsrApp) << "Entry. Position:" << position << ", Size:" << size;
69+
// 清理已存在的摄像头实例,避免重复初始化导致资源泄漏
70+
if (m_cameraWidget) {
71+
if (m_cameraWidget->getCameraStatus()) {
72+
m_cameraWidget->cameraStop();
73+
}
74+
delete m_cameraWidget;
75+
m_cameraWidget = nullptr;
76+
}
6977
m_cameraWidget = new CameraWidget();
7078

7179
QRect r = this->geometry();

src/main_window.cpp

Lines changed: 76 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3024,46 +3024,100 @@ void MainWindow::responseEsc()
30243024

30253025
void MainWindow::compositeChanged()
30263026
{
3027+
bool newComposite = m_wmHelper->hasBlurWindow();
3028+
30273029
// 滚动截图过程中动态切换为2D模式,直接结束
30283030
if (m_functionType == status::shot) {
30293031
// Treeland:工具栏在 sourceReady 后才创建,Wayland 初始化可能先触发 hasBlurWindowChanged
30303032
if (m_toolBar) {
3031-
m_toolBar->setScrollShotDisabled(!m_wmHelper->hasBlurWindow());
3033+
m_toolBar->setScrollShotDisabled(!newComposite);
30323034
}
30333035
return;
30343036
}
3035-
if (!m_wmHelper->hasBlurWindow() && m_functionType == status::scrollshot) {
3037+
if (!newComposite && m_functionType == status::scrollshot) {
30363038
saveScreenShot();
30373039
return;
30383040
}
30393041

3040-
// 在非录屏状态下,通过快捷键关闭特效模式
3041-
if (recordButtonStatus != RECORD_BUTTON_RECORDING) {
3042-
m_hasComposite = m_wmHelper->hasBlurWindow();
3043-
update();
3044-
return;
3045-
}
3046-
3047-
if (m_hasComposite && !m_wmHelper->hasBlurWindow()) {
3048-
// 录屏过程中 由初始3D转2D模式, 强制暂停录屏.
3049-
// 如果录屏由 由初始2D转3D模式, 则不强制退出录屏.
3050-
// 强制退出通知
3051-
forciblySavingNotify();
3052-
if (recordButtonStatus == RECORD_BUTTON_RECORDING) {
3053-
// 录屏过程中, 从3D切换回2D, 停止录屏。
3054-
stopRecord();
3055-
return;
3042+
// 录制中或非录制(含倒计时):合成器变化时统一处理 2D/3D 切换
3043+
if (m_hasComposite != newComposite) {
3044+
if (newComposite) {
3045+
// 2D → 3D:恢复主窗口,隐藏录制框
3046+
qCInfo(dsrApp) << "Composite state change to 3D";
3047+
if (this->testAttribute(Qt::WA_TranslucentBackground))
3048+
show();
3049+
if (m_pRecorderRegion)
3050+
m_pRecorderRegion->hide();
30563051
} else {
3057-
// 倒计时3s内, 从3D切换回2D直接退出。
3058-
exitApp();
3052+
// 3D → 2D:确保录制框存在并迁移摄像头,再隐藏主窗口、显示录制框
3053+
qCInfo(dsrApp) << "Composite state change to 2D";
3054+
ensureRecorderRegionAndMigrateCameraFor2D();
3055+
if (this->testAttribute(Qt::WA_TranslucentBackground))
3056+
hide();
3057+
if (m_pRecorderRegion) {
3058+
m_pRecorderRegion->show();
3059+
m_pRecorderRegion->setCameraShow(true);
3060+
}
30593061
}
3062+
update();
30603063
}
3064+
3065+
// 更新内部状态
3066+
m_hasComposite = newComposite;
3067+
30613068
// 2D录屏, 切换模式后,更新当前按钮的样式
30623069
if (m_keyBoardStatus && m_pRecorderRegion) {
30633070
m_pRecorderRegion->updateKeyBoardButtonStyle();
30643071
}
30653072
}
30663073

3074+
void MainWindow::ensureRecorderRegionAndMigrateCameraFor2D()
3075+
{
3076+
if (!m_pRecorderRegion) {
3077+
// 对齐 master 既有录制框创建逻辑:设备名 + 多屏缩放 move,避免多屏/缩放/设备名异常
3078+
m_pRecorderRegion = new RecorderRegionShow();
3079+
#ifdef QT_TESTLIB_LIB
3080+
m_pRecorderRegion->setDevcieName(m_devnumMonitor ? m_devnumMonitor->availableCamera() : QString());
3081+
#else
3082+
m_pRecorderRegion->setDevcieName(m_devnumMonitor->availableCamera());
3083+
#endif
3084+
m_pRecorderRegion->resize(recordWidth + 2, recordHeight + 2);
3085+
if (m_pixelRatio > 1 && m_screenCount > 1) {
3086+
if (m_isVertical) {
3087+
if (recordY > m_screenInfo[0].height / m_pixelRatio) {
3088+
// 多屏放缩情况下,小屏在上,整体需要偏移一定距离
3089+
m_pRecorderRegion->move(std::max(recordX - 1, 0),
3090+
std::max(recordY - 1, 0) + m_screenInfo[0].height -
3091+
static_cast<int>(m_screenInfo[0].height / m_pixelRatio));
3092+
} else {
3093+
m_pRecorderRegion->move(std::max(recordX - 1, 0), std::max(recordY - 1, 0));
3094+
}
3095+
3096+
} else {
3097+
if (recordX > m_screenInfo[0].width / m_pixelRatio) {
3098+
m_pRecorderRegion->move(std::max(recordX - 1, 0) + m_screenInfo[0].width -
3099+
static_cast<int>(m_screenInfo[0].width / m_pixelRatio),
3100+
std::max(recordY - 1, 0));
3101+
} else {
3102+
m_pRecorderRegion->move(std::max(recordX - 1, 0), std::max(recordY - 1, 0));
3103+
}
3104+
}
3105+
} else {
3106+
m_pRecorderRegion->move(std::max(recordX - 1, 0), std::max(recordY - 1, 0));
3107+
}
3108+
3109+
if (m_cameraWidget && m_selectedCamera) {
3110+
m_cameraWidget->hide();
3111+
m_cameraWidget->cameraStop();
3112+
m_pRecorderRegion->initCameraInfo(m_cameraWidget->postion(), m_cameraWidget->geometry().size());
3113+
}
3114+
} else if (m_cameraWidget && m_selectedCamera) {
3115+
m_cameraWidget->hide();
3116+
m_cameraWidget->cameraStop();
3117+
m_pRecorderRegion->initCameraInfo(m_cameraWidget->postion(), m_cameraWidget->geometry().size());
3118+
}
3119+
}
3120+
30673121
void MainWindow::updateToolBarPos()
30683122
{
30693123
if (m_shotflag == 1 || !m_toolBar) {
@@ -6969,6 +7023,8 @@ void MainWindow::startRecord()
69697023
if (m_pRecorderRegion) {
69707024
m_pRecorderRegion->setCameraShow();
69717025
m_pRecorderRegion->show();
7026+
} else {
7027+
qCWarning(dsrApp) << "m_pRecorderRegion is null, cannot show recording border";
69727028
}
69737029
}
69747030
#endif

src/main_window.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,8 @@ public slots:
488488
void showReleaseFeedback(int x, int y);
489489
void responseEsc();
490490
void compositeChanged();
491+
/// 确保存在录制框并在 2D 下把主窗口摄像头迁到录制框
492+
void ensureRecorderRegionAndMigrateCameraFor2D();
491493
void updateToolBarPos();
492494
void onRecordingStarted(); // 录屏开始
493495
void onRecordingStopped(); // 录屏停止

tests/ut_screen_shot_recorder/ut_main_window_ef_state_cov.h

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313

1414
using namespace testing;
1515

16+
// 回归(bug 349319):录屏跨合成器切换不再停止,改为迁移摄像头/创建录制框
17+
ACCESS_PRIVATE_FUN(MainWindow, void(), ensureRecorderRegionAndMigrateCameraFor2D);
18+
ACCESS_PRIVATE_FIELD(MainWindow, bool, m_hasComposite);
19+
1620
// 现有 MainWindowEFTest 以「空状态」调用 EF 辅助函数,多数命中早返回。
1721
// 本文件通过设置 m_functionType(shot/record)、recordButtonStatus 等状态字段,
1822
// 驱动 mousePressEF/mouseMoveEF/mouseReleaseEF 的更深分支(无需改源码)。
@@ -28,6 +32,8 @@ class MainWindowEFStateCovTest : public Test
2832
static int myWidth_stub() { return 1920; }
2933
static int myHeight_stub() { return 1080; }
3034
static void myPassInput_stub(int) {}
35+
static bool mw_hasNoBlurWindow_stub() { return false; }
36+
static bool mw_hasBlurWindow_stub() { return true; }
3137

3238
void SetUp() override
3339
{
@@ -105,3 +111,70 @@ TEST_F(MainWindowEFStateCovTest, shotModeWheel)
105111
bool needRepaint = false;
106112
EXPECT_NO_FATAL_FAILURE(call_private_fun::MainWindowwheelEF(*m_w, &wheel, needRepaint));
107113
}
114+
115+
116+
// 回归(bug 349319):录屏中 3D→2D 切换不再停止录屏,改为创建录制框并迁移摄像头
117+
TEST_F(MainWindowEFStateCovTest, recordModeCompositeSwitchTo2DCreatesRegion)
118+
{
119+
setFunc(MainWindow::status::record);
120+
access_private_field::MainWindowm_hasComposite(*m_w) = true;
121+
access_private_field::MainWindowm_screenCount(*m_w) = 1;
122+
access_private_field::MainWindowm_pixelRatio(*m_w) = 1.0;
123+
stub.set(ADDR(DWindowManagerHelper, hasBlurWindow), mw_hasNoBlurWindow_stub);
124+
125+
EXPECT_EQ(access_private_field::MainWindowm_pRecorderRegion(*m_w), nullptr);
126+
EXPECT_NO_FATAL_FAILURE(m_w->compositeChanged());
127+
// 切换后未停止录屏,且录制框已创建(旧逻辑会 stopRecord/exitApp,不会创建录制框)
128+
EXPECT_NE(access_private_field::MainWindowm_pRecorderRegion(*m_w), nullptr);
129+
}
130+
131+
// 回归(bug 349319):ensureRecorderRegionAndMigrateCameraFor2D 空时创建录制框
132+
TEST_F(MainWindowEFStateCovTest, ensureRecorderRegionAndMigrateCameraFor2DCreatesRegion)
133+
{
134+
setFunc(MainWindow::status::record);
135+
access_private_field::MainWindowm_screenCount(*m_w) = 1;
136+
access_private_field::MainWindowm_pixelRatio(*m_w) = 1.0;
137+
EXPECT_EQ(access_private_field::MainWindowm_pRecorderRegion(*m_w), nullptr);
138+
EXPECT_NO_FATAL_FAILURE(call_private_fun::MainWindowensureRecorderRegionAndMigrateCameraFor2D(*m_w));
139+
EXPECT_NE(access_private_field::MainWindowm_pRecorderRegion(*m_w), nullptr);
140+
}
141+
142+
143+
// 回归(bug 349319):录屏中 2D→3D 切换恢复主窗口、隐藏录制框,且不重新创建录制框
144+
TEST_F(MainWindowEFStateCovTest, recordModeCompositeSwitchTo3DHidesRegion)
145+
{
146+
setFunc(MainWindow::status::record);
147+
access_private_field::MainWindowm_hasComposite(*m_w) = false; // 当前 2D
148+
access_private_field::MainWindowm_screenCount(*m_w) = 1;
149+
access_private_field::MainWindowm_pixelRatio(*m_w) = 1.0;
150+
// 预置已存在的录制框并显示,验证 2D→3D 分支会隐藏它且不重新 new
151+
auto *region = new RecorderRegionShow();
152+
region->resize(800, 600);
153+
region->move(50, 50);
154+
region->show();
155+
access_private_field::MainWindowm_pRecorderRegion(*m_w) = region;
156+
stub.set(ADDR(DWindowManagerHelper, hasBlurWindow), mw_hasBlurWindow_stub); // -> 3D
157+
158+
EXPECT_NO_FATAL_FAILURE(m_w->compositeChanged());
159+
// 录制框指针不变(未被重新 new)
160+
EXPECT_EQ(access_private_field::MainWindowm_pRecorderRegion(*m_w), region);
161+
// 录制框被隐藏
162+
EXPECT_FALSE(region->isVisible());
163+
}
164+
165+
// 回归(bug 349319):ensureRecorderRegionAndMigrateCameraFor2D 复用已存在录制框,不新建
166+
TEST_F(MainWindowEFStateCovTest, ensureRecorderRegionAndMigrateCameraFor2DReusesExisting)
167+
{
168+
setFunc(MainWindow::status::record);
169+
access_private_field::MainWindowm_screenCount(*m_w) = 1;
170+
access_private_field::MainWindowm_pixelRatio(*m_w) = 1.0;
171+
// 预置已存在的录制框(无摄像头 -> 不触发迁移,仅验证复用/不新建路径)
172+
auto *region = new RecorderRegionShow();
173+
region->resize(800, 600);
174+
region->move(50, 50);
175+
access_private_field::MainWindowm_pRecorderRegion(*m_w) = region;
176+
177+
EXPECT_NO_FATAL_FAILURE(call_private_fun::MainWindowensureRecorderRegionAndMigrateCameraFor2D(*m_w));
178+
// 指针不变、未新建
179+
EXPECT_EQ(access_private_field::MainWindowm_pRecorderRegion(*m_w), region);
180+
}

0 commit comments

Comments
 (0)