diff --git a/dbusinterface.cpp b/dbusinterface.cpp index 8aaaa32ed..ea5f5955d 100644 --- a/dbusinterface.cpp +++ b/dbusinterface.cpp @@ -1,276 +1,317 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2012 Martin Gräßlin 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 . *********************************************************************/ // own #include "dbusinterface.h" #include "compositingadaptor.h" // kwin +#include "abstract_client.h" #include "atoms.h" #include "composite.h" #include "debug_console.h" #include "main.h" #include "placement.h" #include "platform.h" #include "kwinadaptor.h" #include "scene.h" #include "workspace.h" #include "virtualdesktops.h" #ifdef KWIN_BUILD_ACTIVITIES #include "activities.h" #endif // Qt #include #include namespace KWin { DBusInterface::DBusInterface(QObject *parent) : QObject(parent) , m_serviceName(QStringLiteral("org.kde.KWin")) { (void) new KWinAdaptor(this); QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject(QStringLiteral("/KWin"), this); const QByteArray dBusSuffix = qgetenv("KWIN_DBUS_SERVICE_SUFFIX"); if (!dBusSuffix.isNull()) { m_serviceName = m_serviceName + QLatin1Char('.') + dBusSuffix; } if (!dbus.registerService(m_serviceName)) { QDBusServiceWatcher *dog = new QDBusServiceWatcher(m_serviceName, dbus, QDBusServiceWatcher::WatchForUnregistration, this); connect (dog, SIGNAL(serviceUnregistered(QString)), SLOT(becomeKWinService(QString))); } else { announceService(); } dbus.connect(QString(), QStringLiteral("/KWin"), QStringLiteral("org.kde.KWin"), QStringLiteral("reloadConfig"), Workspace::self(), SLOT(slotReloadConfig())); connect(kwinApp(), &Application::x11ConnectionChanged, this, &DBusInterface::announceService); } void DBusInterface::becomeKWinService(const QString &service) { // TODO: this watchdog exists to make really safe that we at some point get the service // but it's probably no longer needed since we explicitly unregister the service with the deconstructor if (service == m_serviceName && QDBusConnection::sessionBus().registerService(m_serviceName) && sender()) { sender()->deleteLater(); // bye doggy :'( announceService(); } } DBusInterface::~DBusInterface() { QDBusConnection::sessionBus().unregisterService(m_serviceName); // KApplication automatically also grabs org.kde.kwin, so it's often been used externally - ensure to free it as well QDBusConnection::sessionBus().unregisterService(QStringLiteral("org.kde.kwin")); if (kwinApp()->x11Connection()) { xcb_delete_property(kwinApp()->x11Connection(), kwinApp()->x11RootWindow(), atoms->kwin_dbus_service); } } void DBusInterface::announceService() { if (!kwinApp()->x11Connection()) { return; } const QByteArray service = m_serviceName.toUtf8(); xcb_change_property(kwinApp()->x11Connection(), XCB_PROP_MODE_REPLACE, kwinApp()->x11RootWindow(), atoms->kwin_dbus_service, atoms->utf8_string, 8, service.size(), service.constData()); } // wrap void methods with no arguments to Workspace #define WRAP(name) \ void DBusInterface::name() \ {\ Workspace::self()->name();\ } WRAP(reconfigure) #undef WRAP void DBusInterface::killWindow() { Workspace::self()->slotKillWindow(); } #define WRAP(name) \ void DBusInterface::name() \ {\ Placement::self()->name();\ } WRAP(cascadeDesktop) WRAP(unclutterDesktop) #undef WRAP // wrap returning methods with no arguments to Workspace #define WRAP( rettype, name ) \ rettype DBusInterface::name( ) \ {\ return Workspace::self()->name(); \ } WRAP(QString, supportInformation) #undef WRAP bool DBusInterface::startActivity(const QString &in0) { #ifdef KWIN_BUILD_ACTIVITIES if (!Activities::self()) { return false; } return Activities::self()->start(in0); #else Q_UNUSED(in0) return false; #endif } bool DBusInterface::stopActivity(const QString &in0) { #ifdef KWIN_BUILD_ACTIVITIES if (!Activities::self()) { return false; } return Activities::self()->stop(in0); #else Q_UNUSED(in0) return false; #endif } int DBusInterface::currentDesktop() { return VirtualDesktopManager::self()->current(); } bool DBusInterface::setCurrentDesktop(int desktop) { return VirtualDesktopManager::self()->setCurrent(desktop); } void DBusInterface::nextDesktop() { VirtualDesktopManager::self()->moveTo(); } void DBusInterface::previousDesktop() { VirtualDesktopManager::self()->moveTo(); } void DBusInterface::showDebugConsole() { DebugConsole *console = new DebugConsole; console->show(); } +QVariantMap DBusInterface::queryWindowInfo() +{ + m_replyQueryWindowInfo = message(); + setDelayedReply(true); + kwinApp()->platform()->startInteractiveWindowSelection( + [this] (Toplevel *t) { + if (auto c = qobject_cast(t)) { + const QVariantMap ret{ + {QStringLiteral("resourceClass"), c->resourceClass()}, + {QStringLiteral("resourceName"), c->resourceName()}, + {QStringLiteral("role"), c->windowRole()}, + {QStringLiteral("caption"), c->captionNormal()}, + {QStringLiteral("clientMachine"), c->wmClientMachine(true)}, + {QStringLiteral("type"), c->windowType()}, + {QStringLiteral("x"), c->x()}, + {QStringLiteral("y"), c->y()}, + {QStringLiteral("width"), c->width()}, + {QStringLiteral("height"), c->height()}, + {QStringLiteral("x11DesktopNumber"), c->desktop()}, + {QStringLiteral("minimized"), c->isMinimized()}, + {QStringLiteral("shaded"), c->isShade()}, + {QStringLiteral("fullscreen"), c->isFullScreen()}, + {QStringLiteral("keepAbove"), c->keepAbove()}, + {QStringLiteral("keepBelow"), c->keepBelow()}, + {QStringLiteral("noBorder"), c->noBorder()}, + {QStringLiteral("skipTaskbar"), c->skipTaskbar()}, + {QStringLiteral("skipPager"), c->skipPager()}, + {QStringLiteral("skipSwitcher"), c->skipSwitcher()}, + {QStringLiteral("maximizeHorizontal"), c->maximizeMode() & MaximizeHorizontal}, + {QStringLiteral("maximizeVertical"), c->maximizeMode() & MaximizeVertical} + }; + QDBusConnection::sessionBus().send(m_replyQueryWindowInfo.createReply(ret)); + } else { + QDBusConnection::sessionBus().send(m_replyQueryWindowInfo.createErrorReply(QString(), QString())); + } + } + ); + return QVariantMap{}; +} + CompositorDBusInterface::CompositorDBusInterface(Compositor *parent) : QObject(parent) , m_compositor(parent) { connect(m_compositor, &Compositor::compositingToggled, this, &CompositorDBusInterface::compositingToggled); new CompositingAdaptor(this); QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.registerObject(QStringLiteral("/Compositor"), this); dbus.connect(QString(), QStringLiteral("/Compositor"), QStringLiteral("org.kde.kwin.Compositing"), QStringLiteral("reinit"), m_compositor, SLOT(slotReinitialize())); } QString CompositorDBusInterface::compositingNotPossibleReason() const { return kwinApp()->platform()->compositingNotPossibleReason(); } QString CompositorDBusInterface::compositingType() const { if (!m_compositor->hasScene()) { return QStringLiteral("none"); } switch (m_compositor->scene()->compositingType()) { case XRenderCompositing: return QStringLiteral("xrender"); case OpenGL2Compositing: if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { return QStringLiteral("gles"); } else { return QStringLiteral("gl2"); } case QPainterCompositing: return QStringLiteral("qpainter"); case NoCompositing: default: return QStringLiteral("none"); } } bool CompositorDBusInterface::isActive() const { return m_compositor->isActive(); } bool CompositorDBusInterface::isCompositingPossible() const { return kwinApp()->platform()->compositingPossible(); } bool CompositorDBusInterface::isOpenGLBroken() const { return kwinApp()->platform()->openGLCompositingIsBroken(); } bool CompositorDBusInterface::platformRequiresCompositing() const { return kwinApp()->platform()->requiresCompositing(); } void CompositorDBusInterface::resume() { m_compositor->resume(Compositor::ScriptSuspend); } void CompositorDBusInterface::suspend() { m_compositor->suspend(Compositor::ScriptSuspend); } QStringList CompositorDBusInterface::supportedOpenGLPlatformInterfaces() const { QStringList interfaces; bool supportsGlx = false; #if HAVE_EPOXY_GLX supportsGlx = (kwinApp()->operationMode() == Application::OperationModeX11); #endif if (QOpenGLContext::openGLModuleType() == QOpenGLContext::LibGLES) { supportsGlx = false; } if (supportsGlx) { interfaces << QStringLiteral("glx"); } interfaces << QStringLiteral("egl"); return interfaces; } } // namespace diff --git a/dbusinterface.h b/dbusinterface.h index 5781a28e8..489030acb 100644 --- a/dbusinterface.h +++ b/dbusinterface.h @@ -1,171 +1,174 @@ /******************************************************************** KWin - the KDE window manager This file is part of the KDE project. Copyright (C) 2012 Martin Gräßlin 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 . *********************************************************************/ #ifndef KWIN_DBUS_INTERFACE_H #define KWIN_DBUS_INTERFACE_H #include #include namespace KWin { class Compositor; /** * @brief This class is a wrapper for the org.kde.KWin D-Bus interface. * * The main purpose of this class is to be exported on the D-Bus as object /KWin. * It is a pure wrapper to provide the deprecated D-Bus methods which have been * removed from Workspace which used to implement the complete D-Bus interface. * * Nowadays the D-Bus interfaces are distributed, parts of it are exported on * /Compositor, parts on /Effects and parts on /KWin. The implementation in this * class just delegates the method calls to the actual implementation in one of the * three singletons. * * @author Martin Gräßlin **/ -class DBusInterface: public QObject +class DBusInterface: public QObject, protected QDBusContext { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.KWin") public: explicit DBusInterface(QObject *parent); virtual ~DBusInterface(); public: // PROPERTIES public Q_SLOTS: // METHODS Q_NOREPLY void cascadeDesktop(); int currentDesktop(); Q_NOREPLY void killWindow(); void nextDesktop(); void previousDesktop(); Q_NOREPLY void reconfigure(); bool setCurrentDesktop(int desktop); bool startActivity(const QString &in0); bool stopActivity(const QString &in0); QString supportInformation(); Q_NOREPLY void unclutterDesktop(); Q_NOREPLY void showDebugConsole(); + QVariantMap queryWindowInfo(); + private Q_SLOTS: void becomeKWinService(const QString &service); private: void announceService(); QString m_serviceName; + QDBusMessage m_replyQueryWindowInfo; }; class CompositorDBusInterface : public QObject { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.kwin.Compositing") /** * @brief Whether the Compositor is active. That is a Scene is present and the Compositor is * not shutting down itself. **/ Q_PROPERTY(bool active READ isActive) /** * @brief Whether compositing is possible. Mostly means whether the required X extensions * are available. **/ Q_PROPERTY(bool compositingPossible READ isCompositingPossible) /** * @brief The reason why compositing is not possible. Empty String if compositing is possible. **/ Q_PROPERTY(QString compositingNotPossibleReason READ compositingNotPossibleReason) /** * @brief Whether OpenGL has failed badly in the past (crash) and is considered as broken. **/ Q_PROPERTY(bool openGLIsBroken READ isOpenGLBroken) /** * The type of the currently used Scene: * @li @c none No Compositing * @li @c xrender XRender * @li @c gl1 OpenGL 1 * @li @c gl2 OpenGL 2 * @li @c gles OpenGL ES 2 **/ Q_PROPERTY(QString compositingType READ compositingType) /** * @brief All currently supported OpenGLPlatformInterfaces. * * Possible values: * @li glx * @li egl * * Values depend on operation mode and compile time options. **/ Q_PROPERTY(QStringList supportedOpenGLPlatformInterfaces READ supportedOpenGLPlatformInterfaces) Q_PROPERTY(bool platformRequiresCompositing READ platformRequiresCompositing) public: explicit CompositorDBusInterface(Compositor *parent); virtual ~CompositorDBusInterface() = default; bool isActive() const; bool isCompositingPossible() const; QString compositingNotPossibleReason() const; bool isOpenGLBroken() const; QString compositingType() const; QStringList supportedOpenGLPlatformInterfaces() const; bool platformRequiresCompositing() const; public Q_SLOTS: /** * @brief Suspends the Compositor if it is currently active. * * Note: it is possible that the Compositor is not able to suspend. Use @link isActive to check * whether the Compositor has been suspended. * * @return void * @see resume * @see isActive **/ void suspend(); /** * @brief Resumes the Compositor if it is currently suspended. * * Note: it is possible that the Compositor cannot be resumed, that is there might be Clients * blocking the usage of Compositing or the Scene might be broken. Use @link isActive to check * whether the Compositor has been resumed. Also check @link isCompositingPossible and * @link isOpenGLBroken. * * Note: The starting of the Compositor can require some time and is partially done threaded. * After this method returns the setup may not have been completed. * * @return void * @see suspend * @see isActive * @see isCompositingPossible * @see isOpenGLBroken **/ void resume(); Q_SIGNALS: void compositingToggled(bool active); private: Compositor *m_compositor; }; } // namespace #endif // KWIN_DBUS_INTERFACE_H diff --git a/kcmkwin/kwinrules/CMakeLists.txt b/kcmkwin/kwinrules/CMakeLists.txt index fbe3e6d21..b53f7b5f5 100644 --- a/kcmkwin/kwinrules/CMakeLists.txt +++ b/kcmkwin/kwinrules/CMakeLists.txt @@ -1,63 +1,62 @@ # KI18N Translation Domain for this library add_definitions(-DTRANSLATION_DOMAIN=\"kcmkwinrules\") add_definitions(-DKCMRULES) ########### next target ############### include_directories(../../) set (kwinrules_MOC_HDRS yesnobox.h ../../client_machine.h ../../cursor.h ../../plugins/platforms/x11/standalone/x11cursor.h) qt5_wrap_cpp(kwinrules_MOC_SRCS ${kwinrules_MOC_HDRS}) set(kwinrules_SRCS ruleswidget.cpp ruleslist.cpp kwinsrc.cpp detectwidget.cpp ${kwinrules_MOC_SRCS}) ki18n_wrap_ui(kwinrules_SRCS ruleslist.ui detectwidget.ui editshortcut.ui ruleswidgetbase.ui) set(kwin_rules_dialog_KDEINIT_SRCS main.cpp ${kwinrules_SRCS}) kf5_add_kdeinit_executable( kwin_rules_dialog ${kwin_rules_dialog_KDEINIT_SRCS}) set(kwin_kcm_rules_XCB_LIBS XCB::XCB XCB::XFIXES XCB::CURSOR ) set(kcm_libs Qt5::Concurrent Qt5::X11Extras KF5::Completion KF5::ConfigWidgets KF5::I18n KF5::Service KF5::WindowSystem KF5::XmlGui - ${X11_LIBRARIES} ) if(KWIN_BUILD_ACTIVITIES) set(kcm_libs ${kcm_libs} KF5::Activities) endif() target_link_libraries(kdeinit_kwin_rules_dialog ${kcm_libs} ${kwin_kcm_rules_XCB_LIBS}) install(TARGETS kdeinit_kwin_rules_dialog ${INSTALL_TARGETS_DEFAULT_ARGS} ) install(TARGETS kwin_rules_dialog DESTINATION ${LIBEXEC_INSTALL_DIR} ) ########### next target ############### set(kcm_kwinrules_PART_SRCS kcm.cpp ${kwinrules_SRCS}) add_library(kcm_kwinrules MODULE ${kcm_kwinrules_PART_SRCS}) target_link_libraries(kcm_kwinrules ${kcm_libs} ${kwin_kcm_rules_XCB_LIBS}) install(TARGETS kcm_kwinrules DESTINATION ${PLUGIN_INSTALL_DIR} ) ########### next target ############### ########### install files ############### install( FILES kwinrules.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) diff --git a/kcmkwin/kwinrules/detectwidget.cpp b/kcmkwin/kwinrules/detectwidget.cpp index 2e0cfa396..db00184e7 100644 --- a/kcmkwin/kwinrules/detectwidget.cpp +++ b/kcmkwin/kwinrules/detectwidget.cpp @@ -1,249 +1,176 @@ /* * Copyright (c) 2004 Lubos Lunak * * 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 #include #include #include #include #include #include #include #include #include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(NET::WindowType) namespace KWin { DetectWidget::DetectWidget(QWidget* parent) : QWidget(parent) { setupUi(this); } DetectDialog::DetectDialog(QWidget* parent, const char* name) - : QDialog(parent), - grabber() + : 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(WId window, int secs) -{ - if (window == 0) - QTimer::singleShot(secs*1000, this, SLOT(selectWindow())); - else - readWindow(window); -} - -void DetectDialog::readWindow(WId w) +void DetectDialog::detect(int secs) { - if (w == 0) { - emit detectionDone(false); - return; - } - info.reset(new KWindowInfo(w, NET::WMAllProperties, NET::WM2AllProperties)); // read everything - if (!info->valid()) { - emit detectionDone(false); - return; - } - wmclass_class = info->windowClassClass(); - wmclass_name = info->windowClassName(); - role = info->windowRole(); - type = info->windowType(NET::NormalMask | NET::DesktopMask | NET::DockMask - | NET::ToolbarMask | NET::MenuMask | NET::DialogMask | NET::OverrideMask | NET::TopMenuMask - | NET::UtilityMask | NET::SplashMask); - title = info->name(); - machine = info->clientMachine(); - executeDialog(); + 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() { - if (!KWin::Cursor::self()) { - qApp->setProperty("x11Connection", QVariant::fromValue(QX11Info::connection())); - qApp->setProperty("x11RootWindow", QVariant::fromValue(QX11Info::appRootWindow())); - new X11Cursor(this); - } - // use a dialog, so that all user input is blocked - // use WX11BypassWM and moving away so that it's not actually visible - // grab only mouse, so that keyboard can be used e.g. for switching windows - grabber.reset(new QDialog(nullptr, Qt::X11BypassWindowManagerHint)); - grabber->move(-1000, -1000); - grabber->setModal(true); - grabber->show(); - // Qt uses QX11Info::appTime() to grab the pointer, what can silently fail (#318437) ... - XSync(QX11Info::display(), false); - if (XGrabPointer(QX11Info::display(), grabber->winId(), false, ButtonReleaseMask, - GrabModeAsync, GrabModeAsync, None, KWin::Cursor::x11Cursor(Qt::CrossCursor), - CurrentTime) == Success) { // ...so we use the far more convincing CurrentTime - QCoreApplication::instance()->installNativeEventFilter(this); - } else { - // ... and if we fail, cleanup, so we won't receive random events - grabber.reset(); - } -} - -bool DetectDialog::nativeEventFilter(const QByteArray &eventType, void *message, long int*) -{ - if (eventType != QByteArrayLiteral("xcb_generic_event_t")) { - return false; - } - auto *event = reinterpret_cast(message); - if ((event->response_type & ~0x80) != XCB_BUTTON_RELEASE) { - return false; - } - QCoreApplication::instance()->removeNativeEventFilter(this); - grabber.reset(); - auto *me = reinterpret_cast(event); - if (me->detail != XCB_BUTTON_INDEX_1) { - emit detectionDone(false); - return true; - } - readWindow(findWindow()); - return true; -} - -WId DetectDialog::findWindow() -{ - Window root; - Window child; - uint mask; - int rootX, rootY, x, y; - Window parent = QX11Info::appRootWindow(); - Atom wm_state = XInternAtom(QX11Info::display(), "WM_STATE", False); - for (int i = 0; - i < 10; - ++i) { - XQueryPointer(QX11Info::display(), parent, &root, &child, - &rootX, &rootY, &x, &y, &mask); - if (child == None) - return 0; - Atom type; - int format; - unsigned long nitems, after; - unsigned char* prop; - if (XGetWindowProperty(QX11Info::display(), child, wm_state, 0, 0, False, AnyPropertyType, - &type, &format, &nitems, &after, &prop) == Success) { - if (prop != nullptr) - XFree(prop); - if (type != None) - return child; + QDBusMessage message = QDBusMessage::createMethodCall(QStringLiteral("org.kde.KWin"), + QStringLiteral("/KWin"), + QStringLiteral("org.kde.KWin"), + QStringLiteral("queryWindowInfo")); + QDBusPendingReply async = QDBusConnection::sessionBus().asyncCall(message); + + QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this); + connect(callWatcher, &QDBusPendingCallWatcher::finished, this, + [this](QDBusPendingCallWatcher *self) { + QDBusPendingReply 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(); + title = m_windowInfo.value("caption").toString(); + machine = m_windowInfo.value("clientMachine").toByteArray(); + executeDialog(); } - parent = child; - } - return 0; + ); } } // namespace diff --git a/kcmkwin/kwinrules/detectwidget.h b/kcmkwin/kwinrules/detectwidget.h index efc8980c4..c0a208f60 100644 --- a/kcmkwin/kwinrules/detectwidget.h +++ b/kcmkwin/kwinrules/detectwidget.h @@ -1,92 +1,83 @@ /* * Copyright (c) 2004 Lubos Lunak * * 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. */ #ifndef __DETECTWIDGET_H__ #define __DETECTWIDGET_H__ #include #include -#include #include "../../rules.h" //Added by qt3to4: #include #include #include "ui_detectwidget.h" namespace KWin { class DetectWidget : public QWidget, public Ui_DetectWidget { Q_OBJECT public: explicit DetectWidget(QWidget* parent = nullptr); }; class DetectDialog - : public QDialog, public QAbstractNativeEventFilter + : public QDialog { Q_OBJECT public: explicit DetectDialog(QWidget* parent = nullptr, const char* name = nullptr); - void detect(WId window, int secs = 0); + void detect(int secs = 0); QByteArray selectedClass() const; bool selectedWholeClass() const; QByteArray selectedRole() const; bool selectedWholeApp() const; NET::WindowType selectedType() const; QString selectedTitle() const; Rules::StringMatch titleMatch() const; QByteArray selectedMachine() const; - const KWindowInfo& windowInfo() const; - virtual bool nativeEventFilter(const QByteArray& eventType, void* message, long int* result) override; + const QVariantMap &windowInfo() const { + return m_windowInfo; + } + Q_SIGNALS: void detectionDone(bool); private Q_SLOTS: void selectWindow(); private: - void readWindow(WId window); void executeDialog(); - WId findWindow(); QByteArray wmclass_class; QByteArray wmclass_name; QByteArray role; NET::WindowType type; QString title; QByteArray extrarole; QByteArray machine; DetectWidget* widget; - QScopedPointer grabber; - QScopedPointer info; + QVariantMap m_windowInfo; }; -inline -const KWindowInfo& DetectDialog::windowInfo() const -{ - Q_ASSERT(!info.isNull()); - return *(info.data()); -} - } // namespace #endif diff --git a/kcmkwin/kwinrules/ruleswidget.cpp b/kcmkwin/kwinrules/ruleswidget.cpp index b6765da9a..240027f24 100644 --- a/kcmkwin/kwinrules/ruleswidget.cpp +++ b/kcmkwin/kwinrules/ruleswidget.cpp @@ -1,963 +1,1003 @@ /* * Copyright (c) 2004 Lubos Lunak * * 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 "ruleswidget.h" #include #include #include #include #include #include #include #include #include #ifdef KWIN_BUILD_ACTIVITIES #include #endif #include #include #include #include #include "../../rules.h" #include "detectwidget.h" +Q_DECLARE_METATYPE(NET::WindowType) + namespace KWin { #define SETUP( var, type ) \ connect( enable_##var, SIGNAL(toggled(bool)), rule_##var, SLOT(setEnabled(bool))); \ connect( enable_##var, SIGNAL(toggled(bool)), this, SLOT(updateEnable##var())); \ connect( rule_##var, SIGNAL(activated(int)), this, SLOT(updateEnable##var())); \ enable_##var->setWhatsThis( enableDesc ); \ rule_##var->setWhatsThis( type##RuleDesc ); RulesWidget::RulesWidget(QWidget* parent) : detect_dlg(nullptr) { Q_UNUSED(parent); setupUi(this); QRegularExpressionValidator* validator = new QRegularExpressionValidator(QRegularExpression("[0-9\\-+,xX:]*"), this); maxsize->setValidator(validator); minsize->setValidator(validator); position->setValidator(validator); Ui::RulesWidgetBase::size->setValidator(validator); QString enableDesc = i18n("Enable this checkbox to alter this window property for the specified window(s)."); QString setRuleDesc = i18n("Specify how the window property should be affected:
    " "
  • Do Not Affect: The window property will not be affected and therefore" " the default handling for it will be used. Specifying this will block more generic" " window settings from taking effect.
  • " "
  • Apply Initially: The window property will be only set to the given value" " after the window is created. No further changes will be affected.
  • " "
  • Remember: The value of the window property will be remembered and every" " time the window is created, the last remembered value will be applied.
  • " "
  • Force: The window property will be always forced to the given value.
  • " "
  • Apply Now: The window property will be set to the given value immediately" " and will not be affected later (this action will be deleted afterwards).
  • " "
  • Force temporarily: The window property will be forced to the given value" " until it is hidden (this action will be deleted after the window is hidden).
  • " "
