HI Devs, I am trying to correct a bug in @zren 's material-decoration-lim : GitHub - Zren/material-decoration: Material-ish window decoration theme for KWin, with LIM, based on zzag's original design. The problem only occurs with Libreoffice and is as follows: when you open the startcenter and then a file from there, the window changes to that of the application (Writer, Calc, etc.) but the menu remains that of the startcenter.
From a bit of investigation with xprop, I have noticed that when switching, the window ID remains the same, as does the property _KDE_NET_WM_APPMENU_SERVICE_NAME, but change the property _KDE_NET_WM_APPMENU_OBJECT_PATH.
I therefore thought of creating a filter in this file: material-decoration/src/AppMenuModel.cc at master · Zren/material-decoration · GitHub
On lines 139-141, I have modified them as follows:
void (KX11Extras::*myWindowChangeSignal)(WId window, NET::Properties properties, NET::Properties2 properties2) = &KX11Extras::windowChanged;
connect(KX11Extras::self(), myWindowChangeSignal,
this, [this](WId window, NET::Properties properties, NET::Properties2 properties2) {
if (properties2 & NET::WM2AppMenuObjectPath) {
qCDebug(category) << "WM2AppMenuObjectPath changed";
QTimer::singleShot(50, this, &AppMenuModel::onWinIdChanged);
However, debugging tells me that the code is executed at least four times every time I open a window, which seems inefficient to me. I have introduced the QTimer but it doesn’t seem like a great solution. I wonder why this signal is emitted so repeatedly. Do you have any suggestions?
Thanks in advance.
krake
February 7, 2025, 10:25am
2
Can’t help you on the “why” but your code will still emit the other signal as many times as the original, just delayed.
If you want to to “compress” events you will need some state.
For example
if (properties2 & NET::WM2AppMenuObjectPath && !m_handlingAppMenuObjectPathChange) {
qCDebug(category) << "WM2AppMenuObjectPath changed";
// bool member of "this"
m_handlingAppMenuObjectPathChange = true;
QTimer::singleShot(50, this, [this]() {
m_handlingAppMenuObjectPathChange = false;
onWinIdChanged();
}
}
Guido_I
February 7, 2025, 11:02am
3
krake:
if (properties2 & NET::WM2AppMenuObjectPath && !m_handlingAppMenuObjectPathChange) {
qCDebug(category) << "WM2AppMenuObjectPath changed";
// bool member of "this"
m_handlingAppMenuObjectPathChange = true;
QTimer::singleShot(50, this, [this]() {
m_handlingAppMenuObjectPathChange = false;
onWinIdChanged();
}
}
thank you! Is the timer still necessary?
krake
February 7, 2025, 11:19am
4
The timer is the “compressor”, e.g. the thing that combines several calls into one.
You could try a different timeout though, possibly even 0
.
It really depends on why the signal is emitted several times and what the timing of those emits are.
You might also be able to write the connect in a simpler form
connect(KX11Extras::self(), &KX11Extras::windowChanged,
this, [this](WId, NET::Properties, NET::Properties2 properties2) {
if (properties2 & NET::WM2AppMenuObjectPath) {
Guido_I
February 7, 2025, 11:35am
5
Something is wrong, this is the code I have now:
void (KX11Extras::*myWindowChangeSignal)(WId window, NET::Properties properties, NET::Properties2 properties2) = &KX11Extras::windowChanged;
connect(KX11Extras::self(), myWindowChangeSignal,
this, [this](WId window, NET::Properties properties, NET::Properties2 properties2) {
if ((properties2 & NET::WM2AppMenuObjectPath) && !m_handlingAppMenuObjectPathChange) {
qCDebug(category) << "WM2AppMenuObjectPath changed";
// bool member of "this"
m_handlingAppMenuObjectPathChange = true;
QTimer::singleShot(50, this, [this]() {
m_handlingAppMenuObjectPathChange = false;
onWinIdChanged();
});
}
I still get the debug message four times.
krake
February 7, 2025, 11:51am
6
If you put log output into the code triggered by the timer, does it also happen 4 times?
If the new output happens once after each old output then the timeout is too short.
Also make sure you have initialized the m_handlingAppMenuObjectPathChange
to false
to ensure the first time this gets executed is having the correct value
Sorry I posted my code before I added the debug message.
This is the code:
void (KX11Extras::*myWindowChangeSignal)(WId, NET::Properties, NET::Properties2) = &KX11Extras::windowChanged;
connect(KX11Extras::self(), myWindowChangeSignal,
this, [this](WId window, NET::Properties properties, NET::Properties2 properties2) {
// Controlliamo che la maschera contenga WM2AppMenuObjectPath
if ((properties2 & NET::WM2AppMenuObjectPath) && !m_handlingAppMenuObjectPathChange) {
qCDebug(category) << "WM2AppMenuObjectPath changed";
// bool member of "this"
m_handlingAppMenuObjectPathChange = true;
QTimer::singleShot(50, this, [this]() {
m_handlingAppMenuObjectPathChange = false;
qCDebug(category) << "onWinIdChanged called";
onWinIdChanged();
});
and these are the log messages:
07.02.2025 16:44:04:131
kwin_x11
kdecoration.material: AppMenuModel::setWinId QVariant(int, -1) => QVariant(qulonglong, 62914569)
07.02.2025 16:44:04:168
kwin_x11
kdecoration.material: WM2AppMenuObjectPath changed
07.02.2025 16:44:04:168
kwin_x11
kdecoration.material: WM2AppMenuObjectPath changed
07.02.2025 16:44:04:168
kwin_x11
kdecoration.material: WM2AppMenuObjectPath changed
07.02.2025 16:44:04:168
kwin_x11
kdecoration.material: WM2AppMenuObjectPath changed
07.02.2025 16:44:04:168
kwin_x11
kdecoration.material: WM2AppMenuObjectPath changed
07.02.2025 16:44:04:218
kwin_x11
kdecoration.material: onWinIdChanged called
07.02.2025 16:44:04:218
kwin_x11
kdecoration.material: onWinIdChanged called
07.02.2025 16:44:04:218
kwin_x11
kdecoration.material: onWinIdChanged called
07.02.2025 16:44:04:218
kwin_x11
kdecoration.material: onWinIdChanged called
07.02.2025 16:44:04:218
kwin_x11
kdecoration.material: onWinIdChanged called
As you can see, it is called several times, the same number of times the message appears on WM2AppMenu…etc. And all in the same millisecond.
I don’t know, it’s as if there are several threads doing the same thing 4 times, but it seems a bit absurd to me. Also the fact that the posts do not appear alternately but first 4 and then 4 seems strange to me.
Guido_I
February 7, 2025, 4:02pm
10
mmmh…
5 kwins, 5 messages
krake
February 7, 2025, 4:11pm
11
The only way I can see that would lead to that output is if there are four instances of AppMenuModel.
In which case you are not actually getting four signal emits but each one gets its single one.
In which case there is no need for any workaround.
Guido_I
February 7, 2025, 4:26pm
12
krake:
AppMenuModel
AppMenuModel is part of the decoration itself.
In any case, maybe it’s not a coincidence that in the properties of the kwin process I see 5 repetitions and have 5 repetitions of the message.
Guido_I
February 7, 2025, 4:34pm
13
However, I have another point where I have a debug message:
void AppMenuModel::setWinId(const QVariant &id)
{
if (m_winId == id) {
return;
}
qCDebug(category) << "AppMenuModel::setWinId" << m_winId << " => " << id;
m_winId = id;
emit winIdChanged();
}
and is never repeated five times.
krake
February 7, 2025, 4:37pm
14
It seems that the signal is not emitted multiple times as initially suspected but only once and there are simply several identical receivers.
You could expand your log to output PID and address of this
qDebug(category) << QCoreApplication::applicationPid() << this << .....
Or even the current thread
qDebug(category) << QCoreApplication::applicationPid() << QThread::currentThread() << this << .....
Guido_I
February 7, 2025, 4:57pm
16
I have now also put a debug in onwinidchanged, that is also repeated
07.02.2025 17:56:13:907
kwin_x11
kdecoration.material: AppMenuModel::setWinId QVariant(int, -1) => QVariant(qulonglong, 81788937)
07.02.2025 17:56:13:907
kwin_x11
kdecoration.material: LOG INSIDE onWinIdChanged: Pid: 17117 Thread: QThread(0x5a762c227160, name = Qt mainThread) This: Material::AppMenuModel(0x5a762da101a0)
07.02.2025 17:56:13:944
kwin_x11
kdecoration.material: WM2AppMenuObjectPath changed for 81788937
07.02.2025 17:56:13:944
kwin_x11
kdecoration.material: WM2AppMenuObjectPath changed for 81788937
07.02.2025 17:56:13:944
kwin_x11
kdecoration.material: WM2AppMenuObjectPath changed for 81788937
07.02.2025 17:56:13:944
kwin_x11
kdecoration.material: WM2AppMenuObjectPath changed for 81788937
07.02.2025 17:56:13:944
kwin_x11
kdecoration.material: WM2AppMenuObjectPath changed for 81788937
07.02.2025 17:56:14:452
kwin_x11
kdecoration.material: onWinIdChanged called for 81788937
07.02.2025 17:56:14:452
kwin_x11
kdecoration.material: Pid: 17117 Thread: QThread(0x5a762c227160, name = Qt mainThread) This: Material::AppMenuModel(0x5a762c7435b0)
07.02.2025 17:56:14:452
kwin_x11
kdecoration.material: LOG INSIDE onWinIdChanged: Pid: 17117 Thread: QThread(0x5a762c227160, name = Qt mainThread) This: Material::AppMenuModel(0x5a762c7435b0)
07.02.2025 17:56:14:452
kwin_x11
kdecoration.material: onWinIdChanged called for 81788937
07.02.2025 17:56:14:452
kwin_x11
kdecoration.material: Pid: 17117 Thread: QThread(0x5a762c227160, name = Qt mainThread) This: Material::AppMenuModel(0x5a762cb27be0)
07.02.2025 17:56:14:452
kwin_x11
kdecoration.material: LOG INSIDE onWinIdChanged: Pid: 17117 Thread: QThread(0x5a762c227160, name = Qt mainThread) This: Material::AppMenuModel(0x5a762cb27be0)
07.02.2025 17:56:14:452
kwin_x11
kdecoration.material: onWinIdChanged called for 81788937
07.02.2025 17:56:14:452
kwin_x11
kdecoration.material: Pid: 17117 Thread: QThread(0x5a762c227160, name = Qt mainThread) This: Material::AppMenuModel(0x5a762c8ea180)
07.02.2025 17:56:14:452
kwin_x11
kdecoration.material: LOG INSIDE onWinIdChanged: Pid: 17117 Thread: QThread(0x5a762c227160, name = Qt mainThread) This: Material::AppMenuModel(0x5a762c8ea180)
07.02.2025 17:56:14:452
kwin_x11
kdecoration.material: onWinIdChanged called for 81788937
07.02.2025 17:56:14:452
kwin_x11
kdecoration.material: Pid: 17117 Thread: QThread(0x5a762c227160, name = Qt mainThread) This: Material::AppMenuModel(0x5a762cb2a3f0)
07.02.2025 17:56:14:452
kwin_x11
kdecoration.material: LOG INSIDE onWinIdChanged: Pid: 17117 Thread: QThread(0x5a762c227160, name = Qt mainThread) This: Material::AppMenuModel(0x5a762cb2a3f0)
07.02.2025 17:56:14:453
kwin_x11
kdecoration.material: onWinIdChanged called for 81788937
07.02.2025 17:56:14:453
kwin_x11
kdecoration.material: Pid: 17117 Thread: QThread(0x5a762c227160, name = Qt mainThread) This: Material::AppMenuModel(0x5a762da101a0)
07.02.2025 17:56:14:453
kwin_x11
kdecoration.material: LOG INSIDE onWinIdChanged: Pid: 17117 Thread: QThread(0x5a762c227160, name = Qt mainThread) This: Material::AppMenuModel(0x5a762da101a0)
krake
February 7, 2025, 5:02pm
17
right, so same process, same thread, different instances of AppMenuModel.
So all fine, no need for the QTimer hack
1 Like
Guido_I
February 7, 2025, 5:07pm
18
OK, now re-reading your message where you were talking about “multiple receivers” I had an epiphany.
Obviously there are multiple receivers: there are multiple windows open, hence multiple decorations.
In fact, by closing all the windows, I no longer have any repetition of the messages (or rather I have two, but one is the registry window and the other the one that has just been opened.
However, this does not detract from the fact that to me it seems that the code is executed several times simply because there are more open windows, or am I wrong?
krake
February 8, 2025, 8:05am
19
Yes, correct.
Every decoration seems to have its own instance of the model and they are all connected the same way.
Depending on how the model works it might be possible to share it between decorations or each model could only react when “its” window updates.
1 Like