From f497a5bb87270174b8e0106b7eca1992d44ff15d Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Wed, 1 Jun 2016 16:23:23 +0200 Subject: Use xdg_shell configure events to determine active window According to the xdg_shell protocol, the compositor is allowed to set multiple active windows. Qt's model, however, allows only a single active window. In order to map between the models, a list of the compositor's active windows is kept in QWaylandDisplay in the order they were activated. Hence, the front of this list will always be the most recently activated window, and it will be mapped as Qt's active window. Previously keyboard focus was used to determine the active window, this method has been disabled for xdg_shell. Functionality for delaying the call to QWindowSystemInterface::handleWindowActivated has been moved from QWaylandInputDevice::Keyboard to QWaylandDisplay so the implementations can share the workaround. Task-number: QTBUG-53702 Change-Id: I878151f9c52ed09a8d6571c6208920436c3ca8fc Reviewed-by: Giulio Camuffo --- src/client/qwaylanddisplay.cpp | 69 ++++++++++++++++++++++++++++++++++++++ src/client/qwaylanddisplay_p.h | 13 +++++++ src/client/qwaylandinputdevice.cpp | 34 ++----------------- src/client/qwaylandinputdevice_p.h | 4 --- src/client/qwaylandxdgsurface.cpp | 15 ++++++++- src/client/qwaylandxdgsurface_p.h | 1 + 6 files changed, 100 insertions(+), 36 deletions(-) (limited to 'src/client') diff --git a/src/client/qwaylanddisplay.cpp b/src/client/qwaylanddisplay.cpp index 4358bf69..f6d86bb3 100644 --- a/src/client/qwaylanddisplay.cpp +++ b/src/client/qwaylanddisplay.cpp @@ -130,6 +130,8 @@ QWaylandDisplay::QWaylandDisplay(QWaylandIntegration *waylandIntegration) , mLastInputSerial(0) , mLastInputDevice(0) , mLastInputWindow(0) + , mLastKeyboardFocus(Q_NULLPTR) + , mSyncCallback(Q_NULLPTR) { qRegisterMetaType("uint32_t"); @@ -383,6 +385,73 @@ void QWaylandDisplay::setLastInputDevice(QWaylandInputDevice *device, uint32_t s mLastInputWindow = win; } +bool QWaylandDisplay::shellManagesActiveState() const +{ + //TODO: This should be part of a shell interface used by the shell protocol implementations + return mShellXdg; +} + +void QWaylandDisplay::handleWindowActivated(QWaylandWindow *window) +{ + if (mActiveWindows.contains(window)) + return; + + mActiveWindows.append(window); + requestWaylandSync(); +} + +void QWaylandDisplay::handleWindowDeactivated(QWaylandWindow *window) +{ + Q_ASSERT(!mActiveWindows.empty()); + + if (mActiveWindows.last() == window) + requestWaylandSync(); + + mActiveWindows.removeOne(window); +} + +void QWaylandDisplay::handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice) +{ + QWaylandWindow *keyboardFocus = inputDevice->keyboardFocus(); + + if (!shellManagesActiveState() && mLastKeyboardFocus != keyboardFocus) { + if (keyboardFocus) + handleWindowActivated(keyboardFocus); + if (mLastKeyboardFocus) + handleWindowDeactivated(mLastKeyboardFocus); + } + mLastKeyboardFocus = inputDevice->keyboardFocus(); +} + +void QWaylandDisplay::handleWaylandSync() +{ + // This callback is used to set the window activation because we may get an activate/deactivate + // pair, and the latter one would be lost in the QWindowSystemInterface queue, if we issue the + // handleWindowActivated() calls immediately. + QWindow *activeWindow = mActiveWindows.empty() ? Q_NULLPTR : mActiveWindows.last()->window(); + if (activeWindow != QGuiApplication::focusWindow()) + QWindowSystemInterface::handleWindowActivated(activeWindow); +} + +const wl_callback_listener QWaylandDisplay::syncCallbackListener = { + [](void *data, struct wl_callback *callback, uint32_t time){ + Q_UNUSED(time); + wl_callback_destroy(callback); + QWaylandDisplay *display = static_cast(data); + display->mSyncCallback = Q_NULLPTR; + display->handleWaylandSync(); + } +}; + +void QWaylandDisplay::requestWaylandSync() +{ + if (mSyncCallback) + return; + + mSyncCallback = wl_display_sync(mDisplay); + wl_callback_add_listener(mSyncCallback, &syncCallbackListener, this); +} + } QT_END_NAMESPACE diff --git a/src/client/qwaylanddisplay_p.h b/src/client/qwaylanddisplay_p.h index adc012b5..237be5b7 100644 --- a/src/client/qwaylanddisplay_p.h +++ b/src/client/qwaylanddisplay_p.h @@ -48,6 +48,7 @@ #include #include #include +#include #include @@ -168,6 +169,11 @@ public: QWaylandWindow *lastInputWindow() const; void setLastInputDevice(QWaylandInputDevice *device, uint32_t serial, QWaylandWindow *window); + bool shellManagesActiveState() const; + void handleWindowActivated(QWaylandWindow *window); + void handleWindowDeactivated(QWaylandWindow *window); + void handleKeyboardFocusChanged(QWaylandInputDevice *inputDevice); + public slots: void blockingReadEvents(); void flushRequests(); @@ -177,6 +183,9 @@ private: void exitWithError(); void checkError() const; + void handleWaylandSync(); + void requestWaylandSync(); + struct Listener { RegistryListener listener; void *data; @@ -207,6 +216,10 @@ private: uint32_t mLastInputSerial; QWaylandInputDevice *mLastInputDevice; QPointer mLastInputWindow; + QWaylandWindow *mLastKeyboardFocus; + QVector mActiveWindows; + struct wl_callback *mSyncCallback; + static const wl_callback_listener syncCallbackListener; void registry_global(uint32_t id, const QString &interface, uint32_t version) Q_DECL_OVERRIDE; void registry_global_remove(uint32_t id) Q_DECL_OVERRIDE; diff --git a/src/client/qwaylandinputdevice.cpp b/src/client/qwaylandinputdevice.cpp index e2412eaa..5eaed9ea 100644 --- a/src/client/qwaylandinputdevice.cpp +++ b/src/client/qwaylandinputdevice.cpp @@ -69,7 +69,6 @@ QWaylandInputDevice::Keyboard::Keyboard(QWaylandInputDevice *p) , mXkbMap(0) , mXkbState(0) #endif - , mFocusCallback(0) , mNativeModifiers(0) { connect(&mRepeatTimer, SIGNAL(timeout()), this, SLOT(repeatKey())); @@ -122,8 +121,6 @@ QWaylandInputDevice::Keyboard::~Keyboard() #endif if (mFocus) QWindowSystemInterface::handleWindowActivated(0); - if (mFocusCallback) - wl_callback_destroy(mFocusCallback); if (mParent->mVersion >= 3) wl_keyboard_release(object()); else @@ -591,10 +588,7 @@ void QWaylandInputDevice::Keyboard::keyboard_enter(uint32_t time, struct wl_surf QWaylandWindow *window = QWaylandWindow::fromWlSurface(surface); mFocus = window; - if (!mFocusCallback) { - mFocusCallback = wl_display_sync(mParent->mDisplay); - wl_callback_add_listener(mFocusCallback, &QWaylandInputDevice::Keyboard::callback, this); - } + mParent->mQDisplay->handleKeyboardFocusChanged(mParent); } void QWaylandInputDevice::Keyboard::keyboard_leave(uint32_t time, struct wl_surface *surface) @@ -609,31 +603,9 @@ void QWaylandInputDevice::Keyboard::keyboard_leave(uint32_t time, struct wl_surf mFocus = NULL; - // Use a callback to set the focus because we may get a leave/enter pair, and - // the latter one would be lost in the QWindowSystemInterface queue, if - // we issue the handleWindowActivated() calls immediately. - if (!mFocusCallback) { - mFocusCallback = wl_display_sync(mParent->mDisplay); - wl_callback_add_listener(mFocusCallback, &QWaylandInputDevice::Keyboard::callback, this); - } - mRepeatTimer.stop(); -} - -const wl_callback_listener QWaylandInputDevice::Keyboard::callback = { - QWaylandInputDevice::Keyboard::focusCallback -}; + mParent->mQDisplay->handleKeyboardFocusChanged(mParent); -void QWaylandInputDevice::Keyboard::focusCallback(void *data, struct wl_callback *callback, uint32_t time) -{ - Q_UNUSED(time); - Q_UNUSED(callback); - QWaylandInputDevice::Keyboard *self = static_cast(data); - if (self->mFocusCallback) { - wl_callback_destroy(self->mFocusCallback); - self->mFocusCallback = 0; - } - - QWindowSystemInterface::handleWindowActivated(self->mFocus ? self->mFocus->window() : 0); + mRepeatTimer.stop(); } void QWaylandInputDevice::Keyboard::keyboard_key(uint32_t serial, uint32_t time, uint32_t key, uint32_t state) diff --git a/src/client/qwaylandinputdevice_p.h b/src/client/qwaylandinputdevice_p.h index 1df90292..e38ad2f8 100644 --- a/src/client/qwaylandinputdevice_p.h +++ b/src/client/qwaylandinputdevice_p.h @@ -185,7 +185,6 @@ public: xkb_keymap *mXkbMap; xkb_state *mXkbState; #endif - struct wl_callback *mFocusCallback; uint32_t mNativeModifiers; int mRepeatKey; @@ -197,9 +196,6 @@ public: #endif QTimer mRepeatTimer; - static const wl_callback_listener callback; - static void focusCallback(void *data, struct wl_callback *callback, uint32_t time); - Qt::KeyboardModifiers modifiers() const; private slots: diff --git a/src/client/qwaylandxdgsurface.cpp b/src/client/qwaylandxdgsurface.cpp index 8852d2df..bbda03dc 100644 --- a/src/client/qwaylandxdgsurface.cpp +++ b/src/client/qwaylandxdgsurface.cpp @@ -52,6 +52,7 @@ QWaylandXdgSurface::QWaylandXdgSurface(struct ::xdg_surface *xdg_surface, QWayla , m_maximized(false) , m_minimized(false) , m_fullscreen(false) + , m_active(false) , m_extendedWindow(Q_NULLPTR) { if (window->display()->windowExtension()) @@ -60,6 +61,9 @@ QWaylandXdgSurface::QWaylandXdgSurface(struct ::xdg_surface *xdg_surface, QWayla QWaylandXdgSurface::~QWaylandXdgSurface() { + if (m_active) + window()->display()->handleWindowDeactivated(m_window); + xdg_surface_destroy(object()); delete m_extendedWindow; } @@ -176,6 +180,7 @@ void QWaylandXdgSurface::xdg_surface_configure(int32_t width, int32_t height, st size_t numStates = states->size / sizeof(uint32_t); bool aboutToMaximize = false; bool aboutToFullScreen = false; + bool aboutToActivate = false; for (size_t i = 0; i < numStates; i++) { switch (state[i]) { @@ -189,13 +194,21 @@ void QWaylandXdgSurface::xdg_surface_configure(int32_t width, int32_t height, st m_normalSize = QSize(width, height); break; case XDG_SURFACE_STATE_ACTIVATED: - // TODO: here about the missing window activation + aboutToActivate = true; break; default: break; } } + if (!m_active && aboutToActivate) { + m_active = true; + window()->display()->handleWindowActivated(m_window); + } else if (m_active && !aboutToActivate) { + m_active = false; + window()->display()->handleWindowDeactivated(m_window); + } + if (!m_fullscreen && aboutToFullScreen) { if (!m_maximized) m_normalSize = m_window->window()->frameGeometry().size(); diff --git a/src/client/qwaylandxdgsurface_p.h b/src/client/qwaylandxdgsurface_p.h index d4380d25..e367980b 100644 --- a/src/client/qwaylandxdgsurface_p.h +++ b/src/client/qwaylandxdgsurface_p.h @@ -106,6 +106,7 @@ private: bool m_maximized; bool m_minimized; bool m_fullscreen; + bool m_active; QSize m_normalSize; QMargins m_margins; QWaylandExtendedSurface *m_extendedWindow; -- cgit v1.2.1 From d8b4bef3ddff327598027c8f94a61e3d0b61a2dd Mon Sep 17 00:00:00 2001 From: Johan Klokkhammer Helsing Date: Fri, 8 Jul 2016 14:46:34 +0200 Subject: Fix high-DPI scaling of window decorations for shared memory buffers Change-Id: I6833ab86ffdb4e37dad5108baddb7a54cfb5e9fa Reviewed-by: Giulio Camuffo --- src/client/qwaylandshmbackingstore.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) (limited to 'src/client') diff --git a/src/client/qwaylandshmbackingstore.cpp b/src/client/qwaylandshmbackingstore.cpp index 3bbe24c3..5c93d2ba 100644 --- a/src/client/qwaylandshmbackingstore.cpp +++ b/src/client/qwaylandshmbackingstore.cpp @@ -301,29 +301,37 @@ void QWaylandShmBackingStore::updateDecorations() QPainter decorationPainter(entireSurface()); decorationPainter.setCompositionMode(QPainter::CompositionMode_Source); QImage sourceImage = windowDecoration()->contentImage(); - QRect target; + + qreal dp = sourceImage.devicePixelRatio(); + int dpWidth = int(sourceImage.width() / dp); + int dpHeight = int(sourceImage.height() / dp); + QMatrix sourceMatrix; + sourceMatrix.scale(dp, dp); + QRect target; // needs to be in device independent pixels + //Top target.setX(0); target.setY(0); - target.setWidth(sourceImage.width()); + target.setWidth(dpWidth); target.setHeight(windowDecorationMargins().top()); - decorationPainter.drawImage(target, sourceImage, target); + decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target)); //Left target.setWidth(windowDecorationMargins().left()); - target.setHeight(sourceImage.height()); - decorationPainter.drawImage(target, sourceImage, target); + target.setHeight(dpHeight); + decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target)); //Right - target.setX(sourceImage.width() - windowDecorationMargins().right()); - decorationPainter.drawImage(target, sourceImage, target); + target.setX(dpWidth - windowDecorationMargins().right()); + target.setWidth(windowDecorationMargins().right()); + decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target)); //Bottom target.setX(0); - target.setY(sourceImage.height() - windowDecorationMargins().bottom()); - target.setWidth(sourceImage.width()); + target.setY(dpHeight - windowDecorationMargins().bottom()); + target.setWidth(dpWidth); target.setHeight(windowDecorationMargins().bottom()); - decorationPainter.drawImage(target, sourceImage, target); + decorationPainter.drawImage(target, sourceImage, sourceMatrix.mapRect(target)); } QWaylandAbstractDecoration *QWaylandShmBackingStore::windowDecoration() const -- cgit v1.2.1