Support user fullscreen for XdgShellSurfaces

Summary:
So far ShellClient did not support that the user can set a window to
fullscreen. This was omitted in the initial implementation as WlShell
doesn't support passing the state back to the surface.

With XdgShell this problem doesn't exist any more and we can implement
it. The implementation is mostly based on the one for Client and
adjusted for the Wayland world.

Test Plan:
New test cases and manual testing (send kate and kwrite to
fullscreen through alt+f3 menu)

Reviewers: #kwin, #plasma

Subscribers: plasma-devel, kwin

Tags: #kwin

Differential Revision: https://phabricator.kde.org/D8169
icc-effect-5.14.5
Martin Flöser 2017-10-07 11:41:17 +02:00
parent 9a965405e3
commit 188491d392
6 changed files with 145 additions and 37 deletions

View File

@ -367,7 +367,8 @@ public:
virtual bool isHiddenInternal() const = 0;
// TODO: remove boolean trap
virtual void hideClient(bool hide) = 0;
virtual bool isFullScreenable() const = 0;
bool isFullScreenable() const;
bool isFullScreenable(bool fullscreen_hack) const;
virtual bool isFullScreen() const = 0;
// TODO: remove boolean trap
virtual AbstractClient *findModal(bool allow_itself = false) = 0;

View File