"); QString forceRuleDesc = i18n("Specify how the window property should be affected:
    " "
  • Do Not Affect: The window property will not be affected and therefore" " the default handling for it will be used. Specifying this will block more generic" " window settings from taking effect.
  • " "
  • Force: The window property will be always forced to the given value.
  • " "
  • Force temporarily: The window property will be forced to the given value" " until it is hidden (this action will be deleted after the window is hidden).
  • " "
"); // window tabs have enable signals done in designer // geometry tab SETUP(position, set); SETUP(size, set); SETUP(desktop, set); SETUP(screen, set); #ifdef KWIN_BUILD_ACTIVITIES SETUP(activity, set); #endif SETUP(maximizehoriz, set); SETUP(maximizevert, set); SETUP(minimize, set); SETUP(shade, set); SETUP(fullscreen, set); SETUP(placement, force); // preferences tab SETUP(above, set); SETUP(below, set); SETUP(noborder, set); SETUP(decocolor, force); SETUP(skiptaskbar, set); SETUP(skippager, set); SETUP(skipswitcher, set); SETUP(acceptfocus, force); SETUP(closeable, force); SETUP(autogroup, force); SETUP(autogroupfg, force); SETUP(autogroupid, force); SETUP(opacityactive, force); SETUP(opacityinactive, force); SETUP(shortcut, force); // workarounds tab SETUP(fsplevel, force); SETUP(fpplevel, force); SETUP(type, force); SETUP(ignoregeometry, set); SETUP(minsize, force); SETUP(maxsize, force); SETUP(strictgeometry, force); SETUP(disableglobalshortcuts, force); SETUP(blockcompositing, force); connect (shortcut_edit, SIGNAL(clicked()), SLOT(shortcutEditClicked())); edit_reg_wmclass->hide(); edit_reg_role->hide(); edit_reg_title->hide(); edit_reg_machine->hide(); #ifndef KWIN_BUILD_ACTIVITIES rule_activity->hide(); enable_activity->hide(); activity->hide(); #endif int i; for (i = 1; i <= KWindowSystem::numberOfDesktops(); ++i) desktop->addItem(QString::number(i).rightJustified(2) + ':' + KWindowSystem::desktopName(i)); desktop->addItem(i18n("All Desktops")); #ifdef KWIN_BUILD_ACTIVITIES m_activities = new KActivities::Consumer(this); connect(m_activities, &KActivities::Consumer::activitiesChanged, this, [this] { updateActivitiesList(); }); connect(m_activities, &KActivities::Consumer::serviceStatusChanged, this, [this] { updateActivitiesList(); }); updateActivitiesList(); #endif KColorSchemeManager *schemes = new KColorSchemeManager(this); decocolor->setModel(schemes->model()); // hide autogrouping as it's currently not supported // BUG 370301 line_11->hide(); enable_autogroup->hide(); autogroup->hide(); rule_autogroup->hide(); enable_autogroupid->hide(); autogroupid->hide(); rule_autogroupid->hide(); enable_autogroupfg->hide(); autogroupfg->hide(); rule_autogroupfg->hide(); } #undef SETUP #define UPDATE_ENABLE_SLOT(var) \ void RulesWidget::updateEnable##var() \ { \ /* leave the label readable label_##var->setEnabled( enable_##var->isChecked() && rule_##var->currentIndex() != 0 );*/ \ Ui::RulesWidgetBase::var->setEnabled( enable_##var->isChecked() && rule_##var->currentIndex() != 0 ); \ } // geometry tab UPDATE_ENABLE_SLOT(position) UPDATE_ENABLE_SLOT(size) UPDATE_ENABLE_SLOT(desktop) UPDATE_ENABLE_SLOT(screen) #ifdef KWIN_BUILD_ACTIVITIES UPDATE_ENABLE_SLOT(activity) #endif UPDATE_ENABLE_SLOT(maximizehoriz) UPDATE_ENABLE_SLOT(maximizevert) UPDATE_ENABLE_SLOT(minimize) UPDATE_ENABLE_SLOT(shade) UPDATE_ENABLE_SLOT(fullscreen) UPDATE_ENABLE_SLOT(placement) // preferences tab UPDATE_ENABLE_SLOT(above) UPDATE_ENABLE_SLOT(below) UPDATE_ENABLE_SLOT(noborder) UPDATE_ENABLE_SLOT(decocolor) UPDATE_ENABLE_SLOT(skiptaskbar) UPDATE_ENABLE_SLOT(skippager) UPDATE_ENABLE_SLOT(skipswitcher) UPDATE_ENABLE_SLOT(acceptfocus) UPDATE_ENABLE_SLOT(closeable) UPDATE_ENABLE_SLOT(autogroup) UPDATE_ENABLE_SLOT(autogroupfg) UPDATE_ENABLE_SLOT(autogroupid) UPDATE_ENABLE_SLOT(opacityactive) UPDATE_ENABLE_SLOT(opacityinactive) void RulesWidget::updateEnableshortcut() { shortcut->setEnabled(enable_shortcut->isChecked() && rule_shortcut->currentIndex() != 0); shortcut_edit->setEnabled(enable_shortcut->isChecked() && rule_shortcut->currentIndex() != 0); } // workarounds tab UPDATE_ENABLE_SLOT(fsplevel) UPDATE_ENABLE_SLOT(fpplevel) UPDATE_ENABLE_SLOT(type) UPDATE_ENABLE_SLOT(ignoregeometry) UPDATE_ENABLE_SLOT(minsize) UPDATE_ENABLE_SLOT(maxsize) UPDATE_ENABLE_SLOT(strictgeometry) UPDATE_ENABLE_SLOT(disableglobalshortcuts) UPDATE_ENABLE_SLOT(blockcompositing) #undef UPDATE_ENABLE_SLOT static const int set_rule_to_combo[] = { 0, // Unused 0, // Don't Affect 3, // Force 1, // Apply 2, // Remember 4, // ApplyNow 5 // ForceTemporarily }; static const Rules::SetRule combo_to_set_rule[] = { (Rules::SetRule)Rules::DontAffect, (Rules::SetRule)Rules::Apply, (Rules::SetRule)Rules::Remember, (Rules::SetRule)Rules::Force, (Rules::SetRule)Rules::ApplyNow, (Rules::SetRule)Rules::ForceTemporarily }; static const int force_rule_to_combo[] = { 0, // Unused 0, // Don't Affect 1, // Force 0, // Apply 0, // Remember 0, // ApplyNow 2 // ForceTemporarily }; static const Rules::ForceRule combo_to_force_rule[] = { (Rules::ForceRule)Rules::DontAffect, (Rules::ForceRule)Rules::Force, (Rules::ForceRule)Rules::ForceTemporarily }; static QString positionToStr(const QPoint& p) { if (p == invalidPoint) return QString(); return QString::number(p.x()) + ',' + QString::number(p.y()); } static QPoint strToPosition(const QString& str) { // two numbers, with + or -, separated by any of , x X : QRegExp reg("\\s*([+-]?[0-9]*)\\s*[,xX:]\\s*([+-]?[0-9]*)\\s*"); if (!reg.exactMatch(str)) return invalidPoint; return QPoint(reg.cap(1).toInt(), reg.cap(2).toInt()); } static QString sizeToStr(const QSize& s) { if (!s.isValid()) return QString(); return QString::number(s.width()) + ',' + QString::number(s.height()); } static QSize strToSize(const QString& str) { // two numbers, with + or -, separated by any of , x X : QRegExp reg("\\s*([+-]?[0-9]*)\\s*[,xX:]\\s*([+-]?[0-9]*)\\s*"); if (!reg.exactMatch(str)) return QSize(); return QSize(reg.cap(1).toInt(), reg.cap(2).toInt()); } int RulesWidget::desktopToCombo(int d) const { if (d >= 1 && d < desktop->count()) return d - 1; return desktop->count() - 1; // on all desktops } int RulesWidget::comboToDesktop(int val) const { if (val == desktop->count() - 1) return NET::OnAllDesktops; return val + 1; } #ifdef KWIN_BUILD_ACTIVITIES int RulesWidget::activityToCombo(QString d) const { // TODO: ivan - do a multiselection list for (int i = 0; i < activity->count(); i++) { if (activity->itemData(i).toString() == d) { return i; } } return activity->count() - 1; // on all activities } QString RulesWidget::comboToActivity(int val) const { // TODO: ivan - do a multiselection list if (val < 0 || val >= activity->count()) return QString(); return activity->itemData(val).toString(); } void RulesWidget::updateActivitiesList() { activity->clear(); // cloned from kactivities/src/lib/core/consumer.cpp #define NULL_UUID "00000000-0000-0000-0000-000000000000" activity->addItem(i18n("All Activities"), QString::fromLatin1(NULL_UUID)); #undef NULL_UUID if (m_activities->serviceStatus() == KActivities::Consumer::Running) { foreach (const QString & activityId, m_activities->activities(KActivities::Info::Running)) { const KActivities::Info info(activityId); activity->addItem(info.name(), activityId); } } auto rules = this->rules(); if (rules->activityrule == Rules::UnusedSetRule) { enable_activity->setChecked(false); Ui::RulesWidgetBase::activity->setCurrentIndex(0); } else { enable_activity->setChecked(true); Ui::RulesWidgetBase::activity->setCurrentIndex(activityToCombo(m_selectedActivityId)); } updateEnableactivity(); } #endif static int placementToCombo(Placement::Policy placement) { static const int conv[] = { 1, // NoPlacement 0, // Default 0, // Unknown 6, // Random 2, // Smart 4, // Cascade 5, // Centered 7, // ZeroCornered 8, // UnderMouse 9, // OnMainWindow 3 // Maximizing }; return conv[ placement ]; } static Placement::Policy comboToPlacement(int val) { static const Placement::Policy conv[] = { Placement::Default, Placement::NoPlacement, Placement::Smart, Placement::Maximizing, Placement::Cascade, Placement::Centered, Placement::Random, Placement::ZeroCornered, Placement::UnderMouse, Placement::OnMainWindow // no Placement::Unknown }; return conv[ val ]; } static int typeToCombo(NET::WindowType type) { if (type < NET::Normal || type > NET::Splash || type == NET::Override) // The user must NOT set a window to be unmanaged. // This case is not handled in KWin and will lead to segfaults. // Even iff it was supported, it would mean to allow the user to shoot himself // since an unmanaged window has to manage itself, what is probably not the case when the hint is not set. // Rule opportunity might be a relict from the Motif Hint window times of KDE1 return 0; // Normal static const int conv[] = { 0, // Normal 7, // Desktop 3, // Dock 4, // Toolbar 5, // Menu 1, // Dialog 8, // Override - ignored. 9, // TopMenu 2, // Utility 6 // Splash }; return conv[ type ]; } static NET::WindowType comboToType(int val) { static const NET::WindowType conv[] = { NET::Normal, NET::Dialog, NET::Utility, NET::Dock, NET::Toolbar, NET::Menu, NET::Splash, NET::Desktop, NET::TopMenu }; return conv[ val ]; } #define GENERIC_RULE( var, func, Type, type, uimethod, uimethod0 ) \ if ( rules->var##rule == Rules::Unused##Type##Rule ) \ { \ enable_##var->setChecked( false ); \ rule_##var->setCurrentIndex( 0 ); \ Ui::RulesWidgetBase::var->uimethod0; \ updateEnable##var(); \ } \ else \ { \ enable_##var->setChecked( true ); \ rule_##var->setCurrentIndex( type##_rule_to_combo[ rules->var##rule ] ); \ Ui::RulesWidgetBase::var->uimethod( func( rules->var )); \ updateEnable##var(); \ } #define CHECKBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setChecked, setChecked( false )) #define LINEEDIT_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setText, setText( QString() )) #define COMBOBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setCurrentIndex, setCurrentIndex( 0 )) #define SPINBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, setValue, setValue(0)) #define CHECKBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setChecked, setChecked( false )) #define LINEEDIT_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setText, setText( QString() )) #define COMBOBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setCurrentIndex, setCurrentIndex( 0 )) #define SPINBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, setValue, setValue(0)) void RulesWidget::setRules(Rules* rules) { Rules tmp; if (rules == nullptr) rules = &tmp; // empty description->setText(rules->description); wmclass->setText(rules->wmclass); whole_wmclass->setChecked(rules->wmclasscomplete); wmclass_match->setCurrentIndex(rules->wmclassmatch); wmclassMatchChanged(); role->setText(rules->windowrole); role_match->setCurrentIndex(rules->windowrolematch); roleMatchChanged(); types->item(0)->setSelected(rules->types & NET::NormalMask); types->item(1)->setSelected(rules->types & NET::DialogMask); types->item(2)->setSelected(rules->types & NET::UtilityMask); types->item(3)->setSelected(rules->types & NET::DockMask); types->item(4)->setSelected(rules->types & NET::ToolbarMask); types->item(5)->setSelected(rules->types & NET::MenuMask); types->item(6)->setSelected(rules->types & NET::SplashMask); types->item(7)->setSelected(rules->types & NET::DesktopMask); types->item(8)->setSelected(rules->types & NET::OverrideMask); types->item(9)->setSelected(rules->types & NET::TopMenuMask); title->setText(rules->title); title_match->setCurrentIndex(rules->titlematch); titleMatchChanged(); machine->setText(rules->clientmachine); machine_match->setCurrentIndex(rules->clientmachinematch); machineMatchChanged(); LINEEDIT_SET_RULE(position, positionToStr); LINEEDIT_SET_RULE(size, sizeToStr); COMBOBOX_SET_RULE(desktop, desktopToCombo); SPINBOX_SET_RULE(screen, inc); #ifdef KWIN_BUILD_ACTIVITIES m_selectedActivityId = rules->activity; COMBOBOX_SET_RULE(activity, activityToCombo); #endif CHECKBOX_SET_RULE(maximizehoriz,); CHECKBOX_SET_RULE(maximizevert,); CHECKBOX_SET_RULE(minimize,); CHECKBOX_SET_RULE(shade,); CHECKBOX_SET_RULE(fullscreen,); COMBOBOX_FORCE_RULE(placement, placementToCombo); CHECKBOX_SET_RULE(above,); CHECKBOX_SET_RULE(below,); CHECKBOX_SET_RULE(noborder,); auto decoColorToCombo = [this](const QString &value) { for (int i = 0; i < decocolor->count(); ++i) { if (decocolor->itemData(i).toString() == value) { return i; } } // search for Breeze for (int i = 0; i < decocolor->count(); ++i) { if (QFileInfo(decocolor->itemData(i).toString()).baseName() == QStringLiteral("Breeze")) { return i; } } return 0; }; COMBOBOX_FORCE_RULE(decocolor, decoColorToCombo); CHECKBOX_SET_RULE(skiptaskbar,); CHECKBOX_SET_RULE(skippager,); CHECKBOX_SET_RULE(skipswitcher,); CHECKBOX_FORCE_RULE(acceptfocus,); CHECKBOX_FORCE_RULE(closeable,); CHECKBOX_FORCE_RULE(autogroup,); CHECKBOX_FORCE_RULE(autogroupfg,); LINEEDIT_FORCE_RULE(autogroupid,); SPINBOX_FORCE_RULE(opacityactive,); SPINBOX_FORCE_RULE(opacityinactive,); LINEEDIT_SET_RULE(shortcut,); COMBOBOX_FORCE_RULE(fsplevel,); COMBOBOX_FORCE_RULE(fpplevel,); COMBOBOX_FORCE_RULE(type, typeToCombo); CHECKBOX_SET_RULE(ignoregeometry,); LINEEDIT_FORCE_RULE(minsize, sizeToStr); LINEEDIT_FORCE_RULE(maxsize, sizeToStr); CHECKBOX_FORCE_RULE(strictgeometry,); CHECKBOX_FORCE_RULE(disableglobalshortcuts,); CHECKBOX_FORCE_RULE(blockcompositing,); } #undef GENERIC_RULE #undef CHECKBOX_SET_RULE #undef LINEEDIT_SET_RULE #undef COMBOBOX_SET_RULE #undef SPINBOX_SET_RULE #undef CHECKBOX_FORCE_RULE #undef LINEEDIT_FORCE_RULE #undef COMBOBOX_FORCE_RULE #undef SPINBOX_FORCE_RULE #define GENERIC_RULE( var, func, Type, type, uimethod ) \ if ( enable_##var->isChecked() && rule_##var->currentIndex() >= 0) \ { \ rules->var##rule = combo_to_##type##_rule[ rule_##var->currentIndex() ]; \ rules->var = func( Ui::RulesWidgetBase::var->uimethod()); \ } \ else \ rules->var##rule = Rules::Unused##Type##Rule; #define CHECKBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, isChecked ) #define LINEEDIT_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, text ) #define COMBOBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, currentIndex ) #define SPINBOX_SET_RULE( var, func ) GENERIC_RULE( var, func, Set, set, value) #define CHECKBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, isChecked ) #define LINEEDIT_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, text ) #define COMBOBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, currentIndex ) #define SPINBOX_FORCE_RULE( var, func ) GENERIC_RULE( var, func, Force, force, value) Rules* RulesWidget::rules() const { Rules* rules = new Rules(); rules->description = description->text(); rules->wmclass = wmclass->text().toUtf8(); rules->wmclasscomplete = whole_wmclass->isChecked(); rules->wmclassmatch = static_cast< Rules::StringMatch >(wmclass_match->currentIndex()); rules->windowrole = role->text().toUtf8(); rules->windowrolematch = static_cast< Rules::StringMatch >(role_match->currentIndex()); rules->types = 0; bool all_types = true; for (int i = 0; i < types->count(); ++i) if (!types->item(i)->isSelected()) all_types = false; if (all_types) // if all types are selected, use AllTypesMask (for future expansion) rules->types = NET::AllTypesMask; else { rules->types |= types->item(0)->isSelected() ? NET::NormalMask : NET::WindowTypeMask(0); rules->types |= types->item(1)->isSelected() ? NET::DialogMask : NET::WindowTypeMask(0); rules->types |= types->item(2)->isSelected() ? NET::UtilityMask : NET::WindowTypeMask(0); rules->types |= types->item(3)->isSelected() ? NET::DockMask : NET::WindowTypeMask(0); rules->types |= types->item(4)->isSelected() ? NET::ToolbarMask : NET::WindowTypeMask(0); rules->types |= types->item(5)->isSelected() ? NET::MenuMask : NET::WindowTypeMask(0); rules->types |= types->item(6)->isSelected() ? NET::SplashMask : NET::WindowTypeMask(0); rules->types |= types->item(7)->isSelected() ? NET::DesktopMask : NET::WindowTypeMask(0); rules->types |= types->item(8)->isSelected() ? NET::OverrideMask : NET::WindowTypeMask(0); rules->types |= types->item(9)->isSelected() ? NET::TopMenuMask : NET::WindowTypeMask(0); } rules->title = title->text(); rules->titlematch = static_cast< Rules::StringMatch >(title_match->currentIndex()); rules->clientmachine = machine->text().toUtf8(); rules->clientmachinematch = static_cast< Rules::StringMatch >(machine_match->currentIndex()); LINEEDIT_SET_RULE(position, strToPosition); LINEEDIT_SET_RULE(size, strToSize); COMBOBOX_SET_RULE(desktop, comboToDesktop); SPINBOX_SET_RULE(screen, dec); #ifdef KWIN_BUILD_ACTIVITIES COMBOBOX_SET_RULE(activity, comboToActivity); #endif CHECKBOX_SET_RULE(maximizehoriz,); CHECKBOX_SET_RULE(maximizevert,); CHECKBOX_SET_RULE(minimize,); CHECKBOX_SET_RULE(shade,); CHECKBOX_SET_RULE(fullscreen,); COMBOBOX_FORCE_RULE(placement, comboToPlacement); CHECKBOX_SET_RULE(above,); CHECKBOX_SET_RULE(below,); CHECKBOX_SET_RULE(noborder,); auto comboToDecocolor = [this](int index) -> QString { return QFileInfo(decocolor->itemData(index).toString()).baseName(); }; COMBOBOX_FORCE_RULE(decocolor, comboToDecocolor); CHECKBOX_SET_RULE(skiptaskbar,); CHECKBOX_SET_RULE(skippager,); CHECKBOX_SET_RULE(skipswitcher,); CHECKBOX_FORCE_RULE(acceptfocus,); CHECKBOX_FORCE_RULE(closeable,); CHECKBOX_FORCE_RULE(autogroup,); CHECKBOX_FORCE_RULE(autogroupfg,); LINEEDIT_FORCE_RULE(autogroupid,); SPINBOX_FORCE_RULE(opacityactive,); SPINBOX_FORCE_RULE(opacityinactive,); LINEEDIT_SET_RULE(shortcut,); COMBOBOX_FORCE_RULE(fsplevel,); COMBOBOX_FORCE_RULE(fpplevel,); COMBOBOX_FORCE_RULE(type, comboToType); CHECKBOX_SET_RULE(ignoregeometry,); LINEEDIT_FORCE_RULE(minsize, strToSize); LINEEDIT_FORCE_RULE(maxsize, strToSize); CHECKBOX_FORCE_RULE(strictgeometry,); CHECKBOX_FORCE_RULE(disableglobalshortcuts,); CHECKBOX_FORCE_RULE(blockcompositing,); return rules; } #undef GENERIC_RULE #undef CHECKBOX_SET_RULE #undef LINEEDIT_SET_RULE #undef COMBOBOX_SET_RULE #undef SPINBOX_SET_RULE #undef CHECKBOX_FORCE_RULE #undef LINEEDIT_FORCE_RULE #undef COMBOBOX_FORCE_RULE #undef SPINBOX_FORCE_RULE #define STRING_MATCH_COMBO( type ) \ void RulesWidget::type##MatchChanged() \ { \ edit_reg_##type->setEnabled( type##_match->currentIndex() == Rules::RegExpMatch ); \ type->setEnabled( type##_match->currentIndex() != Rules::UnimportantMatch ); \ } STRING_MATCH_COMBO(wmclass) STRING_MATCH_COMBO(role) STRING_MATCH_COMBO(title) STRING_MATCH_COMBO(machine) #undef STRING_MATCH_COMBO void RulesWidget::detectClicked() { assert(detect_dlg == nullptr); detect_dlg = new DetectDialog; connect(detect_dlg, SIGNAL(detectionDone(bool)), this, SLOT(detected(bool))); - detect_dlg->detect(0, Ui::RulesWidgetBase::detection_delay->value()); + detect_dlg->detect(Ui::RulesWidgetBase::detection_delay->value()); } void RulesWidget::detected(bool ok) { if (ok) { wmclass->setText(detect_dlg->selectedClass()); wmclass_match->setCurrentIndex(Rules::ExactMatch); wmclassMatchChanged(); // grrr whole_wmclass->setChecked(detect_dlg->selectedWholeClass()); role->setText(detect_dlg->selectedRole()); role_match->setCurrentIndex(detect_dlg->selectedRole().isEmpty() ? Rules::UnimportantMatch : Rules::ExactMatch); roleMatchChanged(); if (detect_dlg->selectedWholeApp()) { for (int i = 0; i < types->count(); ++i) types->item(i)->setSelected(true); } else { NET::WindowType type = detect_dlg->selectedType(); for (int i = 0; i < types->count(); ++i) types->item(i)->setSelected(false); types->item(typeToCombo(type))->setSelected(true); } title->setText(detect_dlg->selectedTitle()); title_match->setCurrentIndex(detect_dlg->titleMatch()); titleMatchChanged(); machine->setText(detect_dlg->selectedMachine()); machine_match->setCurrentIndex(Rules::UnimportantMatch); machineMatchChanged(); // prefill values from to window to settings which already set - const KWindowInfo& info = detect_dlg->windowInfo(); - prefillUnusedValues(info); + prefillUnusedValues(detect_dlg->windowInfo()); } delete detect_dlg; detect_dlg = nullptr; detect_dlg_ok = ok; } #define GENERIC_PREFILL( var, func, info, uimethod ) \ if ( !enable_##var->isChecked()) \ { \ Ui::RulesWidgetBase::var->uimethod( func( info )); \ } #define CHECKBOX_PREFILL( var, func, info ) GENERIC_PREFILL( var, func, info, setChecked ) #define LINEEDIT_PREFILL( var, func, info ) GENERIC_PREFILL( var, func, info, setText ) #define COMBOBOX_PREFILL( var, func, info ) GENERIC_PREFILL( var, func, info, setCurrentIndex ) #define SPINBOX_PREFILL( var, func, info ) GENERIC_PREFILL( var, func, info, setValue ) void RulesWidget::prefillUnusedValues(const KWindowInfo& info) { LINEEDIT_PREFILL(position, positionToStr, info.frameGeometry().topLeft()); LINEEDIT_PREFILL(size, sizeToStr, info.frameGeometry().size()); COMBOBOX_PREFILL(desktop, desktopToCombo, info.desktop()); // COMBOBOX_PREFILL(activity, activityToCombo, info.activity()); // TODO: ivan CHECKBOX_PREFILL(maximizehoriz, , info.state() & NET::MaxHoriz); CHECKBOX_PREFILL(maximizevert, , info.state() & NET::MaxVert); CHECKBOX_PREFILL(minimize, , info.isMinimized()); CHECKBOX_PREFILL(shade, , info.state() & NET::Shaded); CHECKBOX_PREFILL(fullscreen, , info.state() & NET::FullScreen); //COMBOBOX_PREFILL( placement, placementToCombo ); CHECKBOX_PREFILL(above, , info.state() & NET::KeepAbove); CHECKBOX_PREFILL(below, , info.state() & NET::KeepBelow); // noborder is only internal KWin information, so let's guess CHECKBOX_PREFILL(noborder, , info.frameGeometry() == info.geometry()); CHECKBOX_PREFILL(skiptaskbar, , info.state() & NET::SkipTaskbar); CHECKBOX_PREFILL(skippager, , info.state() & NET::SkipPager); CHECKBOX_PREFILL(skipswitcher, , false); //CHECKBOX_PREFILL( acceptfocus, ); //CHECKBOX_PREFILL( closeable, ); //CHECKBOX_PREFILL( autogroup, ); //CHECKBOX_PREFILL( autogroupfg, ); //LINEEDIT_PREFILL( autogroupid, ); SPINBOX_PREFILL(opacityactive, , 100 /*get the actual opacity somehow*/); SPINBOX_PREFILL(opacityinactive, , 100 /*get the actual opacity somehow*/); //LINEEDIT_PREFILL( shortcut, ); //COMBOBOX_PREFILL( fsplevel, ); //COMBOBOX_PREFILL( fpplevel, ); COMBOBOX_PREFILL(type, typeToCombo, info.windowType(SUPPORTED_MANAGED_WINDOW_TYPES_MASK)); //CHECKBOX_PREFILL( ignoregeometry, ); LINEEDIT_PREFILL(minsize, sizeToStr, info.frameGeometry().size()); LINEEDIT_PREFILL(maxsize, sizeToStr, info.frameGeometry().size()); //CHECKBOX_PREFILL( strictgeometry, ); //CHECKBOX_PREFILL( disableglobalshortcuts, ); //CHECKBOX_PREFILL( blockcompositing, ); } +void RulesWidget::prefillUnusedValues(const QVariantMap& info) +{ + const QSize windowSize{info.value("width").toInt(), info.value("height").toInt()}; + LINEEDIT_PREFILL(position, positionToStr, QPoint(info.value("x").toInt(), info.value("y").toInt())); + LINEEDIT_PREFILL(size, sizeToStr, windowSize); + COMBOBOX_PREFILL(desktop, desktopToCombo, info.value("x11DesktopNumber").toInt()); + // COMBOBOX_PREFILL(activity, activityToCombo, info.activity()); // TODO: ivan + CHECKBOX_PREFILL(maximizehoriz, , info.value("maximizeHorizontal").toBool()); + CHECKBOX_PREFILL(maximizevert, , info.value("maximizeVertical").toBool()); + CHECKBOX_PREFILL(minimize, , info.value("minimized").toBool()); + CHECKBOX_PREFILL(shade, , info.value("shaded").toBool()); + CHECKBOX_PREFILL(fullscreen, , info.value("fullscreen").toBool()); + //COMBOBOX_PREFILL( placement, placementToCombo ); + CHECKBOX_PREFILL(above, , info.value("keepAbove").toBool()); + CHECKBOX_PREFILL(below, , info.value("keepBelow").toBool()); + CHECKBOX_PREFILL(noborder, , info.value("noBorder").toBool()); + CHECKBOX_PREFILL(skiptaskbar, , info.value("skipTaskbar").toBool()); + CHECKBOX_PREFILL(skippager, , info.value("skipPager").toBool()); + CHECKBOX_PREFILL(skipswitcher, , info.value("skipSwitcher").toBool()); + //CHECKBOX_PREFILL( acceptfocus, ); + //CHECKBOX_PREFILL( closeable, ); + //CHECKBOX_PREFILL( autogroup, ); + //CHECKBOX_PREFILL( autogroupfg, ); + //LINEEDIT_PREFILL( autogroupid, ); + SPINBOX_PREFILL(opacityactive, , 100 /*get the actual opacity somehow*/); + SPINBOX_PREFILL(opacityinactive, , 100 /*get the actual opacity somehow*/); + //LINEEDIT_PREFILL( shortcut, ); + //COMBOBOX_PREFILL( fsplevel, ); + //COMBOBOX_PREFILL( fpplevel, ); + COMBOBOX_PREFILL(type, typeToCombo, info.value("type").value()); + //CHECKBOX_PREFILL( ignoregeometry, ); + LINEEDIT_PREFILL(minsize, sizeToStr, windowSize); + LINEEDIT_PREFILL(maxsize, sizeToStr, windowSize); + //CHECKBOX_PREFILL( strictgeometry, ); + //CHECKBOX_PREFILL( disableglobalshortcuts, ); + //CHECKBOX_PREFILL( blockcompositing, ); +} + #undef GENERIC_PREFILL #undef CHECKBOX_PREFILL #undef LINEEDIT_PREFILL #undef COMBOBOX_PREFILL #undef SPINBOX_PREFILL bool RulesWidget::finalCheck() { if (description->text().isEmpty()) { if (!wmclass->text().isEmpty()) description->setText(i18n("Settings for %1", wmclass->text())); else description->setText(i18n("Unnamed entry")); } bool all_types = true; for (int i = 0; i < types->count(); ++i) if (!types->item(i)->isSelected()) all_types = false; if (wmclass_match->currentIndex() == Rules::UnimportantMatch && all_types) { if (KMessageBox::warningContinueCancel(window(), i18n("You have specified the window class as unimportant.\n" "This means the settings will possibly apply to windows from all applications. " "If you really want to create a generic setting, it is recommended you at least " "limit the window types to avoid special window types.")) != KMessageBox::Continue) return false; } return true; } void RulesWidget::prepareWindowSpecific(WId window) { + // TODO: adjust for Wayland tabs->setCurrentIndex(1); // geometry tab, skip tab for window identification KWindowInfo info(window, NET::WMAllProperties, NET::WM2AllProperties); // read everything prefillUnusedValues(info); } void RulesWidget::shortcutEditClicked() { QPointer dlg = new EditShortcutDialog(window()); dlg->setShortcut(shortcut->text()); if (dlg->exec() == QDialog::Accepted) shortcut->setText(dlg->shortcut()); delete dlg; } RulesDialog::RulesDialog(QWidget* parent, const char* name) : QDialog(parent) { setObjectName(name); setModal(true); setWindowTitle(i18n("Edit Window-Specific Settings")); setWindowIcon(QIcon::fromTheme("preferences-system-windows-actions")); setLayout(new QVBoxLayout); widget = new RulesWidget(this); layout()->addWidget(widget); QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect(buttons, SIGNAL(accepted()), SLOT(accept())); connect(buttons, SIGNAL(rejected()), SLOT(reject())); layout()->addWidget(buttons); } // window is set only for Alt+F3/Window-specific settings, because the dialog // is then related to one specific window Rules* RulesDialog::edit(Rules* r, WId window, bool show_hints) { rules = r; widget->setRules(rules); if (window != 0) widget->prepareWindowSpecific(window); if (show_hints) QTimer::singleShot(0, this, SLOT(displayHints())); exec(); return rules; } void RulesDialog::displayHints() { QString str = "

