kwin/src/killprompt.cpp
Kai Uwe Broulik 5c96c38e39 Support kill prompt for XdgTopLevelWindows
Instead of killing the window without asking, show the kill prompt
like it's done for X11 windows.

The window in question is exported through XDG foreign so the kill
helper can parent itself to it, and an activation token is also provided.

Also, the more contemporary desktop file name is now used for
identification rather than window class.

A no-display desktop file is installed for the kill helper so that it
can get a proper window icon and suppress startup notification.
2023-11-27 14:10:15 +00:00

107 lines
3 KiB
C++

/*
* SPDX-FileCopyrightText: 2023 Kai Uwe Broulik <kde@broulik.de>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "killprompt.h"
#include "client_machine.h"
#include "wayland/display.h"
#include "wayland/seat.h"
#include "wayland/xdgforeign_v2.h"
#include "wayland_server.h"
#include "x11window.h"
#include "xdgactivationv1.h"
#include "xdgshellwindow.h"
#include <QDir>
#include <QFileInfo>
#include <QString>
namespace KWin
{
KillPrompt::KillPrompt(Window *window)
: m_window(window)
{
Q_ASSERT(qobject_cast<X11Window *>(window) || qobject_cast<XdgToplevelWindow *>(window));
m_process.setProcessChannelMode(QProcess::ForwardedChannels);
const QFileInfo binaryInfo(KWIN_KILLER_BIN);
const QFileInfo buildDirBinary{QDir{QCoreApplication::applicationDirPath()}, binaryInfo.fileName()};
if (buildDirBinary.exists()) {
m_process.setProgram(buildDirBinary.absoluteFilePath());
} else {
m_process.setProgram(KWIN_KILLER_BIN);
}
}
bool KillPrompt::isRunning() const
{
return m_process.state() == QProcess::Running;
}
void KillPrompt::start(quint32 timestamp)
{
if (isRunning()) {
return;
}
QProcessEnvironment env = kwinApp()->processStartupEnvironment();
QString wid;
QString timestampString;
QString hostname = QStringLiteral("localhost");
QString appId = !m_window->desktopFileName().isEmpty() ? m_window->desktopFileName() : m_window->resourceClass();
QString platform;
if (auto *x11Window = qobject_cast<X11Window *>(m_window)) {
platform = QStringLiteral("xcb");
wid = QString::number(x11Window->window());
timestampString = QString::number(timestamp);
if (!x11Window->clientMachine()->isLocal()) {
hostname = x11Window->clientMachine()->hostName();
}
} else if (auto *xdgToplevel = qobject_cast<XdgToplevelWindow *>(m_window)) {
platform = QStringLiteral("wayland");
auto *exported = waylandServer()->exportAsForeign(xdgToplevel->surface());
wid = exported->handle();
auto *seat = waylandServer()->seat();
const QString token = waylandServer()->xdgActivationIntegration()->requestPrivilegedToken(nullptr, seat->display()->serial(), seat, QStringLiteral("org.kde.kwin.killer"));
env.insert(QStringLiteral("XDG_ACTIVATION_TOKEN"), token);
env.remove(QStringLiteral("QT_WAYLAND_RECONNECT"));
}
QStringList args{
QStringLiteral("-platform"),
platform,
QStringLiteral("--pid"),
QString::number(m_window->pid()),
QStringLiteral("--windowname"),
m_window->captionNormal(),
QStringLiteral("--applicationname"),
appId,
QStringLiteral("--wid"),
wid,
QStringLiteral("--hostname"),
hostname,
QStringLiteral("--timestamp"),
timestampString,
};
m_process.setArguments(args);
m_process.setProcessEnvironment(env);
m_process.start();
}
void KillPrompt::quit()
{
m_process.terminate();
}
} // namespace KWin