KillWindow support for Wayland windows
Summary: AbstractClient gains a new pure virtual killWindow method and this gets implemented in ShellClient. ShellClient performs the killing by sending a term signal to the process. This can only work if the client connected through the socket and didn't get a socketpair fd passed. In that case the pid is KWin's and KWin doesn't want to terminate. Thus this is special handled to destroy the connection instead. In case terminating the process has no effect, the connection gets destroyed after five seconds. The KillWindow is adjusted to operate on AbstractClient instead of Client. This implements T4463. Test Plan: Killed windows and auto test Reviewers: #kwin, #plasma_on_wayland Subscribers: plasma-devel, kwin Tags: #plasma_on_wayland, #kwin Differential Revision: https://phabricator.kde.org/D3370
This commit is contained in:
parent
af56c6b050
commit
6bee7f4aac
8 changed files with 128 additions and 3 deletions
|
@ -614,6 +614,13 @@ public:
|
||||||
return m_desktopFileName;
|
return m_desktopFileName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to terminate the process of this AbstractClient.
|
||||||
|
*
|
||||||
|
* Implementing subclasses can perform a windowing system solution for terminating.
|
||||||
|
**/
|
||||||
|
virtual void killWindow() = 0;
|
||||||
|
|
||||||
// TODO: remove boolean trap
|
// TODO: remove boolean trap
|
||||||
static bool belongToSameApplication(const AbstractClient* c1, const AbstractClient* c2, bool active_hack = false);
|
static bool belongToSameApplication(const AbstractClient* c1, const AbstractClient* c2, bool active_hack = false);
|
||||||
|
|
||||||
|
|
|
@ -5,3 +5,7 @@ ecm_mark_as_test(copy)
|
||||||
add_executable(paste paste.cpp)
|
add_executable(paste paste.cpp)
|
||||||
target_link_libraries(paste Qt5::Gui)
|
target_link_libraries(paste Qt5::Gui)
|
||||||
ecm_mark_as_test(paste)
|
ecm_mark_as_test(paste)
|
||||||
|
######################
|
||||||
|
add_executable(kill kill.cpp)
|
||||||
|
target_link_libraries(kill Qt5::Widgets)
|
||||||
|
ecm_mark_as_test(kill)
|
||||||
|
|
32
autotests/integration/helper/kill.cpp
Normal file
32
autotests/integration/helper/kill.cpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/********************************************************************
|
||||||
|
KWin - the KDE window manager
|
||||||
|
This file is part of the KDE project.
|
||||||
|
|
||||||
|
Copyright (C) 2016 Martin Gräßlin <mgraesslin@kde.org>
|
||||||
|
|
||||||
|
This program is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation; either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*********************************************************************/
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("wayland"));
|
||||||
|
QApplication app(argc, argv);
|
||||||
|
QWidget w;
|
||||||
|
w.setGeometry(QRect(0, 0, 100, 200));
|
||||||
|
w.show();
|
||||||
|
|
||||||
|
return app.exec();
|
||||||
|
}
|
|
@ -33,8 +33,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include <KWayland/Client/surface.h>
|
#include <KWayland/Client/surface.h>
|
||||||
#include <KWayland/Client/xdgshell.h>
|
#include <KWayland/Client/xdgshell.h>
|
||||||
|
|
||||||
|
#include <KWayland/Server/clientconnection.h>
|
||||||
|
#include <KWayland/Server/display.h>
|
||||||
#include <KWayland/Server/shell_interface.h>
|
#include <KWayland/Server/shell_interface.h>
|
||||||
|
|
||||||
|
// system
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
using namespace KWin;
|
using namespace KWin;
|
||||||
using namespace KWayland::Client;
|
using namespace KWayland::Client;
|
||||||
|
|
||||||
|
@ -64,6 +71,8 @@ private Q_SLOTS:
|
||||||
void testHidden();
|
void testHidden();
|
||||||
void testDesktopFileName();
|
void testDesktopFileName();
|
||||||
void testCaptionSimplified();
|
void testCaptionSimplified();
|
||||||
|
void testKillWindow_data();
|
||||||
|
void testKillWindow();
|
||||||
};
|
};
|
||||||
|
|
||||||
void TestShellClient::initTestCase()
|
void TestShellClient::initTestCase()
|
||||||
|
@ -633,5 +642,53 @@ void TestShellClient::testCaptionSimplified()
|
||||||
QCOMPARE(c->caption(), origTitle.simplified());
|
QCOMPARE(c->caption(), origTitle.simplified());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TestShellClient::testKillWindow_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<bool>("socketMode");
|
||||||
|
|
||||||
|
QTest::newRow("display") << false;
|
||||||
|
QTest::newRow("socket") << true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TestShellClient::testKillWindow()
|
||||||
|
{
|
||||||
|
// this test verifies that killWindow properly terminates a process
|
||||||
|
// for this an external binary is launched
|
||||||
|
const QString kill = QFINDTESTDATA(QStringLiteral("helper/kill"));
|
||||||
|
QVERIFY(!kill.isEmpty());
|
||||||
|
QSignalSpy shellClientAddedSpy(waylandServer(), &WaylandServer::shellClientAdded);
|
||||||
|
QVERIFY(shellClientAddedSpy.isValid());
|
||||||
|
|
||||||
|
QScopedPointer<QProcess> process(new QProcess);
|
||||||
|
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||||
|
QFETCH(bool, socketMode);
|
||||||
|
if (socketMode) {
|
||||||
|
int sx[2];
|
||||||
|
QVERIFY(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) >= 0);
|
||||||
|
waylandServer()->display()->createClient(sx[0]);
|
||||||
|
int socket = dup(sx[1]);
|
||||||
|
QVERIFY(socket != -1);
|
||||||
|
env.insert(QStringLiteral("WAYLAND_SOCKET"), QByteArray::number(socket));
|
||||||
|
env.remove("WAYLAND_DISPLAY");
|
||||||
|
} else {
|
||||||
|
env.insert("WAYLAND_DISPLAY", s_socketName);
|
||||||
|
}
|
||||||
|
process->setProcessEnvironment(env);
|
||||||
|
process->setProcessChannelMode(QProcess::ForwardedChannels);
|
||||||
|
process->setProgram(kill);
|
||||||
|
process->start();
|
||||||
|
QVERIFY(process->waitForStarted());
|
||||||
|
|
||||||
|
AbstractClient *killClient = nullptr;
|
||||||
|
QVERIFY(shellClientAddedSpy.wait());
|
||||||
|
killClient = shellClientAddedSpy.first().first().value<AbstractClient*>();
|
||||||
|
QVERIFY(killClient);
|
||||||
|
QSignalSpy finishedSpy(process.data(), static_cast<void(QProcess::*)(int,QProcess::ExitStatus)>(&QProcess::finished));
|
||||||
|
QVERIFY(finishedSpy.isValid());
|
||||||
|
killClient->killWindow();
|
||||||
|
QVERIFY(finishedSpy.wait());
|
||||||
|
QVERIFY(!finishedSpy.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
WAYLANDTEST_MAIN(TestShellClient)
|
WAYLANDTEST_MAIN(TestShellClient)
|
||||||
#include "shell_client_test.moc"
|
#include "shell_client_test.moc"
|
||||||
|
|
2
client.h
2
client.h
|
@ -249,7 +249,7 @@ public:
|
||||||
static bool belongToSameApplication(const Client* c1, const Client* c2, bool active_hack = false);
|
static bool belongToSameApplication(const Client* c1, const Client* c2, bool active_hack = false);
|
||||||
static bool sameAppWindowRoleMatch(const Client* c1, const Client* c2, bool active_hack);
|
static bool sameAppWindowRoleMatch(const Client* c1, const Client* c2, bool active_hack);
|
||||||
|
|
||||||
void killWindow();
|
void killWindow() override;
|
||||||
void toggleShade();
|
void toggleShade();
|
||||||
void showContextHelp() override;
|
void showContextHelp() override;
|
||||||
void cancelShadeHoverTimer();
|
void cancelShadeHoverTimer();
|
||||||
|
|
|
@ -20,7 +20,7 @@ You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*********************************************************************/
|
*********************************************************************/
|
||||||
#include "killwindow.h"
|
#include "killwindow.h"
|
||||||
#include "client.h"
|
#include "abstract_client.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "unmanaged.h"
|
#include "unmanaged.h"
|
||||||
|
@ -43,7 +43,7 @@ void KillWindow::start()
|
||||||
if (!t) {
|
if (!t) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (Client *c = qobject_cast<Client*>(t)) {
|
if (AbstractClient *c = qobject_cast<AbstractClient*>(t)) {
|
||||||
c->killWindow();
|
c->killWindow();
|
||||||
} else if (Unmanaged *u = qobject_cast<Unmanaged*>(t)) {
|
} else if (Unmanaged *u = qobject_cast<Unmanaged*>(t)) {
|
||||||
xcb_kill_client(connection(), u->window());
|
xcb_kill_client(connection(), u->window());
|
||||||
|
|
|
@ -34,6 +34,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include <KDecoration2/DecoratedClient>
|
#include <KDecoration2/DecoratedClient>
|
||||||
|
|
||||||
#include <KWayland/Client/surface.h>
|
#include <KWayland/Client/surface.h>
|
||||||
|
#include <KWayland/Server/clientconnection.h>
|
||||||
#include <KWayland/Server/seat_interface.h>
|
#include <KWayland/Server/seat_interface.h>
|
||||||
#include <KWayland/Server/shell_interface.h>
|
#include <KWayland/Server/shell_interface.h>
|
||||||
#include <KWayland/Server/surface_interface.h>
|
#include <KWayland/Server/surface_interface.h>
|
||||||
|
@ -48,6 +49,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include <QOpenGLFramebufferObject>
|
#include <QOpenGLFramebufferObject>
|
||||||
#include <QWindow>
|
#include <QWindow>
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
using namespace KWayland::Server;
|
using namespace KWayland::Server;
|
||||||
|
|
||||||
static const QByteArray s_schemePropertyName = QByteArrayLiteral("KDE_COLOR_SCHEME_PATH");
|
static const QByteArray s_schemePropertyName = QByteArrayLiteral("KDE_COLOR_SCHEME_PATH");
|
||||||
|
@ -1380,4 +1385,22 @@ bool ShellClient::dockWantsInput() const
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ShellClient::killWindow()
|
||||||
|
{
|
||||||
|
if (isInternal()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!surface()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto c = surface()->client();
|
||||||
|
if (c->processId() == getpid()) {
|
||||||
|
c->destroy();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
::kill(c->processId(), SIGTERM);
|
||||||
|
// give it time to terminate and only if terminate fails, try destroy Wayland connection
|
||||||
|
QTimer::singleShot(5000, c, &ClientConnection::destroy);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,6 +132,8 @@ public:
|
||||||
|
|
||||||
void showOnScreenEdge() override;
|
void showOnScreenEdge() override;
|
||||||
|
|
||||||
|
void killWindow() override;
|
||||||
|
|
||||||
// TODO: const-ref
|
// TODO: const-ref
|
||||||
void placeIn(QRect &area);
|
void placeIn(QRect &area);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue