Skip to content

Commit bb39392

Browse files
fix(pin_screenshot): adapt toolbar and window management for Treeland compositor
- Add isTreelandMode flag with case-insensitive DDE_CURRENT_COMPOSITOR check - Use QSurfaceFormat alpha buffer for Treeland before QApplication creation - Move DGuiApplicationHelper palette setup after QApplication to fix Treeland crash - Make toolbar a subsurface of the main pin window via QWindow::setParent - Replace DBlurEffectWidget blur with custom QPainter rounded-rect background (DBlur is non-functional under Treeland subsurface) - Use startSystemMove() / windowHandle()->setPosition() for drag & key move - Fix geometry() vs mapFromGlobal() coordinate mismatch for toolbar hit-test 新增 isTreelandMode 标志,通过 DDE_CURRENT_COMPOSITOR 环境变量判断合成器类型。 Treeland 模式下:设置 QSurfaceFormat alpha 缓冲、调整 DGuiApplicationHelper 调色板初始化顺序避免崩溃、将工具栏设为主窗口子表面替代独立窗口、用 QPainter 自定义圆角背景替代 DBlurEffectWidget、使用 startSystemMove() 实现拖拽移动、 修复工具栏命中测试的坐标计算偏差。 Log: 修复截图钉图功能在Treeland合成器下工具栏显示异常和窗口管理问题 PMS: TASK-389563 Influence: 修复后截图钉图功能可在Treeland合成器下正常使用,工具栏正确显示并支持拖拽移动
1 parent 3354ac4 commit bb39392

9 files changed

Lines changed: 194 additions & 64 deletions

File tree

src/main_window.cpp

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// Copyright (C) 2020 ~ 2021 Uniontech Software Technology Co.,Ltd.
2-
// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
1+
// Copyright (C) 2020 ~ 2026 Uniontech Software Technology Co.,Ltd.
2+
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
33
//
44
// SPDX-License-Identifier: GPL-3.0-or-later
55

@@ -3819,8 +3819,20 @@ void MainWindow::changeShotToolEvent(const QString &func)
38193819
"com.deepin.PinScreenShots", "/com/deepin/PinScreenShots", QDBusConnection::sessionBus(), this);
38203820
// 保存贴图到剪贴板
38213821
saveScreenShot();
3822-
m_pinInterface->openImageAndName(m_resultPixmap.toImage(), m_saveFileName, QPoint(recordX, recordY));
3823-
QTimer::singleShot(2, [=] { exitApp(); });
3822+
QImage pinImage = m_resultPixmap.toImage();
3823+
QDBusPendingCall pendingCall = m_pinInterface->openImageAndName(pinImage, m_saveFileName, QPoint(recordX, recordY));
3824+
auto *watcher = new QDBusPendingCallWatcher(pendingCall, this);
3825+
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, watcher]() {
3826+
if (watcher->isError()) {
3827+
qCWarning(dsrApp) << "[PIN_DIAG] D-Bus openImageAndName failed:"
3828+
<< watcher->error().name()
3829+
<< watcher->error().message();
3830+
} else {
3831+
qCWarning(dsrApp) << "[PIN_DIAG] D-Bus openImageAndName succeeded";
3832+
}
3833+
watcher->deleteLater();
3834+
exitApp();
3835+
});
38243836

