Merge "Merge remote-tracking branch 'origin/5.12' into 5.13" into refs/staging/5.13

This commit is contained in:
Liang Qi 2019-05-03 11:44:22 +00:00 committed by The Qt Project
commit 3eb4e7ca76
4 changed files with 205 additions and 33 deletions

View File

@ -69,6 +69,8 @@
#include <QtWaylandClient/private/qwayland-text-input-unstable-v2.h>
#include <QtCore/private/qcore_unix_p.h>
#include <QtCore/QAbstractEventDispatcher>
#include <QtGui/qpa/qwindowsysteminterface.h>
#include <QtGui/private/qguiapplication_p.h>
@ -204,7 +206,6 @@ void QWaylandDisplay::flushRequests()
wl_display_flush(mDisplay);
}
void QWaylandDisplay::blockingReadEvents()
{
if (wl_display_dispatch(mDisplay) < 0) {
@ -218,6 +219,41 @@ void QWaylandDisplay::exitWithError()
::exit(1);
}
wl_event_queue *QWaylandDisplay::createEventQueue()
{
return wl_display_create_queue(mDisplay);
}
void QWaylandDisplay::dispatchQueueWhile(wl_event_queue *queue, std::function<bool ()> condition, int timeout)
{
if (!condition())
return;
QElapsedTimer timer;
timer.start();
struct pollfd pFd = qt_make_pollfd(wl_display_get_fd(mDisplay), POLLIN);
while (timeout == -1 || timer.elapsed() < timeout) {
while (wl_display_prepare_read_queue(mDisplay, queue) != 0)
wl_display_dispatch_queue_pending(mDisplay, queue);
wl_display_flush(mDisplay);
const int remaining = qMax(timeout - timer.elapsed(), 0ll);
const int pollTimeout = timeout == -1 ? -1 : remaining;
if (qt_poll_msecs(&pFd, 1, pollTimeout) > 0)
wl_display_read_events(mDisplay);
else
wl_display_cancel_read(mDisplay);
if (wl_display_dispatch_queue_pending(mDisplay, queue) < 0) {
checkError();
exitWithError();
}
if (!condition())
break;
}
}
QWaylandScreen *QWaylandDisplay::screenForOutput(struct wl_output *output) const
{
for (int i = 0; i < mScreens.size(); ++i) {

View File

@ -192,6 +192,9 @@ public:
void handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice);
void handleWindowDestroyed(QWaylandWindow *window);
wl_event_queue *createEventQueue();
void dispatchQueueWhile(wl_event_queue *queue, std::function<bool()> condition, int timeout = -1);
public slots:
void blockingReadEvents();
void flushRequests();

View File

@ -62,6 +62,7 @@
#include <QtGui/private/qwindow_p.h>
#include <QtCore/QDebug>
#include <QtCore/QThread>
QT_BEGIN_NAMESPACE
@ -74,6 +75,7 @@ QWaylandWindow *QWaylandWindow::mMouseGrab = nullptr;
QWaylandWindow::QWaylandWindow(QWindow *window)
: QPlatformWindow(window)
, mDisplay(waylandScreen()->display())
, mFrameQueue(mDisplay->createEventQueue())
, mResizeAfterSwap(qEnvironmentVariableIsSet("QT_WAYLAND_RESIZE_AFTER_SWAP"))
{
static WId id = 1;
@ -357,6 +359,8 @@ void QWaylandWindow::sendExposeEvent(const QRect &rect)
{
if (!(mShellSurface && mShellSurface->handleExpose(rect)))
QWindowSystemInterface::handleExposeEvent(window(), rect);
else
qCDebug(lcQpaWayland) << "sendExposeEvent: intercepted by shell extension, not sending";
mLastExposeGeometry = rect;
}
@ -541,18 +545,11 @@ void QWaylandWindow::handleScreenRemoved(QScreen *qScreen)
void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y)
{
Q_ASSERT(!buffer->committed());
if (mFrameCallback) {
wl_callback_destroy(mFrameCallback);
mFrameCallback = nullptr;
}
if (buffer) {
mFrameCallback = frame();
wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this);
mWaitingForFrameSync = true;
handleUpdate();
buffer->setBusy();
attach(buffer->buffer(), x, y);
QtWayland::wl_surface::attach(buffer->buffer(), x, y);
} else {
QtWayland::wl_surface::attach(nullptr, 0, 0);
}
@ -613,32 +610,61 @@ void QWaylandWindow::commit()
}
const wl_callback_listener QWaylandWindow::callbackListener = {
QWaylandWindow::frameCallback
[](void *data, wl_callback *callback, uint32_t time) {
Q_UNUSED(callback);
Q_UNUSED(time);
auto *window = static_cast<QWaylandWindow*>(data);
if (window->thread() != QThread::currentThread())
QMetaObject::invokeMethod(window, [=] { window->handleFrameCallback(); }, Qt::QueuedConnection);
else
window->handleFrameCallback();
}
};
void QWaylandWindow::frameCallback(void *data, struct wl_callback *callback, uint32_t time)
void QWaylandWindow::handleFrameCallback()
{
Q_UNUSED(time);
Q_UNUSED(callback);
QWaylandWindow *self = static_cast<QWaylandWindow*>(data);
bool wasExposed = isExposed();
self->mWaitingForFrameSync = false;
if (self->mUpdateRequested) {
self->mUpdateRequested = false;
self->deliverUpdateRequest();
if (mFrameCallbackTimerId != -1) {
killTimer(mFrameCallbackTimerId);
mFrameCallbackTimerId = -1;
}
mWaitingForFrameCallback = false;
mFrameCallbackTimedOut = false;
if (!wasExposed && isExposed())
sendExposeEvent(QRect(QPoint(), geometry().size()));
if (wasExposed && hasPendingUpdateRequest())
deliverUpdateRequest();
}
QMutex QWaylandWindow::mFrameSyncMutex;
void QWaylandWindow::waitForFrameSync()
bool QWaylandWindow::waitForFrameSync(int timeout)
{
QMutexLocker locker(&mFrameSyncMutex);
if (!mWaitingForFrameSync)
return;
mDisplay->flushRequests();
while (mWaitingForFrameSync)
mDisplay->blockingReadEvents();
if (!mWaitingForFrameCallback)
return true;
wl_proxy_set_queue(reinterpret_cast<wl_proxy *>(mFrameCallback), mFrameQueue);
mDisplay->dispatchQueueWhile(mFrameQueue, [&]() { return mWaitingForFrameCallback; }, timeout);
if (mWaitingForFrameCallback) {
qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
mFrameCallbackTimedOut = true;
mWaitingForUpdate = false;
sendExposeEvent(QRect());
}
// Stop current frame timer if any, can't use killTimer directly, because we might be on a diffent thread
if (mFrameCallbackTimerId != -1) {
int id = mFrameCallbackTimerId;
mFrameCallbackTimerId = -1;
QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection);
}
return !mWaitingForFrameCallback;
}
QMargins QWaylandWindow::frameMargins() const
@ -976,6 +1002,9 @@ bool QWaylandWindow::isExposed() const
if (!window()->isVisible())
return false;
if (mFrameCallbackTimedOut)
return false;
if (mShellSurface)
return mShellSurface->isExposed();
@ -1051,12 +1080,107 @@ QVariant QWaylandWindow::property(const QString &name, const QVariant &defaultVa
return m_properties.value(name, defaultValue);
}
void QWaylandWindow::timerEvent(QTimerEvent *event)
{
if (event->timerId() == mFallbackUpdateTimerId) {
killTimer(mFallbackUpdateTimerId);
mFallbackUpdateTimerId = -1;
qCDebug(lcWaylandBackingstore) << "mFallbackUpdateTimer timed out";
if (!isExposed()) {
qCDebug(lcWaylandBackingstore) << "Fallback update timer: Window not exposed,"
<< "not delivering update request.";
return;
}
if (mWaitingForUpdate && hasPendingUpdateRequest() && !mWaitingForFrameCallback) {
qCWarning(lcWaylandBackingstore) << "Delivering update request through fallback timer,"
<< "may not be in sync with display";
deliverUpdateRequest();
}
}
if (event->timerId() == mFrameCallbackTimerId) {
killTimer(mFrameCallbackTimerId);
mFrameCallbackTimerId = -1;
qCDebug(lcWaylandBackingstore) << "Didn't receive frame callback in time, window should now be inexposed";
mFrameCallbackTimedOut = true;
mWaitingForUpdate = false;
sendExposeEvent(QRect());
}
}
void QWaylandWindow::requestUpdate()
{
if (!mWaitingForFrameSync)
QPlatformWindow::requestUpdate();
else
mUpdateRequested = true;
Q_ASSERT(hasPendingUpdateRequest()); // should be set by QPA
// If we have a frame callback all is good and will be taken care of there
if (mWaitingForFrameCallback)
return;
// If we've already called deliverUpdateRequest(), but haven't seen any attach+commit/swap yet
if (mWaitingForUpdate) {
// Ideally, we should just have returned here, but we're not guaranteed that the client
// will actually update, so start this timer to deliver another request update after a while
// *IF* the client doesn't update.
int fallbackTimeout = 100;
mFallbackUpdateTimerId = startTimer(fallbackTimeout);
return;
}
// Some applications (such as Qt Quick) depend on updates being delivered asynchronously,
// so use invokeMethod to delay the delivery a bit.
QMetaObject::invokeMethod(this, [this] {
// Things might have changed in the meantime
if (hasPendingUpdateRequest() && !mWaitingForUpdate && !mWaitingForFrameCallback)
deliverUpdateRequest();
}, Qt::QueuedConnection);
}
// Should be called whenever we commit a buffer (directly through wl_surface.commit or indirectly
// with eglSwapBuffers) to know when it's time to commit the next one.
// Can be called from the render thread (without locking anything) so make sure to not make races in this method.
void QWaylandWindow::handleUpdate()
{
// TODO: Should sync subsurfaces avoid requesting frame callbacks?
if (mFrameCallback) {
wl_callback_destroy(mFrameCallback);
mFrameCallback = nullptr;
}
if (mFallbackUpdateTimerId != -1) {
// Ideally, we would stop the fallback timer here, but since we're on another thread,
// it's not allowed. Instead we set mFallbackUpdateTimer to -1 here, so we'll just
// ignore it if it times out before it's cleaned up by the invokeMethod call.
int id = mFallbackUpdateTimerId;
mFallbackUpdateTimerId = -1;
QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection);
}
mFrameCallback = frame();
wl_callback_add_listener(mFrameCallback, &QWaylandWindow::callbackListener, this);
mWaitingForFrameCallback = true;
mWaitingForUpdate = false;
// Stop current frame timer if any, can't use killTimer directly, see comment above.
if (mFrameCallbackTimerId != -1) {
int id = mFrameCallbackTimerId;
mFrameCallbackTimerId = -1;
QMetaObject::invokeMethod(this, [=] { killTimer(id); }, Qt::QueuedConnection);
}
// Start a timer for handling the case when the compositor stops sending frame callbacks.
QMetaObject::invokeMethod(this, [=] { // Again; can't do it directly
if (mWaitingForFrameCallback)
mFrameCallbackTimerId = startTimer(100);
}, Qt::QueuedConnection);
}
void QWaylandWindow::deliverUpdateRequest()
{
mWaitingForUpdate = true;
QPlatformWindow::deliverUpdateRequest();
}
void QWaylandWindow::addAttachOffset(const QPoint point)

