2025-06-26 19:43:39 +02:00
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <src/includes.hpp>
|
|
|
|
|
#include <sstream>
|
|
|
|
|
#include <any>
|
|
|
|
|
|
|
|
|
|
#define private public
|
|
|
|
|
#include <src/config/ConfigManager.hpp>
|
|
|
|
|
#include <src/config/ConfigDescriptions.hpp>
|
2025-07-15 16:24:40 -04:00
|
|
|
#include <src/layout/IHyprLayout.hpp>
|
|
|
|
|
#include <src/managers/LayoutManager.hpp>
|
2025-08-04 16:29:39 -03:00
|
|
|
#include <src/managers/input/InputManager.hpp>
|
2025-09-02 13:16:43 +02:00
|
|
|
#include <src/managers/PointerManager.hpp>
|
2025-08-28 11:20:29 +02:00
|
|
|
#include <src/managers/input/trackpad/TrackpadGestures.hpp>
|
2025-11-17 18:34:02 +00:00
|
|
|
#include <src/desktop/rule/windowRule/WindowRuleEffectContainer.hpp>
|
|
|
|
|
#include <src/desktop/rule/windowRule/WindowRuleApplicator.hpp>
|
2025-07-15 16:24:40 -04:00
|
|
|
#include <src/Compositor.hpp>
|
2025-11-26 07:44:26 +09:00
|
|
|
#include <src/desktop/state/FocusState.hpp>
|
2025-06-26 19:43:39 +02:00
|
|
|
#undef private
|
|
|
|
|
|
2025-08-04 16:29:39 -03:00
|
|
|
#include <hyprutils/utils/ScopeGuard.hpp>
|
2025-08-28 11:20:29 +02:00
|
|
|
#include <hyprutils/string/VarList.hpp>
|
2025-08-04 16:29:39 -03:00
|
|
|
using namespace Hyprutils::Utils;
|
2025-08-28 11:20:29 +02:00
|
|
|
using namespace Hyprutils::String;
|
2025-08-04 16:29:39 -03:00
|
|
|
|
2025-06-26 19:43:39 +02:00
|
|
|
#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,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2025-07-15 16:24:40 -04:00
|
|
|
// Trigger a snap move event for the active window
|
|
|
|
|
static SDispatchResult snapMove(std::string in) {
|
2025-11-26 07:44:26 +09:00
|
|
|
const auto PLASTWINDOW = Desktop::focusState()->window();
|
2025-07-15 16:24:40 -04:00
|
|
|
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 {};
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-04 16:29:39 -03:00
|
|
|
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{
|
2025-08-14 19:44:56 +05:00
|
|
|
.timeMs = sc<uint32_t>(Time::millis(Time::steadyNow())),
|
2025-08-04 16:29:39 -03:00
|
|
|
.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:
|
2025-09-02 13:16:43 +02:00
|
|
|
bool m_isVirtual = false;
|
2025-08-04 16:29:39 -03:00
|
|
|
};
|
|
|
|
|
|
2025-09-02 13:16:43 +02:00
|
|
|
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;
|
2025-10-06 12:10:56 -07:00
|
|
|
SP<CTestKeyboard> g_keyboard;
|
2025-09-02 13:16:43 +02:00
|
|
|
|
2025-08-28 11:20:29 +02:00
|
|
|
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};
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-04 16:29:39 -03:00
|
|
|
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 {};
|
|
|
|
|
}
|
|
|
|
|
|
2025-09-02 13:16:43 +02:00
|
|
|
static SDispatchResult scroll(std::string in) {
|
2025-10-11 02:40:18 +02:00
|
|
|
double by;
|
2025-09-02 13:16:43 +02:00
|
|
|
try {
|
2025-10-11 02:40:18 +02:00
|
|
|
by = std::stod(in);
|
2025-09-02 13:16:43 +02:00
|
|
|
} 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 {};
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-06 12:10:56 -07:00
|
|
|
static SDispatchResult keybind(std::string in) {
|
2025-12-05 14:16:22 +00:00
|
|
|
CVarList2 data(std::move(in));
|
2025-10-06 12:10:56 -07:00
|
|
|
// 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 {
|
2025-12-05 14:16:22 +00:00
|
|
|
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
|
2025-10-06 12:10:56 -07:00
|
|
|
} 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 {};
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-17 18:34:02 +00:00
|
|
|
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) {
|
2025-11-26 07:44:26 +09:00
|
|
|
const auto PLASTWINDOW = Desktop::focusState()->window();
|
|
|
|
|
|
|
|
|
|
if (!PLASTWINDOW)
|
2025-11-17 18:34:02 +00:00
|
|
|
return {.success = false, .error = "No window"};
|
|
|
|
|
|
2025-11-26 07:44:26 +09:00
|
|
|
if (!PLASTWINDOW->m_ruleApplicator->m_otherProps.props.contains(ruleIDX))
|
2025-11-17 18:34:02 +00:00
|
|
|
return {.success = false, .error = "No rule"};
|
|
|
|
|
|
2025-11-26 07:44:26 +09:00
|
|
|
if (PLASTWINDOW->m_ruleApplicator->m_otherProps.props[ruleIDX]->effect != "effect")
|
2025-11-17 18:34:02 +00:00
|
|
|
return {.success = false, .error = "Effect isn't \"effect\""};
|
|
|
|
|
|
|
|
|
|
return {};
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-26 19:43:39 +02:00
|
|
|
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
|
|
|
|
|
PHANDLE = handle;
|
|
|
|
|
|
|
|
|
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:test", ::test);
|
2025-07-15 16:24:40 -04:00
|
|
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:snapmove", ::snapMove);
|
2025-08-04 16:29:39 -03:00
|
|
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:vkb", ::vkb);
|
2025-08-28 11:20:29 +02:00
|
|
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:alt", ::pressAlt);
|
|
|
|
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:gesture", ::simulateGesture);
|
2025-09-02 13:16:43 +02:00
|
|
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:scroll", ::scroll);
|
2025-10-06 12:10:56 -07:00
|
|
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:keybind", ::keybind);
|
2025-11-17 18:34:02 +00:00
|
|
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:add_rule", ::addRule);
|
|
|
|
|
HyprlandAPI::addDispatcherV2(PHANDLE, "plugin:test:check_rule", ::checkRule);
|
2025-09-02 13:16:43 +02:00
|
|
|
|
|
|
|
|
// init mouse
|
|
|
|
|
g_mouse = CTestMouse::create(false);
|
|
|
|
|
g_pInputManager->newMouse(g_mouse);
|
2025-06-26 19:43:39 +02:00
|
|
|
|
2025-10-06 12:10:56 -07:00
|
|
|
// init keyboard
|
|
|
|
|
g_keyboard = CTestKeyboard::create(false);
|
|
|
|
|
g_pInputManager->newKeyboard(g_keyboard);
|
|
|
|
|
|
2025-06-26 19:43:39 +02:00
|
|
|
return {"hyprtestplugin", "hyprtestplugin", "Vaxry", "1.0"};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
APICALL EXPORT void PLUGIN_EXIT() {
|
2025-09-02 13:16:43 +02:00
|
|
|
g_mouse->destroy();
|
|
|
|
|
g_mouse.reset();
|
2025-10-06 12:10:56 -07:00
|
|
|
g_keyboard->destroy();
|
|
|
|
|
g_keyboard.reset();
|
2025-10-11 02:40:18 +02:00
|
|
|
}
|