feat(idle): add protocol-based idle behavior management with IPC commands

This commit is contained in:
Lysec
2026-04-09 15:19:39 +02:00
parent f0422efe72
commit 76ebd30c30
13 changed files with 430 additions and 29 deletions
+24
View File
@@ -71,6 +71,8 @@ set(XDG_ACTIVATION_XML
"${WAYLAND_PROTOCOLS_PKGDATADIR}/staging/xdg-activation/xdg-activation-v1.xml")
set(EXT_SESSION_LOCK_XML
"${WAYLAND_PROTOCOLS_PKGDATADIR}/staging/ext-session-lock/ext-session-lock-v1.xml")
set(EXT_IDLE_NOTIFY_XML
"${WAYLAND_PROTOCOLS_PKGDATADIR}/staging/ext-idle-notify/ext-idle-notify-v1.xml")
set(IDLE_INHIBIT_XML
"${WAYLAND_PROTOCOLS_PKGDATADIR}/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml")
set(WLR_FOREIGN_TOPLEVEL_XML
@@ -112,6 +114,10 @@ set(EXT_SESSION_LOCK_PROTOCOL_C
"${GENERATED_PROTOCOL_DIR}/ext-session-lock-v1-client-protocol.c")
set(EXT_SESSION_LOCK_PROTOCOL_H
"${GENERATED_PROTOCOL_DIR}/ext-session-lock-v1-client-protocol.h")
set(EXT_IDLE_NOTIFY_PROTOCOL_C
"${GENERATED_PROTOCOL_DIR}/ext-idle-notify-v1-client-protocol.c")
set(EXT_IDLE_NOTIFY_PROTOCOL_H
"${GENERATED_PROTOCOL_DIR}/ext-idle-notify-v1-client-protocol.h")
set(IDLE_INHIBIT_PROTOCOL_C
"${GENERATED_PROTOCOL_DIR}/idle-inhibit-unstable-v1-client-protocol.c")
set(IDLE_INHIBIT_PROTOCOL_H
@@ -268,6 +274,20 @@ add_custom_command(
VERBATIM
)
add_custom_command(
OUTPUT "${EXT_IDLE_NOTIFY_PROTOCOL_C}"
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${EXT_IDLE_NOTIFY_XML}" "${EXT_IDLE_NOTIFY_PROTOCOL_C}"
DEPENDS "${EXT_IDLE_NOTIFY_XML}"
VERBATIM
)
add_custom_command(
OUTPUT "${EXT_IDLE_NOTIFY_PROTOCOL_H}"
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" client-header "${EXT_IDLE_NOTIFY_XML}" "${EXT_IDLE_NOTIFY_PROTOCOL_H}"
DEPENDS "${EXT_IDLE_NOTIFY_XML}"
VERBATIM
)
add_custom_command(
OUTPUT "${IDLE_INHIBIT_PROTOCOL_C}"
COMMAND "${WAYLAND_SCANNER_EXECUTABLE}" private-code "${IDLE_INHIBIT_XML}" "${IDLE_INHIBIT_PROTOCOL_C}"
@@ -330,6 +350,8 @@ add_custom_target(noctalia_wayland_protocols
"${XDG_ACTIVATION_PROTOCOL_H}"
"${EXT_SESSION_LOCK_PROTOCOL_C}"
"${EXT_SESSION_LOCK_PROTOCOL_H}"
"${EXT_IDLE_NOTIFY_PROTOCOL_C}"
"${EXT_IDLE_NOTIFY_PROTOCOL_H}"
"${IDLE_INHIBIT_PROTOCOL_C}"
"${IDLE_INHIBIT_PROTOCOL_H}"
"${WLR_FOREIGN_TOPLEVEL_PROTOCOL_C}"
@@ -359,6 +381,7 @@ add_executable(noctalia
src/debug/debug_service.cpp
src/font/font_service.cpp
src/idle/idle_inhibitor.cpp
src/idle/idle_manager.cpp
src/ipc/ipc_client.cpp
src/ipc/ipc_service.cpp
src/launcher/app_provider.cpp
@@ -479,6 +502,7 @@ add_executable(noctalia
"${EXT_DATA_CONTROL_PROTOCOL_C}"
"${XDG_ACTIVATION_PROTOCOL_C}"
"${EXT_SESSION_LOCK_PROTOCOL_C}"
"${EXT_IDLE_NOTIFY_PROTOCOL_C}"
"${IDLE_INHIBIT_PROTOCOL_C}"
"${WLR_FOREIGN_TOPLEVEL_PROTOCOL_C}"
"${WLR_DATA_CONTROL_PROTOCOL_C}"
+72
View File
@@ -14,6 +14,7 @@ Changes are detected automatically via inotify — no restart required.
- [Built-in widgets](#built-in-widgets)
- [Weather](#weather)
- [Audio](#audio)
- [Idle](#idle)
- [Shell](#shell)
- [OSD](#osd)
- [Wallpaper](#wallpaper)
@@ -358,6 +359,77 @@ When it is `true`, those sliders allow values up to `150%`.
---
## Idle
Idle behavior is defined as named entries under `[idle.behavior.*]`.
When no `config.toml` exists, Noctalia uses this built-in default:
```toml
[idle.behavior.lock]
timeout = 660
command = "noctalia:lock"
enabled = false
```
```toml
[idle.behavior.lock]
timeout = 16
command = "noctalia:lock"
[idle.behavior.screen-off]
timeout = 32
command = "noctalia:dpms-off"
[idle.behavior.custom]
timeout = 48
command = "noctalia-msg lock"
```
Available fields:
| Setting | Type | Default | Description |
|----------------|--------|---------|-------------|
| `enabled` | bool | `true` | Enable or disable this behavior entry |
| `timeout` | int | `0` | Timeout in seconds before the behavior triggers |
| `command` | string | `""` | Command to execute when the idle timeout is reached |
`command` can be either:
- a regular shell command such as `notify-send 'Idle' 'Locking soon'`
- a Noctalia IPC command using the `noctalia:` prefix
When you use the `noctalia:` prefix, the rest of the string is executed through the same IPC command registry as `noctalia-ipc`.
That means all existing Noctalia IPC commands are available inside idle behaviors, not just a special idle-only subset.
Examples:
- `noctalia:lock`
- `noctalia:dpms-off`
- `noctalia:dpms-on`
- `noctalia:enable-idle-inhibitor`
- `noctalia:disable-idle-inhibitor`
- `noctalia:toggle-idle-inhibitor`
- `noctalia:toggle-launcher`
- `noctalia:toggle-session-menu`
Idle behavior uses the Wayland `ext_idle_notifier_v1` protocol, so it reacts to compositor idle notifications instead of polling. The standard idle notification path respects active idle inhibitors.
Examples:
```toml
[idle.behavior.notify]
timeout = 300
command = "notify-send 'Noctalia' 'You have been idle for 5 minutes'"
[idle.behavior.lock]
timeout = 660
command = "noctalia:lock"
enabled = false
```
---
## Shell
Shell-wide UI settings for non-bar surfaces.
+18 -1
View File
@@ -18,6 +18,7 @@ A lightweight Wayland shell and bar with no Qt or GTK dependency.
| Multi-monitor | `zxdg-output-unstable-v1` |
| Active window metadata | `zwlr-foreign-toplevel-management-unstable-v1` |
| Lockscreen | `ext-session-lock-v1` |
| Idle detection | `ext-idle-notify-v1` |
| Cursor | `wp-cursor-shape-v1` |
| Rendering | `EGL`, `OpenGL ES 3`, `wayland-egl` |
| Text | `freetype`, `harfbuzz`, `msdfgen` (vendored), `fontconfig` |
@@ -163,6 +164,22 @@ gdbus call --session --dest dev.noctalia.Debug --object-path /dev/noctalia/Debug
gdbus call --session --dest dev.noctalia.Debug --object-path /dev/noctalia/Debug --method dev.noctalia.Debug.EmitInternalNotification "Noctalia" "Test" "Hello from debug" 5000 1
```
## Configuration
Noctalia reads `$XDG_CONFIG_HOME/noctalia/config.toml` or `~/.config/noctalia/config.toml`.
If no config file exists, it falls back to built-in defaults in code, including this disabled idle behavior:
```toml
[idle.behavior.lock]
timeout = 660
command = "noctalia:lock"
enabled = false
```
Idle behaviors use a single `command` field.
That can be either a normal shell command such as `notify-send 'Idle' 'Locking soon'`, or a Noctalia IPC command prefixed with `noctalia:` such as `noctalia:lock` or `noctalia:toggle-launcher`.
See [CONFIG.md](/mnt/storage/GitHub/noctalia-dev/Nextalia/CONFIG.md) for the full configuration reference.
## Roadmap
### Hardware and networking
@@ -224,7 +241,7 @@ gdbus call --session --dest dev.noctalia.Debug --object-path /dev/noctalia/Debug
- [ ] Lock keys (Caps/Num)
- [ ] Dark mode button
- [ ] Night light button
- [ ] Keep awake (idle inhibitor)
- [x] Keep awake (idle inhibitor)
- [ ] Audio visualizer
- [x] Launcher button
- [x] Session menu button
+30
View File
@@ -95,6 +95,13 @@ namespace {
return true;
}
bool launchShellCommand(const std::string& command) {
if (command.empty()) {
return false;
}
return launchDetachedCommand({"/bin/sh", "-lc", command.c_str()});
}
bool launchFirstAvailableCommand(std::initializer_list<std::initializer_list<const char*>> commandVariants) {
for (const auto& variant : commandVariants) {
if (variant.size() == 0) {
@@ -223,6 +230,10 @@ void Application::initServices() {
m_idleInhibitor.initialize(m_wayland, &m_renderContext);
m_idleInhibitor.setChangeCallback([this]() { m_bar.refresh(); });
m_idleManager.initialize(m_wayland);
m_idleManager.setCommandRunner([this](const std::string& command) { return runIdleCommand(command); });
m_idleManager.reload(m_configService.config().idle);
m_configService.addReloadCallback([this]() { m_idleManager.reload(m_configService.config().idle); });
m_wallpaper.initialize(m_wayland, &m_configService, &m_stateService);
m_overview.initialize(m_wayland, &m_configService, &m_stateService, &m_wallpaper);
@@ -609,6 +620,25 @@ void Application::initIpc() {
"toggle-idle-inhibitor", "Toggle the compositor idle inhibitor");
}
bool Application::runIdleCommand(const std::string& command) {
constexpr std::string_view prefix = "noctalia:";
if (command.rfind(prefix, 0) == 0) {
const std::string response = m_ipcService.execute(command.substr(prefix.size()));
if (response.rfind("error:", 0) == 0) {
kLog.warn("idle IPC command '{}' failed: {}", command, response.substr(0, response.find('\n')));
return false;
}
return true;
}
if (!launchShellCommand(command)) {
kLog.warn("idle command failed to launch: {}", command);
return false;
}
return true;
}
std::vector<PollSource*> Application::buildPollSources() {
std::vector<PollSource*> sources;
if (m_bus != nullptr) {
+3
View File
@@ -18,6 +18,7 @@
#include "dbus/upower/upower_service.h"
#include "debug/debug_service.h"
#include "idle/idle_inhibitor.h"
#include "idle/idle_manager.h"
#include "ipc/ipc_poll_source.h"
#include "ipc/ipc_service.h"
#include "net/http_client.h"
@@ -67,6 +68,7 @@ private:
void initServices();
void initUi();
void initIpc();
bool runIdleCommand(const std::string& command);
[[nodiscard]] std::vector<PollSource*> buildPollSources();
WaylandConnection m_wayland;
@@ -80,6 +82,7 @@ private:
std::unique_ptr<SystemMonitorService> m_systemMonitor;
std::unique_ptr<DebugService> m_debugService;
IdleInhibitor m_idleInhibitor;
IdleManager m_idleManager;
std::unique_ptr<MprisService> m_mprisService;
std::unique_ptr<PowerProfilesService> m_powerProfilesService;
std::unique_ptr<UPowerService> m_upowerService;
+46
View File
@@ -67,6 +67,12 @@ ConfigService::ConfigService() {
} else {
kLog.info("no config file found, using defaults");
seedBuiltinWidgets(m_config);
m_config.idle.behaviors.push_back(IdleBehaviorConfig{
.name = "lock",
.enabled = false,
.timeoutSeconds = 660,
.command = "noctalia:lock",
});
m_config.bars.push_back(BarConfig{});
}
setupWatch();
@@ -100,6 +106,12 @@ void ConfigService::forceReload() {
loadFromFile(m_configPath);
} else {
seedBuiltinWidgets(m_config);
m_config.idle.behaviors.push_back(IdleBehaviorConfig{
.name = "lock",
.enabled = false,
.timeoutSeconds = 660,
.command = "noctalia:lock",
});
m_config.bars.push_back(BarConfig{});
}
for (const auto& cb : m_reloadCallbacks) {
@@ -143,6 +155,12 @@ void ConfigService::checkReload() {
loadFromFile(m_configPath);
} else {
seedBuiltinWidgets(m_config);
m_config.idle.behaviors.push_back(IdleBehaviorConfig{
.name = "lock",
.enabled = false,
.timeoutSeconds = 660,
.command = "noctalia:lock",
});
m_config.bars.push_back(BarConfig{});
}
@@ -527,12 +545,40 @@ void ConfigService::loadFromFile(const std::string& path) {
}
}
// Parse [idle.behavior.*]
if (auto* idleTbl = tbl["idle"].as_table()) {
if (auto* behaviorTbl = (*idleTbl)["behavior"].as_table()) {
for (const auto& [name, node] : *behaviorTbl) {
auto* entryTbl = node.as_table();
if (entryTbl == nullptr) {
continue;
}
IdleBehaviorConfig behavior;
behavior.name = std::string(name.str());
if (auto v = (*entryTbl)["enabled"].value<bool>()) {
behavior.enabled = *v;
}
if (auto v = (*entryTbl)["timeout"].value<int64_t>()) {
behavior.timeoutSeconds = static_cast<std::int32_t>(*v);
}
if (auto v = (*entryTbl)["command"].value<std::string>()) {
behavior.command = *v;
}
m_config.idle.behaviors.push_back(std::move(behavior));
}
}
}
if (m_config.bars.empty()) {
kLog.info("no [bar.*] defined, using defaults");
m_config.bars.push_back(BarConfig{});
}
kLog.info("{} bar(s) defined", m_config.bars.size());
kLog.info("idle behaviors={}", m_config.idle.behaviors.size());
}
// ── WidgetConfig accessors ───────────────────────────────────────────────────
+12
View File
@@ -123,6 +123,17 @@ struct AudioConfig {
bool enableOverdrive = false;
};
struct IdleBehaviorConfig {
std::string name;
bool enabled = true;
std::int32_t timeoutSeconds = 0;
std::string command;
};
struct IdleConfig {
std::vector<IdleBehaviorConfig> behaviors;
};
struct Config {
std::vector<BarConfig> bars;
std::unordered_map<std::string, WidgetConfig> widgets;
@@ -132,6 +143,7 @@ struct Config {
OsdConfig osd;
WeatherConfig weather;
AudioConfig audio;
IdleConfig idle;
};
class ConfigService {
+124
View File
@@ -0,0 +1,124 @@
#include "idle/idle_manager.h"
#include "core/log.h"
#include "wayland/wayland_connection.h"
#include "ext-idle-notify-v1-client-protocol.h"
namespace {
constexpr Logger kLog("idle");
const ext_idle_notification_v1_listener kIdleNotificationListener = {
.idled = &IdleManager::handleIdled,
.resumed = &IdleManager::handleResumed,
};
} // namespace
IdleManager::IdleManager() = default;
IdleManager::~IdleManager() { clearBehaviors(); }
bool IdleManager::initialize(WaylandConnection& wayland) {
m_wayland = &wayland;
m_notifier = m_wayland->idleNotifier();
if (m_notifier == nullptr) {
kLog.info("idle notify protocol unavailable");
}
return true;
}
void IdleManager::setCommandRunner(CommandRunner runner) { m_commandRunner = std::move(runner); }
void IdleManager::reload(const IdleConfig& config) {
clearBehaviors();
if (m_notifier == nullptr) {
return;
}
if (m_wayland == nullptr || m_wayland->seat() == nullptr) {
kLog.warn("cannot register idle behaviors without a Wayland seat");
return;
}
for (const auto& behavior : config.behaviors) {
createBehavior(behavior);
}
}
void IdleManager::clearBehaviors() {
for (auto& behavior : m_behaviors) {
if (behavior->notification != nullptr) {
ext_idle_notification_v1_destroy(behavior->notification);
behavior->notification = nullptr;
}
}
m_behaviors.clear();
}
void IdleManager::createBehavior(const IdleBehaviorConfig& config) {
if (!config.enabled) {
return;
}
if (config.timeoutSeconds < 0) {
kLog.warn("idle behavior '{}' ignored: timeout must be >= 0 seconds", config.name);
return;
}
if (config.command.empty()) {
kLog.warn("idle behavior '{}' ignored: needs a command", config.name);
return;
}
auto behavior = std::make_unique<BehaviorState>();
behavior->owner = this;
behavior->config = config;
const auto timeoutMs = static_cast<std::uint32_t>(config.timeoutSeconds) * 1000u;
behavior->notification = ext_idle_notifier_v1_get_idle_notification(m_notifier, timeoutMs, m_wayland->seat());
if (behavior->notification == nullptr) {
kLog.warn("failed to register idle behavior '{}'", config.name);
return;
}
ext_idle_notification_v1_add_listener(behavior->notification, &kIdleNotificationListener, behavior.get());
kLog.info("registered idle behavior '{}' timeout={}s", config.name, config.timeoutSeconds);
m_behaviors.push_back(std::move(behavior));
}
void IdleManager::runBehavior(BehaviorState& behavior) {
const auto& config = behavior.config;
if (!runCommand(config.command)) {
kLog.warn("idle behavior '{}' command failed", config.name);
}
}
bool IdleManager::runCommand(const std::string& command) const {
if (command.empty()) {
return true;
}
if (!m_commandRunner) {
return false;
}
return m_commandRunner(command);
}
void IdleManager::handleIdled(void* data, ext_idle_notification_v1* /*notification*/) {
auto* behavior = static_cast<BehaviorState*>(data);
if (behavior == nullptr || behavior->owner == nullptr || behavior->idled) {
return;
}
behavior->idled = true;
kLog.info("idle behavior '{}' triggered", behavior->config.name);
behavior->owner->runBehavior(*behavior);
}
void IdleManager::handleResumed(void* data, ext_idle_notification_v1* /*notification*/) {
auto* behavior = static_cast<BehaviorState*>(data);
if (behavior == nullptr || !behavior->idled) {
return;
}
behavior->idled = false;
kLog.info("idle behavior '{}' resumed", behavior->config.name);
}
+47
View File
@@ -0,0 +1,47 @@
#pragma once
#include "config/config_service.h"
#include <functional>
#include <memory>
#include <string>
#include <vector>
class WaylandConnection;
struct ext_idle_notification_v1;
struct ext_idle_notifier_v1;
class IdleManager {
public:
using CommandRunner = std::function<bool(const std::string&)>;
IdleManager();
~IdleManager();
IdleManager(const IdleManager&) = delete;
IdleManager& operator=(const IdleManager&) = delete;
bool initialize(WaylandConnection& wayland);
void setCommandRunner(CommandRunner runner);
void reload(const IdleConfig& config);
static void handleIdled(void* data, ext_idle_notification_v1* notification);
static void handleResumed(void* data, ext_idle_notification_v1* notification);
private:
struct BehaviorState {
IdleManager* owner = nullptr;
IdleBehaviorConfig config;
ext_idle_notification_v1* notification = nullptr;
bool idled = false;
};
void clearBehaviors();
void createBehavior(const IdleBehaviorConfig& config);
void runBehavior(BehaviorState& behavior);
bool runCommand(const std::string& command) const;
WaylandConnection* m_wayland = nullptr;
ext_idle_notifier_v1* m_notifier = nullptr;
CommandRunner m_commandRunner;
std::vector<std::unique_ptr<BehaviorState>> m_behaviors;
};
+28 -28
View File
@@ -90,6 +90,24 @@ void IpcService::dispatch() {
}
}
std::string IpcService::execute(const std::string& line) const {
std::string command;
std::string args;
const auto spacePos = line.find(' ');
if (spacePos == std::string::npos) {
command = line;
} else {
command = line.substr(0, spacePos);
args = line.substr(spacePos + 1);
}
if (command == "--help" || command == "-h") {
return buildHelp();
}
return executeParsed(command, args);
}
void IpcService::handleConnection(int connFd) {
// Set receive timeout so a slow client doesn't stall the main loop
timeval tv{};
@@ -122,34 +140,7 @@ void IpcService::handleConnection(int connFd) {
return;
}
// Split on first space: command + optional args
const std::string line(buf, static_cast<std::size_t>(total));
std::string command;
std::string args;
const auto spacePos = line.find(' ');
if (spacePos == std::string::npos) {
command = line;
} else {
command = line.substr(0, spacePos);
args = line.substr(spacePos + 1);
}
// Built-in --help
if (command == "--help" || command == "-h") {
const std::string response = buildHelp();
::send(connFd, response.data(), response.size(), MSG_NOSIGNAL);
return;
}
const auto it =
std::find_if(m_handlers.begin(), m_handlers.end(), [&command](const auto& e) { return e.first == command; });
std::string response;
if (it == m_handlers.end()) {
response = "error: unknown command (try: noctalia msg --help)\n";
} else {
response = it->second.fn(args);
}
const std::string response = execute(std::string(buf, static_cast<std::size_t>(total)));
::send(connFd, response.data(), response.size(), MSG_NOSIGNAL);
}
@@ -175,6 +166,15 @@ std::string IpcService::buildHelp() const {
return out;
}
std::string IpcService::executeParsed(const std::string& command, const std::string& args) const {
const auto it =
std::find_if(m_handlers.begin(), m_handlers.end(), [&command](const auto& e) { return e.first == command; });
if (it == m_handlers.end()) {
return "error: unknown command (try: noctalia msg --help)\n";
}
return it->second.fn(args);
}
std::string IpcService::resolveSocketPath() {
const char* runtime = std::getenv("XDG_RUNTIME_DIR");
if (runtime == nullptr || runtime[0] == '\0') {
+4
View File
@@ -26,6 +26,9 @@ public:
// Called by IpcPollSource when POLLIN fires on the listening fd.
void dispatch();
// Execute a command line using the same handler registry as socket IPC.
[[nodiscard]] std::string execute(const std::string& line) const;
// Register a handler for a command name. The handler receives everything after
// the first space as `args`. Must return a string ending with '\n'.
// `usage` describes the command signature, e.g. "toggle-panel <id>".
@@ -42,6 +45,7 @@ private:
void handleConnection(int connFd);
std::string buildHelp() const;
[[nodiscard]] std::string executeParsed(const std::string& command, const std::string& args) const;
[[nodiscard]] static std::string resolveSocketPath();
int m_listenFd = -1;
+17
View File
@@ -12,6 +12,7 @@
#include "cursor-shape-v1-client-protocol.h"
#include "dwl-ipc-unstable-v2-client-protocol.h"
#include "ext-data-control-v1-client-protocol.h"
#include "ext-idle-notify-v1-client-protocol.h"
#include "ext-session-lock-v1-client-protocol.h"
#include "ext-workspace-v1-client-protocol.h"
#include "idle-inhibit-unstable-v1-client-protocol.h"
@@ -33,6 +34,7 @@ namespace {
constexpr std::uint32_t kCursorShapeManagerVersion = 1;
constexpr std::uint32_t kXdgActivationVersion = 1;
constexpr std::uint32_t kExtSessionLockManagerVersion = 1;
constexpr std::uint32_t kExtIdleNotifierVersion = 1;
constexpr std::uint32_t kIdleInhibitManagerVersion = 1;
constexpr std::uint32_t kOutputVersion = 4;
@@ -298,6 +300,7 @@ bool WaylandConnection::hasExtWorkspaceManager() const noexcept { return m_hasEx
bool WaylandConnection::hasMangoWorkspaceManager() const noexcept { return m_hasMangoWorkspaceGlobal; }
bool WaylandConnection::hasForeignToplevelManager() const noexcept { return m_hasForeignToplevelManagerGlobal; }
bool WaylandConnection::hasSessionLockManager() const noexcept { return m_sessionLockManager != nullptr; }
bool WaylandConnection::hasIdleNotifier() const noexcept { return m_idleNotifier != nullptr; }
bool WaylandConnection::hasIdleInhibitManager() const noexcept { return m_idleInhibitManager != nullptr; }
bool WaylandConnection::hasXdgActivation() const noexcept { return m_xdgActivation != nullptr; }
@@ -336,11 +339,14 @@ wl_display* WaylandConnection::display() const noexcept { return m_display; }
wl_compositor* WaylandConnection::compositor() const noexcept { return m_compositor; }
wl_seat* WaylandConnection::seat() const noexcept { return m_seatHandler.seat(); }
wl_shm* WaylandConnection::shm() const noexcept { return m_shm; }
zwlr_layer_shell_v1* WaylandConnection::layerShell() const noexcept { return m_layerShell; }
ext_session_lock_manager_v1* WaylandConnection::sessionLockManager() const noexcept { return m_sessionLockManager; }
ext_idle_notifier_v1* WaylandConnection::idleNotifier() const noexcept { return m_idleNotifier; }
zwp_idle_inhibit_manager_v1* WaylandConnection::idleInhibitManager() const noexcept { return m_idleInhibitManager; }
const std::vector<WaylandOutput>& WaylandConnection::outputs() const noexcept { return m_outputs; }
@@ -478,6 +484,13 @@ void WaylandConnection::bindGlobal(wl_registry* registry, std::uint32_t name, co
return;
}
if (interfaceName == ext_idle_notifier_v1_interface.name) {
const auto bindVersion = std::min(version, kExtIdleNotifierVersion);
m_idleNotifier = static_cast<ext_idle_notifier_v1*>(
wl_registry_bind(registry, name, &ext_idle_notifier_v1_interface, bindVersion));
return;
}
if (interfaceName == zwp_idle_inhibit_manager_v1_interface.name) {
const auto bindVersion = std::min(version, kIdleInhibitManagerVersion);
m_idleInhibitManager = static_cast<zwp_idle_inhibit_manager_v1*>(
@@ -573,6 +586,10 @@ void WaylandConnection::cleanup() {
ext_session_lock_manager_v1_destroy(m_sessionLockManager);
m_sessionLockManager = nullptr;
}
if (m_idleNotifier != nullptr) {
ext_idle_notifier_v1_destroy(m_idleNotifier);
m_idleNotifier = nullptr;
}
if (m_idleInhibitManager != nullptr) {
zwp_idle_inhibit_manager_v1_destroy(m_idleInhibitManager);
m_idleInhibitManager = nullptr;
+5
View File
@@ -21,6 +21,7 @@ struct zwlr_layer_shell_v1;
struct zxdg_output_manager_v1;
struct zxdg_output_v1;
struct wp_cursor_shape_manager_v1;
struct ext_idle_notifier_v1;
struct zwp_idle_inhibit_manager_v1;
struct xdg_activation_v1;
struct ext_session_lock_manager_v1;
@@ -86,12 +87,15 @@ public:
[[nodiscard]] bool hasMangoWorkspaceManager() const noexcept;
[[nodiscard]] bool hasForeignToplevelManager() const noexcept;
[[nodiscard]] bool hasSessionLockManager() const noexcept;
[[nodiscard]] bool hasIdleNotifier() const noexcept;
[[nodiscard]] bool hasIdleInhibitManager() const noexcept;
[[nodiscard]] wl_display* display() const noexcept;
[[nodiscard]] wl_compositor* compositor() const noexcept;
[[nodiscard]] wl_seat* seat() const noexcept;
[[nodiscard]] wl_shm* shm() const noexcept;
[[nodiscard]] zwlr_layer_shell_v1* layerShell() const noexcept;
[[nodiscard]] ext_session_lock_manager_v1* sessionLockManager() const noexcept;
[[nodiscard]] ext_idle_notifier_v1* idleNotifier() const noexcept;
[[nodiscard]] zwp_idle_inhibit_manager_v1* idleInhibitManager() const noexcept;
[[nodiscard]] const std::vector<WaylandOutput>& outputs() const noexcept;
[[nodiscard]] WaylandOutput* findOutputByWl(wl_output* wlOutput);
@@ -131,6 +135,7 @@ private:
wp_cursor_shape_manager_v1* m_cursorShapeManager = nullptr;
xdg_activation_v1* m_xdgActivation = nullptr;
ext_session_lock_manager_v1* m_sessionLockManager = nullptr;
ext_idle_notifier_v1* m_idleNotifier = nullptr;
zwp_idle_inhibit_manager_v1* m_idleInhibitManager = nullptr;
void* m_dataControlManager = nullptr;
const DataControlOps* m_dataControlOps = nullptr;