View File

@ -122,7 +122,7 @@ public:
void commit();
void waitForFrameSync();
bool waitForFrameSync(int timeout);
QMargins frameMargins() const override;
QSize surfaceSize() const;
@ -194,7 +194,10 @@ public:
bool startSystemMove(const QPoint &pos) override;
void timerEvent(QTimerEvent *event) override;
void requestUpdate() override;
void handleUpdate();
void deliverUpdateRequest() override;
public slots:
void applyConfigure();
@ -217,10 +220,17 @@ protected:
Qt::MouseButtons mMousePressedInContentArea = Qt::NoButton;
WId mWindowId;
bool mWaitingForFrameSync = false;
bool mWaitingForFrameCallback = false;
bool mFrameCallbackTimedOut = false; // Whether the frame callback has timed out
int mFrameCallbackTimerId = -1; // Started on commit, reset on frame callback
struct ::wl_callback *mFrameCallback = nullptr;
struct ::wl_event_queue *mFrameQueue = nullptr;
QWaitCondition mFrameSyncWait;
// True when we have called deliverRequestUpdate, but the client has not yet attached a new buffer
bool mWaitingForUpdate = false;
int mFallbackUpdateTimerId = -1; // Started when waiting for app to commit
QMutex mResizeLock;
bool mWaitingToApplyConfigure = false;
bool mCanResize = true;
@ -259,11 +269,10 @@ private:
void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e);
void handleScreenChanged();
bool mUpdateRequested = false;
QRect mLastExposeGeometry;
static const wl_callback_listener callbackListener;
static void frameCallback(void *data, struct wl_callback *wl_callback, uint32_t time);
void handleFrameCallback();
static QMutex mFrameSyncMutex;
static QWaylandWindow *mMouseGrab;