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/D8169icc-effect-5.14.5
parent
9a965405e3
commit
188491d392
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
2
client.h
2
client.h
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue