Files
hyprEasymotion/main.cpp
2024-11-20 15:43:58 +08:00

250 lines
9.3 KiB
C++

#include <hyprland/src/plugins/PluginAPI.hpp>
#include <unistd.h>
#include <any>
#include <ranges>
#include <hyprland/src/Compositor.hpp>
#include <hyprland/src/desktop/Window.hpp>
#include <hyprland/src/config/ConfigManager.hpp>
#include "easymotionDeco.hpp"
#include "globals.hpp"
// Do NOT change this function.
APICALL EXPORT std::string PLUGIN_API_VERSION() {
return HYPRLAND_API_VERSION;
}
void easymotionExitDispatch(std::string args)
{
for (auto &ml : g_pGlobalState->motionLabels | std::ranges::views::reverse) {
ml->getOwner()->removeWindowDeco(ml);
}
HyprlandAPI::invokeHyprctlCommand("dispatch", "submap reset");
g_pEventManager->postEvent(SHyprIPCEvent{"easymotionexit", ""});
}
void easymotionActionDispatch(std::string args)
{
for (auto &ml : g_pGlobalState->motionLabels) {
if (ml->m_szLabel == args) {
g_pEventManager->postEvent(SHyprIPCEvent{"easymotionselect", std::format("{},{}", ml->m_szWindowAddress, ml->m_szLabel)});
g_pKeybindManager->m_mDispatchers["exec"](ml->m_szActionCmd);
easymotionExitDispatch("");
break;
}
}
}
void addEasyMotionKeybinds()
{
g_pKeybindManager->addKeybind(SKeybind{"escape", {}, 0, 0, 0, {}, "easymotionexit", "", 0, "__easymotionsubmap__", "", 0, 0, 0, 0, 0, 0, 0, 0});
//catchall
g_pKeybindManager->addKeybind(SKeybind{"", {}, 0, 1, 0, {}, "", "", 0, "__easymotionsubmap__", "", 0, 0, 0, 0, 0, 0, 0, 0});
}
void addLabelToWindow(PHLWINDOW window, SMotionActionDesc *actionDesc, std::string &label)
{
std::unique_ptr<CHyprEasyLabel> motionlabel = std::make_unique<CHyprEasyLabel>(window, actionDesc);
motionlabel.get()->m_szLabel = label;
g_pGlobalState->motionLabels.push_back(motionlabel.get());
HyprlandAPI::addWindowDecoration(PHANDLE, window, std::move(motionlabel));
}
static bool parseBorderGradient(std::string VALUE, CGradientValueData *DATA) {
std::string V = VALUE;
CVarList varlist(V, 0, ' ');
DATA->m_vColors.clear();
std::string parseError = "";
for (auto& var : varlist) {
if (var.find("deg") != std::string::npos) {
// last arg
try {
DATA->m_fAngle = std::stoi(var.substr(0, var.find("deg"))) * (PI / 180.0); // radians
} catch (...) {
Debug::log(WARN, "Error parsing gradient {}", V);
return false;
}
break;
}
if (DATA->m_vColors.size() >= 10) {
Debug::log(WARN, "Error parsing gradient {}: max colors is 10.", V);
return false;
break;
}
try {
DATA->m_vColors.push_back(CColor(configStringToInt(var)));
} catch (std::exception& e) {
Debug::log(WARN, "Error parsing gradient {}", V);
}
}
if (DATA->m_vColors.size() == 0) {
Debug::log(WARN, "Error parsing gradient {}", V);
DATA->m_vColors.push_back(0); // transparent
}
return true;
}
void easymotionDispatch(std::string args)
{
static auto *const TEXTSIZE = (Hyprlang::INT* const*)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:textsize")->getDataStaticPtr();
static auto *const TEXTCOLOR = (Hyprlang::INT* const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:textcolor")->getDataStaticPtr();
static auto *const BGCOLOR = (Hyprlang::INT* const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:bgcolor")->getDataStaticPtr();
static auto *const TEXTFONT = (Hyprlang::STRING const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:textfont")->getDataStaticPtr();
static auto *const TEXTPADDING = (Hyprlang::STRING const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:textpadding")->getDataStaticPtr();
static auto *const BORDERSIZE = (Hyprlang::INT* const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:bordersize")->getDataStaticPtr();
static auto *const BORDERCOLOR = (Hyprlang::STRING const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:bordercolor")->getDataStaticPtr();
static auto *const ROUNDING = (Hyprlang::INT* const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:rounding")->getDataStaticPtr();
static auto *const MOTIONKEYS = (Hyprlang::STRING const *)HyprlandAPI::getConfigValue(PHANDLE, "plugin:easymotion:motionkeys")->getDataStaticPtr();
CVarList emargs(args, 0, ',');
SMotionActionDesc actionDesc;
actionDesc.textSize = **TEXTSIZE;
actionDesc.textColor = **TEXTCOLOR;
actionDesc.backgroundColor = **BGCOLOR;
actionDesc.textFont = *TEXTFONT;
CVarList cpadding = CVarList(*TEXTPADDING);
actionDesc.boxPadding.parseGapData(cpadding);
actionDesc.rounding = **ROUNDING;
actionDesc.borderSize = **BORDERSIZE;
if(!parseBorderGradient(*BORDERCOLOR, &actionDesc.borderColor)) {
actionDesc.borderColor.m_vColors.clear();
actionDesc.borderColor.m_fAngle = 0;
}
actionDesc.motionKeys = *MOTIONKEYS;
for(size_t i = 0; i < emargs.size(); i++)
{
CVarList kv(emargs[i], 2, ':');
if (kv[0] == "action") {
actionDesc.commandString = kv[1];
} else if (kv[0] == "textsize") {
actionDesc.textSize = configStringToInt(kv[1]);
} else if (kv[0] == "textcolor") {
actionDesc.textColor = CColor(configStringToInt(kv[1]));
} else if (kv[0] == "bgcolor") {
actionDesc.backgroundColor = CColor(configStringToInt(kv[1]));
} else if (kv[0] == "textfont") {
actionDesc.textFont = kv[1];
} else if (kv[0] == "textpadding") {
CVarList padVars = CVarList(kv[1], 0, 's');
actionDesc.boxPadding.parseGapData(padVars);
} else if (kv[0] == "rounding") {
actionDesc.rounding = configStringToInt(kv[1]);
} else if (kv[0] == "bordersize") {
actionDesc.borderSize = configStringToInt(kv[1]);
} else if (kv[0] == "bordercolor") {
CVarList varlist(kv[1], 0, 's');
actionDesc.borderColor.m_vColors.clear();
actionDesc.borderColor.m_fAngle = 0;
if(!parseBorderGradient(kv[1], &actionDesc.borderColor)) {
actionDesc.borderColor.m_vColors.clear();
actionDesc.borderColor.m_fAngle = 0;
}
} else if (kv[0] == "motionkeys") {
actionDesc.motionKeys = kv[1];
}
}
int key_idx = 0;
for (auto &w : g_pCompositor->m_vWindows) {
for (auto &m : g_pCompositor->m_vMonitors) {
if (w->m_pWorkspace == m->activeWorkspace || m->activeSpecialWorkspace == w->m_pWorkspace) {
if (w->isHidden() || !w->m_bIsMapped || w->m_bFadingOut)
continue;
if (w->m_pWorkspace->m_bHasFullscreenWindow &&
g_pCompositor->getFullscreenWindowOnWorkspace(w->workspaceID()) != w) {
continue;
}
std::string lstr = actionDesc.motionKeys.substr(key_idx++, 1);
addLabelToWindow(w, &actionDesc, lstr);
}
}
}
if (!g_pGlobalState->motionLabels.empty())
HyprlandAPI::invokeHyprctlCommand("dispatch", "submap __easymotionsubmap__");
}
bool oneasymotionKeypress(void *self, std::any data) {
if (g_pGlobalState->motionLabels.empty()) return false;
auto map = std::any_cast<std::unordered_map<std::string, std::any>>(data);
std::any kany = map["keyboard"];
IKeyboard::SKeyEvent ev = std::any_cast<IKeyboard::SKeyEvent>(map["event"]);
SP<IKeyboard>keyboard = std::any_cast<SP<IKeyboard>>(kany);
const auto KEYCODE = ev.keycode + 8;
const xkb_keysym_t KEYSYM = xkb_state_key_get_one_sym(keyboard->xkbState, KEYCODE);
if (ev.state != WL_KEYBOARD_KEY_STATE_PRESSED) return false;
xkb_keysym_t actionKeysym = 0;
for (auto &ml : g_pGlobalState->motionLabels) {
if (ml->m_szLabel != "") {
actionKeysym = xkb_keysym_from_name(ml->m_szLabel.c_str(), XKB_KEYSYM_NO_FLAGS);
if (actionKeysym && (actionKeysym == KEYSYM)) {
easymotionActionDispatch(ml->m_szLabel);
return true;
}
}
}
return false;
}
APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
PHANDLE = handle;
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:textsize", Hyprlang::INT{15});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:textcolor", Hyprlang::INT{configStringToInt("rgba(ffffffff)")});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:bgcolor", Hyprlang::INT{configStringToInt("rgba(000000ff)")});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:textfont", Hyprlang::STRING{"Sans"});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:textpadding", Hyprlang::STRING{"0"});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:bordersize", Hyprlang::INT{0});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:bordercolor", Hyprlang::STRING{"rgba(ffffffff)"});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:rounding", Hyprlang::INT{0});
HyprlandAPI::addConfigValue(PHANDLE, "plugin:easymotion:motionkeys", Hyprlang::STRING{"abcdefghijklmnopqrstuvwxyz1234567890"});
g_pGlobalState = std::make_unique<SGlobalState>();
HyprlandAPI::addDispatcher(PHANDLE, "easymotion", easymotionDispatch);
HyprlandAPI::addDispatcher(PHANDLE, "easymotionaction", easymotionActionDispatch);
HyprlandAPI::addDispatcher(PHANDLE, "easymotionexit", easymotionExitDispatch);
static auto KPHOOK = HyprlandAPI::registerCallbackDynamic(PHANDLE, "keyPress", [&](void *self, SCallbackInfo &info, std::any data) {
info.cancelled = oneasymotionKeypress(self, data);
});
static auto CRHOOK = HyprlandAPI::registerCallbackDynamic(PHANDLE, "configReloaded", [&](void *self, SCallbackInfo&, std::any data) {addEasyMotionKeybinds();});
HyprlandAPI::reloadConfig();
return {"hypreasymotion", "Easymotion navigation", "Zakk", "1.0"};
}
APICALL EXPORT void PLUGIN_EXIT() {
}