mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-03-21 17:34:57 -05:00
Add support for kwin (wayland) (#1393)
This commit is contained in:
parent
456a9c04c7
commit
f2c7b532d9
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.16...3.26)
|
||||
cmake_minimum_required(VERSION 3.21...3.26)
|
||||
|
||||
project(advanced-scene-switcher VERSION 1.0.0)
|
||||
|
||||
|
|
@ -415,6 +415,7 @@ else()
|
|||
set_target_properties(${LIB_NAME} PROPERTIES PREFIX "")
|
||||
set_target_properties(${LIB_NAME} PROPERTIES SOVERSION 1)
|
||||
|
||||
find_package(Qt6 REQUIRED COMPONENTS DBus)
|
||||
find_package(X11 REQUIRED COMPONENTS Xss)
|
||||
target_include_directories(${LIB_NAME} PRIVATE "${X11_INCLUDE_DIR}"
|
||||
"${X11_Xss_INCLUDE_PATH}")
|
||||
|
|
@ -446,7 +447,8 @@ else()
|
|||
)
|
||||
endif()
|
||||
target_include_directories(${LIB_NAME} PRIVATE "${PROC_INCLUDE_DIR}")
|
||||
target_sources(${LIB_NAME} PRIVATE lib/linux/advanced-scene-switcher-nix.cpp)
|
||||
target_sources(${LIB_NAME} PRIVATE lib/linux/advanced-scene-switcher-nix.cpp
|
||||
lib/linux/kwin-helpers.cpp)
|
||||
|
||||
# Don't include irrelevant folders into sources archive
|
||||
list(APPEND CPACK_SOURCE_IGNORE_FILES "\\.deps/.*")
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#endif
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include "kwin-helpers.h"
|
||||
|
||||
namespace advss {
|
||||
|
||||
|
|
@ -44,6 +45,10 @@ static XScreenSaverAllocInfoFunc allocSSFunc = nullptr;
|
|||
static XScreenSaverQueryInfoFunc querySSFunc = nullptr;
|
||||
bool canGetIdleTime = false;
|
||||
|
||||
static bool KWin = false;
|
||||
static FocusNotifier notifier;
|
||||
static QString KWinScriptObjectPath;
|
||||
|
||||
static QLibrary *libprocps = nullptr;
|
||||
#ifdef PROCPS_AVAILABLE
|
||||
typedef PROCTAB *(*openproc_func)(int flags);
|
||||
|
|
@ -275,6 +280,11 @@ int getActiveWindow(Window *&window)
|
|||
|
||||
void GetCurrentWindowTitle(std::string &title)
|
||||
{
|
||||
if (KWin) {
|
||||
title = FocusNotifier::getActiveWindowTitle();
|
||||
return;
|
||||
}
|
||||
|
||||
Window *data = 0;
|
||||
if (getActiveWindow(data) != Success || !data) {
|
||||
return;
|
||||
|
|
@ -286,6 +296,7 @@ void GetCurrentWindowTitle(std::string &title)
|
|||
|
||||
auto name = getWindowName(data[0]);
|
||||
XFree(data);
|
||||
|
||||
if (name.empty()) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -412,6 +423,10 @@ void GetProcessList(QStringList &processes)
|
|||
|
||||
long getForegroundProcessPid()
|
||||
{
|
||||
if (KWin) {
|
||||
return FocusNotifier::getActiveWindowPID();
|
||||
}
|
||||
|
||||
Window *window;
|
||||
if (getActiveWindow(window) != Success || !window || !*window) {
|
||||
return -1;
|
||||
|
|
@ -437,6 +452,7 @@ long getForegroundProcessPid()
|
|||
|
||||
pid = *((long *)prop);
|
||||
XFree(prop);
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
|
|
@ -562,6 +578,17 @@ void PlatformInit()
|
|||
return;
|
||||
}
|
||||
|
||||
KWin = isKWinAvailable();
|
||||
if (!(KWin && startKWinScript(KWinScriptObjectPath) &&
|
||||
registerKWinDBusListener(¬ifier))) {
|
||||
// something bad happened while trying to initialize
|
||||
// the KWin script/dbus so disable it
|
||||
KWin = false;
|
||||
blog(LOG_INFO, "not using KWin compat");
|
||||
} else {
|
||||
blog(LOG_INFO, "using KWin compat");
|
||||
}
|
||||
|
||||
initXss();
|
||||
initProcps();
|
||||
initProc2();
|
||||
|
|
@ -583,6 +610,8 @@ void PlatformCleanup()
|
|||
cleanupHelper(libproc2);
|
||||
cleanupDisplay();
|
||||
XSetErrorHandler(NULL);
|
||||
if (KWin && !KWinScriptObjectPath.isEmpty())
|
||||
stopKWinScript(KWinScriptObjectPath);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
|
|
|||
166
lib/linux/kwin-helpers.cpp
Normal file
166
lib/linux/kwin-helpers.cpp
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
#include "kwin-helpers.h"
|
||||
|
||||
#include "log-helper.hpp"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileDevice>
|
||||
#include <QTextStream>
|
||||
#include <QtDBus/QDBusConnectionInterface>
|
||||
#include <QtDBus/QDBusInterface>
|
||||
#include <QtDBus/QDBusReply>
|
||||
|
||||
namespace advss {
|
||||
|
||||
int FocusNotifier::activePID = -1;
|
||||
std::string FocusNotifier::activeTitle = {};
|
||||
|
||||
int FocusNotifier::getActiveWindowPID()
|
||||
{
|
||||
return activePID;
|
||||
}
|
||||
|
||||
std::string FocusNotifier::getActiveWindowTitle()
|
||||
{
|
||||
return activeTitle;
|
||||
}
|
||||
|
||||
void FocusNotifier::focusChanged(const int pid)
|
||||
{
|
||||
activePID = pid;
|
||||
}
|
||||
|
||||
void FocusNotifier::focusTitle(const QString &title)
|
||||
{
|
||||
activeTitle = title.toStdString();
|
||||
}
|
||||
|
||||
bool isKWinAvailable()
|
||||
{
|
||||
const QDBusConnectionInterface *interface =
|
||||
QDBusConnection::sessionBus().interface();
|
||||
if (!interface)
|
||||
return false;
|
||||
|
||||
const QStringList services =
|
||||
interface->registeredServiceNames().value();
|
||||
return services.contains("org.kde.KWin");
|
||||
}
|
||||
|
||||
bool startKWinScript(QString &scriptObjectPath)
|
||||
{
|
||||
const QString scriptPath =
|
||||
"/tmp/AdvancedSceneSwitcher/KWinFocusNotifier.js";
|
||||
|
||||
const QString script =
|
||||
R"(workspace.windowActivated.connect(function(client) {
|
||||
if (!client) return;
|
||||
if (!client.pid) return;
|
||||
if (!client.caption) return;
|
||||
|
||||
callDBus(
|
||||
"com.github.AdvancedSceneSwitcher",
|
||||
"/com/github/AdvancedSceneSwitcher",
|
||||
"com.github.AdvancedSceneSwitcher",
|
||||
"focusChanged",
|
||||
client.pid
|
||||
);
|
||||
callDBus(
|
||||
"com.github.AdvancedSceneSwitcher",
|
||||
"/com/github/AdvancedSceneSwitcher",
|
||||
"com.github.AdvancedSceneSwitcher",
|
||||
"focusTitle",
|
||||
client.caption
|
||||
);
|
||||
}))";
|
||||
|
||||
if (const QDir dir; !dir.mkpath(QFileInfo(scriptPath).absolutePath())) {
|
||||
blog(LOG_ERROR, "error creating /tmp/AdvancedSceneSwitcher");
|
||||
return false;
|
||||
}
|
||||
|
||||
QFile scriptFile(scriptPath);
|
||||
if (!scriptFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
||||
blog(LOG_ERROR,
|
||||
"error opening KWinFocusNotifier.js for writing");
|
||||
return false;
|
||||
}
|
||||
|
||||
scriptFile.setPermissions(QFileDevice::ReadOwner |
|
||||
QFileDevice::WriteOwner);
|
||||
QTextStream outputStream(&scriptFile);
|
||||
outputStream << script;
|
||||
scriptFile.close();
|
||||
|
||||
const QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
|
||||
QDBusInterface scriptingIface("org.kde.KWin", "/Scripting",
|
||||
"org.kde.kwin.Scripting", bus);
|
||||
if (!scriptingIface.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QDBusReply<bool> scriptRunningReply =
|
||||
scriptingIface.call("isScriptLoaded", scriptPath);
|
||||
if (scriptRunningReply.isValid() && scriptRunningReply.value()) {
|
||||
// script already registered and running, don't do it again
|
||||
// this will leave the script running since we do not have
|
||||
// a valid script id anymore, but at the very least this prevents
|
||||
// it from running multiple times
|
||||
return true;
|
||||
}
|
||||
|
||||
const QDBusReply<int> scriptIdReply =
|
||||
scriptingIface.call("loadScript", scriptPath);
|
||||
if (!scriptIdReply.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const int scriptId = scriptIdReply.value();
|
||||
scriptObjectPath =
|
||||
QString("/Scripting/Script%1").arg(QString::number(scriptId));
|
||||
|
||||
QDBusInterface scriptRunner("org.kde.KWin", scriptObjectPath,
|
||||
"org.kde.kwin.Script", bus);
|
||||
if (!scriptRunner.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const QDBusReply<void> runReply = scriptRunner.call("run");
|
||||
return runReply.isValid();
|
||||
}
|
||||
|
||||
bool stopKWinScript(const QString &scriptObjectPath)
|
||||
{
|
||||
QDBusInterface scriptRunner("org.kde.KWin", scriptObjectPath,
|
||||
"org.kde.kwin.Script",
|
||||
QDBusConnection::sessionBus());
|
||||
if (!scriptRunner.isValid()) {
|
||||
return false;
|
||||
}
|
||||
const QDBusReply<void> stopReply = scriptRunner.call("stop");
|
||||
return stopReply.isValid();
|
||||
}
|
||||
|
||||
bool registerKWinDBusListener(FocusNotifier *notifier)
|
||||
{
|
||||
static const QString serviceName = "com.github.AdvancedSceneSwitcher";
|
||||
static const QString objectPath = "/com/github/AdvancedSceneSwitcher";
|
||||
auto bus = QDBusConnection::sessionBus();
|
||||
|
||||
if (bus.objectRegisteredAt(objectPath)) {
|
||||
// already registered?
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!bus.registerService(serviceName)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bus.registerObject(objectPath, notifier,
|
||||
QDBusConnection::ExportAllSlots)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
} // namespace advss
|
||||
33
lib/linux/kwin-helpers.h
Normal file
33
lib/linux/kwin-helpers.h
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <string>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class FocusNotifier final : public QObject {
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "com.github.AdvancedSceneSwitcher")
|
||||
|
||||
static int activePID;
|
||||
static std::string activeTitle;
|
||||
|
||||
public:
|
||||
using QObject::QObject;
|
||||
|
||||
static int getActiveWindowPID();
|
||||
static std::string getActiveWindowTitle();
|
||||
|
||||
public slots:
|
||||
void focusChanged(const int pid);
|
||||
void focusTitle(const QString &title);
|
||||
};
|
||||
|
||||
bool isKWinAvailable();
|
||||
bool startKWinScript(QString &scriptObjectPath);
|
||||
bool stopKWinScript(const QString &scriptObjectPath);
|
||||
bool registerKWinDBusListener(FocusNotifier *notifier);
|
||||
void printDBusError();
|
||||
|
||||
} // namespace advss
|
||||
Loading…
Reference in New Issue
Block a user