@ -66,6 +66,11 @@ private Q_SLOTS:
void testMinimizeActiveWindow();
void testFullscreen_data();
void testFullscreen();
void testUserCanSetFullscreen_data();
void testUserCanSetFullscreen();
void testUserSetFullscreenWlShell();
void testUserSetFullscreenXdgShell_data();
void testUserSetFullscreenXdgShell();
void testMaximizedToFullscreen_data();
void testMaximizedToFullscreen();
void testWindowOpensLargerThanScreen_data();
@ -465,6 +470,97 @@ void TestShellClient::testFullscreen()
QCOMPARE(c->isDecorated(), decoMode == ServerSideDecoration::Mode::Server);
}
void TestShellClient::testUserCanSetFullscreen_data()
{
QTest::addColumn<Test::ShellSurfaceType>("type");
QTest::addColumn<bool>("expected");
QTest::newRow("wlShell") << Test::ShellSurfaceType::WlShell << false;
QTest::newRow("xdgShellV5") << Test::ShellSurfaceType::XdgShellV5 << true;
QTest::newRow("xdgShellV6") << Test::ShellSurfaceType::XdgShellV6 << true;
}
void TestShellClient::testUserCanSetFullscreen()
{
QScopedPointer<Surface> surface(Test::createSurface());
QFETCH(Test::ShellSurfaceType, type);
QScopedPointer<QObject> shellSurface(Test::createShellSurface(type, surface.data()));
auto c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(c);
QVERIFY(c->isActive());
QVERIFY(!c->isFullScreen());
QTEST(c->userCanSetFullScreen(), "expected");
}
void TestShellClient::testUserSetFullscreenWlShell()
{
// wlshell cannot sync fullscreen to the client
QScopedPointer<Surface> surface(Test::createSurface());
QScopedPointer<ShellSurface> shellSurface(Test::createShellSurface(surface.data()));
auto c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(c);
QVERIFY(c->isActive());
QVERIFY(!c->isFullScreen());
QSignalSpy fullscreenChangedSpy(c, &AbstractClient::fullScreenChanged);
QVERIFY(fullscreenChangedSpy.isValid());
c->setFullScreen(true);
QCOMPARE(fullscreenChangedSpy.count(), 0);
QVERIFY(!c->isFullScreen());
}
void TestShellClient::testUserSetFullscreenXdgShell_data()
{
QTest::addColumn<Test::ShellSurfaceType>("type");
QTest::newRow("xdgShellV5") << Test::ShellSurfaceType::XdgShellV5;
QTest::newRow("xdgShellV6") << Test::ShellSurfaceType::XdgShellV6;
}
void TestShellClient::testUserSetFullscreenXdgShell()
{
QScopedPointer<Surface> surface(Test::createSurface());
QFETCH(Test::ShellSurfaceType, type);
QScopedPointer<XdgShellSurface> shellSurface(dynamic_cast<XdgShellSurface*>(Test::createShellSurface(type, surface.data())));
QVERIFY(!shellSurface.isNull());
QSignalSpy configureRequestedSpy(shellSurface.data(), &XdgShellSurface::configureRequested);
QVERIFY(configureRequestedSpy.isValid());
auto c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(c);
QVERIFY(c->isActive());
QVERIFY(!c->isFullScreen());
// two, one for initial sync, second as it becomes active
QTRY_COMPARE(configureRequestedSpy.count(), 2);
QSignalSpy fullscreenChangedSpy(c, &AbstractClient::fullScreenChanged);
QVERIFY(fullscreenChangedSpy.isValid());
c->setFullScreen(true);
QCOMPARE(c->isFullScreen(), true);
configureRequestedSpy.clear();
QVERIFY(configureRequestedSpy.wait());
QCOMPARE(configureRequestedSpy.count(), 1);
QCOMPARE(configureRequestedSpy.first().at(0).toSize(), screens()->size(0));
const auto states = configureRequestedSpy.first().at(1).value<KWayland::Client::XdgShellSurface::States>();
QVERIFY(states.testFlag(KWayland::Client::XdgShellSurface::State::Fullscreen));
QVERIFY(states.testFlag(KWayland::Client::XdgShellSurface::State::Activated));
QVERIFY(!states.testFlag(KWayland::Client::XdgShellSurface::State::Maximized));
QVERIFY(!states.testFlag(KWayland::Client::XdgShellSurface::State::Resizing));
QCOMPARE(fullscreenChangedSpy.count(), 1);
QVERIFY(c->isFullScreen());
shellSurface->ackConfigure(configureRequestedSpy.first().at(2).value<quint32>());
// unset fullscreen again
c->setFullScreen(false);
QCOMPARE(c->isFullScreen(), false);
configureRequestedSpy.clear();
QVERIFY(configureRequestedSpy.wait());
QCOMPARE(configureRequestedSpy.count(), 1);
QCOMPARE(configureRequestedSpy.first().at(0).toSize(), QSize(100, 50));
QVERIFY(!configureRequestedSpy.first().at(1).value<KWayland::Client::XdgShellSurface::States>().testFlag(KWayland::Client::XdgShellSurface::State::Fullscreen));
QCOMPARE(fullscreenChangedSpy.count(), 2);
QVERIFY(!c->isFullScreen());
}
void TestShellClient::testMaximizedToFullscreen_data()
{

View File

@ -149,8 +149,6 @@ public:
void setFullScreen(bool set, bool user = true) override;
bool isFullScreen() const override;
bool isFullScreenable() const override;
bool isFullScreenable(bool fullscreen_hack) const;
bool userCanSetFullScreen() const override;
QRect geometryFSRestore() const {
return geom_fs_restore; // Only for session saving

View File

@ -2483,12 +2483,12 @@ void Client::changeMaximize(bool vertical, bool horizontal, bool adjust)
emit quickTileModeChanged();
}
bool Client::isFullScreenable() const
bool AbstractClient::isFullScreenable() const
{
return isFullScreenable(false);
}
bool Client::isFullScreenable(bool fullscreen_hack) const
bool AbstractClient::isFullScreenable(bool fullscreen_hack) const
{
if (!rules()->checkFullScreen(true))
return false;

View File

@ -565,7 +565,7 @@ void ShellClient::setGeometry(int x, int y, int w, int h, ForceGeometry_t force)
{
Q_UNUSED(force)
// TODO: better merge with Client's implementation
if (QSize(w, h) == geom.size()) {
if (QSize(w, h) == geom.size() && !m_positionAfterResize.isValid()) {
// size didn't change, update directly
doSetGeometry(QRect(x, y, w, h));
} else {
@ -693,11 +693,6 @@ bool ShellClient::isCloseable() const
return m_qtExtendedSurface ? true : false;
}
bool ShellClient::isFullScreenable() const
{
return false;
}
bool ShellClient::isFullScreen() const
{
return m_fullScreen;
@ -889,8 +884,46 @@ bool ShellClient::noBorder() const
void ShellClient::setFullScreen(bool set, bool user)
{
Q_UNUSED(set)
Q_UNUSED(user)
if (!isFullScreen() && !set)
return;
if (user && !userCanSetFullScreen())
return;
set = rules()->checkFullScreen(set && !isSpecialWindow());
setShade(ShadeNone);
bool was_fs = isFullScreen();
if (was_fs)
workspace()->updateFocusMousePosition(Cursor::pos()); // may cause leave event
else
m_geomFsRestore = geometry();
m_fullScreen = set;
if (was_fs == isFullScreen())
return;
if (set) {
untab();
workspace()->raiseClient(this);
}
RequestGeometryBlocker requestBlocker(this);
StackingUpdatesBlocker blocker1(workspace());
workspace()->updateClientLayer(this); // active fullscreens get different layer
updateDecoration(false, false);
if (isFullScreen()) {
setGeometry(workspace()->clientArea(FullScreenArea, this));
} else {
if (!m_geomFsRestore.isNull()) {
int currentScreen = screen();
setGeometry(QRect(m_geomFsRestore.topLeft(), adjustedSize(m_geomFsRestore.size())));
if( currentScreen != screen())
workspace()->sendClientToScreen( this, currentScreen );
} else {
// does this ever happen?
setGeometry(workspace()->clientArea(MaximizeArea, this));
}
}
updateWindowRules(Rules::Fullscreen|Rules::Position|Rules::Size);
if (was_fs != isFullScreen()) {
emit fullScreenChanged();
}
}
void ShellClient::setNoBorder(bool set)
@ -951,6 +984,9 @@ void ShellClient::doSetActive()
bool ShellClient::userCanSetFullScreen() const
{
if (m_xdgShellSurface) {
return true;
}
return false;
}
@ -1117,29 +1153,7 @@ void ShellClient::requestGeometry(const QRect &rect)
void ShellClient::clientFullScreenChanged(bool fullScreen)
{
RequestGeometryBlocker requestBlocker(this);
StackingUpdatesBlocker blocker(workspace());
const bool emitSignal = m_fullScreen != fullScreen;
m_fullScreen = fullScreen;
updateDecoration(false, false);
workspace()->updateClientLayer(this); // active fullscreens get different layer
if (fullScreen) {
m_geomFsRestore = geometry();
requestGeometry(workspace()->clientArea(FullScreenArea, this));
workspace()->raiseClient(this);
} else {
if (m_geomFsRestore.isValid()) {
requestGeometry(m_geomFsRestore);
} else {
requestGeometry(workspace()->clientArea(MaximizeArea, this));
}
}
if (emitSignal) {
emit fullScreenChanged();
}
setFullScreen(fullScreen, false);
}
void ShellClient::resizeWithChecks(int w, int h, ForceGeometry_t force)

View File

@ -80,7 +80,6 @@ public:
void closeWindow() override;
AbstractClient *findModal(bool allow_itself = false) override;
bool isCloseable() const override;
bool isFullScreenable() const override;
bool isFullScreen() const override;
bool isMaximizable() const override;
bool isMinimizable() const override;