Compare commits

..

6 commits

Author SHA1 Message Date
Jan Grulich
f84906798f Merge branch 'f36' into f35 2022-03-06 19:51:55 +01:00
Jan Grulich
2e911d9f5b Merge branch 'rawhide' into f35 2022-02-04 16:25:56 +01:00
Jan Grulich
56cd925b3a Merge branch 'rawhide' into f35 2021-12-15 11:33:13 +01:00
Jan Grulich
2307080716 Merge branch 'rawhide' into f35 2021-11-01 08:24:36 +01:00
Jan Grulich
b69f5eadfe Merge branch 'rawhide' into f35 2021-10-01 09:54:26 +02:00
Jan Grulich
b990a1dcee Disable rpath
Resolves: bz#1982699
2021-09-07 15:16:13 +02:00
15 changed files with 242 additions and 1453 deletions

27
.gitignore vendored
View file

@ -12,30 +12,3 @@
/qtbase-everywhere-src-6.2.1.tar.xz
/qtbase-everywhere-src-6.2.2.tar.xz
/qtbase-everywhere-src-6.2.3.tar.xz
/qtbase-everywhere-src-6.3.0.tar.xz
/qtbase-everywhere-src-6.3.1.tar.xz
/qtbase-everywhere-src-6.4.0.tar.xz
/qtbase-everywhere-src-6.4.1.tar.xz
/qtbase-everywhere-src-6.4.2.tar.xz
/qtbase-everywhere-src-6.4.3.tar.xz
/qtbase-everywhere-src-6.5.0.tar.xz
/qtbase-everywhere-src-6.5.1.tar.xz
/qtbase-everywhere-src-6.5.2.tar.xz
/qt-everywhere-src-6.5.3.tar.xz
/qtbase-everywhere-src-6.5.3.tar.xz
/qtbase-everywhere-src-6.6.0.tar.xz
/qtbase-everywhere-src-6.6.1.tar.xz
/qtbase-everywhere-src-6.6.2.tar.xz
/qtbase-everywhere-src-6.7.0.tar.xz
/qtbase-everywhere-src-6.7.1.tar.xz
/qtbase-everywhere-src-6.7.2.tar.xz
/qtbase-everywhere-src-6.8.0.tar.xz
/qtbase-everywhere-src-6.8.1.tar.xz
/qtbase-everywhere-src-6.8.2.tar.xz
/qtbase-everywhere-src-6.9.0-rc.tar.xz
/qtbase-everywhere-src-6.9.0.tar.xz
/qtbase-everywhere-src-6.9.1.tar.xz
/qtbase-everywhere-src-6.9.2.tar.xz
/qtbase-everywhere-src-6.10.0-rc.tar.xz
/qtbase-everywhere-src-6.10.0.tar.xz
/qtbase-everywhere-src-6.10.1.tar.xz

File diff suppressed because it is too large Load diff

View file

@ -1,51 +0,0 @@
From 4e517492a03b6c0b710a2d9a3df05922ac233992 Mon Sep 17 00:00:00 2001
From: Christophe Marin <christophe@krop.fr>
Date: Wed, 24 May 2023 11:23:39 +0200
Subject: [PATCH] CMake: Install objects files into 'ARCHDATADIR'
Change-Id: I917a9ef4d83f93eb5c3e47964bf1814d1b53fdab
---
cmake/QtResourceHelpers.cmake | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/cmake/QtResourceHelpers.cmake b/cmake/QtResourceHelpers.cmake
index 2df1fed50f..e4f34b5ff1 100644
--- a/cmake/QtResourceHelpers.cmake
+++ b/cmake/QtResourceHelpers.cmake
@@ -38,7 +38,7 @@ function(qt_internal_add_resource target resourceName)
if (out_targets)
qt_install(TARGETS ${out_targets}
EXPORT "${INSTALL_CMAKE_NAMESPACE}${target}Targets"
- DESTINATION "${INSTALL_LIBDIR}"
+ DESTINATION "${INSTALL_ARCHDATADIR}"
)
qt_internal_add_targets_to_additional_targets_export_file(
TARGETS ${out_targets}
@@ -47,7 +47,7 @@ function(qt_internal_add_resource target resourceName)
qt_internal_install_resource_pdb_files("${out_targets}")
qt_internal_record_rcc_object_files("${target}" "${out_targets}"
- INSTALL_DIRECTORY "${INSTALL_LIBDIR}")
+ INSTALL_DIRECTORY "${INSTALL_ARCHDATADIR}")
endif()
if (arg_OUTPUT_TARGETS)
@@ -77,7 +77,7 @@ function(qt_internal_record_rcc_object_files target resource_targets)
# It's comprised of thee following path parts:
#
# part (1) INSTALL_DIRECTORY.
- # A usual value is '${INSTALL_LIBDIR}/' for libraries
+ # A usual value is '${INSTALL_ARCHDATADIR}/' for libraries
# and '${INSTALL_QMLDIR}/foo/bar/' for qml plugin resources.
#
# part (2) the value computed by CMake's computeInstallObjectDir comprised of an
@@ -128,6 +128,6 @@ function(qt_internal_install_resource_pdb_files objlib_targets)
_qt_resource_generated_cpp_relative_path)
get_filename_component(rel_obj_file_dir "${generated_cpp_file_relative_path}" DIRECTORY)
qt_internal_install_pdb_files(${target}
- "${INSTALL_LIBDIR}/objects-$<CONFIG>/${target}/${rel_obj_file_dir}")
+ "${INSTALL_ARCHDATADIR}/objects-$<CONFIG>/${target}/${rel_obj_file_dir}")
endforeach()
endfunction()
--
2.40.1

36
qtbase-gcc11.patch Normal file
View file

