Add windows runner

Move windows runner from plasma-workspace to kwin (convert it to dbus runner)

This will allow to interract with windows on wayland too
This commit is contained in:
Benjamin Port 2020-07-21 15:33:03 +02:00 committed by Benjamin Port
parent 648dab44da
commit cf4335ca62
9 changed files with 728 additions and 2 deletions

View file

@ -111,6 +111,13 @@ set_package_properties(KF5DocTools PROPERTIES
) )
add_feature_info("KF5DocTools" KF5DocTools_FOUND "Enable building documentation") add_feature_info("KF5DocTools" KF5DocTools_FOUND "Enable building documentation")
find_package(KF5Runner ${KF5_MIN_VERSION} CONFIG)
set_package_properties(KF5Runner PROPERTIES
PURPOSE "Enable building of KWin with krunner support"
TYPE OPTIONAL
)
add_feature_info("KF5Runner" KF5Runner_FOUND "Enable building of KWin with krunner support")
find_package(KF5Kirigami2 ${KF5_MIN_VERSION} CONFIG) find_package(KF5Kirigami2 ${KF5_MIN_VERSION} CONFIG)
set_package_properties(KF5Kirigami2 PROPERTIES set_package_properties(KF5Kirigami2 PROPERTIES
DESCRIPTION "A QtQuick based components set" DESCRIPTION "A QtQuick based components set"
@ -336,6 +343,7 @@ option(KWIN_BUILD_KCMS "Enable building of KWin configuration modules." ON)
option(KWIN_BUILD_TABBOX "Enable building of KWin Tabbox functionality" ON) option(KWIN_BUILD_TABBOX "Enable building of KWin Tabbox functionality" ON)
option(KWIN_BUILD_XRENDER_COMPOSITING "Enable building of KWin with XRender Compositing support" ON) option(KWIN_BUILD_XRENDER_COMPOSITING "Enable building of KWin with XRender Compositing support" ON)
cmake_dependent_option(KWIN_BUILD_ACTIVITIES "Enable building of KWin with kactivities support" ON "KF5Activities_FOUND" OFF) cmake_dependent_option(KWIN_BUILD_ACTIVITIES "Enable building of KWin with kactivities support" ON "KF5Activities_FOUND" OFF)
cmake_dependent_option(KWIN_BUILD_RUNNERS "Enable building of KWin with krunner support" ON "KF5Runner_FOUND" OFF)
# Binary name of KWin # Binary name of KWin
set(KWIN_NAME "kwin") set(KWIN_NAME "kwin")
@ -590,6 +598,12 @@ if (KWIN_BUILD_ACTIVITIES)
) )
endif() endif()
if (KWIN_BUILD_RUNNERS)
set(kwin_SRCS ${kwin_SRCS}
runners/windowsrunnerinterface.cpp
)
endif()
if (HAVE_LINUX_VT_H) if (HAVE_LINUX_VT_H)
set(kwin_SRCS ${kwin_SRCS} set(kwin_SRCS ${kwin_SRCS}
virtual_terminal.cpp virtual_terminal.cpp
@ -619,6 +633,9 @@ qt5_add_dbus_adaptor(kwin_SRCS org.kde.kwin.ColorCorrect.xml colorcorrection/col
qt5_add_dbus_adaptor(kwin_SRCS ${kwin_effects_dbus_xml} effects.h KWin::EffectsHandlerImpl) qt5_add_dbus_adaptor(kwin_SRCS ${kwin_effects_dbus_xml} effects.h KWin::EffectsHandlerImpl)
qt5_add_dbus_adaptor(kwin_SRCS org.kde.KWin.VirtualDesktopManager.xml dbusinterface.h KWin::VirtualDesktopManagerDBusInterface) qt5_add_dbus_adaptor(kwin_SRCS org.kde.KWin.VirtualDesktopManager.xml dbusinterface.h KWin::VirtualDesktopManagerDBusInterface)
qt5_add_dbus_adaptor(kwin_SRCS org.kde.KWin.Session.xml sm.h KWin::SessionManager) qt5_add_dbus_adaptor(kwin_SRCS org.kde.KWin.Session.xml sm.h KWin::SessionManager)
if (KWIN_BUILD_RUNNERS)
qt5_add_dbus_adaptor(kwin_SRCS "runners/org.kde.krunner1.xml" runners/windowsrunnerinterface.h KWin::WindowsRunner)
endif()
qt5_add_dbus_interface(kwin_SRCS ${KSCREENLOCKER_DBUS_INTERFACES_DIR}/kf5_org.freedesktop.ScreenSaver.xml screenlocker_interface) qt5_add_dbus_interface(kwin_SRCS ${KSCREENLOCKER_DBUS_INTERFACES_DIR}/kf5_org.freedesktop.ScreenSaver.xml screenlocker_interface)
qt5_add_dbus_interface(kwin_SRCS ${KSCREENLOCKER_DBUS_INTERFACES_DIR}/org.kde.screensaver.xml kscreenlocker_interface) qt5_add_dbus_interface(kwin_SRCS ${KSCREENLOCKER_DBUS_INTERFACES_DIR}/org.kde.screensaver.xml kscreenlocker_interface)
@ -695,6 +712,10 @@ if (KWIN_BUILD_ACTIVITIES)
set(kwin_KDE_LIBS ${kwin_KDE_LIBS} KF5::Activities) set(kwin_KDE_LIBS ${kwin_KDE_LIBS} KF5::Activities)
endif() endif()
if (KWIN_BUILD_RUNNERS)
set(kwin_KDE_LIBS ${kwin_KDE_LIBS} KF5::Runner)
endif()
set(kwinLibs set(kwinLibs
${kwin_OWN_LIBS} ${kwin_OWN_LIBS}
${kwin_QT_LIBS} ${kwin_QT_LIBS}
@ -826,6 +847,11 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kwin_export.h DESTINATION ${INCLUDE_IN
# Install the KWin/Script service type # Install the KWin/Script service type
install(FILES scripting/kwinscript.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR}) install(FILES scripting/kwinscript.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR})
# install KWin krunner runner
if (KWIN_BUILD_RUNNERS)
install(FILES runners/kwin-runner-windows.desktop DESTINATION ${KDE_INSTALL_DATAROOTDIR}/krunner/dbusplugins)
endif()
add_subdirectory(qml) add_subdirectory(qml)
if (BUILD_TESTING) if (BUILD_TESTING)

View file

@ -1,3 +1,3 @@
#! /usr/bin/env bash #! /usr/bin/env bash
$EXTRACTRC *.kcfg *.ui >> rc.cpp $EXTRACTRC *.kcfg *.ui >> rc.cpp
$XGETTEXT *.h *.cpp colorcorrection/*.cpp helpers/killer/*.cpp plugins/scenes/opengl/*.cpp tabbox/*.cpp scripting/*.cpp -o $podir/kwin.pot $XGETTEXT *.h *.cpp colorcorrection/*.cpp helpers/killer/*.cpp plugins/scenes/opengl/*.cpp tabbox/*.cpp scripting/*.cpp runners/*.cpp -o $podir/kwin.pot

91
runners/dbusutils_p.h Normal file
View file

@ -0,0 +1,91 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2017 David Edmundson <kde@davidedmundson.co.uk>
SPDX-FileCopyrightText: 2018 Laurent Montel <montel@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QList>
#include <QString>
#include <QDBusArgument>
#include <QVariantMap>
#include <KRunner/QueryMatch>
struct RemoteMatch
{
//sssuda{sv}
QString id;
QString text;
QString iconName;
Plasma::QueryMatch::Type type = Plasma::QueryMatch::NoMatch;
qreal relevance = 0;
QVariantMap properties;
};
typedef QList<RemoteMatch> RemoteMatches;
struct RemoteAction
{
QString id;
QString text;
QString iconName;
};
typedef QList<RemoteAction> RemoteActions;
inline QDBusArgument &operator<< (QDBusArgument &argument, const RemoteMatch &match) {
argument.beginStructure();
argument << match.id;
argument << match.text;
argument << match.iconName;
argument << match.type;
argument << match.relevance;
argument << match.properties;
argument.endStructure();
return argument;
}
inline const QDBusArgument &operator>>(const QDBusArgument &argument, RemoteMatch &match) {
argument.beginStructure();
argument >> match.id;
argument >> match.text;
argument >> match.iconName;
uint type;
argument >> type;
match.type = (Plasma::QueryMatch::Type)type;
argument >> match.relevance;
argument >> match.properties;
argument.endStructure();
return argument;
}
inline QDBusArgument &operator<< (QDBusArgument &argument, const RemoteAction &action)
{
argument.beginStructure();
argument << action.id;
argument << action.text;
argument << action.iconName;
argument.endStructure();
return argument;
}
inline const QDBusArgument &operator>>(const QDBusArgument &argument, RemoteAction &action) {
argument.beginStructure();
argument >> action.id;
argument >> action.text;
argument >> action.iconName;
argument.endStructure();
return argument;
}
Q_DECLARE_METATYPE(RemoteMatch)
Q_DECLARE_METATYPE(RemoteMatches)
Q_DECLARE_METATYPE(RemoteAction)
Q_DECLARE_METATYPE(RemoteActions)

View file

@ -0,0 +1,154 @@
[Desktop Entry]
# ctxt: plasma runner
Name=Windows
Name[ar]=النوافذ
Name[ast]=Ventanes
Name[az]=Pəncərələr
Name[bg]=Прозорци
Name[bn]=
Name[bs]=Prozori
Name[ca]=Finestres
Name[ca@valencia]=Finestres
Name[cs]=Okna
Name[csb]=Òkna
Name[da]=Vinduer
Name[de]=Fenster
Name[el]=Παράθυρα
Name[en_GB]=Windows
Name[eo]=Fenestroj
Name[es]=Ventanas
Name[et]=Aknad
Name[eu]=Leihoak
Name[fa]=پنجرهها
Name[fi]=Ikkunat
Name[fr]=Fenêtres
Name[fy]=Finsters
Name[ga]=Fuinneoga
Name[gl]=Xanelas
Name[gu]=િ
Name[he]=חלונות
Name[hi]=ि
Name[hr]=Prozori
Name[hu]=Ablakok
Name[ia]=Fenestras
Name[id]=Windows
Name[is]=Gluggar
Name[it]=Finestre
Name[ja]=
Name[kk]=Терезелер
Name[km]=
Name[kn]=ಿಿ
Name[ko]=
Name[lt]=Langai
Name[lv]=Logi
Name[mk]=Прозорци
Name[ml]=
Name[mr]=
Name[nb]=Vinduer
Name[nds]=Finstern
Name[nl]=Vensters
Name[nn]=Vindauge
Name[pa]=ਿ
Name[pl]=Okna
Name[pt]=Janelas
Name[pt_BR]=Janelas
Name[ro]=Ferestre
Name[ru]=Окна
Name[si]=
Name[sk]=Okná
Name[sl]=Okna
Name[sr]=прозори
Name[sr@ijekavian]=прозори
Name[sr@ijekavianlatin]=prozori
Name[sr@latin]=prozori
Name[sv]=Fönster
Name[th]=
Name[tr]=Pencereler
Name[ug]=كۆزنەكلەر
Name[uk]=Вікна
Name[wa]=Finiesses
Name[x-test]=xxWindowsxx
Name[zh_CN]=
Name[zh_TW]=
Comment=List windows and desktops and switch them
Comment[ar]=اسرد النوافذ وأسطح المكتب وبدّل بينها
Comment[az]=Pəncərələrin, İş Masalarının siyahısı və onlar arası keçid
Comment[bg]=Показване и превключване на прозорци и работни плотове
Comment[bs]=Nabrajanje i prebacivanje između prozora i površî
Comment[ca]=Llista finestres i escriptoris, i canvia entre ells
Comment[ca@valencia]=Llista finestres i escriptoris i canvia entre ells
Comment[cs]=Seznam oken a ploch k přepínání
Comment[da]=Oplist vinduer og skriveborde og skift mellem dem
Comment[de]=Listet Fenster und Arbeitsflächen auf und wechselt zwischen ihnen.
Comment[el]=Εμφάνιση λίστας παραθύρων και επιφανειών εργασίας για εναλλαγή
Comment[en_GB]=List windows and desktops and switch them
Comment[eo]=Listi fenestrojn kaj labortablojn kaj ŝanĝi ilin
Comment[es]=Lista ventanas y escritorios y cambia entre ellos
Comment[et]=Akende ja töölauade näitamine võimalusega neile lülituda
Comment[eu]=Zerrendatu leihoak eta mahaigainak, eta batetik bestera aldatu
Comment[fi]=Luettele ikkunat ja työpöydät ja vaihda niitä
Comment[fr]=Liste les fenêtres / bureaux et passe de l'un à l'autre
Comment[fy]=Sommet finsters en buroblêden en wikselt har
Comment[ga]=Taispeáin fuinneoga agus deasca agus athraigh eatarthu
Comment[gl]=Lista as xanelas e escritorio e salta entre eles
Comment[he]=משמש להצגת חלונות ושולחנות עבודה ולהחלפה ביניהם
Comment[hr]=Popis prozora i radnih površina za laku promjenu među njima
Comment[hu]=Kilistázza az ablakokat és asztalokat, és vált rájuk
Comment[ia]=Lista fenestras e scriptorios e commuta los
Comment[id]=Daftar window dan desktop dan alihkan mereka
Comment[is]=Telur upp glugga og skjáborð og skiptir á milli þeirra
Comment[it]=Elenca e passa tra le finestre ed i desktop
Comment[ja]=
Comment[kk]=Бар терезе мен үстелдерді ұсынып оларға ауысу
Comment[km]=
Comment[kn]=ಿ ಿ ಿ
Comment[ko]=
Comment[lt]=Išvardyti langus ir darbalaukius bei perjungti į juos
Comment[lv]=Parāda logus un darbvirsmas un pārslēdz tos
Comment[mk]=Приказ на прозорци и раб. површини и преминување меѓу нив
Comment[ml]= ിി ിി
Comment[mr]=
Comment[nb]=List vinduer og skrivebord og bytt mellom dem
Comment[nds]=Finstern un Schriefdischen oplisten un wesseln
Comment[nl]=Toon vensters en bureaubladen en schakel ze om
Comment[nn]=Vis vindauge og skrivebord, og byt mellom dei
Comment[pa]=ਿ ਿ ਿ
Comment[pl]=Wypisuje okna i pulpity oraz przełącza pomiędzy nimi
Comment[pt]=Listar as janelas e ecrãs e mudar entre eles
Comment[pt_BR]=Lista as janelas e áreas de trabalho e alterna entre elas
Comment[ro]=Enumeră ferestre și birouri și le comută
Comment[ru]=Список окон и рабочих столов с возможностью переключения между ними
Comment[si]=
Comment[sk]=Zoznam okien a plôch a ich prepínanie
Comment[sl]=Seznam oken in namizij ter preklop med njimi
Comment[sr]=Набрајање и пребацивање између прозора и површи̂
Comment[sr@ijekavian]=Набрајање и пребацивање између прозора и површи̂
Comment[sr@ijekavianlatin]=Nabrajanje i prebacivanje između prozora i površî
Comment[sr@latin]=Nabrajanje i prebacivanje između prozora i površî
Comment[sv]=Lista fönster och skrivbord, och byt mellan dem
Comment[th]=
Comment[tr]=Pencereleri ve masaüstlerini listele ve seç
Comment[ug]=ھەممە كۆزنەكلەر ۋە ئۈستەلئۈستى تىزىمىنى كۆرسىتىپ، ئۇلارنى ئالماشتۇرىدۇ
Comment[uk]=Показує список вікон і стільниць і перемикає їх
Comment[vi]=Lit kê các ca s, màn hình làm vic và chuyn đi chúng
Comment[wa]=Fé l' djivêye des fniesses eyet des scribannes eyet passer d' n' onk a l' ôte
Comment[x-test]=xxList windows and desktops and switch themxx
Comment[zh_CN]=
Comment[zh_TW]=
X-KDE-ServiceTypes=Plasma/Runner
Type=Service
Icon=preferences-system-windows
X-KDE-PluginInfo-Author=Martin Gräßlin
X-KDE-PluginInfo-Email=kde@martin-graesslin.com
X-KDE-PluginInfo-Name=windows
X-KDE-PluginInfo-Version=1.0
X-KDE-PluginInfo-License=GPL
X-KDE-PluginInfo-EnabledByDefault=true
X-Plasma-AdvertiseSingleRunnerQueryMode=true
X-Plasma-API=DBus
X-Plasma-DBusRunner-Service=org.kde.KWin
X-Plasma-DBusRunner-Path=/WindowsRunner
X-Plasma-Request-Actions-Once=true
X-Plasma-Runner-Min-Letter-Count=3
X-Plasma-Runner-Syntaxes=:q:,:q:,:q:,window,desktop
X-Plasma-Runner-Syntax-Descriptions=Finds windows whose name or window application match :q:. It is possible to interact with the windows by using one of the following keywords: activate\\, close\\, min(imize)\\, max(imize)\\, fullscreen\\, shade\\, keep above and keep below.,Finds windows which are on desktop named :q:,Switch to desktop named :q:,Lists all windows and allows to activate them.,Lists all other desktops and allows to switch to them.

View file

@ -0,0 +1,55 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.kde.krunner1">
<!--
Returns a list of actions supported by this runner.
For example, a song match returned by a music player runner can be queued, added to the playlist, or played.
This should be constant
Structure is:
- ID
- Text
- IconName
-->
<method name="Actions">
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="RemoteActions" />
<arg name="matches" type="a(sss)" direction="out">
</arg>
</method>
<!--
Execute an action
-->
<method name="Run">
<!--
The Unique ID from Match.
-->
<arg name="matchId" type="s" direction="in"/>
<!--
The action ID to run. For the default action this will be empty.
-->
<arg name="actionId" type="s" direction="in"/>
</method>
<!--
Fetch matching results for a given query.
Note: Multiple Match calls may be made with new queries before a call has returned
has returned.
-->
<method name="Match">
<arg name="query" type="s" direction="in"/>
<!--
Return a list of items that match the
Structure is:
- Id
- Text
- IconName
- Type (see PlasmaQuery::Type)
- Relevance
- Properties (VariantMap)
-->
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="RemoteMatches"/>
<arg name="matches" type="a(sssuda{sv})" direction="out"/>
</method>
</interface>
</node>

View file

@ -0,0 +1,330 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2009 Martin Gräßlin <kde@martin-graesslin.com>
SPDX-FileCopyrightText: 2020 Benjamin Port <benjamin.port@enioka.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "windowsrunnerinterface.h"
#include "abstract_client.h"
#include "workspace.h"
#include "krunner1adaptor.h"
#include <KLocalizedString>
namespace KWin
{
WindowsRunner::WindowsRunner(QObject *parent)
: QObject(parent)
{
new Krunner1Adaptor(this);
qDBusRegisterMetaType<RemoteMatch>();
qDBusRegisterMetaType<RemoteMatches>();
qDBusRegisterMetaType<RemoteAction>();
qDBusRegisterMetaType<RemoteActions>();
QDBusConnection::sessionBus().registerObject(QStringLiteral("/WindowsRunner"), this);
QDBusConnection::sessionBus().registerService(QStringLiteral("org.kde.KWin"));
}
WindowsRunner::~WindowsRunner()
{
}
RemoteActions WindowsRunner::Actions()
{
RemoteActions actions;
return actions;
}
RemoteMatches WindowsRunner::Match(const QString &searchTerm)
{
RemoteMatches matches;
auto term = searchTerm;
WindowsRunnerAction action = ActivateAction;
if (term.endsWith(i18nc("Note this is a KRunner keyword", "activate") , Qt::CaseInsensitive)) {
action = ActivateAction;
term = term.left(term.lastIndexOf(i18nc("Note this is a KRunner keyword", "activate")) - 1);
} else if (term.endsWith(i18nc("Note this is a KRunner keyword", "close") , Qt::CaseInsensitive)) {
action = CloseAction;
term = term.left(term.lastIndexOf(i18nc("Note this is a KRunner keyword", "close")) - 1);
} else if (term.endsWith(i18nc("Note this is a KRunner keyword", "min") , Qt::CaseInsensitive)) {
action = MinimizeAction;
term = term.left(term.lastIndexOf(i18nc("Note this is a KRunner keyword", "min")) - 1);
} else if (term.endsWith(i18nc("Note this is a KRunner keyword", "minimize") , Qt::CaseInsensitive)) {
action = MinimizeAction;
term = term.left(term.lastIndexOf(i18nc("Note this is a KRunner keyword", "minimize")) - 1);
} else if (term.endsWith(i18nc("Note this is a KRunner keyword", "max") , Qt::CaseInsensitive)) {
action = MaximizeAction;
term = term.left(term.lastIndexOf(i18nc("Note this is a KRunner keyword", "max")) - 1);
} else if (term.endsWith(i18nc("Note this is a KRunner keyword", "maximize") , Qt::CaseInsensitive)) {
action = MaximizeAction;
term = term.left(term.lastIndexOf(i18nc("Note this is a KRunner keyword", "maximize")) - 1);
} else if (term.endsWith(i18nc("Note this is a KRunner keyword", "fullscreen") , Qt::CaseInsensitive)) {
action = FullscreenAction;
term = term.left(term.lastIndexOf(i18nc("Note this is a KRunner keyword", "fullscreen")) - 1);
} else if (term.endsWith(i18nc("Note this is a KRunner keyword", "shade") , Qt::CaseInsensitive)) {
action = ShadeAction;
term = term.left(term.lastIndexOf(i18nc("Note this is a KRunner keyword", "shade")) - 1);
} else if (term.endsWith(i18nc("Note this is a KRunner keyword", "keep above") , Qt::CaseInsensitive)) {
action = KeepAboveAction;
term = term.left(term.lastIndexOf(i18nc("Note this is a KRunner keyword", "keep above")) - 1);
} else if (term.endsWith(i18nc("Note this is a KRunner keyword", "keep below") , Qt::CaseInsensitive)) {
action = KeepBelowAction;
term = term.left(term.lastIndexOf(i18nc("Note this is a KRunner keyword", "keep below")) - 1);
}
// keyword match: when term starts with "window" we list all windows
// the list can be restricted to windows matching a given name, class, role or desktop
if (term.startsWith(i18nc("Note this is a KRunner keyword", "window") , Qt::CaseInsensitive)) {
const QStringList keywords = term.split(QLatin1Char(' '));
QString windowName;
QString windowAppName;
VirtualDesktop *targetDesktop = nullptr;
QVariant desktopId;
for (const QString& keyword : keywords) {
if (keyword.endsWith(QLatin1Char('='))) {
continue;
}
if (keyword.startsWith(i18nc("Note this is a KRunner keyword", "name") + QStringLiteral("=") , Qt::CaseInsensitive)) {
windowName = keyword.split(QStringLiteral("="))[1];
} else if (keyword.startsWith(i18nc("Note this is a KRunner keyword", "appname") + QStringLiteral("=") , Qt::CaseInsensitive)) {
windowAppName = keyword.split(QStringLiteral("="))[1];
} else if (keyword.startsWith(i18nc("Note this is a KRunner keyword", "desktop") + QStringLiteral("=") , Qt::CaseInsensitive)) {
desktopId = keyword.split(QStringLiteral("="))[1];
for (const auto desktop : VirtualDesktopManager::self()->desktops()) {
if (desktop->name().contains(desktopId.toString(), Qt::CaseInsensitive) || desktop->x11DesktopNumber() == desktopId.toUInt()) {
targetDesktop = desktop;
}
}
} else {
// not a keyword - use as name if name is unused, but another option is set
if (windowName.isEmpty() && !keyword.contains(QLatin1Char('=')) && (!windowAppName.isEmpty() || targetDesktop)) {
windowName = keyword;
}
}
}
for (const AbstractClient *client : Workspace::self()->allClientList()) {
if (!client->isNormalWindow()) {
continue;
}
const QString appName = client->resourceClass();
const QString name = client->caption();
if (!windowName.isEmpty() && !name.startsWith(windowName, Qt::CaseInsensitive)) {
continue;
}
if (!windowAppName.isEmpty() && !appName.contains(windowAppName, Qt::CaseInsensitive)) {
continue;
}
if (targetDesktop && !client->desktops().contains(targetDesktop) && !client->isOnAllDesktops()) {
continue;
}
// check for windows when no keywords were used
// check the name and app name for containing the query without the keyword
if (windowName.isEmpty() && windowAppName.isEmpty() && !targetDesktop) {
const QString& test = term.mid(keywords[0].length() + 1);
if (!name.contains(test, Qt::CaseInsensitive) && !appName.contains(test, Qt::CaseInsensitive)) {
continue;
}
}
// blacklisted everything else: we have a match
if (actionSupported(client, action)){
matches << windowsMatch(client, action);
}
}
if (!matches.isEmpty()) {
// the window keyword found matches - do not process other syntax possibilities
return matches;
}
}
bool desktopAdded = false;
// check for desktop keyword
if (term.startsWith(i18nc("Note this is a KRunner keyword", "desktop") , Qt::CaseInsensitive)) {
const QStringList parts = term.split(QLatin1Char(' '));
if (parts.size() == 1) {
// only keyword - list all desktops
for (auto desktop : VirtualDesktopManager::self()->desktops()) {
matches << desktopMatch(desktop);
desktopAdded = true;
}
}
}
// check for matching desktops by name
for (const AbstractClient *client : Workspace::self()->allClientList()) {
if (!client->isNormalWindow()) {
continue;
}
const QString appName = client->resourceClass();
const QString name = client->caption();
if (name.startsWith(term, Qt::CaseInsensitive) || appName.startsWith(term, Qt::CaseInsensitive)) {
matches << windowsMatch(client, action, 0.8, Plasma::QueryMatch::ExactMatch);
} else if ((name.contains(term, Qt::CaseInsensitive) || appName.contains(term, Qt::CaseInsensitive)) && actionSupported(client, action)) {
matches << windowsMatch(client, action, 0.7, Plasma::QueryMatch::PossibleMatch);
}
}
for (auto *desktop : VirtualDesktopManager::self()->desktops()) {
if (desktop->name().contains(term, Qt::CaseInsensitive)) {
if (!desktopAdded && desktop != VirtualDesktopManager::self()->currentDesktop()) {
matches << desktopMatch(desktop, ActivateDesktopAction, 0.8);
}
// search for windows on desktop and list them with less relevance
for (const AbstractClient *client : Workspace::self()->allClientList()) {
if (!client->isNormalWindow()) {
continue;
}
if ((client->desktops().contains(desktop) || client->isOnAllDesktops()) && actionSupported(client, action)) {
matches << windowsMatch(client, action, 0.5, Plasma::QueryMatch::PossibleMatch);
}
}
}
}
return matches;
}
void WindowsRunner::Run(const QString &id, const QString &actionId)
{
// Split id to get actionId and realId. We don't use actionId because our actions list is not constant
const QStringList parts = id.split(QLatin1Char('_'));
auto action = WindowsRunnerAction(parts[0].toInt());
auto objectId = parts[1];
if (action == ActivateDesktopAction) {
QByteArray desktopId = objectId.toLocal8Bit();
auto desktop = VirtualDesktopManager::self()->desktopForId(desktopId);
VirtualDesktopManager::self()->setCurrent(desktop);
return;
}
const auto uuid = QUuid::fromString(objectId);
const auto client = workspace()->findAbstractClient(uuid);
switch (action) {
case ActivateAction:
workspace()->activateClient(client);
break;
case CloseAction:
client->closeWindow();
break;
case MinimizeAction:
client->setMinimized(!client->isMinimized());
break;
case MaximizeAction:
client->setMaximize(client->maximizeMode() == MaximizeRestore, client->maximizeMode() == MaximizeRestore);
break;
case FullscreenAction:
client->setFullScreen(!client->isFullScreen());
break;
case ShadeAction:
client->toggleShade();
break;
case KeepAboveAction:
client->setKeepAbove(!client->keepAbove());
break;
case KeepBelowAction:
client->setKeepBelow(!client->keepBelow());
break;
}
}
RemoteMatch WindowsRunner::desktopMatch(const VirtualDesktop *desktop, const WindowsRunnerAction action, qreal relevance) const
{
RemoteMatch match;
match.id = QString::number((int)action) + QLatin1Char('_') + desktop->id();
match.type = Plasma::QueryMatch::ExactMatch;
match.iconName = QStringLiteral("user-desktop");
match.text = desktop->name();
match.relevance = relevance;
QVariantMap properties;
properties[QStringLiteral("subtext")] = i18n("Switch to desktop %1", desktop->name());
match.properties = properties;
return match;
}
RemoteMatch WindowsRunner::windowsMatch(const AbstractClient *client, const WindowsRunnerAction action, qreal relevance, Plasma::QueryMatch::Type type) const
{
RemoteMatch match;
match.id = QString::number((int)action) + QLatin1Char('_') + client->internalId().toString();
match.text = client->caption();
match.iconName = client->icon().name();
match.relevance = relevance;
match.type = type;
QVariantMap properties;
const QVector<VirtualDesktop *> desktops = client->desktops();
bool allDesktops = client->isOnAllDesktops();
const VirtualDesktop *targetDesktop = VirtualDesktopManager::self()->currentDesktop();
// Show on current desktop unless window is only attached to other desktop, in this case show on the first attached desktop
if (!allDesktops && !client->isOnCurrentDesktop() && !desktops.isEmpty()) {
targetDesktop = desktops.first();
}
const QString desktopName = targetDesktop->name();
switch (action) {
case CloseAction:
properties[QStringLiteral("subtext")] = i18n("Close running window on %1", desktopName);
break;
case MinimizeAction:
properties[QStringLiteral("subtext")] = i18n("(Un)minimize running window on %1", desktopName);
break;
case MaximizeAction:
properties[QStringLiteral("subtext")] = i18n("Maximize/restore running window on %1", desktopName);
break;
case FullscreenAction:
properties[QStringLiteral("subtext")] = i18n("Toggle fullscreen for running window on %1", desktopName);
break;
case ShadeAction:
properties[QStringLiteral("subtext")] = i18n("(Un)shade running window on %1", desktopName);
break;
case KeepAboveAction:
properties[QStringLiteral("subtext")] = i18n("Toggle keep above for running window on %1", desktopName);
break;
case KeepBelowAction:
properties[QStringLiteral("subtext")] = i18n("Toggle keep below running window on %1", desktopName);
break;
case ActivateAction:
default:
properties[QStringLiteral("subtext")] = i18n("Activate running window on %1", desktopName);
break;
}
match.properties = properties;
return match;
}
bool WindowsRunner::actionSupported(const AbstractClient *client, const WindowsRunnerAction action) const
{
switch (action) {
case CloseAction:
return client->isCloseable();
case MinimizeAction:
return client->isMinimizable();
case MaximizeAction:
return client->isMaximizable();
case ShadeAction:
return client->isShadeable();
case FullscreenAction:
return client->isFullScreenable();
case KeepAboveAction:
case KeepBelowAction:
case ActivateAction:
default:
return true;
}
}
}

View file

@ -0,0 +1,63 @@
/*
KWin - the KDE window manager
This file is part of the KDE project.
SPDX-FileCopyrightText: 2009 Martin Gräßlin <kde@martin-graesslin.com>
SPDX-FileCopyrightText: 2020 Benjamin Port <benjamin.port@enioka.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef WINDOWSRUNNER_H
#define WINDOWSRUNNER_H
#pragma once
#include <QObject>
#include <QDBusContext>
#include <QDBusMessage>
#include <QString>
#include <QDBusArgument>
#include <KRunner/QueryMatch>
#include "dbusutils_p.h"
namespace KWin
{
class VirtualDesktop;
class AbstractClient;
class WindowsRunner : public QObject, protected QDBusContext
{
Q_OBJECT
Q_CLASSINFO("D-Bus Interface", "org.kde.KWin.WindowsRunner")
public:
explicit WindowsRunner(QObject *parent = nullptr);
~WindowsRunner() override;
RemoteActions Actions();
RemoteMatches Match(const QString &searchTerm);
void Run(const QString &id, const QString &actionId);
private:
enum WindowsRunnerAction {
// Windows related actions
ActivateAction,
CloseAction,
MinimizeAction,
MaximizeAction,
FullscreenAction,
ShadeAction,
KeepAboveAction,
KeepBelowAction,
// Desktop related actions
ActivateDesktopAction
};
RemoteMatch desktopMatch(const VirtualDesktop *desktop, const WindowsRunnerAction action = ActivateDesktopAction, qreal relevance = 1.0) const;
RemoteMatch windowsMatch(const AbstractClient *client, const WindowsRunnerAction action = ActivateAction, qreal relevance = 1.0, Plasma::QueryMatch::Type type = Plasma::QueryMatch::ExactMatch) const;
bool actionSupported(const AbstractClient *client, const WindowsRunnerAction action) const;
};
}
#endif // WINDOWSRUNNER_H

View file

@ -59,6 +59,7 @@
#include <KStartupInfo> #include <KStartupInfo>
// Qt // Qt
#include <QtConcurrentRun> #include <QtConcurrentRun>
#include <runners/windowsrunnerinterface.h>
namespace KWin namespace KWin
{ {
@ -197,7 +198,7 @@ Workspace::Workspace()
}); });
new DBusInterface(this); new DBusInterface(this);
new WindowsRunner(this);
Outline::create(this); Outline::create(this);
initShortcuts(); initShortcuts();
@ -1689,6 +1690,11 @@ AbstractClient *Workspace::findAbstractClient(std::function<bool (const Abstract
return nullptr; return nullptr;
} }
AbstractClient *Workspace::findAbstractClient(const QUuid &internalId) const
{
return qobject_cast<AbstractClient *>(findToplevel(internalId));
}
Unmanaged *Workspace::findUnmanaged(std::function<bool (const Unmanaged*)> func) const Unmanaged *Workspace::findUnmanaged(std::function<bool (const Unmanaged*)> func) const
{ {
return Toplevel::findInList(m_unmanaged, func); return Toplevel::findInList(m_unmanaged, func);

View file

@ -115,6 +115,7 @@ public:
*/ */
X11Client *findClient(std::function<bool (const X11Client *)> func) const; X11Client *findClient(std::function<bool (const X11Client *)> func) const;
AbstractClient *findAbstractClient(std::function<bool (const AbstractClient*)> func) const; AbstractClient *findAbstractClient(std::function<bool (const AbstractClient*)> func) const;
AbstractClient *findAbstractClient(const QUuid &internalId) const;
/** /**
* @brief Finds the Client matching the given match @p predicate for the given window. * @brief Finds the Client matching the given match @p predicate for the given window.
* *