38253837
} else if (func == "scrollShot") { // 点击滚动截图
38263838
{
@@ -7012,20 +7024,25 @@ QPixmap MainWindow::paintImage()
70127024
backgroundImage = m_backgroundPixmap.toImage(); // 非treeland模式下的背景图像
70137025
}
70147026
QImage saveImage;
7015-
// 修正方案:对逻辑坐标进行相同的边界调整,然后转换为物理坐标
7016-
// 虚线绘制的调整逻辑:x=max(x,1), y=max(y,1), width=min(width-2, maxWidth-2), height=min(height-1, maxHeight-2)
7017-
int adjustedX = std::max(recordX, 1);
7018-
int adjustedY = std::max(recordY, 1);
7019-
int adjustedWidth = std::min(recordWidth - 2, m_backgroundRect.width() - 2);
7020-
int adjustedHeight = std::min(recordHeight - 1, m_backgroundRect.height() - 2);
7021-
7022-
QRect target(static_cast<int>(adjustedX * m_pixelRatio),
7023-
static_cast<int>(adjustedY * m_pixelRatio),
7024-
static_cast<int>(adjustedWidth * m_pixelRatio),
7025-
static_cast<int>(adjustedHeight * m_pixelRatio));
7026-
7027-
// 从背景图中裁剪出截图区域
7028-
saveImage = backgroundImage.copy(target);
7027+
// Treeland:capture 帧已是 captureRegion/cropRect 大小的选区图(与 test_capture 一致),
7028+
// 不能再按全屏 recordX/recordY 二次裁剪,否则坐标越界导致截图残缺。
7029+
if (Utils::isTreelandMode) {
7030+
saveImage = backgroundImage;
7031+
} else {
7032+
// 修正方案:对逻辑坐标进行相同的边界调整,然后转换为物理坐标
7033+
// 虚线绘制的调整逻辑:x=max(x,1), y=max(y,1), width=min(width-2, maxWidth-2), height=min(height-1, maxHeight-2)
7034+
int adjustedX = std::max(recordX, 1);
7035+
int adjustedY = std::max(recordY, 1);
7036+
int adjustedWidth = std::min(recordWidth - 2, m_backgroundRect.width() - 2);
7037+
int adjustedHeight = std::min(recordHeight - 1, m_backgroundRect.height() - 2);
7038+
7039+
QRect target(static_cast<int>(adjustedX * m_pixelRatio),
7040+
static_cast<int>(adjustedY * m_pixelRatio),
7041+
static_cast<int>(adjustedWidth * m_pixelRatio),
7042+
static_cast<int>(adjustedHeight * m_pixelRatio));
7043+
7044+
saveImage = backgroundImage.copy(target);
7045+
}
70297046
if (m_shapesWidget)
70307047
// 在图片上绘制编辑的内容
70317048
m_shapesWidget->paintImage(saveImage);

src/pin_screenshots/main.cpp

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// Copyright (C) 2019 ~ 2019 Deepin Technology Co., Ltd.
2-
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
1+
// Copyright (C) 2019 ~ 2026 Deepin Technology Co., Ltd.
2+
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
33
//
44
// SPDX-License-Identifier: GPL-3.0-or-later
55

@@ -22,6 +22,7 @@
2222

2323
#include <QScreen>
2424
#include <QGuiApplication>
25+
#include <QSurfaceFormat>
2526

2627
DWIDGET_USE_NAMESPACE
2728

@@ -30,9 +31,11 @@ bool isWaylandProtocol()
3031
qCDebug(dsrApp) << "Checking if Wayland protocol is in use.";
3132
QProcessEnvironment e = QProcessEnvironment::systemEnvironment();
3233

33-
// check is treeland environment.
34-
if (e.value(QStringLiteral("DDE_CURRENT_COMPOSITOR")) == QStringLiteral("TreeLand")) {
35-
qCDebug(dsrApp) << "DDE_CURRENT_COMPOSITOR is TreeLand, not Wayland.";
34+
// TreeLand:环境变量取值在不同版本可能有大小写差异,与主程序一致做不区分大小写判断
35+
const QString compositor = e.value(QStringLiteral("DDE_CURRENT_COMPOSITOR"));
36+
if (compositor.compare(QStringLiteral("treeland"), Qt::CaseInsensitive) == 0) {
37+
PUtils::isTreelandMode = true;
38+
qCDebug(dsrApp) << "DDE_CURRENT_COMPOSITOR is TreeLand-like:" << compositor;
3639
return false;
3740
}
3841

@@ -53,18 +56,20 @@ int main(int argc, char *argv[])
5356
return 0;
5457
}
5558
PUtils::isWaylandMode = isWaylandProtocol();
59+
qCInfo(dsrApp) << "PinScreenShots session: isWaylandMode=" << PUtils::isWaylandMode
60+
<< "isTreelandMode=" << PUtils::isTreelandMode;
5661
if (PUtils::isWaylandMode) {
5762
qCDebug(dsrApp) << "Setting Wayland shell integration";
5863
qputenv("QT_WAYLAND_SHELL_INTEGRATION", "kwayland-shell");
5964
}
60-
61-
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
62-
DGuiApplicationHelper::instance()->setPaletteType(DGuiApplicationHelper::UnknownType);
63-
qCDebug(dsrApp) << "Set palette type for Qt6.";
64-
#else
65-
DGuiApplicationHelper::setUseInactiveColorGroup(false);
66-
qCDebug(dsrApp) << "Set inactive color group for Qt5.";
67-
#endif
65+
// Treeland 时 isWaylandMode 为 false,不会走上面的 Wayland 分支;Wayland 下透明/模糊常需默认 surface 带 alpha
66+
if (PUtils::isTreelandMode) {
67+
QSurfaceFormat format;
68+
format.setAlphaBufferSize(8);
69+
format.setRenderableType(QSurfaceFormat::OpenGLES);
70+
QSurfaceFormat::setDefaultFormat(format);
71+
qCInfo(dsrApp) << "Treeland: QSurfaceFormat default alpha buffer set (before QApplication).";
72+
}
6873

6974
#if(DTK_VERSION < DTK_VERSION_CHECK(5,4,0,0))
7075
DApplication::loadDXcbPlugin();
@@ -75,6 +80,15 @@ int main(int argc, char *argv[])
7580
QScopedPointer<DApplication> app(DApplication::globalApplication(argc, argv));
7681
qCDebug(dsrApp) << "Created DApplication for DTK >= 5.4.0.0.";
7782
#endif
83+
84+
#if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0))
85+
DGuiApplicationHelper::instance()->setPaletteType(DGuiApplicationHelper::UnknownType);
86+
qCDebug(dsrApp) << "Set palette type for Qt6 (after QApplication).";
87+
#else
88+
DGuiApplicationHelper::setUseInactiveColorGroup(false);
89+
qCDebug(dsrApp) << "Set inactive color group for Qt5.";
90+
#endif
91+
7892
app->setOrganizationName("deepin");
7993
app->setApplicationName("deepin-screen-recorder");
8094
app->setProductName(QObject::tr("Pin Screenshots"));

src/pin_screenshots/mainwindow.cpp

Lines changed: 79 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// Copyright (C) 2019 ~ 2019 Deepin Technology Co., Ltd.
2-
// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
1+
// Copyright (C) 2019 ~ 2026 Deepin Technology Co., Ltd.
2+
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
33
//
44
// SPDX-License-Identifier: GPL-3.0-or-later
55

@@ -17,6 +17,8 @@
1717
#include <QFileDialog>
1818
#include <QStandardPaths>
1919
#include <QTimer>
20+
#include <QWindow>
21+
#include <QShowEvent>
2022

2123
#define MOVENUM 1
2224

@@ -57,8 +59,14 @@ void MainWindow::initMainWindow()
5759
m_ocrInterface = nullptr;
5860
m_toolBar = nullptr;
5961
//去菜单栏,置顶窗口
60-
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::BypassWindowManagerHint);
61-
qCDebug(dsrApp) << "Set window flags.";
62+
if (PUtils::isTreelandMode) {
63+
// Treeland: 由合成器管理主窗口移动,工具栏作为 subsurface 跟随。
64+
setWindowFlags(Qt::FramelessWindowHint | Qt::Window | Qt::WindowStaysOnTopHint);
65+
qCDebug(dsrApp) << "Set window flags for Treeland mode.";
66+
} else {
67+
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::BypassWindowManagerHint);
68+
qCDebug(dsrApp) << "Set window flags.";
69+
}
6270

