Skip to content

Commit 19ffb79

Browse files
committed
refactor: dbus monitor/signal and executable DataSource
- port dbus calls to org.kde.plasma.workspace.dbus - move executable DataSource to separate component - adds DBusSignalMonitor pref: port lock screen dbus method polling to `ActiveChanged` signal
1 parent a41977a commit 19ffb79

File tree

12 files changed

+369
-172
lines changed

12 files changed

+369
-172
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
pragma ComponentBehavior: Bound
2+
pragma ValueTypeBehavior: Addressable
3+
4+
import QtQuick
5+
6+
Item {
7+
id: root
8+
property string busType
9+
property string service: ""
10+
property string objectPath: ""
11+
property string iface: ""
12+
property string method: ""
13+
property var arguments: []
14+
property var signature: null
15+
property var inSignature: null
16+
signal callFinished(reply: var)
17+
18+
function builCmd() {
19+
let cmd = "gdbus call --session --dest " + service + " --object-path " + objectPath + " --method " + iface + "." + method;
20+
if (root.arguments.length !== 0) {
21+
cmd += ` '${root.arguments.join(" ")}'`;
22+
}
23+
return cmd;
24+
}
25+
26+
RunCommand {
27+
id: runCommand
28+
onExited: (cmd, exitCode, exitStatus, stdout, stderr) => {
29+
if (exitCode !== 0) {
30+
console.error(cmd, exitCode, exitStatus, stdout, stderr);
31+
return;
32+
}
33+
stdout = stdout.trim().replace(/^\([']?/, "") // starting ( or ('
34+
.replace(/[']?,\)$/, ""); // ending ,) or ',)
35+
const reply = {
36+
value: stdout
37+
};
38+
root.callFinished(reply);
39+
}
40+
}
41+
42+
function call(callback) {
43+
runCommand.run(builCmd());
44+
if (callback)
45+
callFinished.connect(callback);
46+
}
47+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import QtQuick
2+
3+
Item {
4+
id: root
5+
6+
property string busType
7+
property string service: ""
8+
property string objectPath: ""
9+
property string iface: ""
10+
property string method: ""
11+
property var arguments: []
12+
property var signature: null
13+
property var inSignature: null
14+
property bool useGdbus: false
15+
16+
function call(callback) {
17+
if (dbusLoader.item && typeof dbusLoader.item.call === "function")
18+
dbusLoader.item.call(callback);
19+
else
20+
console.error("No valid DBus implementation loaded.");
21+
}
22+
23+
onArgumentsChanged: {
24+
if (dbusLoader.status === Loader.Ready)
25+
dbusLoader.item.arguments = root.arguments;
26+
}
27+
28+
Loader {
29+
id: dbusLoader
30+
31+
source: root.useGdbus ? "DBusFallback.qml" : "DBusPrimary.qml"
32+
onStatusChanged: {
33+
if (status === Loader.Error)
34+
dbusLoader.source = "DBusFallback.qml";
35+
}
36+
onLoaded: {
37+
dbusLoader.item.service = root.service;
38+
dbusLoader.item.objectPath = root.objectPath;
39+
dbusLoader.item.iface = root.iface;
40+
dbusLoader.item.method = root.method;
41+
dbusLoader.item.arguments = root.arguments;
42+
dbusLoader.item.signature = root.signature;
43+
dbusLoader.item.inSignature = root.inSignature;
44+
}
45+
}
46+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
pragma ComponentBehavior: Bound
2+
pragma ValueTypeBehavior: Addressable
3+
4+
import QtQuick
5+
import org.kde.plasma.workspace.dbus as DBus
6+
7+
QtObject {
8+
id: root
9+
property string busType: DBus.BusType.Session
10+
property string service: ""
11+
property string objectPath: ""
12+
property string iface: ""
13+
property string method: ""
14+
property var arguments: []
15+
property var signature: null
16+
property var inSignature: null
17+
18+
property DBus.dbusMessage msg: {
19+
"service": root.service,
20+
"path": root.objectPath,
21+
"iface": root.iface,
22+
"member": root.method,
23+
"arguments": root.arguments,
24+
"signature": root.signature,
25+
"inSignature": root.inSignature
26+
}
27+
28+
function call(callback) {
29+
const reply = DBus.SessionBus.asyncCall(root.msg) as DBus.DBusPendingReply;
30+
if (callback) {
31+
reply.finished.connect(() => {
32+
callback(reply);
33+
// Make sure to destroy after the reply is finished otherwise it may get leaked because the connected signal
34+
// holds a reference and prevents garbage collection.
35+
// https://discuss.kde.org/t/high-memory-usage-from-plasmashell-is-it-a-memory-leak/29105
36+
reply.destroy();
37+
});
38+
}
39+
}
40+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
pragma ComponentBehavior: Bound
2+
pragma ValueTypeBehavior: Addressable
3+
4+
import QtQuick
5+
import "code/utils.js" as Utils
6+
7+
Item {
8+
id: root
9+
property bool enabled: false
10+
property string busType: "session"
11+
property string service: ""
12+
property string path: ""
13+
property string iface: service
14+
property string method: ""
15+
16+
readonly property string toolsDir: Qt.resolvedUrl("./tools").toString().substring(7) + "/"
17+
readonly property string dbusMessageTool: "'" + toolsDir + "gdbus_get_signal.sh'"
18+
readonly property string monitorCmd: `${dbusMessageTool} ${busType} ${service} ${iface} ${path} ${method}`
19+
20+
signal signalReceived(message: string)
21+
22+
function getMessage(rawOutput) {
23+
let [path, interfaceAndMember, ...message] = rawOutput.split(" ");
24+
25+
return message.join(" ").replace(/^\([']?/, "") // starting ( or ('
26+
.replace(/[']?,\)$/, ""); // ending ,) or ',)
27+
}
28+
29+
RunCommand {
30+
id: runCommand
31+
onExited: (cmd, exitCode, exitStatus, stdout, stderr) => {
32+
if (exitCode !== 130) {
33+
console.error(cmd, exitCode, exitStatus, stdout, stderr);
34+
return;
35+
}
36+
root.signalReceived(root.getMessage(stdout.trim()));
37+
// for some reason it won't restart without a delay???
38+
Utils.delay(50, () => {
39+
runCommand.run(root.monitorCmd);
40+
}, root);
41+
}
42+
}
43+
44+
function toggleMontitor() {
45+
if (enabled) {
46+
runCommand.run(monitorCmd);
47+
} else {
48+
runCommand.terminate(monitorCmd);
49+
}
50+
}
51+
52+
onEnabledChanged: toggleMontitor()
53+
Component.onCompleted: toggleMontitor()
54+
}
Lines changed: 47 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,107 +1,75 @@
1-
/*
2-
* Copyright 2024 Luis Bocanegra <[email protected]>
3-
*
4-
* This program is free software; you can redistribute it and/or modify
5-
* it under the terms of the GNU General Public License as published by
6-
* the Free Software Foundation; either version 2 of the License, or
7-
* (at your option) any later version.
8-
*
9-
* This program is distributed in the hope that it will be useful,
10-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12-
* GNU General Public License for more details.
13-
*
14-
* You should have received a copy of the GNU General Public License
15-
* along with this program; if not, write to the Free Software
16-
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 2.010-1301, USA.
17-
*/
18-
191
import QtQuick
20-
import org.kde.plasma.plasma5support as P5Support
212

223
Item {
23-
id: effectsModel
4+
id: root
245
property var activeEffects: []
256
property var loadedEffects: []
26-
// sed -e "s|^(\(.*\),)$|\1|;s|^<\(.*\)>$|\1|;s|'|\"|g"
27-
property string sed: "sed -e \"s|^(\\(.*\\),)$|\\1|;s|^<\\(.*\\)>$|\\1|;s|'|\\\"|g;s|@as ||g\""
28-
property string activeEffectsCmd: "gdbus call --session --dest org.kde.KWin.Effect.WindowView1 --object-path /Effects --method org.freedesktop.DBus.Properties.Get org.kde.kwin.Effects activeEffects | " + sed
29-
property bool activeEffectsCmdRunning: false
30-
property string loadedEffectsCmd: "gdbus call --session --dest org.kde.KWin.Effect.WindowView1 --object-path /Effects --method org.freedesktop.DBus.Properties.Get org.kde.kwin.Effects loadedEffects | " + sed
31-
property bool loadedEffectsCmdRunning: false
32-
property bool active: false
7+
property bool activeEffectsCallRunning: false
8+
property bool loadedEffectsCallRunning: false
9+
property bool monitorActive: false
10+
property bool monitorLoaded: false
3311

3412
function isEffectActive(effectId) {
3513
return activeEffects.includes(effectId);
3614
}
3715

38-
P5Support.DataSource {
39-
id: runCommand
40-
engine: "executable"
41-
connectedSources: []
16+
DBusMethodCall {
17+
id: dbusKWinActiveEffects
18+
service: "org.kde.KWin"
19+
objectPath: "/Effects"
20+
iface: "org.freedesktop.DBus.Properties"
21+
method: "Get"
22+
arguments: ["org.kde.kwin.Effects", "activeEffects"]
23+
}
4224

43-
onNewData: function (source, data) {
44-
var exitCode = data["exit code"];
45-
var exitStatus = data["exit status"];
46-
var stdout = data["stdout"];
47-
var stderr = data["stderr"];
48-
exited(source, exitCode, exitStatus, stdout, stderr);
49-
disconnectSource(source); // cmd finished
50-
sourceConnected(source);
51-
}
25+
DBusMethodCall {
26+
id: dbusKWinLoadedEffects
27+
service: "org.kde.KWin"
28+
objectPath: "/Effects"
29+
iface: "org.freedesktop.DBus.Properties"
30+
method: "Get"
31+
arguments: ["org.kde.kwin.Effects", "loadedEffects"]
32+
}
5233

53-
function exec(cmd) {
54-
if (cmd === activeEffectsCmd)
55-
activeEffectsCmdRunning = true;
56-
runCommand.connectSource(cmd);
34+
function updateActiveEffects() {
35+
if (!activeEffectsCallRunning) {
36+
activeEffectsCallRunning = true;
37+
dbusKWinActiveEffects.call(reply => {
38+
activeEffectsCallRunning = false;
39+
if (reply?.value) {
40+
activeEffects = reply.value;
41+
}
42+
});
5743
}
58-
59-
signal exited(string cmd, int exitCode, int exitStatus, string stdout, string stderr)
6044
}
6145

62-
Connections {
63-
target: runCommand
64-
function onExited(cmd, exitCode, exitStatus, stdout, stderr) {
65-
if (cmd === activeEffectsCmd) {
66-
activeEffectsCmdRunning = false;
67-
if (exitCode !== 0)
68-
return;
69-
if (stdout.length > 0) {
70-
try {
71-
activeEffects = JSON.parse(stdout.trim());
72-
// console.log("ACTIVE EFFECTS:", activeEffects);
73-
} catch (e) {
74-
console.error(e, e.stack);
75-
}
46+
function updateLoadedEffects() {
47+
if (!loadedEffectsCallRunning) {
48+
loadedEffectsCallRunning = true;
49+
dbusKWinActiveEffects.call(reply => {
50+
loadedEffectsCallRunning = false;
51+
if (reply?.value) {
52+
loadedEffects = reply.value;
7653
}
77-
}
78-
if (cmd === loadedEffectsCmd) {
79-
loadedEffectsCmdRunning = false;
80-
if (exitCode !== 0)
81-
return;
82-
if (stdout.length > 0) {
83-
try {
84-
loadedEffects = JSON.parse(stdout.trim());
85-
// console.log("LOADED EFFECTS:", loadedEffects);
86-
} catch (e) {
87-
console.error(e, e.stack);
88-
}
89-
}
90-
}
54+
});
9155
}
9256
}
9357

94-
function updateActiveEffects() {
95-
if (!activeEffectsCmdRunning)
96-
runCommand.exec(activeEffectsCmd);
58+
Timer {
59+
running: root.monitorLoaded
60+
repeat: true
61+
interval: 1000
62+
onTriggered: {
63+
root.updateLoadedEffects();
64+
}
9765
}
9866

9967
Timer {
100-
running: active
68+
running: root.monitorActive
10169
repeat: true
10270
interval: 100
10371
onTriggered: {
104-
updateActiveEffects();
72+
root.updateActiveEffects();
10573
}
10674
}
10775
}

0 commit comments

Comments
 (0)