@ -0,0 +1,36 @@
diff --git a/examples/corelib/tools/contiguouscache/randomlistmodel.h b/examples/corelib/tools/contiguouscache/randomlistmodel.h
index 1fabb0d9..393ebaa3 100644
--- a/examples/corelib/tools/contiguouscache/randomlistmodel.h
+++ b/examples/corelib/tools/contiguouscache/randomlistmodel.h
@@ -50,6 +50,7 @@
#ifndef RANDOMLISTMODEL_H
#define RANDOMLISTMODEL_H
+#include <limits>
#include <QContiguousCache>
#include <QAbstractListModel>
diff --git a/src/corelib/text/qanystringview.h b/src/corelib/text/qanystringview.h
index a7606253..60747cf0 100644
--- a/src/corelib/text/qanystringview.h
+++ b/src/corelib/text/qanystringview.h
@@ -39,6 +39,7 @@
#ifndef QANYSTRINGVIEW_H
#define QANYSTRINGVIEW_H
+#include <limits>
#include <QtCore/qstringview.h>
#include <QtCore/qutf8stringview.h>
diff --git a/src/corelib/text/qbytearray.h b/src/corelib/text/qbytearray.h
index 9f646aaa..a5af793c 100644
--- a/src/corelib/text/qbytearray.h
+++ b/src/corelib/text/qbytearray.h
@@ -41,6 +41,7 @@
#ifndef QBYTEARRAY_H
#define QBYTEARRAY_H
+#include <limits>
#include <QtCore/qrefcount.h>
#include <QtCore/qnamespace.h>
#include <QtCore/qarraydata.h>

9
qtbase-python3.patch Normal file
View file

@ -0,0 +1,9 @@
diff -up qtbase-everywhere-src-5.11.1/mkspecs/features/uikit/devices.py.me qtbase-everywhere-src-5.11.1/mkspecs/features/uikit/devices.py
--- qtbase-everywhere-src-5.11.1/mkspecs/features/uikit/devices.py.me 2018-06-23 11:29:21.750066271 +0200
+++ qtbase-everywhere-src-5.11.1/mkspecs/features/uikit/devices.py 2018-06-23 11:30:07.457292033 +0200
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
#############################################################################
##

View file