"; str += i18n("This configuration dialog allows altering settings only for the selected window" " or application. Find the setting you want to affect, enable the setting using the checkbox," " select in what way the setting should be affected and to which value."); #if 0 // maybe later str += "

" + i18n("Consult the documentation for more details."); #endif str += "

"; KMessageBox::information(this, str, QString(), "displayhints"); } void RulesDialog::accept() { if (!widget->finalCheck()) return; rules = widget->rules(); QDialog::accept(); } EditShortcut::EditShortcut(QWidget* parent) : QWidget(parent) { setupUi(this); } void EditShortcut::editShortcut() { QPointer< ShortcutDialog > dlg = new ShortcutDialog(QKeySequence(shortcut->text()), window()); if (dlg->exec() == QDialog::Accepted) shortcut->setText(dlg->shortcut().toString()); delete dlg; } void EditShortcut::clearShortcut() { shortcut->clear(); } EditShortcutDialog::EditShortcutDialog(QWidget* parent, const char* name) : QDialog(parent) , widget(new EditShortcut(this)) { setObjectName(name); setModal(true); setWindowTitle(i18n("Edit Shortcut")); setLayout(new QVBoxLayout); QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect(buttons, SIGNAL(accepted()), SLOT(accept())); connect(buttons, SIGNAL(rejected()), SLOT(reject())); layout()->addWidget(widget); layout()->addWidget(buttons); } void EditShortcutDialog::setShortcut(const QString& cut) { widget->shortcut->setText(cut); } QString EditShortcutDialog::shortcut() const { return widget->shortcut->text(); } ShortcutDialog::ShortcutDialog(const QKeySequence& cut, QWidget* parent) : QDialog(parent) , widget(new KKeySequenceWidget(this)) { widget->setKeySequence(cut); // It's a global shortcut so don't allow multikey shortcuts widget->setMultiKeyShortcutsAllowed(false); QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); connect(buttons, SIGNAL(accepted()), SLOT(accept())); connect(buttons, SIGNAL(rejected()), SLOT(reject())); setLayout(new QVBoxLayout); layout()->addWidget(widget); layout()->addWidget(buttons); } void ShortcutDialog::accept() { QKeySequence seq = shortcut(); if (!seq.isEmpty()) { if (seq[0] == Qt::Key_Escape) { reject(); return; } if (seq[0] == Qt::Key_Space || (seq[0] & Qt::KeyboardModifierMask) == 0) { // clear widget->clearKeySequence(); QDialog::accept(); return; } } QDialog::accept(); } QKeySequence ShortcutDialog::shortcut() const { return widget->keySequence(); } } // namespace diff --git a/kcmkwin/kwinrules/ruleswidget.h b/kcmkwin/kwinrules/ruleswidget.h index 7a4e3f27f..714ed885c 100644 --- a/kcmkwin/kwinrules/ruleswidget.h +++ b/kcmkwin/kwinrules/ruleswidget.h @@ -1,178 +1,179 @@ /* * Copyright (c) 2004 Lubos Lunak * * 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. */ #ifndef __RULESWIDGET_H__ #define __RULESWIDGET_H__ #include #include #include #include #include "ui_ruleswidgetbase.h" #include "ui_editshortcut.h" #ifdef KWIN_BUILD_ACTIVITIES namespace KActivities { class Consumer; } // namespace KActivities #endif namespace KWin { class Rules; class DetectDialog; class RulesWidget : public QWidget, public Ui::RulesWidgetBase { Q_OBJECT public: explicit RulesWidget(QWidget* parent = nullptr); void setRules(Rules* r); Rules* rules() const; bool finalCheck(); void prepareWindowSpecific(WId window); Q_SIGNALS: void changed(bool state); protected Q_SLOTS: void detectClicked(); void wmclassMatchChanged(); void roleMatchChanged(); void titleMatchChanged(); void machineMatchChanged(); void shortcutEditClicked(); private Q_SLOTS: // geometry tab void updateEnableposition(); void updateEnablesize(); void updateEnabledesktop(); void updateEnablescreen(); #ifdef KWIN_BUILD_ACTIVITIES void updateEnableactivity(); #endif void updateEnablemaximizehoriz(); void updateEnablemaximizevert(); void updateEnableminimize(); void updateEnableshade(); void updateEnablefullscreen(); void updateEnableplacement(); // preferences tab void updateEnableabove(); void updateEnablebelow(); void updateEnablenoborder(); void updateEnabledecocolor(); void updateEnableskiptaskbar(); void updateEnableskippager(); void updateEnableskipswitcher(); void updateEnableacceptfocus(); void updateEnablecloseable(); void updateEnableautogroup(); void updateEnableautogroupfg(); void updateEnableautogroupid(); void updateEnableopacityactive(); void updateEnableopacityinactive(); // workarounds tab void updateEnablefsplevel(); void updateEnablefpplevel(); void updateEnabletype(); void updateEnableignoregeometry(); void updateEnableminsize(); void updateEnablemaxsize(); void updateEnablestrictgeometry(); void updateEnableshortcut(); void updateEnabledisableglobalshortcuts(); void updateEnableblockcompositing(); // internal void detected(bool); private: int desktopToCombo(int d) const; int comboToDesktop(int val) const; #ifdef KWIN_BUILD_ACTIVITIES int activityToCombo(QString d) const; QString comboToActivity(int val) const; void updateActivitiesList(); KActivities::Consumer *m_activities; QString m_selectedActivityId; // we need this for async activity loading #endif int comboToTiling(int val) const; int inc(int i) const { return i+1; } int dec(int i) const { return i-1; } void prefillUnusedValues(const KWindowInfo& info); + void prefillUnusedValues(const QVariantMap& info); DetectDialog* detect_dlg; bool detect_dlg_ok; }; class RulesDialog : public QDialog { Q_OBJECT public: explicit RulesDialog(QWidget* parent = nullptr, const char* name = nullptr); Rules* edit(Rules* r, WId window, bool show_hints); protected: virtual void accept(); private Q_SLOTS: void displayHints(); private: RulesWidget* widget; Rules* rules; }; class EditShortcut : public QWidget, public Ui_EditShortcut { Q_OBJECT public: explicit EditShortcut(QWidget* parent = nullptr); protected Q_SLOTS: void editShortcut(); void clearShortcut(); }; class EditShortcutDialog : public QDialog { Q_OBJECT public: explicit EditShortcutDialog(QWidget* parent = nullptr, const char* name = nullptr); void setShortcut(const QString& cut); QString shortcut() const; private: EditShortcut* widget; }; // slightly duped from utils.cpp class ShortcutDialog : public QDialog { Q_OBJECT public: explicit ShortcutDialog(const QKeySequence& cut, QWidget* parent = nullptr); virtual void accept(); QKeySequence shortcut() const; private: KKeySequenceWidget* widget; }; } // namespace #endif diff --git a/org.kde.KWin.xml b/org.kde.KWin.xml index 7f57f0c1f..4794335f5 100644 --- a/org.kde.KWin.xml +++ b/org.kde.KWin.xml @@ -1,40 +1,44 @@ + + + +