6371
//设置可以进行鼠标操作
6472
setMouseTracking(true);
@@ -432,18 +440,28 @@ void MainWindow::mousePressEvent(QMouseEvent *event)
432440
{
433441
//qCDebug(dsrApp) << this << __FUNCTION__ << __LINE__ ;
434442
switch (event->button()) {
435-
case Qt::LeftButton:
443+
case Qt::LeftButton: {
444+
const QPointF globalPress = event->globalPosition();
445+
if (PUtils::isTreelandMode) {
446+
region(globalPress.toPoint());
447+
if (dir == NONE && windowHandle()) {
448+
windowHandle()->startSystemMove();
449+
event->accept();
450+
return;
451+
}
452+
}
436453
isLeftPressDown = true;
437454
if (dir != NONE) {
438455
this->mouseGrabber();
439456
} else {
440-
dragPosition = (event->globalPosition() - this->frameGeometry().topLeft()).toPoint();
457+
dragPosition = (globalPress - this->frameGeometry().topLeft()).toPoint();
441458
}
442-
// 鼠标按下时隐藏工具栏
443-
if (!m_toolBar->isHidden())
459+
// 鼠标按下时隐藏工具栏;Treeland 下工具栏是 subsurface,可随主窗口移动。
460+
if (!PUtils::isTreelandMode && !m_toolBar->isHidden())
444461
m_toolBar->hide();
445462
//qCDebug(dsrApp) << this << __FUNCTION__ << __LINE__ ;
446463
break;
464+
}
447465
case Qt::RightButton:
448466
handleMouseRightBtn(event);
449467
break;
@@ -465,7 +483,7 @@ void MainWindow::mouseMoveEvent(QMouseEvent *event)
465483
this->region(gloPoint);
466484
} else {
467485
// 确保在拖动过程中隐藏工具栏
468-
if (!m_toolBar->isHidden()) {
486+
if (!PUtils::isTreelandMode && !m_toolBar->isHidden()) {
469487
m_toolBar->hide();
470488
}
471489

@@ -604,23 +622,30 @@ void MainWindow::keyPressEvent(QKeyEvent *event)
604622
int x = this->pos().x();
605623
int y = this->pos().y();
606624
bool isNeedUpdateToolBar = false;
625+
const auto applyWindowMove = [this](int nx, int ny) {
626+
if (PUtils::isTreelandMode && windowHandle()) {
627+
windowHandle()->setPosition(nx, ny);
628+
} else {
629+
move(nx, ny);
630+
}
631+
};
607632
if (event->key() == Qt::Key_Left) {
608-
this->move(x - MOVENUM, y);
633+
applyWindowMove(x - MOVENUM, y);
609634
isNeedUpdateToolBar = true;
610635
} else if (event->key() == Qt::Key_Right) {
611-
this->move(x + MOVENUM, y);
636+
applyWindowMove(x + MOVENUM, y);
612637
isNeedUpdateToolBar = true;
613638
} else if (event->key() == Qt::Key_Up) {
614639
// 适配wayland
615640
if (y - MOVENUM < 0) {
616-
this->move(x, 0);
641+
applyWindowMove(x, 0);
617642
} else {
618-
this->move(x, y - MOVENUM);
643+
applyWindowMove(x, y - MOVENUM);
619644
}
620645

621646
isNeedUpdateToolBar = true;
622647
} else if (event->key() == Qt::Key_Down) {
623-
this->move(x, y + MOVENUM);
648+
applyWindowMove(x, y + MOVENUM);
624649
isNeedUpdateToolBar = true;
625650
}
626651

@@ -686,7 +711,7 @@ bool MainWindow::eventFilter(QObject *watched, QEvent *event)
686711

687712
// 检查鼠标是否在工具栏上,如果是则不隐藏工具栏
688713
QPoint globalPos = QCursor::pos();
689-
if (m_toolBar->isActiveWindow() || m_toolBar->geometry().contains(globalPos))
714+
if (m_toolBar->isActiveWindow() || m_toolBar->rect().contains(m_toolBar->mapFromGlobal(globalPos)))
690715
return false;
691716

692717
// 检查是否有菜单正在显示
@@ -861,7 +886,6 @@ void MainWindow::sendNotify(QString savePath, bool bSaveState)
861886
onExit();
862887
}
863888

864-
// 更新工具栏显示位置
865889
void MainWindow::updateToolBarPosition()
866890
{
867891
//获取贴图界面右下角的点
@@ -893,11 +917,25 @@ void MainWindow::updateToolBarPosition()
893917
} else if (x + m_toolBar->width() > m_screenSize.width()) {
894918
x = m_screenSize.width() - m_toolBar->width();
895919
}
896-
m_toolBar->showAt(QPoint(x, y), m_isfirstTime);
897-
m_toolBar->activateWindow();
920+
QPoint toolbarPos(x, y);
921+
if (toolbarAttachedToWindow()) {
922+
toolbarPos = mapFromGlobal(toolbarPos);
923+
}
924+
m_toolBar->showAt(toolbarPos, m_isfirstTime);
925+
// Treeland subsurface 下工具栏 QWindow 已 setParent,非顶层;activateWindow 会触发 Qt Warning
926+
if (!(PUtils::isTreelandMode && toolbarAttachedToWindow())) {
927+
m_toolBar->activateWindow();
928+
}
898929
m_isfirstTime = false;
899930
}
900931

932+
bool MainWindow::toolbarAttachedToWindow() const
933+
{
934+
const QWindow *mainWindow = windowHandle();
935+
const QWindow *toolbarWindow = m_toolBar ? m_toolBar->windowHandle() : nullptr;
936+
return mainWindow && toolbarWindow && toolbarWindow->parent() == mainWindow;
937+
}
938+
901939
void MainWindow::checkToolbarVisibility()
902940
{
903941
m_hideToolbarTimer->stop();
@@ -924,13 +962,14 @@ void MainWindow::checkToolbarVisibility()
924962
}
925963
}
926964

