layerrules: add dynamically registered rules for plugins (#13331)
* layerrules: add dynamically registered rules for plugins * be gone * layerrules: add layer tests with waybar * fix: use kitty layers instead of waybar
This commit is contained in:
parent
42f0a6005b
commit
e0c5710059
8 changed files with 230 additions and 14 deletions
|
|
@ -10,7 +10,9 @@
|
|||
#include <src/managers/PointerManager.hpp>
|
||||
#include <src/managers/input/trackpad/TrackpadGestures.hpp>
|
||||
#include <src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp>
|
||||
#include <src/desktop/rule/layerRule/LayerRuleEffectContainer.hpp>
|
||||
#include <src/desktop/rule/windowRule/WindowRuleApplicator.hpp>
|
||||
#include <src/desktop/view/LayerSurface.hpp>
|
||||
#include <src/Compositor.hpp>
|
||||
#include <src/desktop/state/FocusState.hpp>
|
||||
#include <src/layout/LayoutManager.hpp>
|
||||
|
|
@ -270,32 +272,67 @@ static SDispatchResult keybind(std::string in) {
|
|||
return {};
|
||||
}
|
||||
|
||||
static Desktop::Rule::CWindowRuleEffectContainer::storageType ruleIDX = 0;
|
||||
static Desktop::Rule::CWindowRuleEffectContainer::storageType windowRuleIDX = 0;
|
||||
|
||||
//
|
||||
static SDispatchResult addRule(std::string in) {
|
||||
ruleIDX = Desktop::Rule::windowEffects()->registerEffect("plugin_rule");
|
||||
static SDispatchResult addWindowRule(std::string in) {
|
||||
windowRuleIDX = Desktop::Rule::windowEffects()->registerEffect("plugin_rule");
|
||||
|
||||
if (Desktop::Rule::windowEffects()->registerEffect("plugin_rule") != ruleIDX)
|
||||
if (Desktop::Rule::windowEffects()->registerEffect("plugin_rule") != windowRuleIDX)
|
||||
return {.success = false, .error = "re-registering returned a different id?"};
|
||||
return {};
|
||||
}
|
||||
|
||||
static SDispatchResult checkRule(std::string in) {
|
||||
static SDispatchResult checkWindowRule(std::string in) {
|
||||
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||
|
||||
if (!PLASTWINDOW)
|
||||
return {.success = false, .error = "No window"};
|
||||
|
||||
if (!PLASTWINDOW->m_ruleApplicator->m_otherProps.props.contains(ruleIDX))
|
||||
if (!PLASTWINDOW->m_ruleApplicator->m_otherProps.props.contains(windowRuleIDX))
|
||||
return {.success = false, .error = "No rule"};
|
||||
|
||||
if (PLASTWINDOW->m_ruleApplicator->m_otherProps.props[ruleIDX]->effect != "effect")
|
||||
if (PLASTWINDOW->m_ruleApplicator->m_otherProps.props[windowRuleIDX]->effect != "effect")
|
||||
return {.success = false, .error = "Effect isn't \"effect\""};
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Desktop::Rule::CLayerRuleEffectContainer::storageType layerRuleIDX = 0;
|
||||
|
||||
static SDispatchResult addLayerRule(std::string in) {
|
||||
layerRuleIDX = Desktop::Rule::layerEffects()->registerEffect("plugin_rule");
|
||||
|
||||
if (Desktop::Rule::layerEffects()->registerEffect("plugin_rule") != layerRuleIDX)
|
||||
return {.success = false, .error = "re-registering returned a different id?"};
|
||||
return {};
|
||||
}
|
||||
|
||||
static SDispatchResult checkLayerRule(std::string in) {
|
||||
if (g_pCompositor->m_layers.size() != 3)
|
||||
return {.success = false, .error = "Layers under test not here"};
|
||||
|
||||
for (const auto& layer : g_pCompositor->m_layers) {
|
||||
if (layer->m_namespace == "rule-layer") {
|
||||
|
||||
if (!layer->m_ruleApplicator->m_otherProps.props.contains(layerRuleIDX))
|
||||
return {.success = false, .error = "No rule"};
|
||||
|
||||
if (layer->m_ruleApplicator->m_otherProps.props[layerRuleIDX]->effect != "effect")
|
||||
return {.success = false, .error = "Effect isn't \"effect\""};
|
||||
|
||||
} else if (layer->m_namespace == "norule-layer") {
|
||||
|
||||
if (layer->m_ruleApplicator->m_otherProps.props.contains(layerRuleIDX))
|
||||
return {.success = false, .error = "Rule even though it shouldn't"};
|
||||
|
||||
} else
|
||||
return {.success = false, .error = "Unrecognized layer"};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static SDispatchResult floatingFocusOnFullscreen(std::string in) {
|
||||
const auto PLASTWINDOW = Desktop::focusState()->window();
|
||||
|
||||
|
|
@ -325,8 +362,10 @@ APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
|||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:scroll", ::scroll);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:click", ::click);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:keybind", ::keybind);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_rule", ::addRule);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_rule", ::checkRule);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_window_rule", ::addWindowRule);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_window_rule", ::checkWindowRule);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_layer_rule", ::addLayerRule);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_layer_rule", ::checkLayerRule);
|
||||
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:floating_focus_on_fullscreen", ::floatingFocusOnFullscreen);
|
||||
|
||||
// init mouse
|
||||
|
|
|
|||
53
hyprtester/src/tests/main/layer.cpp
Normal file
53
hyprtester/src/tests/main/layer.cpp
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#include "../../Log.hpp"
|
||||
#include "../shared.hpp"
|
||||
#include "tests.hpp"
|
||||
#include "../../shared.hpp"
|
||||
#include "../../hyprctlCompat.hpp"
|
||||
#include <hyprutils/os/Process.hpp>
|
||||
#include <hyprutils/memory/WeakPtr.hpp>
|
||||
|
||||
static int ret = 0;
|
||||
|
||||
using namespace Hyprutils::OS;
|
||||
using namespace Hyprutils::Memory;
|
||||
|
||||
static bool spawnLayer(const std::string& namespace_) {
|
||||
NLog::log("{}Spawning kitty layer {}", Colors::YELLOW, namespace_);
|
||||
if (!Tests::spawnLayerKitty(namespace_)) {
|
||||
NLog::log("{}Error: {} layer did not spawn", Colors::RED, namespace_);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool test() {
|
||||
NLog::log("{}Testing plugin layerrules", Colors::GREEN);
|
||||
|
||||
if (!spawnLayer("rule-layer"))
|
||||
return false;
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:add_layer_rule"));
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
OK(getFromSocket("/keyword layerrule match:namespace rule-layer, plugin_rule effect"));
|
||||
|
||||
if (!spawnLayer("rule-layer"))
|
||||
return false;
|
||||
|
||||
if (!spawnLayer("norule-layer"))
|
||||
return false;
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:check_layer_rule"));
|
||||
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
NLog::log("{}Killing all layers", Colors::YELLOW);
|
||||
Tests::killAllLayers();
|
||||
|
||||
NLog::log("{}Expecting 0 layers", Colors::YELLOW);
|
||||
EXPECT(Tests::layerCount(), 0);
|
||||
|
||||
return !ret;
|
||||
}
|
||||
|
||||
REGISTER_TEST_FN(test)
|
||||
|
|
@ -1086,7 +1086,7 @@ static bool test() {
|
|||
OK(getFromSocket("/reload"));
|
||||
Tests::killAllWindows();
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:add_rule"));
|
||||
OK(getFromSocket("/dispatch plugin:test:add_window_rule"));
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
OK(getFromSocket("/keyword windowrule match:class plugin_kitty, plugin_rule effect"));
|
||||
|
|
@ -1094,12 +1094,12 @@ static bool test() {
|
|||
if (!spawnKitty("plugin_kitty"))
|
||||
return false;
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:check_rule"));
|
||||
OK(getFromSocket("/dispatch plugin:test:check_window_rule"));
|
||||
|
||||
OK(getFromSocket("/reload"));
|
||||
Tests::killAllWindows();
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:add_rule"));
|
||||
OK(getFromSocket("/dispatch plugin:test:add_window_rule"));
|
||||
OK(getFromSocket("/reload"));
|
||||
|
||||
OK(getFromSocket("/keyword windowrule[test-plugin-rule]:match:class plugin_kitty"));
|
||||
|
|
@ -1108,7 +1108,7 @@ static bool test() {
|
|||
if (!spawnKitty("plugin_kitty"))
|
||||
return false;
|
||||
|
||||
OK(getFromSocket("/dispatch plugin:test:check_rule"));
|
||||
OK(getFromSocket("/dispatch plugin:test:check_window_rule"));
|
||||
|
||||
OK(getFromSocket("/reload"));
|
||||
Tests::killAllWindows();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
#include <cerrno>
|
||||
#include <thread>
|
||||
#include <print>
|
||||
#include <fstream>
|
||||
#include "../shared.hpp"
|
||||
#include "../hyprctlCompat.hpp"
|
||||
|
||||
|
|
@ -39,6 +40,38 @@ CUniquePointer<CProcess> Tests::spawnKitty(const std::string& class_, const std:
|
|||
return kitty;
|
||||
}
|
||||
|
||||
CUniquePointer<CProcess> Tests::spawnLayerKitty(const std::string& namespace_, const std::vector<std::string> args) {
|
||||
std::vector<std::string> programArgs = args;
|
||||
if (!namespace_.empty()) {
|
||||
programArgs.insert(programArgs.begin(), "--class");
|
||||
programArgs.insert(programArgs.begin() + 1, namespace_);
|
||||
}
|
||||
|
||||
programArgs.insert(programArgs.begin(), "+kitten");
|
||||
programArgs.insert(programArgs.begin() + 1, "panel");
|
||||
|
||||
CUniquePointer<CProcess> kitty = makeUnique<CProcess>("kitty", programArgs);
|
||||
kitty->addEnv("WAYLAND_DISPLAY", WLDISPLAY);
|
||||
kitty->runAsync();
|
||||
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
|
||||
// wait while the layer spawns
|
||||
int counter = 0;
|
||||
while (processAlive(kitty->pid()) && countOccurrences(getFromSocket("/layers"), std::format("pid: {}", kitty->pid())) == 0) {
|
||||
counter++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
if (counter > 50)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!processAlive(kitty->pid()))
|
||||
return nullptr;
|
||||
|
||||
return kitty;
|
||||
}
|
||||
|
||||
bool Tests::processAlive(pid_t pid) {
|
||||
errno = 0;
|
||||
int ret = kill(pid, 0);
|
||||
|
|
@ -96,6 +129,38 @@ void Tests::waitUntilWindowsN(int n) {
|
|||
}
|
||||
}
|
||||
|
||||
int Tests::layerCount() {
|
||||
return countOccurrences(getFromSocket("/layers"), "namespace: ");
|
||||
}
|
||||
|
||||
bool Tests::killAllLayers() {
|
||||
auto str = getFromSocket("/layers");
|
||||
auto pos = str.find("pid: ");
|
||||
while (pos != std::string::npos) {
|
||||
auto pid = stoi(str.substr(pos + 5, str.find('\n', pos)));
|
||||
kill(pid, 15);
|
||||
|
||||
// we need to wait for a bit because for some reason otherwise we'll end up
|
||||
// with layers with pid -1 if they are all removed at the same time
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
pos = str.find("pid: ", pos + 5);
|
||||
}
|
||||
|
||||
int counter = 0;
|
||||
while (Tests::layerCount() != 0) {
|
||||
counter++;
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
if (counter > 50) {
|
||||
std::println("{}Timed out waiting for layers to close", Colors::RED);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Tests::execAndGet(const std::string& cmd) {
|
||||
CProcess proc("/bin/sh", {"-c", cmd});
|
||||
|
||||
|
|
@ -105,3 +170,14 @@ std::string Tests::execAndGet(const std::string& cmd) {
|
|||
|
||||
return proc.stdOut();
|
||||
}
|
||||
|
||||
bool Tests::writeFile(const std::string& name, const std::string& contents) {
|
||||
std::ofstream of(name, std::ios::trunc);
|
||||
if (!of.good())
|
||||
return false;
|
||||
|
||||
of << contents;
|
||||
of.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,10 +9,14 @@
|
|||
//NOLINTNEXTLINE
|
||||
namespace Tests {
|
||||
Hyprutils::Memory::CUniquePointer<Hyprutils::OS::CProcess> spawnKitty(const std::string& class_ = "", const std::vector<std::string> args = {});
|
||||
Hyprutils::Memory::CUniquePointer<Hyprutils::OS::CProcess> spawnLayerKitty(const std::string& namespace_ = "", const std::vector<std::string> args = {});
|
||||
bool processAlive(pid_t pid);
|
||||
int windowCount();
|
||||
int countOccurrences(const std::string& in, const std::string& what);
|
||||
bool killAllWindows();
|
||||
void waitUntilWindowsN(int n);
|
||||
int layerCount();
|
||||
bool killAllLayers();
|
||||
std::string execAndGet(const std::string& cmd);
|
||||
bool writeFile(const std::string& name, const std::string& contents);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
#include "../../view/LayerSurface.hpp"
|
||||
#include "../../types/OverridableVar.hpp"
|
||||
#include "../../../helpers/MiscFunctions.hpp"
|
||||
#include "../../../event/EventBus.hpp"
|
||||
|
||||
using namespace Desktop;
|
||||
using namespace Desktop::Rule;
|
||||
|
|
@ -32,11 +33,38 @@ void CLayerRuleApplicator::resetProps(std::underlying_type_t<eRuleProperty> prop
|
|||
UNSET(aboveLock)
|
||||
UNSET(ignoreAlpha)
|
||||
UNSET(animationStyle)
|
||||
|
||||
#undef UNSET
|
||||
|
||||
if (prio == Types::PRIORITY_WINDOW_RULE)
|
||||
std::erase_if(m_otherProps.props, [props](const auto& el) { return !el.second || el.second->propMask & props; });
|
||||
}
|
||||
|
||||
void CLayerRuleApplicator::applyDynamicRule(const SP<CLayerRule>& rule) {
|
||||
for (const auto& [key, effect] : rule->effects()) {
|
||||
switch (key) {
|
||||
default: {
|
||||
if (key <= LAYER_RULE_EFFECT_LAST_STATIC) {
|
||||
Log::logger->log(Log::TRACE, "CLayerRuleApplicator::applyDynamicRule: Skipping effect {}, not dynamic", sc<std::underlying_type_t<eLayerRuleEffect>>(key));
|
||||
break;
|
||||
}
|
||||
|
||||
// custom type, add to our vec
|
||||
if (!m_otherProps.props.contains(key)) {
|
||||
m_otherProps.props.emplace(key,
|
||||
makeUnique<SCustomPropContainer>(SCustomPropContainer{
|
||||
.idx = key,
|
||||
.propMask = rule->getPropertiesMask(),
|
||||
.effect = effect,
|
||||
}));
|
||||
} else {
|
||||
auto& e = m_otherProps.props[key];
|
||||
e->propMask |= rule->getPropertiesMask();
|
||||
e->effect = effect;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LAYER_RULE_EFFECT_NONE: {
|
||||
Log::logger->log(Log::ERR, "CLayerRuleApplicator::applyDynamicRule: BUG THIS: LAYER_RULE_EFFECT_NONE??");
|
||||
break;
|
||||
|
|
@ -125,4 +153,7 @@ void CLayerRuleApplicator::propertiesChanged(std::underlying_type_t<eRulePropert
|
|||
|
||||
applyDynamicRule(wr);
|
||||
}
|
||||
|
||||
// for plugins
|
||||
Event::bus()->m_events.layer.updateRules.emit(m_ls.lock());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "LayerRuleEffectContainer.hpp"
|
||||
#include "../../DesktopTypes.hpp"
|
||||
#include "../Rule.hpp"
|
||||
#include "../../types/OverridableVar.hpp"
|
||||
|
|
@ -21,6 +22,17 @@ namespace Desktop::Rule {
|
|||
void propertiesChanged(std::underlying_type_t<eRuleProperty> props);
|
||||
void resetProps(std::underlying_type_t<eRuleProperty> props, Types::eOverridePriority prio = Types::PRIORITY_WINDOW_RULE);
|
||||
|
||||
struct SCustomPropContainer {
|
||||
CLayerRuleEffectContainer::storageType idx = LAYER_RULE_EFFECT_NONE;
|
||||
std::underlying_type_t<eRuleProperty> propMask = RULE_PROP_NONE;
|
||||
std::string effect;
|
||||
};
|
||||
|
||||
// This struct holds props that were dynamically registered. Plugins may read this.
|
||||
struct {
|
||||
std::unordered_map<CLayerRuleEffectContainer::storageType, UP<SCustomPropContainer>> props;
|
||||
} m_otherProps;
|
||||
|
||||
#define COMMA ,
|
||||
#define DEFINE_PROP(type, name, def) \
|
||||
private: \
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ namespace Event {
|
|||
struct {
|
||||
Event<PHLLS> opened;
|
||||
Event<PHLLS> closed;
|
||||
Event<PHLLS> updateRules;
|
||||
} layer;
|
||||
|
||||
struct {
|
||||
|
|
@ -140,4 +141,4 @@ namespace Event {
|
|||
};
|
||||
|
||||
UP<CEventBus>& bus();
|
||||
};
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue