commit b6eef56607a7a6538063c9b2b09d879d5c9bea8c Author: Zakk Date: Fri Apr 5 21:16:13 2024 -0400 Hi diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9d22eb4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +*.so diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9e5476b --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +PLUGIN_NAME=hypreasymotion +INSTALL_LOCATION=${HOME}/.local/share/hyprload/plugins/bin +SOURCE_FILES=$(wildcard ./*.cpp) + +all: $(PLUGIN_NAME).so + +install: all + mkdir -p ${INSTALL_LOCATION} + cp $(PLUGIN_NAME).so ${INSTALL_LOCATION} + +$(PLUGIN_NAME).so: $(SOURCE_FILES) + g++ -shared -Wall -fPIC --no-gnu-unique $(SOURCE_FILES) -g -DWLR_USE_UNSTABLE `pkg-config --cflags pixman-1 libdrm hyprland pangocairo` -std=c++23 -o $(PLUGIN_NAME).so + +clean: + rm -f ./$(PLUGIN_NAME).so diff --git a/README.md b/README.md new file mode 100644 index 0000000..d08ab07 --- /dev/null +++ b/README.md @@ -0,0 +1,68 @@ +# hyprEasymotion +Plugin to enable 'easymotion' navigation. Inspired by Xmonad easymotion (which is in turn inspired by vim-easymotion) + +# Configuration +Easymotion is basically a single dispatcher that brings up window labels and then allows you to execute a user-defined command when one of those labels is typed. + +`bind=SUPER,z,action:hyprctl dispatch focuswindow address:{}` + +This bind will bring up easymotion with SUPER-z. Once you select a window the window +will focus. If you want to change the command, the selected window's address is substituted where "{}" occurs. + + +You can configure the appearance of the labels. Defaults are as follows: + +``` +plugin { + easymotion { + #font size of the text + textsize=15 + + #color of the text, takes standard hyprland color format + textcolor=rgba(ffffffff) + + #background color of the label box. alpha is respected + bgcolor=rgba(000000ff) + + #font to use for the label. This is passed directly to the pango font description + textfont=Sans + + #padding around the text (inside the label box) size in pixels, adjusted for + #monitor scaling. This is the same format as hyprland's gaps_in/gaps_out + textpadding=0 + + #size of the border around the label box + bordersize=0 + + #color of the border. takes the same format as hyprland's border (so it can be a gradient) + bordercolor=rgba(ffffffff) + + #rounded corners? Same as hyprland's 'decoration:rounding' config + rounding=0 + + #which keys to use for labeling windows + motionkeys=abcdefghijklmnopqrstuvwxyz1234567890 + } +} +``` + +Every one of these variables is also settable via the dispatcher, so you can create multiple dispatchers that look different based on function. + +`bind=SUPER,z,bgcolor=rgba(ff0000ff);bordersize=5;action:hyprctl dispatch closewindow address:{}` + +### IMPORTANT +The easymotion arguments are separated by a semicolon, not a comma. (gap/padding format uses commas :/) + +# Installing + +## Hyprpm, Hyprland's official plugin manager (recommended) +1. Run `hyprpm add https://github.com/zakk4223/hyprland-easymotion` and wait for hyprpm to build the plugin. +2. Run `hyprpm enable hyprEasymotion` + + +# TODO +- [ ] Blur? +- [ ] Allow multi-letter labels? +- [ ] Fixed/static label box sizing +- [ ] Location of label in window (edges etc) +- [ ] Auto label placement that tries to avoid being occluded diff --git a/easymotionDeco.cpp b/easymotionDeco.cpp new file mode 100644 index 0000000..2b9ae91 --- /dev/null +++ b/easymotionDeco.cpp @@ -0,0 +1,198 @@ +#include "easymotionDeco.hpp" + +#include +#include +#include +#include +#include + +#include "globals.hpp" + +CHyprEasyLabel::CHyprEasyLabel(CWindow* pWindow, SMotionActionDesc *actionDesc) : IHyprWindowDecoration(pWindow) { + m_pWindow = pWindow; + + const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); + PMONITOR->scheduledRecalc = true; + std::string windowAddr = std::format("0x{:x}", (uintptr_t)pWindow); + m_szActionCmd = std::vformat(actionDesc->commandString, std::make_format_args(windowAddr)); + m_iTextSize = actionDesc->textSize; + m_cTextColor = actionDesc->textColor; + m_cBackgroundColor = actionDesc->backgroundColor; + m_szTextFont = actionDesc->textFont; + m_iPaddingTop = actionDesc->boxPadding.top; + m_iPaddingBottom = actionDesc->boxPadding.bottom; + m_iPaddingRight = actionDesc->boxPadding.right; + m_iPaddingLeft = actionDesc->boxPadding.left; + m_iRounding = actionDesc->rounding; + m_iBorderSize = actionDesc->borderSize; + m_cBorderGradient = actionDesc->borderColor; +} + +CHyprEasyLabel::~CHyprEasyLabel() { + damageEntire(); + std::erase(g_pGlobalState->motionLabels, this); +} + +SDecorationPositioningInfo CHyprEasyLabel::getPositioningInfo() { + SDecorationPositioningInfo info; + info.policy = DECORATION_POSITION_ABSOLUTE; + return info; +} + +void CHyprEasyLabel::onPositioningReply(const SDecorationPositioningReply& reply) { + return; +} + +std::string CHyprEasyLabel::getDisplayName() { + return "EasyMotion"; +} + +void CHyprEasyLabel::renderMotionString(Vector2D& bufferSize, const float scale) { + int textSize = m_iTextSize; + const auto scaledSize = textSize * scale; + const auto textColor = CColor(m_cTextColor); + + const auto LAYOUTSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 0, 0); + const auto LAYOUTCAIRO = cairo_create(LAYOUTSURFACE); + cairo_surface_destroy(LAYOUTSURFACE); + + PangoLayout *layout = pango_cairo_create_layout(LAYOUTCAIRO); + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + pango_layout_set_text(layout, m_szLabel.c_str(), -1); + PangoFontDescription *fontDesc = pango_font_description_from_string(m_szTextFont.c_str()); + + pango_font_description_set_size(fontDesc, scaledSize * PANGO_SCALE); + pango_layout_set_font_description(layout, fontDesc); + pango_font_description_free(fontDesc); + + PangoRectangle ink_rect; + PangoRectangle logical_rect; + pango_layout_get_pixel_extents(layout, &ink_rect, &logical_rect); + Debug::log(LOG, "INK {} {} {} {} LOGICAL {} {} {} {}", ink_rect.x, ink_rect.y, ink_rect.width, ink_rect.height, logical_rect.x, logical_rect.y, logical_rect.width, logical_rect.height); + + layoutWidth = ink_rect.width+m_iPaddingLeft+m_iPaddingRight; + layoutHeight = ink_rect.height+m_iPaddingTop+m_iPaddingBottom; + + bufferSize.x = layoutWidth*scale; + bufferSize.y = layoutHeight*scale; + + const auto CAIROSURFACE = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, bufferSize.x, bufferSize.y); + const auto CAIRO = cairo_create(CAIROSURFACE); + + cairo_save(CAIRO); + cairo_set_operator(CAIRO, CAIRO_OPERATOR_CLEAR); + cairo_paint(CAIRO); + cairo_restore(CAIRO); + cairo_move_to(CAIRO, -ink_rect.x+m_iPaddingLeft, -ink_rect.y+m_iPaddingTop); + cairo_set_source_rgba(CAIRO, textColor.r, textColor.g, textColor.b, textColor.a); + pango_cairo_show_layout(CAIRO, layout); + g_object_unref(layout); + cairo_surface_flush(CAIROSURFACE); + const auto DATA = cairo_image_surface_get_data(CAIROSURFACE); + m_tTextTex.allocate(); + glBindTexture(GL_TEXTURE_2D, m_tTextTex.m_iTexID); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + +#ifndef GLES2 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_R, GL_BLUE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_B, GL_RED); +#endif + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferSize.x, bufferSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, DATA); + + // delete cairo + cairo_destroy(LAYOUTCAIRO); + cairo_destroy(CAIRO); + cairo_surface_destroy(CAIROSURFACE); +} + + +void CHyprEasyLabel::draw(CMonitor* pMonitor, float a) { + if (!g_pCompositor->windowValidMapped(m_pWindow)) + return; + + if (!m_pWindow->m_sSpecialRenderData.decorate) + return; + + const auto PWORKSPACE = m_pWindow->m_pWorkspace; + const auto WORKSPACEOFFSET = PWORKSPACE && !m_pWindow->m_bPinned ? PWORKSPACE->m_vRenderOffset.value() : Vector2D(); + + const auto ROUNDING = m_iRounding; + + const auto scaledRounding = ROUNDING > 0 ? ROUNDING * pMonitor->scale : 0; + + const auto DECOBOX = assignedBoxGlobal(); + + const auto BARBUF = DECOBOX.size() * pMonitor->scale; + + //CBox motionBox = {DECOBOX.x - pMonitor->vecPosition.x, DECOBOX.y - pMonitor->vecPosition.y, DECOBOX.w, + + auto TEXTBUF = DECOBOX.size() * pMonitor->scale; + + + if (m_tTextTex.m_iTexID == 0) { + renderMotionString(TEXTBUF, pMonitor->scale); + } + CBox motionBox = {DECOBOX.x, DECOBOX.y, layoutWidth, layoutHeight}; + motionBox.translate(pMonitor->vecPosition*-1).scale(pMonitor->scale).round(); + + if (motionBox.w < 1 || motionBox.h < 1) + return; + g_pHyprOpenGL->scissor(&motionBox); + g_pHyprOpenGL->renderRect(&motionBox, m_cBackgroundColor, scaledRounding); + + if (m_iBorderSize) { + CBox borderBox = {DECOBOX.x, DECOBOX.y, layoutWidth, layoutHeight}; + borderBox.translate(pMonitor->vecPosition*-1).scale(pMonitor->scale).round(); + if (borderBox.w >= 1 && borderBox.h >= 1) { + g_pHyprOpenGL->renderBorder(&borderBox, m_cBorderGradient, scaledRounding, m_iBorderSize * pMonitor->scale, a); + } + } + + + g_pHyprOpenGL->renderTexture(m_tTextTex, &motionBox, a); + + g_pHyprOpenGL->scissor((CBox*)nullptr); +} + +eDecorationType CHyprEasyLabel::getDecorationType() { + return DECORATION_CUSTOM; +} + +void CHyprEasyLabel::updateWindow(CWindow* pWindow) { + damageEntire(); +} + +void CHyprEasyLabel::damageEntire() { + ; // ignored +} + +eDecorationLayer CHyprEasyLabel::getDecorationLayer() { + return DECORATION_LAYER_OVERLAY; +} + +uint64_t CHyprEasyLabel::getDecorationFlags() { + return DECORATION_PART_OF_MAIN_WINDOW; +} + +CBox CHyprEasyLabel::assignedBoxGlobal() { + + double boxHeight, boxWidth; + double boxSize; + boxHeight = m_pWindow->m_vRealSize.value().y * 0.10; + boxWidth = m_pWindow->m_vRealSize.value().x * 0.10; + boxSize = std::min(boxHeight, boxWidth); + double boxX = m_pWindow->m_vRealPosition.value().x + (m_pWindow->m_vRealSize.value().x-boxSize)/2; + double boxY = m_pWindow->m_vRealPosition.value().y + (m_pWindow->m_vRealSize.value().y-boxSize)/2; + CBox box = {boxX, boxY, boxSize, boxSize}; + + const auto PWORKSPACE = m_pWindow->m_pWorkspace; + const auto WORKSPACEOFFSET = PWORKSPACE && !m_pWindow->m_bPinned ? PWORKSPACE->m_vRenderOffset.value() : Vector2D(); + + return box.translate(WORKSPACEOFFSET); +} + +CWindow* CHyprEasyLabel::getOwner() { + return m_pWindow; +} diff --git a/easymotionDeco.hpp b/easymotionDeco.hpp new file mode 100644 index 0000000..6ea8487 --- /dev/null +++ b/easymotionDeco.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include +#include + +#include +#include +#include "globals.hpp" + +class CHyprEasyLabel : public IHyprWindowDecoration { + public: + CHyprEasyLabel(CWindow*, SMotionActionDesc *actionDesc); + virtual ~CHyprEasyLabel(); + + virtual SDecorationPositioningInfo getPositioningInfo(); + + virtual void onPositioningReply(const SDecorationPositioningReply& reply); + + virtual void draw(CMonitor*, float a); + + virtual eDecorationType getDecorationType(); + + virtual void updateWindow(CWindow*); + + virtual void damageEntire(); + + virtual eDecorationLayer getDecorationLayer(); + + virtual uint64_t getDecorationFlags(); + + bool m_bButtonsDirty = true; + + virtual std::string getDisplayName(); + + CWindow* getOwner(); + + std::string m_szLabel; + std::string m_szActionCmd; + std::string m_szTextFont; + int m_iTextSize; + int m_iPaddingTop; + int m_iPaddingBottom; + int m_iPaddingLeft; + int m_iPaddingRight; + int m_iRounding; + + CColor m_cTextColor; + CColor m_cBackgroundColor; + int m_iBorderSize; + CGradientValueData m_cBorderGradient; + + + + + + private: + int layoutWidth; + int layoutHeight; + SWindowDecorationExtents m_seExtents; + + CWindow* m_pWindow = nullptr; + + CTexture m_tTextTex; + + bool m_bWindowSizeChanged = false; + + void renderText(CTexture& out, const std::string& text, const CColor& color, const Vector2D& bufferSize, const float scale, const int fontSize); + CBox assignedBoxGlobal(); + void renderMotionString(Vector2D& bufferSize, const float scale); + + // for dynamic updates + int m_iLastHeight = 0; +}; diff --git a/globals.hpp b/globals.hpp new file mode 100644 index 0000000..a95f9af --- /dev/null +++ b/globals.hpp @@ -0,0 +1,28 @@ +#pragma once +#include + +#include + +inline HANDLE PHANDLE = nullptr; + +class CHyprEasyLabel; + +struct SGlobalState { + std::vector motionLabels; +}; + +struct SMotionActionDesc { + int textSize = 15; + CColor textColor = CColor(0,0,0,1); + CColor backgroundColor = CColor(1,1,1,1); + std::string textFont = "Sans"; + std::string commandString = ""; + CCssGapData boxPadding = CCssGapData(); + int borderSize = 0; + CGradientValueData borderColor = CGradientValueData(); + int rounding = 0; + std::string motionKeys = "abcdefghijklmnopqrstuvwxyz1234567890"; +}; + +inline std::unique_ptr g_pGlobalState; + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..f921649 --- /dev/null +++ b/main.cpp @@ -0,0 +1,239 @@ +#include +#include + +#include +#include +#include +#include +#include + +#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"); + +} + +void easymotionActionDispatch(std::string args) +{ + for (auto &ml : g_pGlobalState->motionLabels) { + if (ml->m_szLabel == args) { + 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}); + + //catchall + g_pKeybindManager->addKeybind(SKeybind{"", 0, 1, 0, "", "", 0, "__easymotionsubmap__", 0, 0, 0, 0, 0, 0}); + +} + + +void addLabelToWindow(CWindow *window, SMotionActionDesc *actionDesc, std::string &label) +{ + std::unique_ptr motionlabel = std::make_unique(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(int 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]); + 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, ' '); + 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) { + std::string lstr = actionDesc.motionKeys.substr(key_idx++, 1); + addLabelToWindow(w.get(), &actionDesc, lstr); + } + } + } + + HyprlandAPI::invokeHyprctlCommand("dispatch", "submap __easymotionsubmap__"); +} + +bool oneasymotionKeypress(void *self, std::any data) { + + if (g_pGlobalState->motionLabels.empty()) return false; + + auto map = std::any_cast>(data); + std::any kany = map["keyboard"]; + wlr_keyboard_key_event *ev = std::any_cast(map["event"]); + SKeyboard *keyboard = std::any_cast(kany); + + const auto KEYCODE = ev->keycode + 8; + + const xkb_keysym_t KEYSYM = xkb_state_key_get_one_sym(keyboard->xkbTranslationState, 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(); + HyprlandAPI::addDispatcher(PHANDLE, "easymotion", easymotionDispatch); + HyprlandAPI::addDispatcher(PHANDLE, "easymotionaction", easymotionActionDispatch); + HyprlandAPI::addDispatcher(PHANDLE, "easymotionexit", easymotionExitDispatch); + HyprlandAPI::registerCallbackDynamic(PHANDLE, "keyPress", [&](void *self, SCallbackInfo &info, std::any data) { + info.cancelled = oneasymotionKeypress(self, data); + }); + 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() { +}