kwin/kcmkwin/kwinrules/detectwidget.cpp
Martin Flöser 1d71292e13 Add a method to dbus interface to query information about a window
Summary:
This call is added for the window rules kcm which has a detect
functionality. As that detect functionality cannot query any Wayland
windows we need to have some functionality in KWin core. Furthermore
this allows to simplify the code in the kcm as all the custom X11
interaction can be removed. KWin internally has the functionality to
find a window at a given position.

From a security perspective adding this dbus method is fine as the user
stays in control of the functionality. It requires active click to
select a window.

The new dbus call is already used in the rules kcm replacing the
X11 based detect functionality. That a detect is now able to get
information for both X11 and Wayland windows. So far only X11 windows
on X11 were supported. So this fills an important gap in the Wayland
offerings. It should now be possible to create rules for Wayland
windows (though may not be fully functional).

Test Plan: Run the kwin_rules_dialog and it detected the window correctly

Reviewers: #kwin, #plasma

Subscribers: plasma-devel, kwin

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D10490
2018-02-25 14:12:04 +01:00

176 lines
5.2 KiB
C++

/*
* Copyright (c) 2004 Lubos Lunak <l.lunak@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, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "detectwidget.h"
#include "../../plugins/platforms/x11/standalone/x11cursor.h"
#include <KLocalizedString>
#include <QDebug>
#include <kwindowsystem.h>
#include <QLabel>
#include <QRadioButton>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QMouseEvent>
#include <QEvent>
#include <QByteArray>
#include <QTimer>
#include <QDBusConnection>
#include <QDBusMessage>
#include <QDBusPendingCallWatcher>
#include <QDBusPendingReply>
Q_DECLARE_METATYPE(NET::WindowType)
namespace KWin
{
DetectWidget::DetectWidget(QWidget* parent)
: QWidget(parent)
{
setupUi(this);
}
DetectDialog::DetectDialog(QWidget* parent, const char* name)
: QDialog(parent)
{
setObjectName(name);
setModal(true);
setLayout(new QVBoxLayout);
widget = new DetectWidget(this);
layout()->addWidget(widget);
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel, this);
layout()->addWidget(buttons);
connect(buttons, SIGNAL(accepted()), SLOT(accept()));
connect(buttons, SIGNAL(rejected()), SLOT(reject()));
}
void DetectDialog::detect(int secs)
{
QTimer::singleShot(secs*1000, this, SLOT(selectWindow()));
}
void DetectDialog::executeDialog()
{
static const char* const types[] = {
I18N_NOOP("Normal Window"),
I18N_NOOP("Desktop"),
I18N_NOOP("Dock (panel)"),
I18N_NOOP("Toolbar"),
I18N_NOOP("Torn-Off Menu"),
I18N_NOOP("Dialog Window"),
I18N_NOOP("Override Type"),
I18N_NOOP("Standalone Menubar"),
I18N_NOOP("Utility Window"),
I18N_NOOP("Splash Screen")
};
widget->class_label->setText(wmclass_class + QLatin1String(" (") + wmclass_name + ' ' + wmclass_class + ')');
widget->role_label->setText(role);
widget->match_role->setEnabled(!role.isEmpty());
if (type == NET::Unknown)
widget->type_label->setText(i18n("Unknown - will be treated as Normal Window"));
else
widget->type_label->setText(i18n(types[ type ]));
widget->title_label->setText(title);
widget->machine_label->setText(machine);
widget->adjustSize();
adjustSize();
if (width() < 4*height()/3)
resize(4*height()/3, height());
emit detectionDone(exec() == QDialog::Accepted);
}
QByteArray DetectDialog::selectedClass() const
{
if (widget->match_whole_class->isChecked())
return wmclass_name + ' ' + wmclass_class;
return wmclass_class;
}
bool DetectDialog::selectedWholeClass() const
{
return widget->match_whole_class->isChecked();
}
QByteArray DetectDialog::selectedRole() const
{
if (widget->match_role->isChecked())
return role;
return "";
}
QString DetectDialog::selectedTitle() const
{
return title;
}
Rules::StringMatch DetectDialog::titleMatch() const
{
return widget->match_title->isChecked() ? Rules::ExactMatch : Rules::UnimportantMatch;
}
bool DetectDialog::selectedWholeApp() const
{
return !widget->match_type->isChecked();
}
NET::WindowType DetectDialog::selectedType() const
{
return type;
}
QByteArray DetectDialog::selectedMachine() const
{
return machine;
}
void DetectDialog::selectWindow()
{
QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"),
QStringLiteral("/KWin"),
QStringLiteral("org.kde.KWin"),
QStringLiteral("queryWindowInfo"));
QDBusPendingReply<QVariantMap> async = QDBusConnection::sessionBus().asyncCall(message);
QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this);
connect(callWatcher, &QDBusPendingCallWatcher::finished, this,
[this](QDBusPendingCallWatcher *self) {
QDBusPendingReply<QVariantMap> reply = *self;
self->deleteLater();
if (!reply.isValid()) {
emit detectionDone(false);
return;
}
m_windowInfo = reply.value();
wmclass_class = m_windowInfo.value("resourceClass").toByteArray();
wmclass_name = m_windowInfo.value("resourceName").toByteArray();
role = m_windowInfo.value("role").toByteArray();
type = m_windowInfo.value("type").value<NET::WindowType>();
title = m_windowInfo.value("caption").toString();
machine = m_windowInfo.value("clientMachine").toByteArray();
executeDialog();
}
);
}
} // namespace