67int main(
int argc,
char *argv[])
69 KLocalizedString::setApplicationDomain(QByteArrayLiteral(
"kwin"));
70 QApplication app(argc, argv);
71 QApplication::setWindowIcon(QIcon::fromTheme(QStringLiteral(
"tools-report-bug")));
72 QCoreApplication::setApplicationName(QStringLiteral(
"kwin_killer_helper"));
73 QCoreApplication::setOrganizationDomain(QStringLiteral(
"kde.org"));
74 QApplication::setApplicationDisplayName(i18n(
"Window Manager"));
75 QCoreApplication::setApplicationVersion(QStringLiteral(
"1.0"));
76 QApplication::setDesktopFileName(QStringLiteral(
"org.kde.kwin.killer"));
78 QCommandLineOption pidOption(QStringLiteral(
"pid"),
79 i18n(
"PID of the application to terminate"), i18n(
"pid"));
80 QCommandLineOption hostNameOption(QStringLiteral(
"hostname"),
81 i18n(
"Hostname on which the application is running"), i18n(
"hostname"));
82 QCommandLineOption windowNameOption(QStringLiteral(
"windowname"),
83 i18n(
"Caption of the window to be terminated"), i18n(
"caption"));
84 QCommandLineOption applicationNameOption(QStringLiteral(
"applicationname"),
85 i18n(
"Name of the application to be terminated"), i18n(
"name"));
86 QCommandLineOption widOption(QStringLiteral(
"wid"),
87 i18n(
"ID of resource belonging to the application"), i18n(
"id"));
88 QCommandLineOption timestampOption(QStringLiteral(
"timestamp"),
89 i18n(
"Time of user action causing termination"), i18n(
"time"));
90 QCommandLineParser parser;
91 parser.setApplicationDescription(i18n(
"KWin helper utility"));
92 parser.addHelpOption();
93 parser.addVersionOption();
95 parser.addOption(pidOption);
96 parser.addOption(hostNameOption);
97 parser.addOption(windowNameOption);
98 parser.addOption(applicationNameOption);
99 parser.addOption(widOption);
100 parser.addOption(timestampOption);
104 const bool isX11 = app.platformName() == QLatin1String(
"xcb");
106 QString hostname = parser.value(hostNameOption);
108 pid_t pid = parser.value(pidOption).toULong(&pid_ok);
109 QString caption = parser.value(windowNameOption);
110 QString appname = parser.value(applicationNameOption);
112 xcb_window_t wid = XCB_WINDOW_NONE;
113 QString windowHandle;
115 wid = parser.value(widOption).toULong(&id_ok);
117 windowHandle = parser.value(widOption);
121 bool time_ok =
false;
122 xcb_timestamp_t timestamp = parser.value(timestampOption).toULong(&time_ok);
124 if (!pid_ok || pid == 0 || ((!id_ok || wid == XCB_WINDOW_NONE) && windowHandle.isEmpty())
125 || (isX11 && (!time_ok || timestamp == XCB_CURRENT_TIME))
126 || hostname.isEmpty() || caption.isEmpty() || appname.isEmpty()) {
127 fprintf(stdout,
"%s\n", qPrintable(i18n(
"This helper utility is not supposed to be called directly.")));
130 bool isLocal = hostname == QStringLiteral(
"localhost");
132 const auto service = KService::serviceByDesktopName(appname);
134 appname = service->name();
135 QApplication::setApplicationDisplayName(appname);
139 const QString titleSeparator = QString::fromUtf8(
" \xe2\x80\x94 ");
140 caption.remove(titleSeparator + appname);
141 caption.remove(QStringLiteral(
" – ") + appname);
142 caption.remove(QStringLiteral(
" - ") + appname);
144 caption = caption.toHtmlEscaped();
145 appname = appname.toHtmlEscaped();
146 hostname = hostname.toHtmlEscaped();
147 QString pidString = QString::number(pid);
149 QString question = (caption == appname) ? xi18nc(
"@info",
"<para><application>%1</application> is not responding. Do you want to terminate this application?</para>",
151 : xi18nc(
"@info \"window title\" of application name is not responding.",
"<para>\"%1\" of <application>%2</application> is not responding. Do you want to terminate this application?</para>",
153 question += xi18nc(
"@info",
154 "<para><emphasis strong='true'>Terminating this application will close all of its windows. Any unsaved data will be lost.</emphasis></para>");
156 KGuiItem continueButton = KGuiItem(i18nc(
"@action:button Terminate app",
"&Terminate %1", appname), QStringLiteral(
"application-exit"));
157 KGuiItem cancelButton = KGuiItem(i18nc(
"@action:button Wait for frozen app to maybe respond again",
"&Wait Longer"), QStringLiteral(
"chronometer"));
160 QX11Info::setAppUserTime(timestamp);
163 auto *dialog =
new KMessageDialog(KMessageDialog::WarningContinueCancel, question);
164 dialog->setAttribute(Qt::WA_DeleteOnClose);
165 dialog->setCaption(i18nc(
"@title:window",
"Not Responding"));
169 const QIcon appIcon = QIcon::fromTheme(service->icon());
170 if (!appIcon.isNull()) {
172 const QIcon warningBadge = QIcon::fromTheme(QStringLiteral(
"emblem-warning"), QIcon::fromTheme(QStringLiteral(
"emblem-important")));
174 icon = KIconUtils::addOverlay(appIcon, warningBadge, qApp->isRightToLeft() ? Qt::BottomLeftCorner : Qt::BottomRightCorner);
177 dialog->setIcon(icon);
178 dialog->setButtons(continueButton, KGuiItem(), cancelButton);
181 i18nc(
"@info",
"Process ID: %1", pidString)};
183 details << i18nc(
"@info",
"Host name: %1", hostname);
185 dialog->setDetails(details.join(QLatin1Char(
'\n')));
188 std::unique_ptr<XdgImporter> xdgImporter;
189 std::unique_ptr<XdgImported> importedParent;
192 if (QWindow *foreignParent = QWindow::fromWinId(wid)) {
193 dialog->windowHandle()->setTransientParent(foreignParent);
196 xdgImporter = std::make_unique<XdgImporter>();
199 QObject::connect(dialog, &QDialog::finished, &app, [pid, hostname, isLocal](
int result) {
200 if (result == KMessageBox::PrimaryAction) {
203 lst << hostname << QStringLiteral(
"kill") << QString::number(pid);
204 QProcess::startDetached(QStringLiteral(
"xon"), lst);
206 if (::kill(pid, SIGKILL) && errno == EPERM) {
207 KAuth::Action killer(QStringLiteral(
"org.kde.ksysguard.processlisthelper.sendsignal"));
208 killer.setHelperId(QStringLiteral(
"org.kde.ksysguard.processlisthelper"));
209 killer.addArgument(QStringLiteral(
"pid0"), pid);
210 killer.addArgument(QStringLiteral(
"pidcount"), 1);
211 killer.addArgument(QStringLiteral(
"signal"), SIGKILL);
212 if (killer.isValid()) {
213 qDebug() <<
"Using KAuth to kill pid: " << pid;
216 qDebug() <<
"KWin process killer action not valid";
228 if (
auto *waylandWindow = dialog->windowHandle()->nativeInterface<QNativeInterface::Private::QWaylandWindow>()) {
229 importedParent.reset(xdgImporter->import(windowHandle));
230 if (
auto *surface = waylandWindow->surface()) {
231 importedParent->set_parent_of(surface);
236 dialog->windowHandle()->requestActivate();