ScreenshotEffect: Use Service Property to authorize screenshot without confirmation
Summary: Restrict to process with `X-KDE-DBUS-Restricted-Interfaces=org.kde.kwin.Screenshot` in their corresponding Service file, to take screenshots. Such a program can now take immediate screenshots. Adds a utility file to group KService related logic. Needed for D29408 Reviewers: #kwin, apol, davidedmundson, bport, zzag Reviewed By: #kwin, davidedmundson Subscribers: ngraham, kwin Tags: #kwin Differential Revision: https://phabricator.kde.org/D29407
This commit is contained in:
parent
2c55df788f
commit
e3df2e15a6
8 changed files with 154 additions and 60 deletions
|
@ -1,4 +1,5 @@
|
||||||
kwin_core KWin Core DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_CORE]
|
kwin_core KWin Core DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_CORE]
|
||||||
|
kwin_utils KWin utils DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_UTILS]
|
||||||
kwin_virtualkeyboard KWin Virtual Keyboard Integration DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_VIRTUALKEYBOARD]
|
kwin_virtualkeyboard KWin Virtual Keyboard Integration DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_VIRTUALKEYBOARD]
|
||||||
kwineffects KWin Effects DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWINEFFECTS]
|
kwineffects KWin Effects DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWINEFFECTS]
|
||||||
libkwineffects KWin Effects Library DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [LIBKWINEFFECTS]
|
libkwineffects KWin Effects Library DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [LIBKWINEFFECTS]
|
||||||
|
|
|
@ -23,6 +23,7 @@ set(kwin_effect_KDE_LIBS
|
||||||
KF5::Notifications # screenshot effect
|
KF5::Notifications # screenshot effect
|
||||||
KF5::Plasma # screenedge effect
|
KF5::Plasma # screenedge effect
|
||||||
KF5::WindowSystem
|
KF5::WindowSystem
|
||||||
|
KF5::Service # utils / screenshot effect
|
||||||
)
|
)
|
||||||
|
|
||||||
if (HAVE_ACCESSIBILITY)
|
if (HAVE_ACCESSIBILITY)
|
||||||
|
@ -110,6 +111,7 @@ set(kwin4_effect_builtins_sources
|
||||||
windowgeometry/windowgeometry.cpp
|
windowgeometry/windowgeometry.cpp
|
||||||
wobblywindows/wobblywindows.cpp
|
wobblywindows/wobblywindows.cpp
|
||||||
zoom/zoom.cpp
|
zoom/zoom.cpp
|
||||||
|
../service_utils.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
if (HAVE_ACCESSIBILITY)
|
if (HAVE_ACCESSIBILITY)
|
||||||
|
|
|
@ -3,5 +3,6 @@
|
||||||
|
|
||||||
# Source files
|
# Source files
|
||||||
set(kwin4_effect_builtins_sources ${kwin4_effect_builtins_sources}
|
set(kwin4_effect_builtins_sources ${kwin4_effect_builtins_sources}
|
||||||
|
../service_utils.cpp
|
||||||
screenshot/screenshot.cpp
|
screenshot/screenshot.cpp
|
||||||
)
|
)
|
||||||
|
|
|
@ -27,6 +27,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include <QTemporaryFile>
|
#include <QTemporaryFile>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QDBusConnection>
|
#include <QDBusConnection>
|
||||||
|
#include <QDBusConnectionInterface>
|
||||||
|
#include <QDBusReply>
|
||||||
#include <QVarLengthArray>
|
#include <QVarLengthArray>
|
||||||
#include <QPainter>
|
#include <QPainter>
|
||||||
#include <QMatrix4x4>
|
#include <QMatrix4x4>
|
||||||
|
@ -37,6 +39,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include <KNotification>
|
#include <KNotification>
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include "../service_utils.h"
|
||||||
|
|
||||||
class ComparableQPoint : public QPoint
|
class ComparableQPoint : public QPoint
|
||||||
{
|
{
|
||||||
|
@ -63,6 +66,8 @@ namespace KWin
|
||||||
|
|
||||||
const static QString s_errorAlreadyTaking = QStringLiteral("org.kde.kwin.Screenshot.Error.AlreadyTaking");
|
const static QString s_errorAlreadyTaking = QStringLiteral("org.kde.kwin.Screenshot.Error.AlreadyTaking");
|
||||||
const static QString s_errorAlreadyTakingMsg = QStringLiteral("A screenshot is already been taken");
|
const static QString s_errorAlreadyTakingMsg = QStringLiteral("A screenshot is already been taken");
|
||||||
|
const static QString s_errorNotAuthorized = QStringLiteral("org.kde.kwin.Screenshot.Error.NoAuthorized");
|
||||||
|
const static QString s_errorNotAuthorizedMsg = QStringLiteral("The process is not authorized to take a screenshot");
|
||||||
const static QString s_errorFd = QStringLiteral("org.kde.kwin.Screenshot.Error.FileDescriptor");
|
const static QString s_errorFd = QStringLiteral("org.kde.kwin.Screenshot.Error.FileDescriptor");
|
||||||
const static QString s_errorFdMsg = QStringLiteral("No valid file descriptor");
|
const static QString s_errorFdMsg = QStringLiteral("No valid file descriptor");
|
||||||
const static QString s_errorCancelled = QStringLiteral("org.kde.kwin.Screenshot.Error.Cancelled");
|
const static QString s_errorCancelled = QStringLiteral("org.kde.kwin.Screenshot.Error.Cancelled");
|
||||||
|
@ -71,6 +76,7 @@ const static QString s_errorInvalidArea = QStringLiteral("org.kde.kwin.Screensho
|
||||||
const static QString s_errorInvalidAreaMsg = QStringLiteral("Invalid area requested");
|
const static QString s_errorInvalidAreaMsg = QStringLiteral("Invalid area requested");
|
||||||
const static QString s_errorInvalidScreen = QStringLiteral("org.kde.kwin.Screenshot.Error.InvalidScreen");
|
const static QString s_errorInvalidScreen = QStringLiteral("org.kde.kwin.Screenshot.Error.InvalidScreen");
|
||||||
const static QString s_errorInvalidScreenMsg = QStringLiteral("Invalid screen requested");
|
const static QString s_errorInvalidScreenMsg = QStringLiteral("Invalid screen requested");
|
||||||
|
const static QString s_dbusInterfaceName = QStringLiteral("org.kde.kwin.Screenshot");
|
||||||
|
|
||||||
bool ScreenShotEffect::supported()
|
bool ScreenShotEffect::supported()
|
||||||
{
|
{
|
||||||
|
@ -496,13 +502,35 @@ void ScreenShotEffect::screenshotForWindow(qulonglong winid, int mask)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ScreenShotEffect::interactive(int mask)
|
bool ScreenShotEffect::checkCall() const
|
||||||
{
|
{
|
||||||
if (!calledFromDBus()) {
|
if (!calledFromDBus()) {
|
||||||
return QString();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QDBusReply<uint> reply = connection().interface()->servicePid(message().service());
|
||||||
|
if (reply.isValid()) {
|
||||||
|
const uint pid = reply.value();
|
||||||
|
const auto interfaces = KWin::fetchRestrictedDBusInterfacesFromPid(pid);
|
||||||
|
if (!interfaces.contains(s_dbusInterfaceName)) {
|
||||||
|
sendErrorReply(s_errorNotAuthorized, s_errorNotAuthorizedMsg);
|
||||||
|
qCWarning(KWINEFFECTS) << "Process " << pid << " tried to take a screenshot without being granted to DBus interface" << s_dbusInterfaceName;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (isTakingScreenshot()) {
|
if (isTakingScreenshot()) {
|
||||||
sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg);
|
sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ScreenShotEffect::interactive(int mask)
|
||||||
|
{
|
||||||
|
if (!checkCall()) {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
m_type = (ScreenShotType) mask;
|
m_type = (ScreenShotType) mask;
|
||||||
|
@ -528,11 +556,7 @@ QString ScreenShotEffect::interactive(int mask)
|
||||||
|
|
||||||
void ScreenShotEffect::interactive(QDBusUnixFileDescriptor fd, int mask)
|
void ScreenShotEffect::interactive(QDBusUnixFileDescriptor fd, int mask)
|
||||||
{
|
{
|
||||||
if (!calledFromDBus()) {
|
if (!checkCall()) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isTakingScreenshot()) {
|
|
||||||
sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_fd = dup(fd.fileDescriptor());
|
m_fd = dup(fd.fileDescriptor());
|
||||||
|
@ -581,11 +605,7 @@ void ScreenShotEffect::hideInfoMessage()
|
||||||
|
|
||||||
QString ScreenShotEffect::screenshotFullscreen(bool captureCursor)
|
QString ScreenShotEffect::screenshotFullscreen(bool captureCursor)
|
||||||
{
|
{
|
||||||
if (!calledFromDBus()) {
|
if (!checkCall()) {
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
if (isTakingScreenshot()) {
|
|
||||||
sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg);
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
m_replyMessage = message();
|
m_replyMessage = message();
|
||||||
|
@ -598,11 +618,7 @@ QString ScreenShotEffect::screenshotFullscreen(bool captureCursor)
|
||||||
|
|
||||||
void ScreenShotEffect::screenshotFullscreen(QDBusUnixFileDescriptor fd, bool captureCursor, bool shouldReturnNativeSize)
|
void ScreenShotEffect::screenshotFullscreen(QDBusUnixFileDescriptor fd, bool captureCursor, bool shouldReturnNativeSize)
|
||||||
{
|
{
|
||||||
if (!calledFromDBus()) {
|
if (!checkCall()) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isTakingScreenshot()) {
|
|
||||||
sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_fd = dup(fd.fileDescriptor());
|
m_fd = dup(fd.fileDescriptor());
|
||||||
|
@ -613,29 +629,13 @@ void ScreenShotEffect::screenshotFullscreen(QDBusUnixFileDescriptor fd, bool cap
|
||||||
m_captureCursor = captureCursor;
|
m_captureCursor = captureCursor;
|
||||||
m_nativeSize = shouldReturnNativeSize;
|
m_nativeSize = shouldReturnNativeSize;
|
||||||
|
|
||||||
showInfoMessage(InfoMessageMode::Screen);
|
m_scheduledGeometry = effects->virtualScreenGeometry();
|
||||||
effects->startInteractivePositionSelection(
|
effects->addRepaint(m_scheduledGeometry);
|
||||||
[this] (const QPoint &p) {
|
|
||||||
hideInfoMessage();
|
|
||||||
if (p == QPoint(-1, -1)) {
|
|
||||||
// error condition
|
|
||||||
close(m_fd);
|
|
||||||
m_fd = -1;
|
|
||||||
} else {
|
|
||||||
m_scheduledGeometry = effects->virtualScreenGeometry();
|
|
||||||
effects->addRepaint(m_scheduledGeometry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString ScreenShotEffect::screenshotScreen(int screen, bool captureCursor)
|
QString ScreenShotEffect::screenshotScreen(int screen, bool captureCursor)
|
||||||
{
|
{
|
||||||
if (!calledFromDBus()) {
|
if (!checkCall()) {
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
if (isTakingScreenshot()) {
|
|
||||||
sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg);
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
m_scheduledGeometry = effects->clientArea(FullScreenArea, screen, 0);
|
m_scheduledGeometry = effects->clientArea(FullScreenArea, screen, 0);
|
||||||
|
@ -652,11 +652,7 @@ QString ScreenShotEffect::screenshotScreen(int screen, bool captureCursor)
|
||||||
|
|
||||||
void ScreenShotEffect::screenshotScreen(QDBusUnixFileDescriptor fd, bool captureCursor)
|
void ScreenShotEffect::screenshotScreen(QDBusUnixFileDescriptor fd, bool captureCursor)
|
||||||
{
|
{
|
||||||
if (!calledFromDBus()) {
|
if (!checkCall()) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isTakingScreenshot()) {
|
|
||||||
sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_fd = dup(fd.fileDescriptor());
|
m_fd = dup(fd.fileDescriptor());
|
||||||
|
@ -689,11 +685,7 @@ void ScreenShotEffect::screenshotScreen(QDBusUnixFileDescriptor fd, bool capture
|
||||||
|
|
||||||
QString ScreenShotEffect::screenshotArea(int x, int y, int width, int height, bool captureCursor)
|
QString ScreenShotEffect::screenshotArea(int x, int y, int width, int height, bool captureCursor)
|
||||||
{
|
{
|
||||||
if (!calledFromDBus()) {
|
if (!checkCall()) {
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
if (isTakingScreenshot()) {
|
|
||||||
sendErrorReply(s_errorAlreadyTaking, s_errorAlreadyTakingMsg);
|
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
m_scheduledGeometry = QRect(x, y, width, height);
|
m_scheduledGeometry = QRect(x, y, width, height);
|
||||||
|
|
|
@ -154,6 +154,7 @@ private:
|
||||||
void hideInfoMessage();
|
void hideInfoMessage();
|
||||||
bool isTakingScreenshot() const;
|
bool isTakingScreenshot() const;
|
||||||
void computeCoordinatesAfterScaling();
|
void computeCoordinatesAfterScaling();
|
||||||
|
bool checkCall() const;
|
||||||
|
|
||||||
EffectWindow *m_scheduledScreenshot;
|
EffectWindow *m_scheduledScreenshot;
|
||||||
ScreenShotType m_type;
|
ScreenShotType m_type;
|
||||||
|
|
32
service_utils.cpp
Normal file
32
service_utils.cpp
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
/********************************************************************
|
||||||
|
KWin - the KDE window manager
|
||||||
|
This file is part of the KDE project.
|
||||||
|
|
||||||
|
Copyright (C) 2020, Méven Car <meven.car@enika.com>
|
||||||
|
|
||||||
|
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/>.
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
This file is for (very) small utility relating to services/process.
|
||||||
|
|
||||||
|
*/
|
||||||
|
#include "service_utils.h"
|
||||||
|
|
||||||
|
namespace KWin
|
||||||
|
{
|
||||||
|
|
||||||
|
}// namespace
|
||||||
|
|
77
service_utils.h
Normal file
77
service_utils.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
/********************************************************************
|
||||||
|
KWin - the KDE window manager
|
||||||
|
This file is part of the KDE project.
|
||||||
|
|
||||||
|
Copyright (C) 2020, Méven Car <meven.car@enika.com>
|
||||||
|
|
||||||
|
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/>.
|
||||||
|
*********************************************************************/
|
||||||
|
|
||||||
|
#ifndef SERVICE_UTILS_H
|
||||||
|
#define SERVICE_UTILS_H
|
||||||
|
|
||||||
|
// cmake stuff
|
||||||
|
#include <config-kwin.h>
|
||||||
|
// kwin
|
||||||
|
#include <kwinglobals.h>
|
||||||
|
// Qt
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
//KF
|
||||||
|
#include <KApplicationTrader>
|
||||||
|
|
||||||
|
namespace KWin
|
||||||
|
{
|
||||||
|
|
||||||
|
const static QString s_waylandInterfaceName = QStringLiteral("X-KDE-Wayland-Interfaces");
|
||||||
|
const static QString s_dbusRestrictedInterfaceName = QStringLiteral("X-KDE-DBUS-Restricted-Interfaces");
|
||||||
|
|
||||||
|
static QStringList fetchProcessServiceField(const QString &executablePath, const QString &fieldName)
|
||||||
|
{
|
||||||
|
// needed to be able to use the logging category in a header static function
|
||||||
|
static QLoggingCategory KWIN_UTILS ("KWIN_UTILS");
|
||||||
|
const auto servicesFound = KApplicationTrader::query([&executablePath] (const KService::Ptr &service) {
|
||||||
|
|
||||||
|
if (service->exec().isEmpty() || service->exec() != executablePath)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (servicesFound.isEmpty()) {
|
||||||
|
qCDebug(KWIN_UTILS) << "Could not find the desktop file for" << executablePath;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto fieldValues = servicesFound.first()->property(fieldName).toStringList();
|
||||||
|
if (KWIN_UTILS().isDebugEnabled()) {
|
||||||
|
qCDebug(KWIN_UTILS) << "Interfaces found for" << executablePath << fieldName << ":" << fieldValues;
|
||||||
|
}
|
||||||
|
return fieldValues;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QStringList fetchRequestedInterfaces(const QString &executablePath)
|
||||||
|
{
|
||||||
|
return fetchProcessServiceField(executablePath, s_waylandInterfaceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
static QStringList fetchRestrictedDBusInterfacesFromPid(const uint pid)
|
||||||
|
{
|
||||||
|
const auto executablePath = QFileInfo(QStringLiteral("/proc/%1/exe").arg(pid)).symLinkTarget();
|
||||||
|
return fetchProcessServiceField(executablePath, s_dbusRestrictedInterfaceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}// namespace
|
||||||
|
|
||||||
|
#endif // SERVICE_UTILS_H
|
|
@ -26,6 +26,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include "waylandxdgshellintegration.h"
|
#include "waylandxdgshellintegration.h"
|
||||||
#include "workspace.h"
|
#include "workspace.h"
|
||||||
#include "xdgshellclient.h"
|
#include "xdgshellclient.h"
|
||||||
|
#include "service_utils.h"
|
||||||
|
|
||||||
// Client
|
// Client
|
||||||
#include <KWayland/Client/connection_thread.h>
|
#include <KWayland/Client/connection_thread.h>
|
||||||
|
@ -68,9 +69,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#include <KWaylandServer/filtered_display.h>
|
#include <KWaylandServer/filtered_display.h>
|
||||||
#include <KWaylandServer/keyboard_shortcuts_inhibit_v1_interface.h>
|
#include <KWaylandServer/keyboard_shortcuts_inhibit_v1_interface.h>
|
||||||
|
|
||||||
// KF
|
|
||||||
#include <KServiceTypeTrader>
|
|
||||||
|
|
||||||
// Qt
|
// Qt
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
|
@ -243,17 +241,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
QStringList fetchRequestedInterfaces(KWaylandServer::ClientConnection *client) const {
|
QStringList fetchRequestedInterfaces(KWaylandServer::ClientConnection *client) const {
|
||||||
const auto serviceQuery = QStringLiteral("exist Exec and exist [X-KDE-Wayland-Interfaces] and '%1' =~ Exec").arg(client->executablePath());
|
return KWin::fetchRequestedInterfaces(client->executablePath());
|
||||||
const auto servicesFound = KServiceTypeTrader::self()->query(QStringLiteral("Application"), serviceQuery);
|
|
||||||
|
|
||||||
if (servicesFound.isEmpty()) {
|
|
||||||
qCDebug(KWIN_CORE) << "Could not find the desktop file for" << client->executablePath();
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto interfaces = servicesFound.first()->property("X-KDE-Wayland-Interfaces").toStringList();
|
|
||||||
qCDebug(KWIN_CORE) << "Interfaces for" << client->executablePath() << interfaces;
|
|
||||||
return interfaces;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const QSet<QByteArray> interfacesBlackList = {"org_kde_kwin_remote_access_manager", "org_kde_plasma_window_management", "org_kde_kwin_fake_input", "org_kde_kwin_keystate"};
|
const QSet<QByteArray> interfacesBlackList = {"org_kde_kwin_remote_access_manager", "org_kde_plasma_window_management", "org_kde_kwin_fake_input", "org_kde_kwin_keystate"};
|
||||||
|
|
Loading…
Reference in a new issue