Skip to content

Commit b8625aa

Browse files
committed
wayland/idle-inhibit: add idle inhibitor
1 parent a5431dd commit b8625aa

File tree

8 files changed

+377
-0
lines changed

8 files changed

+377
-0
lines changed

src/wayland/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ if (HYPRLAND)
114114
add_subdirectory(hyprland)
115115
endif()
116116

117+
add_subdirectory(idle_inhibit)
118+
list(APPEND WAYLAND_MODULES Quickshell.Wayland._IdleInhibitor)
119+
117120
# widgets for qmenu
118121
target_link_libraries(quickshell-wayland PRIVATE
119122
Qt::Quick Qt::Widgets Qt::WaylandClient Qt::WaylandClientPrivate
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
qt_add_library(quickshell-wayland-idle-inhibit STATIC
2+
proto.cpp
3+
inhibitor.cpp
4+
)
5+
6+
qt_add_qml_module(quickshell-wayland-idle-inhibit
7+
URI Quickshell.Wayland._IdleInhibitor
8+
VERSION 0.1
9+
DEPENDENCIES QtQuick
10+
)
11+
12+
install_qml_module(quickshell-wayland-idle-inhibit)
13+
14+
qs_add_module_deps_light(quickshell-wayland-idle-inhibit Quickshell)
15+
16+
wl_proto(wlp-idle-inhibit idle-inhibit-unstable-v1 "${CMAKE_CURRENT_SOURCE_DIR}")
17+
18+
target_link_libraries(quickshell-wayland-idle-inhibit PRIVATE
19+
Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client
20+
wlp-idle-inhibit
21+
)
22+
23+
qs_module_pch(quickshell-wayland-idle-inhibit SET large)
24+
25+
target_link_libraries(quickshell PRIVATE quickshell-wayland-idle-inhibitplugin)
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<protocol name="idle_inhibit_unstable_v1">
3+
4+
<copyright>
5+
Copyright © 2015 Samsung Electronics Co., Ltd
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a
8+
copy of this software and associated documentation files (the "Software"),
9+
to deal in the Software without restriction, including without limitation
10+
the rights to use, copy, modify, merge, publish, distribute, sublicense,
11+
and/or sell copies of the Software, and to permit persons to whom the
12+
Software is furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice (including the next
15+
paragraph) shall be included in all copies or substantial portions of the
16+
Software.
17+
18+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21+
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24+
DEALINGS IN THE SOFTWARE.
25+
</copyright>
26+
27+
<interface name="zwp_idle_inhibit_manager_v1" version="1">
28+
<description summary="control behavior when display idles">
29+
This interface permits inhibiting the idle behavior such as screen
30+
blanking, locking, and screensaving. The client binds the idle manager
31+
globally, then creates idle-inhibitor objects for each surface.
32+
33+
Warning! The protocol described in this file is experimental and
34+
backward incompatible changes may be made. Backward compatible changes
35+
may be added together with the corresponding interface version bump.
36+
Backward incompatible changes are done by bumping the version number in
37+
the protocol and interface names and resetting the interface version.
38+
Once the protocol is to be declared stable, the 'z' prefix and the
39+
version number in the protocol and interface names are removed and the
40+
interface version number is reset.
41+
</description>
42+
43+
<request name="destroy" type="destructor">
44+
<description summary="destroy the idle inhibitor object">
45+
Destroy the inhibit manager.
46+
</description>
47+
</request>
48+
49+
<request name="create_inhibitor">
50+
<description summary="create a new inhibitor object">
51+
Create a new inhibitor object associated with the given surface.
52+
</description>
53+
<arg name="id" type="new_id" interface="zwp_idle_inhibitor_v1"/>
54+
<arg name="surface" type="object" interface="wl_surface"
55+
summary="the surface that inhibits the idle behavior"/>
56+
</request>
57+
58+
</interface>
59+
60+
<interface name="zwp_idle_inhibitor_v1" version="1">
61+
<description summary="context object for inhibiting idle behavior">
62+
An idle inhibitor prevents the output that the associated surface is
63+
visible on from being set to a state where it is not visually usable due
64+
to lack of user interaction (e.g. blanked, dimmed, locked, set to power
65+
save, etc.) Any screensaver processes are also blocked from displaying.
66+
67+
If the surface is destroyed, unmapped, becomes occluded, loses
68+
visibility, or otherwise becomes not visually relevant for the user, the
69+
idle inhibitor will not be honored by the compositor; if the surface
70+
subsequently regains visibility the inhibitor takes effect once again.
71+
Likewise, the inhibitor isn't honored if the system was already idled at
72+
the time the inhibitor was established, although if the system later
73+
de-idles and re-idles the inhibitor will take effect.
74+
</description>
75+
76+
<request name="destroy" type="destructor">
77+
<description summary="destroy the idle inhibitor object">
78+
Remove the inhibitor effect from the associated wl_surface.
79+
</description>
80+
</request>
81+
82+
</interface>
83+
</protocol>
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#include "inhibitor.hpp"
2+
3+
#include <private/qwaylandwindow_p.h>
4+
#include <qlogging.h>
5+
#include <qobject.h>
6+
#include <qtmetamacros.h>
7+
8+
#include "../../window/proxywindow.hpp"
9+
#include "../../window/windowinterface.hpp"
10+
#include "proto.hpp"
11+
12+
namespace qs::wayland::idle_inhibit {
13+
using QtWaylandClient::QWaylandWindow;
14+
15+
IdleInhibitor::IdleInhibitor() {
16+
this->bBoundWindow.setBinding([this] { return this->bEnabled ? this->bWindowObject : nullptr; });
17+
}
18+
19+
QObject* IdleInhibitor::window() const { return this->bWindowObject; }
20+
21+
void IdleInhibitor::setWindow(QObject* window) {
22+
if (window == this->bWindowObject) return;
23+
24+
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(window);
25+
26+
if (proxyWindow == nullptr) {
27+
if (auto* iface = qobject_cast<WindowInterface*>(window)) {
28+
proxyWindow = iface->proxyWindow();
29+
}
30+
}
31+
32+
this->bWindowObject = proxyWindow ? window : nullptr;
33+
}
34+
35+
void IdleInhibitor::boundWindowChanged() {
36+
auto* window = this->bBoundWindow.value();
37+
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(window);
38+
39+
if (proxyWindow == nullptr) {
40+
if (auto* iface = qobject_cast<WindowInterface*>(window)) {
41+
proxyWindow = iface->proxyWindow();
42+
}
43+
}
44+
45+
if (proxyWindow == this->proxyWindow) return;
46+
47+
if (this->mWaylandWindow) {
48+
QObject::disconnect(this->mWaylandWindow, nullptr, this, nullptr);
49+
this->mWaylandWindow = nullptr;
50+
this->onWaylandSurfaceDestroyed();
51+
}
52+
53+
if (this->proxyWindow) {
54+
QObject::disconnect(this->proxyWindow, nullptr, this, nullptr);
55+
this->proxyWindow = nullptr;
56+
}
57+
58+
if (proxyWindow) {
59+
this->proxyWindow = proxyWindow;
60+
61+
QObject::connect(proxyWindow, &QObject::destroyed, this, &IdleInhibitor::onWindowDestroyed);
62+
63+
QObject::connect(
64+
proxyWindow,
65+
&ProxyWindowBase::backerVisibilityChanged,
66+
this,
67+
&IdleInhibitor::onWindowVisibilityChanged
68+
);
69+
70+
this->onWindowVisibilityChanged();
71+
}
72+
73+
emit this->windowChanged();
74+
}
75+
76+
void IdleInhibitor::onWindowDestroyed() {
77+
this->proxyWindow = nullptr;
78+
this->onWaylandSurfaceDestroyed();
79+
this->bWindowObject = nullptr;
80+
}
81+
82+
void IdleInhibitor::onWindowVisibilityChanged() {
83+
if (!this->proxyWindow->isVisibleDirect()) return;
84+
85+
auto* window = this->proxyWindow->backingWindow();
86+
if (!window->handle()) window->create();
87+
88+
auto* waylandWindow = dynamic_cast<QWaylandWindow*>(window->handle());
89+
if (waylandWindow == this->mWaylandWindow) return;
90+
this->mWaylandWindow = waylandWindow;
91+
92+
QObject::connect(
93+
waylandWindow,
94+
&QObject::destroyed,
95+
this,
96+
&IdleInhibitor::onWaylandWindowDestroyed
97+
);
98+
99+
QObject::connect(
100+
waylandWindow,
101+
&QWaylandWindow::surfaceCreated,
102+
this,
103+
&IdleInhibitor::onWaylandSurfaceCreated
104+
);
105+
106+
QObject::connect(
107+
waylandWindow,
108+
&QWaylandWindow::surfaceDestroyed,
109+
this,
110+
&IdleInhibitor::onWaylandSurfaceDestroyed
111+
);
112+
113+
if (waylandWindow->surface()) this->onWaylandSurfaceCreated();
114+
}
115+
116+
void IdleInhibitor::onWaylandWindowDestroyed() { this->mWaylandWindow = nullptr; }
117+
118+
void IdleInhibitor::onWaylandSurfaceCreated() {
119+
auto* manager = impl::IdleInhibitManager::instance();
120+
121+
if (!manager) {
122+
qWarning() << "Cannot enable idle inhibitor as idle-inhibit-unstable-v1 is not supported by "
123+
"the current compositor.";
124+
return;
125+
}
126+
127+
delete this->inhibitor;
128+
this->inhibitor = manager->createIdleInhibitor(this->mWaylandWindow);
129+
}
130+
131+
void IdleInhibitor::onWaylandSurfaceDestroyed() {
132+
delete this->inhibitor;
133+
this->inhibitor = nullptr;
134+
}
135+
136+
} // namespace qs::wayland::idle_inhibit
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#pragma once
2+
3+
#include <qobject.h>
4+
#include <qproperty.h>
5+
#include <qqmlintegration.h>
6+
#include <qtmetamacros.h>
7+
8+
#include "../../window/proxywindow.hpp"
9+
#include "proto.hpp"
10+
11+
namespace qs::wayland::idle_inhibit {
12+
13+
///! Prevents a wayland session from idling
14+
/// If an idle daemon is running, it may perform actions such as locking the screen
15+
/// or putting the computer to sleep.
16+
///
17+
/// An idle inhibitor prevents a wayland session from being marked as idle, if compositor
18+
/// defined heuristics determine the window the inhibitor is attached to is important.
19+
///
20+
/// A compositor will usually consider a @@Quickshell.PanelWindow or
21+
/// a focused @@Quickshell.FloatingWindow to be important.
22+
///
23+
/// > [!NOTE] Using an idle inhibitor requires the compositor support the [idle-inhibit-unstable-v1] protocol.
24+
///
25+
/// [idle-inhibit-unstable-v1]: https://wayland.app/protocols/idle-inhibit-unstable-v1
26+
class IdleInhibitor: public QObject {
27+
Q_OBJECT;
28+
QML_ELEMENT;
29+
// clang-format off
30+
/// If the idle inhibitor should be enabled. Defaults to false.
31+
Q_PROPERTY(bool enabled READ default WRITE default NOTIFY enabledChanged BINDABLE bindableEnabled);
32+
/// The window to associate the idle inhibitor with. This may be used by the compositor
33+
/// to determine if the inhibitor should be respected.
34+
///
35+
/// Must be set to a non null value to enable the inhibitor.
36+
Q_PROPERTY(QObject* window READ window WRITE setWindow NOTIFY windowChanged);
37+
// clang-format on
38+
39+
public:
40+
IdleInhibitor();
41+
42+
[[nodiscard]] QObject* window() const;
43+
void setWindow(QObject* window);
44+
45+
[[nodiscard]] QBindable<bool> bindableEnabled() { return &this->bEnabled; }
46+
47+
signals:
48+
void enabledChanged();
49+
void windowChanged();
50+
51+
private slots:
52+
void onWindowDestroyed();
53+
void onWindowVisibilityChanged();
54+
void onWaylandWindowDestroyed();
55+
void onWaylandSurfaceCreated();
56+
void onWaylandSurfaceDestroyed();
57+
58+
private:
59+
void boundWindowChanged();
60+
ProxyWindowBase* proxyWindow = nullptr;
61+
QtWaylandClient::QWaylandWindow* mWaylandWindow = nullptr;
62+
63+
impl::IdleInhibitor* inhibitor = nullptr;
64+
65+
// clang-format off
66+
Q_OBJECT_BINDABLE_PROPERTY(IdleInhibitor, bool, bEnabled, &IdleInhibitor::enabledChanged);
67+
Q_OBJECT_BINDABLE_PROPERTY(IdleInhibitor, QObject*, bWindowObject, &IdleInhibitor::windowChanged);
68+
Q_OBJECT_BINDABLE_PROPERTY(IdleInhibitor, QObject*, bBoundWindow, &IdleInhibitor::boundWindowChanged);
69+
// clang-format on
70+
};
71+
72+
} // namespace qs::wayland::idle_inhibit

src/wayland/idle_inhibit/proto.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#include "proto.hpp"
2+
3+
#include <private/qwaylandwindow_p.h>
4+
#include <qwaylandclientextension.h>
5+
6+
namespace qs::wayland::idle_inhibit::impl {
7+
8+
IdleInhibitManager::IdleInhibitManager(): QWaylandClientExtensionTemplate(1) { this->initialize(); }
9+
10+
IdleInhibitManager* IdleInhibitManager::instance() {
11+
static auto* instance = new IdleInhibitManager(); // NOLINT
12+
return instance->isInitialized() ? instance : nullptr;
13+
}
14+
15+
IdleInhibitor* IdleInhibitManager::createIdleInhibitor(QtWaylandClient::QWaylandWindow* surface) {
16+
return new IdleInhibitor(this->create_inhibitor(surface->surface()));
17+
}
18+
19+
IdleInhibitor::~IdleInhibitor() {
20+
if (this->isInitialized()) this->destroy();
21+
}
22+
23+
} // namespace qs::wayland::idle_inhibit::impl

src/wayland/idle_inhibit/proto.hpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#pragma once
2+
3+
#include <private/qwaylandwindow_p.h>
4+
#include <qtclasshelpermacros.h>
5+
#include <qwayland-idle-inhibit-unstable-v1.h>
6+
#include <qwaylandclientextension.h>
7+
8+
#include "wayland-idle-inhibit-unstable-v1-client-protocol.h"
9+
10+
namespace qs::wayland::idle_inhibit::impl {
11+
12+
class IdleInhibitor;
13+
14+
class IdleInhibitManager
15+
: public QWaylandClientExtensionTemplate<IdleInhibitManager>
16+
, public QtWayland::zwp_idle_inhibit_manager_v1 {
17+
public:
18+
explicit IdleInhibitManager();
19+
20+
IdleInhibitor* createIdleInhibitor(QtWaylandClient::QWaylandWindow* surface);
21+
22+
static IdleInhibitManager* instance();
23+
};
24+
25+
class IdleInhibitor: public QtWayland::zwp_idle_inhibitor_v1 {
26+
public:
27+
explicit IdleInhibitor(::zwp_idle_inhibitor_v1* inhibitor)
28+
: QtWayland::zwp_idle_inhibitor_v1(inhibitor) {}
29+
30+
~IdleInhibitor() override;
31+
Q_DISABLE_COPY_MOVE(IdleInhibitor);
32+
};
33+
34+
} // namespace qs::wayland::idle_inhibit::impl

src/wayland/module.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ headers = [
55
"session_lock.hpp",
66
"toplevel_management/qml.hpp",
77
"screencopy/view.hpp",
8+
"idle_inhibit/inhibitor.hpp",
89
]
910
-----

0 commit comments

Comments
 (0)