Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ AI config
.npm-cache/
.claude/
.trellis/
.codegraph/
90 changes: 75 additions & 15 deletions src/main_window.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -491,8 +491,10 @@ void MainWindow::initAttributes()
m_connectionThreadObject->initConnection();
#endif
} else {
// x11自动识别窗口
Utils::getAllWindowInfo(static_cast<quint32>(this->winId()), m_screenWidth, m_screenHeight, windowRects, windowNames);
// x11: 窗口列表延迟到 showFullScreen 之后通过 refetchWindowInfo() 获取
// 避免右键菜单等弹出窗口残留在 windowRects 中
windowRects.clear();
windowNames.clear();
}
// 构建截屏工具栏按钮 by zyg
m_toolBar = new ToolBar(this);
Expand Down Expand Up @@ -599,18 +601,38 @@ void MainWindow::initAttributes()
this,
SLOT(onLockScreenEvent(QDBusMessage)));

if (!isFirstMove && !Utils::isWaylandMode) {
qCDebug(dsrApp) << "发送鼠标事件!";
QMouseEvent *mouseMove =
new QMouseEvent(QEvent::MouseMove, this->cursor().pos(), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(this, mouseMove);
delete mouseMove;
}
// 模拟鼠标事件延迟到 refetchWindowInfo() 中,与窗口列表获取同步

qCInfo(dsrApp) << __LINE__ << __FUNCTION__ << "属性初始化已完成";
}
}

void MainWindow::refetchWindowInfo()
{
qCDebug(dsrApp) << "refetchWindowInfo() called.";
if (Utils::isWaylandMode || Utils::isTreelandMode) {
// Wayland 下窗口列表通过异步回调 waylandwindowinfo() 获取,无需在此处理
return;
}

windowRects.clear();
windowNames.clear();
Utils::getAllWindowInfo(static_cast<quint32>(this->winId()), m_screenWidth, m_screenHeight, windowRects, windowNames);
qCDebug(dsrApp) << "refetchWindowInfo: got" << windowRects.size() << "windows";

if (!isFirstMove) {
qCDebug(dsrApp) << "发送鼠标事件!";
sendSimulatedMouseEvent(QEvent::MouseMove, this->cursor().pos(), Qt::NoButton, Qt::NoButton);
}
}

void MainWindow::sendSimulatedMouseEvent(QEvent::Type type, const QPoint &pos,
Qt::MouseButton button, Qt::MouseButtons buttons)
{
QScopedPointer<QMouseEvent> event(new QMouseEvent(type, pos, button, buttons, Qt::NoModifier));
QApplication::sendEvent(this, event.data());
}

void MainWindow::forceX11WindowPosition()
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
Expand Down Expand Up @@ -2268,6 +2290,7 @@ void MainWindow::topWindow()
this->initLaunchMode("screenShot");
this->showFullScreen();
this->initResource();
refetchWindowInfo();

// wayland 模式下不进入以下步骤
if (Utils::isWaylandMode) {
Expand Down Expand Up @@ -2486,7 +2509,8 @@ void MainWindow::savePath(const QString &path)
this->initLaunchMode("screenShot");
this->showFullScreen();
this->initResource();

refetchWindowInfo();

qCDebug(dsrApp) << "savePath end";
}

Expand All @@ -2512,6 +2536,7 @@ void MainWindow::startScreenshotFor3rd(const QString &path)
this->initLaunchMode("screenShot");
this->showFullScreen();
this->initResource();
refetchWindowInfo();
m_shotWithPath = true; // 自带路径
m_noNotify = true; // 关闭通知
qCDebug(dsrApp) << "startScreenshotFor3rd end";
Expand All @@ -2525,6 +2550,7 @@ void MainWindow::noNotify()
this->initLaunchMode("screenShot");
this->showFullScreen();
this->initResource();
refetchWindowInfo();
}

void MainWindow::initBackground()
Expand Down Expand Up @@ -6010,6 +6036,16 @@ void MainWindow::onMouseDrag(int x, int y)
if (status::record == m_functionType) {
showDragFeedback(x, y);
}

// 截图模式:拖拽阶段(按下左键后移动)XRecord 发出 mouseDrag 而非 mouseMove。
// Dock Grab 存在时 Qt event filter 收不到拖拽移动事件,框选区域无法更新。
// 同样用坐标去重避免正常情况下双重触发。
if (status::shot == m_functionType && isFirstPressButton && !isFirstReleaseButton) {
QPoint pos(x, y);
if (m_currentCursor != pos) {
sendSimulatedMouseEvent(QEvent::MouseMove, pos, Qt::NoButton, Qt::LeftButton);
}
}
}

// 通过x11从底层获取鼠标按压事件
Expand All @@ -6023,6 +6059,15 @@ void MainWindow::onMousePress(int x, int y)
} else if (m_initScroll && status::scrollshot == m_functionType) {
scrollShotMouseClickEvent(x, y);
}

