Set window screen from wl_surface.enter and leave events

Removes the pointer mScreen, which would previously cause a crash if the screen
was removed.

Ensures that QWindow::screen() is correct, except in the cases where:

- The compositor has not yet sent enter and leave events (in which case the
  primary output is returned).
- The compositor is not sending enter and leave events (although this is
  mandatory, some compositors don't do this).
- The application developer has tried to move the window with
  QWindow::setScreen(QScreen *). Since there is no way for a client to ask to
  be moved to a specific monitor in windowed mode, we return the requested
  screen until a new enter or leave event is received.

This will also be useful when implementing/fixing features where the current
screen matters. Examples are QT_AUTO_SCREEN_SCALE_FACTOR and the optional
output parameter to wl_shell_surface.set_fullscreen.

Task-number: QTBUG-62044
Change-Id: Iafde2e278fbc8876e8dafe5b2a4d2482fdc7961a
Reviewed-by: Paul Olav Tvete <paul.tvete@qt.io>
This commit is contained in:
Johan Klokkhammer Helsing 2017-08-17 13:20:04 +02:00 committed by Johan Helsing
parent 844002cb81
commit f35dc39ae9
4 changed files with 78 additions and 5 deletions

View File

@ -175,6 +175,12 @@ QWaylandScreen * QWaylandScreen::waylandScreenFromWindow(QWindow *window)
return static_cast<QWaylandScreen *>(platformScreen);
}
QWaylandScreen *QWaylandScreen::fromWlOutput(::wl_output *output)
{
auto wlOutput = static_cast<QtWayland::wl_output *>(wl_output_get_user_data(output));
return static_cast<QWaylandScreen *>(wlOutput);
}
void QWaylandScreen::output_mode(uint32_t flags, int width, int height, int refresh)
{
if (!(flags & WL_OUTPUT_MODE_CURRENT))

View File

@ -99,6 +99,7 @@ public:
::wl_output *output() { return object(); }
static QWaylandScreen *waylandScreenFromWindow(QWindow *window);
static QWaylandScreen *fromWlOutput(::wl_output *output);
private:
void output_mode(uint32_t flags, int width, int height, int refresh) override;

View File

@ -69,6 +69,8 @@
#include <QtCore/QDebug>
#include <wayland-client-core.h>
QT_BEGIN_NAMESPACE
namespace QtWaylandClient {
@ -78,8 +80,7 @@ QWaylandWindow *QWaylandWindow::mMouseGrab = 0;
QWaylandWindow::QWaylandWindow(QWindow *window)
: QObject()
, QPlatformWindow(window)
, mScreen(QWaylandScreen::waylandScreenFromWindow(window))
, mDisplay(mScreen->display())
, mDisplay(screen()->display())
, mShellSurface(0)
, mSubSurfaceWindow(0)
, mWindowDecoration(0)
@ -99,6 +100,7 @@ QWaylandWindow::QWaylandWindow(QWindow *window)
{
static WId id = 1;
mWindowId = id++;
connect(qApp, &QGuiApplication::screenRemoved, this, &QWaylandWindow::handleScreenRemoved);
initializeWlSurface();
}
@ -356,6 +358,11 @@ void QWaylandWindow::closePopups(QWaylandWindow *parent)
}
}
QWaylandScreen *QWaylandWindow::calculateScreenFromSurfaceEvents() const
{
return mScreens.isEmpty() ? screen() : mScreens.first();
}
void QWaylandWindow::setVisible(bool visible)
{
if (visible) {
@ -491,6 +498,53 @@ void QWaylandWindow::requestResize()
QWindowSystemInterface::flushWindowSystemEvents();
}
void QWaylandWindow::surface_enter(wl_output *output)
{
QWaylandScreen *oldScreen = calculateScreenFromSurfaceEvents();
auto addedScreen = QWaylandScreen::fromWlOutput(output);
if (mScreens.contains(addedScreen)) {
qWarning() << "Unexpected wl_surface.enter received for output with id:"
<< wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output))
<< "screen name:" << addedScreen->name() << "screen model:" << addedScreen->model();
return;
}
mScreens.append(addedScreen);
QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
if (oldScreen != newScreen) //currently this will only happen if the first wl_surface.enter is for a non-primary screen
QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen());
}
void QWaylandWindow::surface_leave(wl_output *output)
{
QWaylandScreen *oldScreen = calculateScreenFromSurfaceEvents();
auto *removedScreen = QWaylandScreen::fromWlOutput(output);
bool wasRemoved = mScreens.removeOne(removedScreen);
if (!wasRemoved) {
qWarning() << "Unexpected wl_surface.leave received for output with id:"
<< wl_proxy_get_id(reinterpret_cast<wl_proxy *>(output))
<< "screen name:" << removedScreen->name() << "screen model:" << removedScreen->model();
return;
}
QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
if (oldScreen != newScreen)
QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen());
}
void QWaylandWindow::handleScreenRemoved(QScreen *qScreen)
{
QWaylandScreen *oldScreen = calculateScreenFromSurfaceEvents();
bool wasRemoved = mScreens.removeOne(static_cast<QWaylandScreen *>(qScreen->handle()));
if (wasRemoved) {
QWaylandScreen *newScreen = calculateScreenFromSurfaceEvents();
if (oldScreen != newScreen)
QWindowSystemInterface::handleWindowScreenChanged(window(), newScreen->QPlatformScreen::screen());
}
}
void QWaylandWindow::attach(QWaylandBuffer *buffer, int x, int y)
{
if (mFrameCallback) {
@ -579,6 +633,11 @@ QWaylandSubSurface *QWaylandWindow::subSurfaceWindow() const
return mSubSurfaceWindow;
}
QWaylandScreen *QWaylandWindow::screen() const
{
return static_cast<QWaylandScreen *>(QPlatformWindow::screen());
}
void QWaylandWindow::handleContentOrientationChange(Qt::ScreenOrientation orientation)
{
if (mDisplay->compositorVersion() < 2)
@ -843,7 +902,7 @@ void QWaylandWindow::handleMouseEventWithDecoration(QWaylandInputDevice *inputDe
#if QT_CONFIG(cursor)
void QWaylandWindow::setMouseCursor(QWaylandInputDevice *device, const QCursor &cursor)
{
device->setCursor(cursor, mScreen);
device->setCursor(cursor, screen());
}
void QWaylandWindow::restoreMouseCursor(QWaylandInputDevice *device)

View File

@ -142,7 +142,7 @@ public:
QWaylandDisplay *display() const { return mDisplay; }
QWaylandShellSurface *shellSurface() const;
QWaylandSubSurface *subSurfaceWindow() const;
QWaylandScreen *screen() const { return mScreen; }
QWaylandScreen *screen() const;
void handleContentOrientationChange(Qt::ScreenOrientation orientation) override;
void setOrientationMask(Qt::ScreenOrientations mask);
@ -209,7 +209,10 @@ public slots:
void requestResize();
protected:
QWaylandScreen *mScreen;
void surface_enter(struct ::wl_output *output) override;
void surface_leave(struct ::wl_output *output) override;
QVector<QWaylandScreen *> mScreens; //As seen by wl_surface.enter/leave events. Chronological order.
QWaylandDisplay *mDisplay;
QWaylandShellSurface *mShellSurface;
QWaylandSubSurface *mSubSurfaceWindow;
@ -244,6 +247,9 @@ protected:
QWaylandShmBackingStore *mBackingStore;
private slots:
void handleScreenRemoved(QScreen *qScreen);
private:
bool setWindowStateInternal(Qt::WindowState flags);
void setGeometry_helper(const QRect &rect);
@ -254,6 +260,7 @@ private:
void reset();
void sendExposeEvent(const QRect &rect);
static void closePopups(QWaylandWindow *parent);
QWaylandScreen *calculateScreenFromSurfaceEvents() const;
void handleMouseEventWithDecoration(QWaylandInputDevice *inputDevice, const QWaylandPointerEvent &e);