Add native Windows cursor overlay to screen broadcasts

This commit is contained in:
evgeny committed 2026-10-16 23:50:52 +02:00
1 parent 49d55a25a1
commit 4a6f95cebd
6 files changed
+202 -17

No files matched your search

+15 -11
View File
@@ -34,7 +34,7 @@ extern "C" {
#include <QWindowCapture>
#include <QVideoFrame>
#endif
#ifdef UTUN_STREAM_X11_CURSOR
#if defined(UTUN_STREAM_X11_CURSOR) || defined(UTUN_STREAM_WIN_CURSOR)
#include "stream_screen_cursor.h"
#endif
#ifdef UTUN_STREAM_PORTAL
@@ -113,7 +113,7 @@ struct StreamPublishEngine::Impl {
std::unique_ptr<StreamPortalCapture> portal;
#endif
std::unique_ptr<QTimer> screenGrabTimer;
#ifdef UTUN_STREAM_X11_CURSOR
#if defined(UTUN_STREAM_X11_CURSOR) || defined(UTUN_STREAM_WIN_CURSOR)
std::unique_ptr<StreamScreenCursor> cursor;
#endif
std::unique_ptr<QWindowCapture> window;
@@ -235,6 +235,8 @@ bool StreamPublishEngine::cursorSupported(const StreamVideoSource& source) {
#endif
#ifdef UTUN_STREAM_X11_CURSOR
return source.kind == StreamVideoSource::Screen && source.screen && source.id != "portal" && QGuiApplication::platformName() == "xcb";
#elif defined(UTUN_STREAM_WIN_CURSOR)
return source.kind == StreamVideoSource::Screen && source.screen && QGuiApplication::platformName() == "windows";
#else
Q_UNUSED(source); return false;
#endif
@@ -373,9 +375,10 @@ bool StreamPublishEngine::openSource(const StreamPublishConfig& config) {
m->portal->start(config.showCursor);
} else
#endif
// Qt X11 screen capture ищет root по имени и путает мониторы; QScreen захватывает именно выбранный экран.
if (selected && QGuiApplication::platformName() == "xcb") {
#ifdef UTUN_STREAM_X11_CURSOR
// QScreen даёт выбранный монитор без курсора; накладываем его один раз до preview и кодирования.
// Для X11 это также исключает путаницу Qt Multimedia с одноимёнными root.
if (selected && (QGuiApplication::platformName() == "xcb" || cursorSupported(config.source))) {
#if defined(UTUN_STREAM_X11_CURSOR) || defined(UTUN_STREAM_WIN_CURSOR)
m->cursor = std::make_unique<StreamScreenCursor>(selected);
#endif
m->screenGrabTimer = std::make_unique<QTimer>(); m->screenGrabTimer->setTimerType(Qt::PreciseTimer);
@@ -384,11 +387,11 @@ bool StreamPublishEngine::openSource(const StreamPublishConfig& config) {
int64_t captured = monotonicUs();
QImage image = screen ? screen->grabWindow(0).toImage() : QImage();
if (image.isNull()) {
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop X11 screen grab failed present=%d", !screen.isNull());
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop native screen grab failed present=%d", !screen.isNull());
m->screenGrabTimer->stop();
QMetaObject::invokeMethod(this, [failed] { failed(text("Video capture stopped")); }); return;
}
#ifdef UTUN_STREAM_X11_CURSOR
#if defined(UTUN_STREAM_X11_CURSOR) || defined(UTUN_STREAM_WIN_CURSOR)
if (this->config().showCursor && !m->cursor->draw(image)) {
m->screenGrabTimer->stop();
QMetaObject::invokeMethod(this, [failed] { failed(text("Could not capture the mouse cursor")); }); return;
@@ -405,15 +408,16 @@ bool StreamPublishEngine::openSource(const StreamPublishConfig& config) {
} else frame = {};
#endif
if (!frame.isValid()) {
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop X11 video frame allocation failed size=%dx%d", image.width(), image.height());
DEBUG_ERROR(DEBUG_CATEGORY_VIDEO, "desktop native video frame allocation failed size=%dx%d", image.width(), image.height());
m->screenGrabTimer->stop();
QMetaObject::invokeMethod(this, [failed] { failed(text("Video frame conversion failed")); }); return;
}
frame.setStartTime(captured); m->sink->setVideoFrame(frame);
});
m->screenGrabTimer->start(std::max(1, 1000 / std::clamp(config.video.fps, 1, 60)));
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop X11 screen grab started source=%s interval_ms=%d cursor=%d supported=%d",
qPrintable(config.source.name), m->screenGrabTimer->interval(), config.showCursor, cursorSupported(config.source));
DEBUG_INFO(DEBUG_CATEGORY_VIDEO, "desktop native screen grab started platform=%s source=%s interval_ms=%d cursor=%d supported=%d",
qPrintable(QGuiApplication::platformName()), qPrintable(config.source.name), m->screenGrabTimer->interval(),
config.showCursor, cursorSupported(config.source));
} else {
m->screen = std::make_unique<QScreenCapture>();
m->screen->setScreen(selected);
@@ -454,7 +458,7 @@ void StreamPublishEngine::closeSource() {
#ifdef UTUN_STREAM_PORTAL
m->portal.reset();
#endif
#ifdef UTUN_STREAM_X11_CURSOR
#if defined(UTUN_STREAM_X11_CURSOR) || defined(UTUN_STREAM_WIN_CURSOR)
m->cursor.reset();
#endif
emit previewVideoChanged(QVariant());