// 截图模式:Dock 右键菜单持有 X11 Pointer Grab 时,按下事件会被路由到
// Dock 而非截图工具,Qt event filter 收不到。通过 XRecord 补发,确保截图
// 工具能正确进入拖拽状态(isFirstPressButton = true)。
// 利用 QueuedConnection 的时序:Qt 同步处理已在前,此处排队到后——若 Qt
// 已处理(isFirstPressButton == true),条件不成立,不重复触发。
if (status::shot == m_functionType && !isFirstPressButton) {
sendSimulatedMouseEvent(QEvent::MouseButtonPress, QPoint(x, y), Qt::LeftButton, Qt::LeftButton);
}
}

// 通过x11从底层获取鼠标释放事件
Expand All @@ -6034,6 +6079,14 @@ void MainWindow::onMouseRelease(int x, int y)
if (status::record == m_functionType) {
showReleaseFeedback(x, y);
}

// 截图模式:与 onMousePress 对应——Dock Grab 导致释放事件也路由到 Dock,
// Qt event filter 收不到,工具栏无法显示。通过 XRecord 补发释放事件。
// 同样利用 QueuedConnection 时序避免重复:Qt 已处理则 isFirstReleaseButton
// 为 true,条件不成立;isFirstPressButton 为 true 确保有匹配的按下事件。
if (status::shot == m_functionType && isFirstPressButton && !isFirstReleaseButton) {
sendSimulatedMouseEvent(QEvent::MouseButtonRelease, QPoint(x, y), Qt::LeftButton, Qt::LeftButton);
}
}

// 通过x11从底层获取鼠标移动事件
Expand All @@ -6050,11 +6103,18 @@ void MainWindow::onMouseMove(int x, int y)
}
}

// 启动截图或者录屏后第一次鼠标移动时需要通过此方法,后面都不会在进入此方法
if (!isFirstMove) {
QMouseEvent *mouseMove;
mouseMove = new QMouseEvent(QEvent::MouseMove, QPoint(x, y), Qt::LeftButton, Qt::LeftButton, Qt::NoModifier);
QApplication::sendEvent(QWidget::focusWidget(), mouseMove);
// 截图悬停阶段(未开始框选)通过 XRecord 持续补发鼠标移动事件。
// Dock Grab 存在时 Qt 正常事件通路被阻断,XRecord 在协议层拦截不受影响。
// 拖拽阶段由 onMouseDrag 负责补发,此处只覆盖悬停阶段(!isFirstPressButton)。
// 非截图模式保持原有逻辑:仅补发第一次(!isFirstMove)。
// m_currentCursor 在 mouseMoveEF 开头被更新:坐标已匹配说明 Qt 正常处理过,
// 无需重复补发,避免正常情况下双重触发。
bool inShotHoverPhase = status::shot == m_functionType && !isFirstPressButton;
if (!isFirstMove || inShotHoverPhase) {
QPoint pos(x, y);
if (m_currentCursor != pos) {
sendSimulatedMouseEvent(QEvent::MouseMove, pos, Qt::NoButton, Qt::NoButton);
}
}
}

Expand Down
8 changes: 8 additions & 0 deletions src/main_window.h
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,14 @@ class MainWindow : public QMainWindow
void initSaveShortcut();

void initLaunchMode(const QString &launchMode);

/**
* @brief 在截图窗口 showFullScreen 之后重新获取窗口列表
* 解决右键菜单等弹出窗口残留在 windowRects 中的问题
*/
void refetchWindowInfo();
void sendSimulatedMouseEvent(QEvent::Type type, const QPoint &pos,
Qt::MouseButton button, Qt::MouseButtons buttons);
//void delayScreenshot(double num);
void fullScreenshot();

Expand Down
4 changes: 4 additions & 0 deletions src/screenshot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ void Screenshot::startScreenshot()
qCDebug(dsrApp) << "Showing window in full screen mode for non-Wayland.";
}
m_window.createWinId();
m_window.refetchWindowInfo();
qCDebug(dsrApp) << "Window ID created.";
//平板模式截图录屏
if (Utils::isTabletEnvironment) {
Expand Down Expand Up @@ -102,6 +103,7 @@ void Screenshot::delayScreenshot(double num)
m_window.showFullScreen();
m_window.initResource();
m_window.createWinId();
m_window.refetchWindowInfo();
qCDebug(dsrApp) << "Screenshot window initialized and shown after delay.";
});
}
Expand Down Expand Up @@ -133,6 +135,7 @@ void Screenshot::OcrScreenshot()
m_window.initLaunchMode("screenOcr");
m_window.showFullScreen();
m_window.createWinId();
m_window.refetchWindowInfo();
qCDebug(dsrApp) << "OCR screenshot window initialized and shown.";
#else
qCDebug(dsrApp) << "OCR_SCROLL_FLAGE_ON is not defined, skipping OCR screenshot initialization.";
Expand All @@ -152,6 +155,7 @@ void Screenshot::ScrollScreenshot()
m_window.initLaunchMode("screenScroll");
m_window.showFullScreen();
m_window.createWinId();
m_window.refetchWindowInfo();
qCDebug(dsrApp) << "Scroll screenshot window initialized and shown.";
} else {
qCDebug(dsrApp) << "Scroll shot exit. Window effects not supported.";
Expand Down
Loading