Hyprland/hyprtester/plugin/src/main.cpp

306 lines
9.4 KiB
C++

#include <unistd.h>
#include <src/includes.hpp>
#include <sstream>
#include <any>
#define private public
#include <src/config/ConfigManager.hpp>
#include <src/config/ConfigDescriptions.hpp>
#include <src/layout/IHyprLayout.hpp>
#include <src/managers/LayoutManager.hpp>
#include <src/managers/input/InputManager.hpp>
#include <src/managers/PointerManager.hpp>
#include <src/managers/input/trackpad/TrackpadGestures.hpp>
#include <src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp>
#include <src/desktop/rule/windowRule/WindowRuleApplicator.hpp>
#include <src/Compositor.hpp>
#include <src/desktop/state/FocusState.hpp>
#undef private
#include <hyprutils/utils/ScopeGuard.hpp>
#include <hyprutils/string/VarList.hpp>
using namespace Hyprutils::Utils;
using namespace Hyprutils::String;
#include "globals.hpp"
// Do NOT change this function.
APICALL EXPORT std::string PLUGIN_API_VERSION() {
return HYPRLAND_API_VERSION;
}
static SDispatchResult test(std::string in) {
bool success = true;
std::string errors = "";
if (g_pConfigManager->m_configValueNumber != CONFIG_OPTIONS.size() + 1 /* autogenerated is special */) {
errors += "config value number mismatches descriptions size\n";
success = false;
}
return SDispatchResult{
.success = success,
.error = errors,
};
}
// Trigger a snap move event for the active window
static SDispatchResult snapMove(std::string in) {
const auto PLASTWINDOW = Desktop::focusState()->window();
if (!PLASTWINDOW->m_isFloating)
return {.success = false, .error = "Window must be floating"};
Vector2D pos = PLASTWINDOW->m_realPosition->goal();
Vector2D size = PLASTWINDOW->m_realSize->goal();
g_pLayoutManager->getCurrentLayout()->performSnap(pos, size, PLASTWINDOW, MBIND_MOVE, -1, size);
*PLASTWINDOW->m_realPosition = pos.round();
return {};
}
class CTestKeyboard : public IKeyboard {
public:
static SP<CTestKeyboard> create(bool isVirtual) {
auto keeb = SP<CTestKeyboard>(new CTestKeyboard());
keeb->m_self = keeb;
keeb->m_isVirtual = isVirtual;
keeb->m_shareStates = !isVirtual;
return keeb;
}
virtual bool isVirtual() {
return m_isVirtual;
}
virtual SP<Aquamarine::IKeyboard> aq() {
return nullptr;
}
void sendKey(uint32_t key, bool pressed) {
auto event = IKeyboard::SKeyEvent{
.timeMs = sc<uint32_t>(Time::millis(Time::steadyNow())),
.keycode = key,
.state = pressed ? WL_KEYBOARD_KEY_STATE_PRESSED : WL_KEYBOARD_KEY_STATE_RELEASED,
};
updatePressed(event.keycode, pressed);
m_keyboardEvents.key.emit(event);
}
void destroy() {
m_events.destroy.emit();
}
private:
bool m_isVirtual = false;
};
class CTestMouse : public IPointer {
public:
static SP<CTestMouse> create(bool isVirtual) {
auto maus = SP<CTestMouse>(new CTestMouse());
maus->m_self = maus;
maus->m_isVirtual = isVirtual;
maus->m_deviceName = "test-mouse";
maus->m_hlName = "test-mouse";
return maus;
}
virtual bool isVirtual() {
return m_isVirtual;
}
virtual SP<Aquamarine::IPointer> aq() {
return nullptr;
}
void destroy() {
m_events.destroy.emit();
}
private:
bool m_isVirtual = false;
};
SP<CTestMouse> g_mouse;
SP<CTestKeyboard> g_keyboard;
static SDispatchResult pressAlt(std::string in) {
g_pInputManager->m_lastMods = in == "1" ? HL_MODIFIER_ALT : 0;
return {.success = true};
}
static SDispatchResult simulateGesture(std::string in) {
CVarList data(in);
uint32_t fingers = 3;
try {
fingers = std::stoul(data[1]);
} catch (...) { return {.success = false}; }
if (data[0] == "down") {
g_pTrackpadGestures->gestureBegin(IPointer::SSwipeBeginEvent{});
g_pTrackpadGestures->gestureUpdate(IPointer::SSwipeUpdateEvent{.fingers = fingers, .delta = {0, 300}});
g_pTrackpadGestures->gestureEnd(IPointer::SSwipeEndEvent{});
} else if (data[0] == "up") {
g_pTrackpadGestures->gestureBegin(IPointer::SSwipeBeginEvent{});
g_pTrackpadGestures->gestureUpdate(IPointer::SSwipeUpdateEvent{.fingers = fingers, .delta = {0, -300}});
g_pTrackpadGestures->gestureEnd(IPointer::SSwipeEndEvent{});
} else if (data[0] == "left") {
g_pTrackpadGestures->gestureBegin(IPointer::SSwipeBeginEvent{});
g_pTrackpadGestures->gestureUpdate(IPointer::SSwipeUpdateEvent{.fingers = fingers, .delta = {-300, 0}});
g_pTrackpadGestures->gestureEnd(IPointer::SSwipeEndEvent{});
} else {
g_pTrackpadGestures->gestureBegin(IPointer::SSwipeBeginEvent{});
g_pTrackpadGestures->gestureUpdate(IPointer::SSwipeUpdateEvent{.fingers = fingers, .delta = {300, 0}});
g_pTrackpadGestures->gestureEnd(IPointer::SSwipeEndEvent{});
}
return {.success = true};
}
static SDispatchResult vkb(std::string in) {
auto tkb0 = CTestKeyboard::create(false);
auto tkb1 = CTestKeyboard::create(false);
auto vkb0 = CTestKeyboard::create(true);
g_pInputManager->newKeyboard(tkb0);
g_pInputManager->newKeyboard(tkb1);
g_pInputManager->newKeyboard(vkb0);
CScopeGuard x([&] {
tkb0->destroy();
tkb1->destroy();
vkb0->destroy();
});
const auto& PRESSED = g_pInputManager->getKeysFromAllKBs();
const uint32_t TESTKEY = 1;
tkb0->sendKey(TESTKEY, true);
if (!std::ranges::contains(PRESSED, TESTKEY)) {
return {
.success = false,
.error = "Expected pressed key not found",
};
}
tkb1->sendKey(TESTKEY, true);
tkb0->sendKey(TESTKEY, false);
if (!std::ranges::contains(PRESSED, TESTKEY)) {
return {
.success = false,
.error = "Expected pressed key not found (kb share state)",
};
}
vkb0->sendKey(TESTKEY, true);
tkb1->sendKey(TESTKEY, false);
if (std::ranges::contains(PRESSED, TESTKEY)) {
return {
.success = false,
.error = "Expected released key found in pressed (vkb no share state)",
};
}
return {};
}
static SDispatchResult scroll(std::string in) {
double by;
try {
by = std::stod(in);
} catch (...) { return SDispatchResult{.success = false, .error = "invalid input"}; }
Debug::log(LOG, "tester: scrolling by {}", by);
g_mouse->m_pointerEvents.axis.emit(IPointer::SAxisEvent{
.delta = by,
.deltaDiscrete = 120,
.mouse = true,
});
return {};
}
static SDispatchResult keybind(std::string in) {
CVarList2 data(std::move(in));
// 0 = release, 1 = press
bool press;
// See src/devices/IKeyboard.hpp : eKeyboardModifiers for modifier bitmasks
// 0 = none, eKeyboardModifiers is shifted to start at 1
uint32_t modifier;
// keycode
uint32_t key;
try {
press = std::stoul(std::string{data[0]}) == 1;
modifier = std::stoul(std::string{data[1]});
key = std::stoul(std::string{data[2]}) - 8; // xkb offset
} catch (...) { return {.success = false, .error = "invalid input"}; }
uint32_t modifierMask = 0;
if (modifier > 0)
modifierMask = 1 << (modifier - 1);
g_pInputManager->m_lastMods = modifierMask;
g_keyboard->sendKey(key, press);
return {};
}
static Desktop::Rule::CWindowRuleEffectContainer::storageType ruleIDX = 0;
//
static SDispatchResult addRule(std::string in) {
ruleIDX = Desktop::Rule::windowEffects()->registerEffect("plugin_rule");
if (Desktop::Rule::windowEffects()->registerEffect("plugin_rule") != ruleIDX)
return {.success = false, .error = "re-registering returned a different id?"};
return {};
}
static SDispatchResult checkRule(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))
return {.success = false, .error = "No rule"};
if (PLASTWINDOW->m_ruleApplicator->m_otherProps.props[ruleIDX]->effect != "effect")
return {.success = false, .error = "Effect isn't \"effect\""};
return {};
}
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
PHANDLE = handle;
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:test", ::test);
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:snapmove", ::snapMove);
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:vkb", ::vkb);
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:alt", ::pressAlt);
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:gesture", ::simulateGesture);
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:scroll", ::scroll);
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:keybind", ::keybind);
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_rule", ::addRule);
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_rule", ::checkRule);
// init mouse
g_mouse = CTestMouse::create(false);
g_pInputManager->newMouse(g_mouse);
// init keyboard
g_keyboard = CTestKeyboard::create(false);
g_pInputManager->newKeyboard(g_keyboard);
return {"hyprtestplugin", "hyprtestplugin", "Vaxry", "1.0"};
}
APICALL EXPORT void PLUGIN_EXIT() {
g_mouse->destroy();
g_mouse.reset();
g_keyboard->destroy();
g_keyboard.reset();
}