Use native Windows monitor handle for cursor overlay
This commit is contained in:
1 parent
4a6f95cebd
commit
e440c62339
3 files changed
+38
-17
No files matched your search
@@ -219,9 +219,13 @@ if(GUI_CAPTURE_ENABLED AND UNIX AND NOT APPLE AND QT_FEATURE_xcb)
|
||||
target_link_libraries(chatgui_stream_publish PRIVATE X11::X11 X11::Xfixes)
|
||||
endif()
|
||||
if(GUI_CAPTURE_ENABLED AND WIN32)
|
||||
target_sources(chatgui_stream_publish PRIVATE src/stream_screen_cursor_win.cpp)
|
||||
target_compile_definitions(chatgui_stream_publish PUBLIC UTUN_STREAM_WIN_CURSOR)
|
||||
target_link_libraries(chatgui_stream_publish PRIVATE user32 gdi32)
|
||||
if(Qt6_VERSION VERSION_GREATER_EQUAL 6.7)
|
||||
target_sources(chatgui_stream_publish PRIVATE src/stream_screen_cursor_win.cpp)
|
||||
target_compile_definitions(chatgui_stream_publish PUBLIC UTUN_STREAM_WIN_CURSOR)
|
||||
target_link_libraries(chatgui_stream_publish PRIVATE user32 gdi32)
|
||||
else()
|
||||
message(STATUS "Windows cursor overlay requires Qt 6.7+ native monitor handles")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
add_library(chatgui_notifications STATIC src/message_notification.cpp src/user_activity_monitor.cpp)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "debug_config.h"
|
||||
#include <QPainter>
|
||||
#include <QScopeGuard>
|
||||
#include <QtGui/qscreen_platform.h>
|
||||
#include <qt_windows.h>
|
||||
#include <cstring>
|
||||
|
||||
@@ -27,14 +28,26 @@ bool StreamScreenCursor::draw(QImage& frame) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor: GetCursorInfo failed error=%lu", GetLastError()); return false;
|
||||
}
|
||||
if (!(cursor.flags & CURSOR_SHOWING)) return true;
|
||||
DEVMODEW mode{}; mode.dmSize = sizeof(mode);
|
||||
const auto device = m_screen->name().toStdWString();
|
||||
if (!EnumDisplaySettingsExW(device.c_str(), ENUM_CURRENT_SETTINGS, &mode, 0)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor: display mode unavailable screen=%s error=%lu",
|
||||
auto* native = m_screen->nativeInterface<QNativeInterface::QWindowsScreen>();
|
||||
if (!native) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor: native monitor unavailable screen=%s", qPrintable(m_screen->name()));
|
||||
return false;
|
||||
}
|
||||
MONITORINFOEXW monitor{}; monitor.cbSize = sizeof(monitor);
|
||||
if (!GetMonitorInfoW(native->handle(), reinterpret_cast<MONITORINFO*>(&monitor))) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor: GetMonitorInfo failed screen=%s error=%lu",
|
||||
qPrintable(m_screen->name()), GetLastError()); return false;
|
||||
}
|
||||
// Qt geometry имеет логический размер; WinAPI возвращает физическое начало, в том числе отрицательное.
|
||||
QPoint position(cursor.ptScreenPos.x - mode.dmPosition.x, cursor.ptScreenPos.y - mode.dmPosition.y);
|
||||
const QSize monitorSize(monitor.rcMonitor.right - monitor.rcMonitor.left,
|
||||
monitor.rcMonitor.bottom - monitor.rcMonitor.top);
|
||||
if (frame.size() != monitorSize) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor: captured frame does not match monitor screen=%s frame=%dx%d monitor=%dx%d",
|
||||
qPrintable(m_screen->name()), frame.width(), frame.height(), monitorSize.width(), monitorSize.height());
|
||||
return false;
|
||||
}
|
||||
// RECT использует физические координаты общего виртуального desktop, включая отрицательные origins.
|
||||
QPoint origin(monitor.rcMonitor.left, monitor.rcMonitor.top);
|
||||
QPoint position(cursor.ptScreenPos.x - origin.x(), cursor.ptScreenPos.y - origin.y());
|
||||
ICONINFO icon{};
|
||||
if (!GetIconInfo(cursor.hCursor, &icon)) {
|
||||
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor: GetIconInfo failed error=%lu", GetLastError()); return false;
|
||||
@@ -91,9 +104,9 @@ bool StreamScreenCursor::draw(QImage& frame) {
|
||||
if (!painter.isActive()) { DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop cursor painter initialization failed"); return false; }
|
||||
painter.drawImage(area.topLeft(), composed);
|
||||
if (!m_reported) {
|
||||
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop cursor drawn screen=%s origin=%ld,%ld position=%d,%d hotspot=%lu,%lu sprite=%dx%d frame=%dx%d",
|
||||
qPrintable(m_screen->name()), mode.dmPosition.x, mode.dmPosition.y, position.x(), position.y(), icon.xHotspot, icon.yHotspot,
|
||||
size.width(), size.height(), frame.width(), frame.height());
|
||||
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop cursor drawn screen=%s device=%ls origin=%d,%d position=%d,%d hotspot=%lu,%lu sprite=%dx%d frame=%dx%d",
|
||||
qPrintable(m_screen->name()), monitor.szDevice, origin.x(), origin.y(), position.x(), position.y(), icon.xHotspot,
|
||||
icon.yHotspot, size.width(), size.height(), frame.width(), frame.height());
|
||||
m_reported = true;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -57,6 +57,7 @@ extern "C" {
|
||||
#ifdef UTUN_STREAM_WIN_CURSOR
|
||||
#include "../src/stream_screen_cursor.h"
|
||||
#include <QWindow>
|
||||
#include <QtGui/qscreen_platform.h>
|
||||
#include <qt_windows.h>
|
||||
#endif
|
||||
|
||||
@@ -422,8 +423,10 @@ private slots:
|
||||
POINT saved{}; QVERIFY(GetCursorPos(&saved));
|
||||
auto restorePosition = qScopeGuard([&] { SetCursorPos(saved.x, saved.y); });
|
||||
for (auto* screen : QGuiApplication::screens()) {
|
||||
DEVMODEW mode{}; mode.dmSize = sizeof(mode);
|
||||
QVERIFY(EnumDisplaySettingsExW(screen->name().toStdWString().c_str(), ENUM_CURRENT_SETTINGS, &mode, 0));
|
||||
auto* native = screen->nativeInterface<QNativeInterface::QWindowsScreen>(); QVERIFY(native);
|
||||
MONITORINFO monitor{}; monitor.cbSize = sizeof(monitor);
|
||||
QVERIFY(GetMonitorInfoW(native->handle(), &monitor));
|
||||
QPoint origin(monitor.rcMonitor.left, monitor.rcMonitor.top);
|
||||
QWindow target(screen); target.setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
|
||||
target.setGeometry(QRect(screen->geometry().topLeft(), QSize(200, 160))); target.show();
|
||||
QVERIFY(QTest::qWaitForWindowExposed(&target));
|
||||
@@ -436,11 +439,12 @@ private slots:
|
||||
StreamScreenCursor overlay(screen);
|
||||
const QColor background(0x12, 0x34, 0x56), inverted(0xed, 0xcb, 0xa9);
|
||||
for (QPoint position : {QPoint(60, 70), QPoint(0, 0)}) {
|
||||
QVERIFY(SetCursorPos(mode.dmPosition.x + position.x(), mode.dmPosition.y + position.y()));
|
||||
QVERIFY(SetCursorPos(origin.x() + position.x(), origin.y() + position.y()));
|
||||
SetCursor(cursor);
|
||||
CURSORINFO info{}; info.cbSize = sizeof(info); QVERIFY(GetCursorInfo(&info));
|
||||
QVERIFY(info.flags & CURSOR_SHOWING); QVERIFY(info.hCursor == cursor);
|
||||
QImage frame(mode.dmPelsWidth, mode.dmPelsHeight, QImage::Format_RGB32);
|
||||
QImage frame(monitor.rcMonitor.right - monitor.rcMonitor.left,
|
||||
monitor.rcMonitor.bottom - monitor.rcMonitor.top, QImage::Format_RGB32);
|
||||
frame.setDevicePixelRatio(screen->devicePixelRatio()); frame.fill(background);
|
||||
QImage blank = frame;
|
||||
QVERIFY(overlay.draw(frame));
|
||||
@@ -451,7 +455,7 @@ private slots:
|
||||
SetCursor(nullptr); frame = blank;
|
||||
QVERIFY(overlay.draw(frame)); QCOMPARE(frame, blank);
|
||||
}
|
||||
QVERIFY(SetCursorPos(mode.dmPosition.x + 60, mode.dmPosition.y + 70)); SetCursor(cursor);
|
||||
QVERIFY(SetCursorPos(origin.x() + 60, origin.y() + 70)); SetCursor(cursor);
|
||||
for (auto* other : QGuiApplication::screens()) {
|
||||
if (other == screen) continue;
|
||||
StreamScreenCursor otherOverlay(other);
|
||||
|
||||
Reference in new issue
Block a user