@ -0,0 +1,16 @@
diff --git a/mkspecs/features/qt_module.prf b/mkspecs/features/qt_module.prf
index 3cacc8df..469e6f50 100644
--- a/mkspecs/features/qt_module.prf
+++ b/mkspecs/features/qt_module.prf
@@ -218,9 +218,9 @@ android: CONFIG += qt_android_deps no_linker_version_script
QMAKE_LFLAGS += $${QMAKE_LFLAGS_VERSION_SCRIPT}$$verscript
internal_module {
- verscript_content = "Qt_$${QT_MAJOR_VERSION}_PRIVATE_API { *; };"
+ verscript_content = "Qt_$${QT_MAJOR_VERSION}.$${QT_MINOR_VERSION}.$${QT_PATCH_VERSION}_PRIVATE_API { *; };"
} else {
- verscript_content = "Qt_$${QT_MAJOR_VERSION}_PRIVATE_API {" \
+ verscript_content = "Qt_$${QT_MAJOR_VERSION}.$${QT_MINOR_VERSION}.$${QT_PATCH_VERSION}_PRIVATE_API {" \
" qt_private_api_tag*;"
private_api_headers = $$SYNCQT.PRIVATE_HEADER_FILES $$SYNCQT.QPA_HEADER_FILES

View file

@ -1,13 +0,0 @@
diff --git a/cmake/QtFlagHandlingHelpers.cmake b/cmake/QtFlagHandlingHelpers.cmake
index 6a62b85c..1fc1f88d 100644
--- a/cmake/QtFlagHandlingHelpers.cmake
+++ b/cmake/QtFlagHandlingHelpers.cmake
@@ -71,7 +71,7 @@ function(qt_internal_add_linker_version_script target)
string(APPEND contents "\n};\nQt_${PROJECT_VERSION_MAJOR}")
if(QT_FEATURE_elf_private_full_version)
- string(APPEND contents ".${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}")
+ string(APPEND contents ".${PROJECT_VERSION_MINOR}")
endif()
string(APPEND contents "_PRIVATE_API { qt_private_api_tag*;\n")
if(arg_PRIVATE_HEADERS)

View file

@ -1,12 +0,0 @@
diff --git a/src/gui/platform/unix/qgenericunixthemes.cpp b/src/gui/platform/unix/qgenericunixthemes.cpp
index 1efd759b..d0129f73 100644
--- a/src/gui/platform/unix/qgenericunixthemes.cpp
+++ b/src/gui/platform/unix/qgenericunixthemes.cpp
@@ -977,6 +977,7 @@ QStringList QGenericUnixTheme::themeNames()
result.push_back(QLatin1StringView(QKdeTheme::name));
#endif
} else if (gtkBasedEnvironments.contains(desktopName)) {
+ result.push_back(QStringLiteral("qgnomeplatform"));
// prefer the GTK3 theme implementation with native dialogs etc.
result.push_back(QStringLiteral("gtk3"));
// fallback to the generic Gnome theme if loading the GTK3 theme fails

View file

@ -1,13 +1,13 @@
diff --git a/src/corelib/global/qtversionchecks.h b/src/corelib/global/qtversionchecks.h
index d3b7a7b0..a8b9c6f3 100644
--- a/src/corelib/global/qtversionchecks.h
+++ b/src/corelib/global/qtversionchecks.h
@@ -26,7 +26,7 @@
diff --git a/src/corelib/global/qglobal.h b/src/corelib/global/qglobal.h
index c6b5c5a9..bb0076b3 100644
--- a/src/corelib/global/qglobal.h
+++ b/src/corelib/global/qglobal.h
@@ -58,7 +58,7 @@
/*
can be used like #if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
can be used like #if (QT_VERSION >= QT_VERSION_CHECK(4, 4, 0))
*/
-#define QT_VERSION_CHECK(major, minor, patch) ((major<<16)|(minor<<8)|(patch))
+#define QT_VERSION_CHECK(qt_version_check_major, qt_version_check_minor, qt_version_check_patch) ((qt_version_check_major<<16)|(qt_version_check_minor<<8)|(qt_version_check_patch))
/*
Helper macros to make some simple code active in Qt 6 or Qt 7 only,
#ifdef QT_BOOTSTRAPPED
#include <QtCore/qconfig-bootstrapped.h>

View file

@ -1,174 +0,0 @@
From 095759818854e5a011aa8f859e566bbc6368ab76 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?B=C5=82a=C5=BCej=20Szczygie=C5=82?= <mumei6102@gmail.com>
Date: Sun, 27 Jul 2025 00:50:00 +0200
Subject: [PATCH] wayland: Compress high frequency mouse events
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Add support for Qt::AA_CompressHighFrequencyEvents on Wayland.
The highest USB HID polling rate is 8 kHz (125 μs). Most mice use lower
polling rate [125 Hz - 1000 Hz]. Reject all events faster than 100 μs,
because it definitely means the application main thread is freezed by
the long operation and events are delivered one after another from the
queue. Since now we rely on the 0 ms timer to deliver the last pending
event when application main thread is no longer freezed.
Pick-to: 6.10
Task-number: QTBUG-138706
Change-Id: Ie9d539e233c5551b1756d599b65495571e195f9d
Reviewed-by: David Edmundson <davidedmundson@kde.org>
---
src/corelib/global/qnamespace.qdoc | 1 +
.../platforms/wayland/qwaylandinputdevice.cpp | 38 +++++++++++++++++++
.../platforms/wayland/qwaylandinputdevice_p.h | 12 ++++++
.../platforms/wayland/qwaylandintegration.cpp | 2 +
tests/auto/wayland/client/tst_client.cpp | 2 +
5 files changed, 55 insertions(+)
diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc
index c845cfa7..f463d736 100644
--- a/src/corelib/global/qnamespace.qdoc
+++ b/src/corelib/global/qnamespace.qdoc
@@ -227,6 +227,7 @@
application later.
On Windows 8 and above the default value is also true, but it only applies
to touch events. Mouse and window events remain unaffected by this flag.
+ On Wayland the default value is also true, but it only applies to mouse events.
On other platforms, the default is false.
(In the future, the compression feature may be implemented across platforms.)
You can test the attribute to see whether compression is enabled.
diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp
index 170e80f8..45c78765 100644
--- a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp
+++ b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp
@@ -63,6 +63,34 @@ Q_LOGGING_CATEGORY(lcQpaWaylandInput, "qt.qpa.wayland.input");
// reasonable number of them. As of 2021 most touchscreen panels support 10 concurrent touchpoints.
static const int MaxTouchPoints = 10;
+QWaylandEventCompressionPrivate::QWaylandEventCompressionPrivate()
+{
+ timeElapsed.start();
+ delayTimer.setSingleShot(true);
+}
+
+bool QWaylandEventCompressionPrivate::compressEvent()
+{
+ using namespace std::chrono_literals;
+
+ if (!QCoreApplication::testAttribute(Qt::AA_CompressHighFrequencyEvents))
+ return false;
+
+ const auto elapsed = timeElapsed.durationElapsed();
+ timeElapsed.start();
+ if (elapsed < 100us || delayTimer.isActive())
+ {
+ // The highest USB HID polling rate is 8 kHz (125 μs). Most mice use lowe polling rate [125 Hz - 1000 Hz].
+ // Reject all events faster than 100 μs, because it definitely means the application main thread is
+ // freezed by long operation and events are delivered one after another from the queue. Since now we rely
+ // on the 0 ms timer to deliver the last pending event when application main thread is no longer freezed.
+ delayTimer.start(0);
+ return true;
+ }
+
+ return false;
+}
+
QWaylandInputDevice::Keyboard::Keyboard(QWaylandInputDevice *p)
: mParent(p)
{
@@ -140,6 +168,8 @@ QWaylandInputDevice::Pointer::Pointer(QWaylandInputDevice *seat)
cursorTimerCallback();
});
#endif
+
+ mEventCompression.delayTimer.callOnTimeout(this, &QWaylandInputDevice::Pointer::flushFrameEvent);
}
QWaylandInputDevice::Pointer::~Pointer()
@@ -922,6 +952,11 @@ void QWaylandInputDevice::Pointer::pointer_axis(uint32_t time, uint32_t axis, in
void QWaylandInputDevice::Pointer::pointer_frame()
{
+ if (mEventCompression.compressEvent()) {
+ qCDebug(lcQpaWaylandInput) << "compressed pointer_frame event";
+ return;
+ }
+
flushFrameEvent();
}
@@ -1051,6 +1086,7 @@ void QWaylandInputDevice::Pointer::setFrameEvent(QWaylandPointerEvent *event)
flushFrameEvent();
}
+ delete mFrameData.event;
mFrameData.event = event;
if (version() < WL_POINTER_FRAME_SINCE_VERSION) {
@@ -1170,6 +1206,8 @@ void QWaylandInputDevice::Pointer::flushScrollEvent()
void QWaylandInputDevice::Pointer::flushFrameEvent()
{
+ mEventCompression.delayTimer.stop();
+
if (auto *event = mFrameData.event) {
if (auto window = event->surface) {
window->handleMouse(mParent, *event);
diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h
index bcaf0258..0b24999e 100644
--- a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h
+++ b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h
@@ -75,6 +75,16 @@ class CursorSurface;
Q_DECLARE_LOGGING_CATEGORY(lcQpaWaylandInput);
+struct QWaylandEventCompressionPrivate
+{
+ QWaylandEventCompressionPrivate();
+
+ bool compressEvent();
+
+ QElapsedTimer timeElapsed;
+ QTimer delayTimer;
+};
+
class Q_WAYLANDCLIENT_EXPORT QWaylandInputDevice
: public QObject
, public QtWayland::wl_seat
@@ -381,6 +391,8 @@ public:
bool mScrollBeginSent = false;
QPointF mScrollDeltaRemainder;
+ QWaylandEventCompressionPrivate mEventCompression;
+
void setFrameEvent(QWaylandPointerEvent *event);
void flushScrollEvent();
void flushFrameEvent();
diff --git a/src/plugins/platforms/wayland/qwaylandintegration.cpp b/src/plugins/platforms/wayland/qwaylandintegration.cpp
index 669d47ee..fc869de6 100644
--- a/src/plugins/platforms/wayland/qwaylandintegration.cpp
+++ b/src/plugins/platforms/wayland/qwaylandintegration.cpp
@@ -87,6 +87,8 @@ QWaylandIntegration::QWaylandIntegration(const QString &platformName)
: mPlatformName(platformName), mFontDb(new QGenericUnixFontDatabase())
#endif
{
+ QCoreApplication::setAttribute(Qt::AA_CompressHighFrequencyEvents);
+
mDisplay.reset(new QWaylandDisplay(this));
mPlatformServices.reset(new QWaylandPlatformServices(mDisplay.data()));
diff --git a/tests/auto/wayland/client/tst_client.cpp b/tests/auto/wayland/client/tst_client.cpp
index 04400e3f..09ac4f7f 100644
--- a/tests/auto/wayland/client/tst_client.cpp
+++ b/tests/auto/wayland/client/tst_client.cpp
@@ -276,6 +276,8 @@ void tst_WaylandClient::events()
exec([&] {
pointer()->sendEnter(s, window.frameOffset() + mousePressPos);
pointer()->sendFrame(client());
+ pointer()->sendMotion(client(), window.frameOffset() + mousePressPos / 2);
+ pointer()->sendFrame(client());
pointer()->sendMotion(client(), window.frameOffset() + mousePressPos);
pointer()->sendFrame(client());
pointer()->sendButton(client(), BTN_LEFT, Pointer::button_state_pressed);

View file

@ -1,55 +0,0 @@
From 18550cd9ad04913d50abfc88a6048b7cfaf80687 Mon Sep 17 00:00:00 2001
From: Igor Khanin <igor@khanin.biz>
Date: Mon, 13 Oct 2025 14:26:39 +0300
Subject: [PATCH] wayland: Convey preference for server side decorations
As discussed in the mailing list, Qt applications generally expect top
level windows to be decorated by the windowing system - unless
explicitly requested by the developer by marking a window as frameless.
Furthermore, Qt's client side decorations for Wayland were only really
intended as a fallback.
However until now the Wayland xdg-shell integration conveyed to the
compositor (if it supports decorations negotiations) that it didn't
care whose responsibility it was to draw window decorations. This did
not matter under KWin, which resolves such cases in favor of SSDs - but
does matter under other SSD-capable compositors like cosmic-comp (which
considers SSDs to be the fallback option).
This changes the xdg-shell integration code to be explicit about
requesting SSDs.
[ChangeLog][QtWaylandClient][Important Behavior Changes] The Wayland
XDG shell integration now requests server side decorations with
compositors supporting the zxdg_decoration_manager_v1 protocol.
Change-Id: Ia025b1f6ff17248a5710f981a0cecd90c47b6cd8
Reviewed-by: David Edmundson <davidedmundson@kde.org>
---
diff --git a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
index a1a173f..8386b65 100644
--- a/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
+++ b/src/plugins/platforms/wayland/plugins/shellintegration/xdg-shell/qwaylandxdgshell.cpp
@@ -201,7 +201,7 @@
delete m_decoration;
m_decoration = nullptr;
} else {
- m_decoration->unsetMode();
+ m_decoration->requestMode(QWaylandXdgToplevelDecorationV1::mode_server_side);
}
}
}
diff --git a/tests/auto/wayland/xdgdecorationv1/tst_xdgdecorationv1.cpp b/tests/auto/wayland/xdgdecorationv1/tst_xdgdecorationv1.cpp
index 5ee8569..65ac08f 100644
--- a/tests/auto/wayland/xdgdecorationv1/tst_xdgdecorationv1.cpp
+++ b/tests/auto/wayland/xdgdecorationv1/tst_xdgdecorationv1.cpp
@@ -149,7 +149,7 @@
QVERIFY(window.frameMargins().isNull());
QCOMPOSITOR_TRY_VERIFY(xdgToplevel());
- QCOMPOSITOR_TRY_VERIFY(toplevelDecoration()->m_unsetModeRequested);
+ QCOMPOSITOR_TRY_VERIFY(toplevelDecoration()->m_requestedMode == XdgToplevelDecorationV1::mode_server_side);
QVERIFY(window.frameMargins().isNull()); // We're still waiting for a configure
exec([&] {
toplevelDecoration()->sendConfigure(XdgToplevelDecorationV1::mode_client_side);

View file

@ -1,235 +0,0 @@
From 9dd0d936d6691904a4bb212dcf48999a5228b84f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?B=C5=82a=C5=BCej=20Szczygie=C5=82?= <mumei6102@gmail.com>
Date: Thu, 7 Aug 2025 15:42:55 +0200
Subject: [PATCH] wayland: Enable event compression and fix scroll end event
We have to deliver the scroll end event in order with other scroll
events, so move scroll end event logic into flushScrollEvent().
Track the target scroll window on scroll begin and check whether we
have a target window to prevent a crash.
Also we can't clear the scroll delta when scroll end event arrives,
because the event compression relies on this value.
Use scoped pointer for frame event to simplify code and make sure it's
deleted in destructor.
Amends 095759818854e5a011aa8f859e566bbc6368ab76
Updates 789681872fb62450fc78d1a06472a40d970a8d57
Change-Id: Ifb149c8fde1286b60439be0dab16b1df65279ea8
Reviewed-by: David Edmundson <davidedmundson@kde.org>
---
.../platforms/wayland/qwaylandinputdevice.cpp | 98 ++++++++++---------
.../platforms/wayland/qwaylandinputdevice_p.h | 6 +-
.../platforms/wayland/qwaylandintegration.cpp | 2 +
3 files changed, 61 insertions(+), 45 deletions(-)
diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp
index 7fcda34c57ca..6ce43714a35f 100644
--- a/src/plugins/platforms/wayland/qwaylandinputdevice.cpp
+++ b/src/plugins/platforms/wayland/qwaylandinputdevice.cpp
@@ -935,10 +935,7 @@ void QWaylandInputDevice::Pointer::pointer_axis(uint32_t time, uint32_t axis, in
mParent->mTime = time;
- if (version() < WL_POINTER_FRAME_SINCE_VERSION) {
- qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
- flushFrameEvent();
- }
+ maybePointerFrame();
}
void QWaylandInputDevice::Pointer::pointer_frame()
@@ -978,11 +975,9 @@ void QWaylandInputDevice::Pointer::pointer_axis_stop(uint32_t time, uint32_t axi
switch (axis) {
case axis_vertical_scroll:
qCDebug(lcQpaWaylandInput) << "Received vertical wl_pointer.axis_stop";
- mFrameData.delta.setY(0); //TODO: what's the point of doing this?
break;
case axis_horizontal_scroll:
qCDebug(lcQpaWaylandInput) << "Received horizontal wl_pointer.axis_stop";
- mFrameData.delta.setX(0);
break;
default:
qCWarning(lcQpaWaylandInput) << "wl_pointer.axis_stop: Unknown axis: " << axis
@@ -990,25 +985,7 @@ void QWaylandInputDevice::Pointer::pointer_axis_stop(uint32_t time, uint32_t axi
return;
}
- // May receive axis_stop for events we haven't sent a ScrollBegin for because
- // most axis_sources do not mandate an axis_stop event to be sent.
- if (!mScrollBeginSent) {
- // TODO: For now, we just ignore these events, but we could perhaps take this as an
- // indication that this compositor will in fact send axis_stop events for these sources
- // and send a ScrollBegin the next time an axis_source event with this type is encountered.
- return;
- }
-
- QWaylandWindow *target = QWaylandWindow::mouseGrab();
- if (!target)
- target = focusWindow();
- Qt::KeyboardModifiers mods = mParent->modifiers();
- const bool inverted = mFrameData.verticalAxisInverted || mFrameData.horizontalAxisInverted;
- WheelEvent wheelEvent(focusWindow(), Qt::ScrollEnd, mParent->mTime, mSurfacePos, mGlobalPos,
- QPoint(), QPoint(), Qt::MouseEventNotSynthesized, mods, inverted);
- target->handleMouse(mParent, wheelEvent);
- mScrollBeginSent = false;
- mScrollDeltaRemainder = QPointF();
+ mScrollEnd = true;
}
void QWaylandInputDevice::Pointer::pointer_axis_discrete(uint32_t axis, int32_t value)
@@ -1069,6 +1046,14 @@ void QWaylandInputDevice::Pointer::pointer_axis_relative_direction(uint32_t axis
}
}
+inline void QWaylandInputDevice::Pointer::maybePointerFrame()
+{
+ if (version() < WL_POINTER_FRAME_SINCE_VERSION) {
+ qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
+ pointer_frame();
+ }
+}
+
void QWaylandInputDevice::Pointer::setFrameEvent(QWaylandPointerEvent *event)
{
qCDebug(lcQpaWaylandInput) << "Setting frame event " << event->type;
@@ -1077,13 +1062,9 @@ void QWaylandInputDevice::Pointer::setFrameEvent(QWaylandPointerEvent *event)
flushFrameEvent();
}
- delete mFrameData.event;
- mFrameData.event = event;
+ mFrameData.event.reset(event);
- if (version() < WL_POINTER_FRAME_SINCE_VERSION) {
- qCDebug(lcQpaWaylandInput) << "Flushing new event; no frame event in this version";
- flushFrameEvent();
- }
+ maybePointerFrame();
}
void QWaylandInputDevice::Pointer::FrameData::resetScrollData()
@@ -1163,11 +1144,24 @@ void QWaylandInputDevice::Pointer::flushScrollEvent()
{
QPoint angleDelta = mFrameData.angleDelta();
+ // The wayland protocol has separate horizontal and vertical axes, Qt has just the one inverted flag
+ // Pragmatically it should't come up
+ const bool inverted = mFrameData.verticalAxisInverted || mFrameData.horizontalAxisInverted;
+
// Angle delta is required for Qt wheel events, so don't try to send events if it's zero
if (!angleDelta.isNull()) {
- QWaylandWindow *target = QWaylandWindow::mouseGrab();
- if (!target)
- target = focusWindow();
+ QWaylandWindow *target = mScrollTarget;
+ if (!mScrollBeginSent) {
+ if (!target)
+ target = QWaylandWindow::mouseGrab();
+ if (!target)
+ target = focusWindow();
+ }
+ if (!target) {
+ qCDebug(lcQpaWaylandInput) << "Flushing scroll event aborted - no scroll target";
+ mFrameData.resetScrollData();
+ return;
+ }
if (isDefinitelyTerminated(mFrameData.axisSource) && !mScrollBeginSent) {
qCDebug(lcQpaWaylandInput) << "Flushing scroll event sending ScrollBegin";
@@ -1177,21 +1171,38 @@ void QWaylandInputDevice::Pointer::flushScrollEvent()
mParent->modifiers(), false));
mScrollBeginSent = true;
mScrollDeltaRemainder = QPointF();
+ mScrollTarget = target;
}
Qt::ScrollPhase phase = mScrollBeginSent ? Qt::ScrollUpdate : Qt::NoScrollPhase;
QPoint pixelDelta = mFrameData.pixelDeltaAndError(&mScrollDeltaRemainder);
- Qt::MouseEventSource source = mFrameData.wheelEventSource();
-
-
- // The wayland protocol has separate horizontal and vertical axes, Qt has just the one inverted flag
- // Pragmatically it should't come up
- const bool inverted = mFrameData.verticalAxisInverted || mFrameData.horizontalAxisInverted;
qCDebug(lcQpaWaylandInput) << "Flushing scroll event" << phase << pixelDelta << angleDelta;
target->handleMouse(mParent, WheelEvent(focusWindow(), phase, mParent->mTime, mSurfacePos, mGlobalPos,
- pixelDelta, angleDelta, source, mParent->modifiers(), inverted));
+ pixelDelta, angleDelta, mFrameData.wheelEventSource(), mParent->modifiers(), inverted));
+ }
+
+ if (mScrollEnd) {
+ if (mScrollBeginSent) {
+ if (auto target = mScrollTarget.get()) {
+ qCDebug(lcQpaWaylandInput) << "Flushing scroll end event";
+ target->handleMouse(mParent, WheelEvent(focusWindow(), Qt::ScrollEnd, mParent->mTime, mSurfacePos, mGlobalPos,
+ QPoint(), QPoint(), mFrameData.wheelEventSource(), mParent->modifiers(), inverted));
+ }
+ mScrollBeginSent = false;
+ mScrollDeltaRemainder = QPointF();
+ } else {
+ // May receive axis_stop for events we haven't sent a ScrollBegin for because
+ // most axis_sources do not mandate an axis_stop event to be sent.
+
+ // TODO: For now, we just ignore these events, but we could perhaps take this as an
+ // indication that this compositor will in fact send axis_stop events for these sources
+ // and send a ScrollBegin the next time an axis_source event with this type is encountered.
+ }
+ mScrollEnd = false;
+ mScrollTarget.clear();
}
+
mFrameData.resetScrollData();
}
@@ -1199,7 +1210,7 @@ void QWaylandInputDevice::Pointer::flushFrameEvent()
{
mEventCompression.delayTimer.stop();
- if (auto *event = mFrameData.event) {
+ if (auto *event = mFrameData.event.get()) {
if (auto window = event->surface) {
window->handleMouse(mParent, *event);
} else if (mFrameData.event->type == QEvent::MouseButtonRelease) {
@@ -1212,8 +1223,7 @@ void QWaylandInputDevice::Pointer::flushFrameEvent()
event->modifiers); // , Qt::MouseEventSource source =
// Qt::MouseEventNotSynthesized);
}
- delete mFrameData.event;
- mFrameData.event = nullptr;
+ mFrameData.event.reset();
}
//TODO: do modifiers get passed correctly here?
diff --git a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h
index 533a10991e5e..aa2ccddbcdb8 100644
--- a/src/plugins/platforms/wayland/qwaylandinputdevice_p.h
+++ b/src/plugins/platforms/wayland/qwaylandinputdevice_p.h
@@ -370,7 +370,7 @@ private Q_SLOTS:
Qt::MouseButton mLastButton = Qt::NoButton;
struct FrameData {
- QWaylandPointerEvent *event = nullptr;
+ QScopedPointer<QWaylandPointerEvent> event;
QPointF delta;
QPoint delta120;
@@ -387,10 +387,14 @@ private Q_SLOTS:
} mFrameData;
bool mScrollBeginSent = false;
+ bool mScrollEnd = false;
QPointF mScrollDeltaRemainder;
+ QPointer<QWaylandWindow> mScrollTarget;
QWaylandEventCompressionPrivate mEventCompression;
+ void maybePointerFrame();
+
void setFrameEvent(QWaylandPointerEvent *event);
void flushScrollEvent();
void flushFrameEvent();

View file

@ -1,39 +0,0 @@
From c93008e4d06abb0072e0e5e57d84a4ae182ecfc0 Mon Sep 17 00:00:00 2001
From: Błażej Szczygieł <mumei6102@gmail.com>
Date: Tue, 21 Oct 2025 19:49:17 +0200
Subject: [PATCH] wayland: Fix crash in QWaylandShmBackingStore::scroll()
Fixes a crash when monitor is unplugged while scrolling.
recreateBackBufferIfNeeded() calls getBuffer() which may set
mFrontBuffer to nullptr.
Amends: 6f25f703fd37a900c139e14a33a4639502bfeae7
Task-number: QTBUG-139231
Change-Id: Ia5bedce2a3f6580c722f73446de81a26d40ea2f4
Reviewed-by: David Edmundson <davidedmundson@kde.org>
---
diff --git a/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp b/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp
index b853db2..fa70b53 100644
--- a/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp
+++ b/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp
@@ -229,7 +229,7 @@
// Inspired by QCALayerBackingStore.
bool QWaylandShmBackingStore::scroll(const QRegion &region, int dx, int dy)
{
- if (Q_UNLIKELY(!mBackBuffer || !mFrontBuffer))
+ if (Q_UNLIKELY(!mBackBuffer))
return false;
const qreal devicePixelRatio = waylandWindow()->scale();
@@ -241,6 +241,8 @@
return false;
recreateBackBufferIfNeeded();
+ if (!mFrontBuffer)
+ return false;
const QPoint scrollDelta(dx, dy);
const QMargins margins = windowDecorationMargins();

View file

@ -1,242 +0,0 @@
From 6f25f703fd37a900c139e14a33a4639502bfeae7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?B=C5=82a=C5=BCej=20Szczygie=C5=82?= <mumei6102@gmail.com>
Date: Wed, 13 Aug 2025 00:48:08 +0200
Subject: [PATCH] wayland: Optimize scroll operation
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Don't copy the dirty region in "recreateBackBufferIfNeeded()" in
"beginPaint()". Mark this region as non-dirty in the back buffer
and paint. Finalize the back buffer (copy remaining dirty region)
before flushing. This allows to optimize scrolling, we no longer
have to do a redundant dirty area copy.
This matches the "QCALayerBackingStore" logic.
Task-number: QTBUG-139231
Change-Id: I6c19491fa8f093de9c9ce7624d8f9b65b0b722c5
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: David Edmundson <davidedmundson@kde.org>
---
.../wayland/qwaylandshmbackingstore.cpp | 111 ++++++++++++------
.../wayland/qwaylandshmbackingstore_p.h | 3 +-
2 files changed, 74 insertions(+), 40 deletions(-)
diff --git a/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp b/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp
index d8fc7b18ca34..591e5064ebd6 100644
--- a/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp
+++ b/src/plugins/platforms/wayland/qwaylandshmbackingstore.cpp
@@ -159,6 +159,8 @@ QWaylandShmBackingStore::QWaylandShmBackingStore(QWindow *window, QWaylandDispla
// recreateBackBufferIfNeeded always resets mBackBuffer
if (mRequestedSize.isValid() && waylandWindow())
recreateBackBufferIfNeeded();
+ else
+ mBackBuffer = nullptr;
qDeleteAll(copy);
wl_event_queue_destroy(oldEventQueue);
});
@@ -183,11 +185,15 @@ QPaintDevice *QWaylandShmBackingStore::paintDevice()
void QWaylandShmBackingStore::updateDirtyStates(const QRegion &region)
{
- // Update dirty state of buffers based on what was painted. The back buffer will
- // not be dirty since we already painted on it, while other buffers will become dirty.
+ // Update dirty state of buffers based on what was painted. The back buffer will be
+ // less dirty, since we painted to it, while other buffers will become more dirty.
+ // This allows us to minimize copies between front and back buffers on swap in the
+ // cases where the painted region overlaps with the previous frame (front buffer).
for (QWaylandShmBuffer *b : std::as_const(mBuffers)) {
if (b != mBackBuffer)
b->dirtyRegion() += region;
+ else
+ b->dirtyRegion() -= region;
}
}
@@ -198,7 +204,7 @@ void QWaylandShmBackingStore::beginPaint(const QRegion &region)
const QMargins margins = windowDecorationMargins();
const QRegion regionTranslated = region.translated(margins.left(), margins.top());
- const bool bufferWasRecreated = recreateBackBufferIfNeeded(regionTranslated);
+ const bool bufferWasRecreated = recreateBackBufferIfNeeded();
updateDirtyStates(regionTranslated);
// Although undocumented, QBackingStore::beginPaint expects the painted region
@@ -223,7 +229,7 @@ void QWaylandShmBackingStore::endPaint()
// Inspired by QCALayerBackingStore.
bool QWaylandShmBackingStore::scroll(const QRegion &region, int dx, int dy)
{
- if (!mBackBuffer)
+ if (Q_UNLIKELY(!mBackBuffer || !mFrontBuffer))
return false;
const qreal devicePixelRatio = waylandWindow()->scale();
@@ -236,19 +242,35 @@ bool QWaylandShmBackingStore::scroll(const QRegion &region, int dx, int dy)
recreateBackBufferIfNeeded();
- QImage *backBufferImage = mBackBuffer->image();
-
const QPoint scrollDelta(dx, dy);
const QMargins margins = windowDecorationMargins();
const QRegion adjustedRegion = region.translated(margins.left(), margins.top());
- const QRect boundingRect = adjustedRegion.boundingRect();
- const QPoint devicePixelDelta = scrollDelta * devicePixelRatio;
+ const QRegion inPlaceRegion = adjustedRegion - mBackBuffer->dirtyRegion();
+ const QRegion frontBufferRegion = adjustedRegion - inPlaceRegion;
+
+ if (!inPlaceRegion.isEmpty()) {
+ const QRect inPlaceBoundingRect = inPlaceRegion.boundingRect();
+ const QPoint devicePixelDelta = scrollDelta * devicePixelRatio;
+
+ qt_scrollRectInImage(*mBackBuffer->image(),
+ QRect(inPlaceBoundingRect.topLeft() * devicePixelRatio,
+ inPlaceBoundingRect.size() * devicePixelRatio),
+ devicePixelDelta);
+ }
- qt_scrollRectInImage(*backBufferImage,
- QRect(boundingRect.topLeft() * devicePixelRatio,
- boundingRect.size() * devicePixelRatio),
- devicePixelDelta);
+ if (!frontBufferRegion.isEmpty()) {
+ QPainter painter(mBackBuffer->image());
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ painter.scale(qreal(1) / devicePixelRatio, qreal(1) / devicePixelRatio);
+ for (const QRect &rect : frontBufferRegion) {
+ QRect sourceRect(rect.topLeft() * devicePixelRatio,
+ rect.size() * devicePixelRatio);
+ QRect destinationRect((rect.topLeft() + scrollDelta) * devicePixelRatio,
+ rect.size() * devicePixelRatio);
+ painter.drawImage(destinationRect, *mFrontBuffer->image(), sourceRect);
+ }
+ }
// We do not mark the source region as dirty, even though it technically has "moved".
// This matches the behavior of other backingstore implementations using qt_scrollRectInImage.
@@ -289,6 +311,8 @@ void QWaylandShmBackingStore::flush(QWindow *window, const QRegion &region, cons
if (windowDecoration() && windowDecoration()->isDirty())
updateDecorations();
+ finalizeBackBuffer();
+
mFrontBuffer = mBackBuffer;
QMargins margins = windowDecorationMargins();
@@ -313,6 +337,8 @@ QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size, bool &b
mBuffers.removeAt(i);
if (mBackBuffer == buffer)
mBackBuffer = nullptr;
+ if (mFrontBuffer == buffer)
+ mFrontBuffer = nullptr;
delete buffer;
}
}
@@ -341,7 +367,7 @@ QWaylandShmBuffer *QWaylandShmBackingStore::getBuffer(const QSize &size, bool &b
return nullptr;
}
-bool QWaylandShmBackingStore::recreateBackBufferIfNeeded(const QRegion &nonDirtyRegion)
+bool QWaylandShmBackingStore::recreateBackBufferIfNeeded()
{
wl_display_dispatch_queue_pending(mDisplay->wl_display(), mEventQueue);
@@ -375,30 +401,6 @@ bool QWaylandShmBackingStore::recreateBackBufferIfNeeded(const QRegion &nonDirty
qsizetype oldSizeInBytes = mBackBuffer ? mBackBuffer->image()->sizeInBytes() : 0;
qsizetype newSizeInBytes = buffer->image()->sizeInBytes();
- // mBackBuffer may have been deleted here but if so it means its size was different so we wouldn't copy it anyway
- if (mBackBuffer != buffer && oldSizeInBytes == newSizeInBytes) {
- const QRegion clipRegion = buffer->dirtyRegion() - nonDirtyRegion;
- const auto clipRects = clipRegion.rects();
- if (!clipRects.empty()) {
- Q_ASSERT(mBackBuffer);
- const QImage *sourceImage = mBackBuffer->image();
- QImage *targetImage = buffer->image();
-
- QPainter painter(targetImage);
- painter.setCompositionMode(QPainter::CompositionMode_Source);
- const qreal targetDevicePixelRatio = painter.device()->devicePixelRatio();
- for (const QRect &clipRect : clipRects) { // Iterate clip rects, because complicated clip region causes higher CPU usage
- if (clipRects.size() > 1)
- painter.save();
- painter.setClipRect(clipRect);
- painter.scale(qreal(1) / targetDevicePixelRatio, qreal(1) / targetDevicePixelRatio);
- painter.drawImage(QRectF(QPointF(), targetImage->size()), *sourceImage, sourceImage->rect());
- if (clipRects.size() > 1)
- painter.restore();
- }
- }
- }
-
mBackBuffer = buffer;
for (QWaylandShmBuffer *buffer : std::as_const(mBuffers)) {
@@ -412,11 +414,40 @@ bool QWaylandShmBackingStore::recreateBackBufferIfNeeded(const QRegion &nonDirty
if (windowDecoration() && window()->isVisible() && oldSizeInBytes != newSizeInBytes)
windowDecoration()->update();
- buffer->dirtyRegion() = QRegion();
-
return bufferWasRecreated;
}
+void QWaylandShmBackingStore::finalizeBackBuffer()
+{
+ Q_ASSERT(mBackBuffer);
+
+ const QRegion clipRegion = mBackBuffer->dirtyRegion();
+ if (clipRegion.isEmpty())
+ return;
+
+ if (Q_UNLIKELY(!mFrontBuffer || mFrontBuffer == mBackBuffer))
+ return;
+
+ const QImage *sourceImage = mFrontBuffer->image();
+ QImage *targetImage = mBackBuffer->image();
+
+ QPainter painter(targetImage);
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ const qreal targetDevicePixelRatio = painter.device()->devicePixelRatio();
+ const auto clipRects = clipRegion.rects();
+ for (const QRect &clipRect : clipRects) { // Iterate clip rects, because complicated clip region causes higher CPU usage
+ if (clipRects.size() > 1)
+ painter.save();
+ painter.setClipRect(clipRect);
+ painter.scale(qreal(1) / targetDevicePixelRatio, qreal(1) / targetDevicePixelRatio);
+ painter.drawImage(QRectF(QPointF(), targetImage->size()), *sourceImage, sourceImage->rect());
+ if (clipRects.size() > 1)
+ painter.restore();
+ }
+
+ mBackBuffer->dirtyRegion() = QRegion();
+}
+
QImage *QWaylandShmBackingStore::entireSurface() const
{
return mBackBuffer->image();
@@ -496,6 +527,8 @@ QImage QWaylandShmBackingStore::toImage() const
// instead of flush() for widgets that have renderToTexture children
// (QOpenGLWidget, QQuickWidget).
+ const_cast<QWaylandShmBackingStore *>(this)->finalizeBackBuffer();
+
return *contentSurface();
}
#endif // opengl
diff --git a/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h b/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h
index efd80159e859..cfcafb283265 100644
--- a/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h
+++ b/src/plugins/platforms/wayland/qwaylandshmbackingstore_p.h
@@ -73,7 +73,8 @@ class Q_WAYLANDCLIENT_EXPORT QWaylandShmBackingStore : public QPlatformBackingSt
QMargins windowDecorationMargins() const;
QImage *entireSurface() const;
QImage *contentSurface() const;
- bool recreateBackBufferIfNeeded(const QRegion &nonDirtyRegion = QRegion());
+ bool recreateBackBufferIfNeeded();
+ void finalizeBackBuffer();
QWaylandWindow *waylandWindow() const;
void iterateBuffer();

View file

@ -1 +1 @@
SHA512 (qtbase-everywhere-src-6.10.1.tar.xz) = fd5dcdc59ec3b39e48563513ae438eb4540a28e72c46961295de2ccb08609289d477ef7e91aac0b8983f2d5b05b901b4f5be10eaca4ac4c6aa8cd598f37a228e
SHA512 (qtbase-everywhere-src-6.2.3.tar.xz) = c1adfb971723213f193223de222e10414868cf091e79c1fedbcaaa495cce57976bb65ac5d20138bc7e5668b92bf88ef054783b55c59b3a8feffc837596764698