927-
const bool mouseOnToolBar = m_toolBar && m_toolBar->isVisible() && m_toolBar->geometry().contains(globalPos);
965+
const bool mouseOnToolBar = m_toolBar && m_toolBar->isVisible()
966+
&& m_toolBar->rect().contains(m_toolBar->mapFromGlobal(globalPos));
928967

929968
bool mouseOnMenu = false;
930969
if (m_toolBar && m_toolBar->isVisible()) {
931970
QList<QMenu*> menus = m_toolBar->findChildren<QMenu*>();
932971
for (QMenu* menu : menus) {
933-
if (menu->isVisible() && menu->geometry().contains(globalPos)) {
972+
if (menu->isVisible() && menu->rect().contains(menu->mapFromGlobal(globalPos))) {
934973
mouseOnMenu = true;
935974
break;
936975
}
@@ -948,6 +987,26 @@ void MainWindow::checkToolbarVisibility()
948987
}
949988
}
950989

990+
void MainWindow::showEvent(QShowEvent *event)
991+
{
992+
DWidget::showEvent(event);
993+
if (!PUtils::isTreelandMode || !m_toolBar) {
994+
return;
995+
}
996+
997+
createWinId();
998+
m_toolBar->createWinId();
999+
if (!windowHandle() || !m_toolBar->windowHandle() || toolbarAttachedToWindow()) {
1000+
return;
1001+
}
1002+
1003+
m_toolBar->windowHandle()->setParent(windowHandle());
1004+
m_toolBar->hide();
1005+
m_toolBar->show();
1006+
qCDebug(dsrApp) << "[Treeland] pin toolbar QWindow::setParent (subsurface with custom style)";
1007+
updateToolBarPosition();
1008+
}
1009+
9511010
void MainWindow::enterEvent(QEnterEvent *event)
9521011
{
9531012
Q_UNUSED(event);

src/pin_screenshots/mainwindow.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
// Copyright (C) 2019 ~ 2019 Deepin Technology Co., Ltd.
2-
// SPDX-FileCopyrightText: 2022 UnionTech Software Technology Co., Ltd.
1+
// Copyright (C) 2019 ~ 2026 Deepin Technology Co., Ltd.
2+
// SPDX-FileCopyrightText: 2026 UnionTech Software Technology Co., Ltd.
33
//
44
// SPDX-License-Identifier: GPL-3.0-or-later
55

@@ -121,6 +121,7 @@ public slots:
121121
void enterEvent(QEnterEvent *event) override;
122122
void leaveEvent(QEvent *event) override;
123123
void resizeEvent(QResizeEvent *event) override;
124+
void showEvent(QShowEvent *event) override;
124125
/**
125126
* @brief 快捷键初始化
126127
*/
@@ -142,6 +143,7 @@ public slots:
142143
* @brief 更新工具栏显示位置
143144
*/
144145
void updateToolBarPosition(); // 工具栏显示位置
146+
bool toolbarAttachedToWindow() const;
145147

146148
private:
147149
/**

src/pin_screenshots/putils.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
// SPDX-FileCopyrightText: 2022 - 2024 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

55
#include "putils.h"
66

77
bool PUtils::isWaylandMode = false;
8+
bool PUtils::isTreelandMode = false;

src/pin_screenshots/putils.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// SPDX-FileCopyrightText: 2022 - 2024 UnionTech Software Technology Co., Ltd.
1+
// SPDX-FileCopyrightText: 2022 - 2026 UnionTech Software Technology Co., Ltd.
22
//
33
// SPDX-License-Identifier: GPL-3.0-or-later
44

@@ -11,6 +11,8 @@ class PUtils : public QObject
1111
{
1212
public:
1313
static bool isWaylandMode;
14+
/** TreeLand 合成器(Wayland 会话下 DDE_CURRENT_COMPOSITOR=TreeLand) */
15+
static bool isTreelandMode;
1416
};
1517

1618
#endif // PUTILS_H

0 commit comments

Comments
 (0)