mirror of
https://github.com/hyprwm/hypridle.git
synced 2024-11-16 23:25:58 +01:00
core: add support for dbus events
This commit is contained in:
parent
a3855cb40b
commit
36d7238afd
9 changed files with 213 additions and 129 deletions
|
@ -32,7 +32,7 @@ message(STATUS "Checking deps...")
|
|||
|
||||
find_package(Threads REQUIRED)
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-client wayland-protocols hyprlang>=0.3.2)
|
||||
pkg_check_modules(deps REQUIRED IMPORTED_TARGET wayland-client wayland-protocols hyprlang>=0.3.2 sdbus-c++)
|
||||
|
||||
file(GLOB_RECURSE SRCFILES CONFIGURE_DEPENDS "src/*.cpp")
|
||||
add_executable(hypridle ${SRCFILES})
|
||||
|
|
|
@ -7,6 +7,12 @@ Configuration is done via `~/.config/hypr/hypridle.conf` in the standard
|
|||
hyprland syntax.
|
||||
|
||||
```ini
|
||||
general {
|
||||
lock_cmd = notify-send "lock!" # dbus/sysd lock command (loginctl lock-session)
|
||||
unlock_cmd = notify-send "unlock!" # same as above, but unlock
|
||||
before_sleep_cmd = notify-send "Zzz" # command ran before sleep
|
||||
}
|
||||
|
||||
listener {
|
||||
timeout = 500 # in seconds
|
||||
on-timeout = notify-send "You are idle!" # command to run when timeout has passed
|
||||
|
@ -21,6 +27,7 @@ will make those events ignored.
|
|||
- wayland
|
||||
- wayland-protocols
|
||||
- hyprlang >= 0.4.0
|
||||
- sdbus-c++
|
||||
|
||||
## Building & Installation
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "ConfigManager.hpp"
|
||||
#include <filesystem>
|
||||
#include "../helpers/VarList.hpp"
|
||||
|
||||
static std::string getConfigDir() {
|
||||
static const char* xdgConfigHome = getenv("XDG_CONFIG_HOME");
|
||||
|
@ -24,6 +23,10 @@ void CConfigManager::init() {
|
|||
m_config.addSpecialConfigValue("listener", "on-timeout", Hyprlang::STRING{""});
|
||||
m_config.addSpecialConfigValue("listener", "on-resume", Hyprlang::STRING{""});
|
||||
|
||||
m_config.addConfigValue("general:lock_cmd", Hyprlang::STRING{""});
|
||||
m_config.addConfigValue("general:unlock_cmd", Hyprlang::STRING{""});
|
||||
m_config.addConfigValue("general:before_sleep_cmd", Hyprlang::STRING{""});
|
||||
|
||||
m_config.commence();
|
||||
|
||||
auto result = m_config.parse();
|
||||
|
@ -73,4 +76,8 @@ Hyprlang::CParseResult CConfigManager::postParse() {
|
|||
|
||||
std::vector<CConfigManager::STimeoutRule> CConfigManager::getRules() {
|
||||
return m_vRules;
|
||||
}
|
||||
}
|
||||
|
||||
void* const* CConfigManager::getValuePtr(const std::string& name) {
|
||||
return m_config.getConfigValuePtr(name.c_str())->getDataStaticPtr();
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ class CConfigManager {
|
|||
};
|
||||
|
||||
std::vector<STimeoutRule> getRules();
|
||||
void* const* getValuePtr(const std::string& name);
|
||||
|
||||
private:
|
||||
Hyprlang::CConfig m_config;
|
||||
|
@ -28,4 +29,4 @@ class CConfigManager {
|
|||
Hyprlang::CParseResult postParse();
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CConfigManager> g_pConfigManager;
|
||||
inline std::unique_ptr<CConfigManager> g_pConfigManager;
|
||||
|
|
|
@ -3,12 +3,16 @@
|
|||
#include "../config/ConfigManager.hpp"
|
||||
#include "signal.h"
|
||||
#include <sys/wait.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
CHypridle::CHypridle() {
|
||||
m_sWaylandState.display = wl_display_connect(nullptr);
|
||||
if (!m_sWaylandState.display) {
|
||||
Debug::log(CRIT, "Couldn't connect to a wayland compositor");
|
||||
throw;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -47,12 +51,14 @@ void CHypridle::run() {
|
|||
|
||||
if (!m_sWaylandIdleState.notifier) {
|
||||
Debug::log(CRIT, "Couldn't bind to ext-idle-notifier-v1, does your compositor support it?");
|
||||
throw;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
const auto RULES = g_pConfigManager->getRules();
|
||||
m_sWaylandIdleState.listeners.resize(RULES.size());
|
||||
|
||||
Debug::log(LOG, "found {} rules", RULES.size());
|
||||
|
||||
for (size_t i = 0; i < RULES.size(); ++i) {
|
||||
auto& l = m_sWaylandIdleState.listeners[i];
|
||||
const auto& r = RULES[i];
|
||||
|
@ -63,9 +69,105 @@ void CHypridle::run() {
|
|||
ext_idle_notification_v1_add_listener(l.notification, &idleListener, &l);
|
||||
}
|
||||
|
||||
while (wl_display_dispatch(m_sWaylandState.display) != -1) {
|
||||
;
|
||||
Debug::log(LOG, "wayland done, registering dbus");
|
||||
|
||||
try {
|
||||
m_sDBUSState.connection = sdbus::createSystemBusConnection();
|
||||
} catch (std::exception& e) {
|
||||
Debug::log(CRIT, "Couldn't create the dbus connection ({})", e.what());
|
||||
exit(1);
|
||||
}
|
||||
|
||||
setupDBUS();
|
||||
enterEventLoop();
|
||||
}
|
||||
|
||||
void CHypridle::enterEventLoop() {
|
||||
|
||||
pollfd pollfds[] = {
|
||||
{
|
||||
.fd = m_sDBUSState.connection->getEventLoopPollData().fd,
|
||||
.events = POLLIN,
|
||||
},
|
||||
{
|
||||
.fd = wl_display_get_fd(m_sWaylandState.display),
|
||||
.events = POLLIN,
|
||||
},
|
||||
};
|
||||
|
||||
std::thread pollThr([this, &pollfds]() {
|
||||
while (1) {
|
||||
int ret = poll(pollfds, 2, 5000 /* 5 seconds, reasonable. It's because we might need to terminate */);
|
||||
if (ret < 0) {
|
||||
Debug::log(CRIT, "[core] Polling fds failed with {}", errno);
|
||||
m_bTerminate = true;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
if (pollfds[i].revents & POLLHUP) {
|
||||
Debug::log(CRIT, "[core] Disconnected from pollfd id {}", i);
|
||||
m_bTerminate = true;
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bTerminate)
|
||||
break;
|
||||
|
||||
if (ret != 0) {
|
||||
Debug::log(TRACE, "[core] got poll event");
|
||||
std::lock_guard<std::mutex> lg(m_sEventLoopInternals.loopRequestMutex);
|
||||
m_sEventLoopInternals.shouldProcess = true;
|
||||
m_sEventLoopInternals.loopSignal.notify_all();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
while (1) { // dbus events
|
||||
// wait for being awakened
|
||||
m_sEventLoopInternals.loopRequestMutex.unlock(); // unlock, we are ready to take events
|
||||
|
||||
std::unique_lock lk(m_sEventLoopInternals.loopMutex);
|
||||
if (m_sEventLoopInternals.shouldProcess == false) // avoid a lock if a thread managed to request something already since we .unlock()ed
|
||||
m_sEventLoopInternals.loopSignal.wait(lk, [this] { return m_sEventLoopInternals.shouldProcess == true; }); // wait for events
|
||||
|
||||
m_sEventLoopInternals.loopRequestMutex.lock(); // lock incoming events
|
||||
|
||||
if (m_bTerminate)
|
||||
break;
|
||||
|
||||
m_sEventLoopInternals.shouldProcess = false;
|
||||
|
||||
std::lock_guard<std::mutex> lg(m_sEventLoopInternals.eventLock);
|
||||
|
||||
if (pollfds[0].revents & POLLIN /* dbus */) {
|
||||
Debug::log(TRACE, "got dbus event");
|
||||
while (m_sDBUSState.connection->processPendingRequest()) {
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
if (pollfds[1].revents & POLLIN /* wl */) {
|
||||
Debug::log(TRACE, "got wl event");
|
||||
wl_display_flush(m_sWaylandState.display);
|
||||
if (wl_display_prepare_read(m_sWaylandState.display) == 0) {
|
||||
wl_display_read_events(m_sWaylandState.display);
|
||||
wl_display_dispatch_pending(m_sWaylandState.display);
|
||||
} else {
|
||||
wl_display_dispatch(m_sWaylandState.display);
|
||||
}
|
||||
}
|
||||
|
||||
// finalize wayland dispatching. Dispatch pending on the queue
|
||||
int ret = 0;
|
||||
do {
|
||||
ret = wl_display_dispatch_pending(m_sWaylandState.display);
|
||||
wl_display_flush(m_sWaylandState.display);
|
||||
} while (ret > 0);
|
||||
}
|
||||
|
||||
Debug::log(ERR, "[core] Terminated");
|
||||
}
|
||||
|
||||
void CHypridle::onGlobal(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t version) {
|
||||
|
@ -165,3 +267,60 @@ void CHypridle::onResumed(SIdleListener* pListener) {
|
|||
Debug::log(LOG, "Running {}", pListener->onRestore);
|
||||
spawn(pListener->onRestore);
|
||||
}
|
||||
|
||||
void handleDbusLogin(sdbus::Message& msg) {
|
||||
// lock & unlock
|
||||
static auto* const PLOCKCMD = (Hyprlang::STRING const*)g_pConfigManager->getValuePtr("general:lock_cmd");
|
||||
static auto* const PUNLOCKCMD = (Hyprlang::STRING const*)g_pConfigManager->getValuePtr("general:unlock_cmd");
|
||||
|
||||
Debug::log(LOG, "Got dbus .Session");
|
||||
|
||||
const auto MEMBER = msg.getMemberName();
|
||||
if (MEMBER == "Lock") {
|
||||
Debug::log(LOG, "Got Lock from dbus");
|
||||
|
||||
if (!std::string{*PLOCKCMD}.empty()) {
|
||||
Debug::log(LOG, "Locking with {}", *PLOCKCMD);
|
||||
spawn(*PLOCKCMD);
|
||||
}
|
||||
} else if (MEMBER == "Unlock") {
|
||||
Debug::log(LOG, "Got Unlock from dbus");
|
||||
|
||||
if (!std::string{*PUNLOCKCMD}.empty()) {
|
||||
Debug::log(LOG, "Locking with {}", *PUNLOCKCMD);
|
||||
spawn(*PUNLOCKCMD);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleDbusSleep(sdbus::Message& msg) {
|
||||
const auto MEMBER = msg.getMemberName();
|
||||
|
||||
if (MEMBER != "PrepareForSleep")
|
||||
return;
|
||||
|
||||
static auto* const PSLEEPCMD = (Hyprlang::STRING const*)g_pConfigManager->getValuePtr("general:before_sleep_cmd");
|
||||
|
||||
Debug::log(LOG, "Got PrepareForSleep from dbus");
|
||||
|
||||
if (std::string{*PSLEEPCMD}.empty())
|
||||
return;
|
||||
|
||||
Debug::log(LOG, "Running before-sleep: {}", *PSLEEPCMD);
|
||||
spawn(*PSLEEPCMD);
|
||||
}
|
||||
|
||||
void CHypridle::setupDBUS() {
|
||||
auto proxy = sdbus::createProxy("org.freedesktop.login1", "/org/freedesktop/login1");
|
||||
auto method = proxy->createMethodCall("org.freedesktop.login1.Manager", "GetSession");
|
||||
method << "auto";
|
||||
auto reply = proxy->callMethod(method);
|
||||
|
||||
sdbus::ObjectPath path;
|
||||
reply >> path;
|
||||
|
||||
Debug::log(LOG, "Using dbus path {}", path.c_str());
|
||||
|
||||
m_sDBUSState.connection->addMatch("type='signal',path='" + path + "',interface='org.freedesktop.login1.Session'", handleDbusLogin, sdbus::floating_slot_t{});
|
||||
m_sDBUSState.connection->addMatch("type='signal',path='/org/freedesktop/login1',interface='org.freedesktop.login1.Manager'", handleDbusSleep, sdbus::floating_slot_t{});
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <memory>
|
||||
#include <vector>
|
||||
#include <wayland-client.h>
|
||||
#include <sdbus-c++/sdbus-c++.h>
|
||||
|
||||
#include "ext-idle-notify-v1-protocol.h"
|
||||
|
||||
|
@ -25,6 +26,11 @@ class CHypridle {
|
|||
void onResumed(SIdleListener*);
|
||||
|
||||
private:
|
||||
void setupDBUS();
|
||||
void enterEventLoop();
|
||||
|
||||
bool m_bTerminate = false;
|
||||
|
||||
struct {
|
||||
wl_display* display = nullptr;
|
||||
wl_registry* registry = nullptr;
|
||||
|
@ -36,6 +42,19 @@ class CHypridle {
|
|||
|
||||
std::vector<SIdleListener> listeners;
|
||||
} m_sWaylandIdleState;
|
||||
|
||||
struct {
|
||||
std::unique_ptr<sdbus::IConnection> connection;
|
||||
sdbus::Slot login1match;
|
||||
} m_sDBUSState;
|
||||
|
||||
struct {
|
||||
std::condition_variable loopSignal;
|
||||
std::mutex loopMutex;
|
||||
std::atomic<bool> shouldProcess = false;
|
||||
std::mutex loopRequestMutex;
|
||||
std::mutex eventLock;
|
||||
} m_sEventLoopInternals;
|
||||
};
|
||||
|
||||
inline std::unique_ptr<CHypridle> g_pHypridle;
|
||||
inline std::unique_ptr<CHypridle> g_pHypridle;
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
#include "VarList.hpp"
|
||||
#include <ranges>
|
||||
#include <algorithm>
|
||||
#include <string_view>
|
||||
|
||||
static std::string removeBeginEndSpacesTabs(std::string str) {
|
||||
if (str.empty())
|
||||
return str;
|
||||
|
||||
int countBefore = 0;
|
||||
while (str[countBefore] == ' ' || str[countBefore] == '\t') {
|
||||
countBefore++;
|
||||
}
|
||||
|
||||
int countAfter = 0;
|
||||
while ((int)str.length() - countAfter - 1 >= 0 && (str[str.length() - countAfter - 1] == ' ' || str[str.length() - 1 - countAfter] == '\t')) {
|
||||
countAfter++;
|
||||
}
|
||||
|
||||
str = str.substr(countBefore, str.length() - countBefore - countAfter);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
CVarList::CVarList(const std::string& in, const size_t lastArgNo, const char delim, const bool removeEmpty) {
|
||||
if (in.empty())
|
||||
m_vArgs.emplace_back("");
|
||||
|
||||
std::string args{in};
|
||||
size_t idx = 0;
|
||||
size_t pos = 0;
|
||||
std::ranges::replace_if(
|
||||
args, [&](const char& c) { return delim == 's' ? std::isspace(c) : c == delim; }, 0);
|
||||
|
||||
for (const auto& s : args | std::views::split(0)) {
|
||||
if (removeEmpty && s.empty())
|
||||
continue;
|
||||
if (++idx == lastArgNo) {
|
||||
m_vArgs.emplace_back(removeBeginEndSpacesTabs(in.substr(pos)));
|
||||
break;
|
||||
}
|
||||
pos += s.size() + 1;
|
||||
m_vArgs.emplace_back(removeBeginEndSpacesTabs(std::string_view{s}.data()));
|
||||
}
|
||||
}
|
||||
|
||||
std::string CVarList::join(const std::string& joiner, size_t from, size_t to) const {
|
||||
size_t last = to == 0 ? size() : to;
|
||||
|
||||
std::string rolling;
|
||||
for (size_t i = from; i < last; ++i) {
|
||||
rolling += m_vArgs[i] + (i + 1 < last ? joiner : "");
|
||||
}
|
||||
|
||||
return rolling;
|
||||
}
|
|
@ -1,63 +0,0 @@
|
|||
#pragma once
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class CVarList {
|
||||
public:
|
||||
/** Split string into arg list
|
||||
@param lastArgNo stop splitting after argv reaches maximum size, last arg will contain rest of unsplit args
|
||||
@param delim if delimiter is 's', use std::isspace
|
||||
@param removeEmpty remove empty args from argv
|
||||
*/
|
||||
CVarList(const std::string& in, const size_t maxSize = 0, const char delim = ',', const bool removeEmpty = false);
|
||||
|
||||
~CVarList() = default;
|
||||
|
||||
size_t size() const {
|
||||
return m_vArgs.size();
|
||||
}
|
||||
|
||||
std::string join(const std::string& joiner, size_t from = 0, size_t to = 0) const;
|
||||
|
||||
void map(std::function<void(std::string&)> func) {
|
||||
for (auto& s : m_vArgs)
|
||||
func(s);
|
||||
}
|
||||
|
||||
void append(const std::string arg) {
|
||||
m_vArgs.emplace_back(arg);
|
||||
}
|
||||
|
||||
std::string operator[](const size_t& idx) const {
|
||||
if (idx >= m_vArgs.size())
|
||||
return "";
|
||||
return m_vArgs[idx];
|
||||
}
|
||||
|
||||
// for range-based loops
|
||||
std::vector<std::string>::iterator begin() {
|
||||
return m_vArgs.begin();
|
||||
}
|
||||
std::vector<std::string>::const_iterator begin() const {
|
||||
return m_vArgs.begin();
|
||||
}
|
||||
std::vector<std::string>::iterator end() {
|
||||
return m_vArgs.end();
|
||||
}
|
||||
std::vector<std::string>::const_iterator end() const {
|
||||
return m_vArgs.end();
|
||||
}
|
||||
|
||||
bool contains(const std::string& el) {
|
||||
for (auto& a : m_vArgs) {
|
||||
if (a == el)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> m_vArgs;
|
||||
};
|
12
src/main.cpp
12
src/main.cpp
|
@ -4,6 +4,16 @@
|
|||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string arg = argv[i];
|
||||
|
||||
if (arg == "--verbose" || arg == "-v")
|
||||
Debug::verbose = true;
|
||||
|
||||
else if (arg == "--quiet" || arg == "-q")
|
||||
Debug::quiet = true;
|
||||
}
|
||||
|
||||
g_pConfigManager = std::make_unique<CConfigManager>();
|
||||
g_pConfigManager->init();
|
||||
|
||||
|
@ -11,4 +21,4 @@ int main(int argc, char** argv, char** envp) {
|
|||
g_pHypridle->run();
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue