aboutsummaryrefslogtreecommitdiffhomepage
path: root/example
diff options
context:
space:
mode:
authorVaxry <[email protected]>2023-02-27 12:32:38 +0000
committerGitHub <[email protected]>2023-02-27 12:32:38 +0000
commit8b81f41e52b55835aaaa7e4962af23a00188cbdc (patch)
treebca65923843ea931c0e0222f29d4e2901cfa8637 /example
parent74a10f26a469de54968e584525f6507928f615f0 (diff)
downloadHyprland-8b81f41e52b55835aaaa7e4962af23a00188cbdc.tar.gz
Hyprland-8b81f41e52b55835aaaa7e4962af23a00188cbdc.zip
Plugin System (#1590)
--------- Co-authored-by: Mihai Fufezan <[email protected]>
Diffstat (limited to 'example')
-rw-r--r--example/examplePlugin/Makefile8
-rw-r--r--example/examplePlugin/customDecoration.cpp74
-rw-r--r--example/examplePlugin/customDecoration.hpp29
-rw-r--r--example/examplePlugin/customLayout.cpp80
-rw-r--r--example/examplePlugin/customLayout.hpp32
-rw-r--r--example/examplePlugin/globals.hpp5
-rw-r--r--example/examplePlugin/main.cpp92
7 files changed, 320 insertions, 0 deletions
diff --git a/example/examplePlugin/Makefile b/example/examplePlugin/Makefile
new file mode 100644
index 00000000..3ccc2930
--- /dev/null
+++ b/example/examplePlugin/Makefile
@@ -0,0 +1,8 @@
+# compile with HYPRLAND_HEADERS=<path_to_hl> make all
+# make sure that the path above is to the root hl repo directory, NOT src/
+# and that you have ran `make protocols` in the hl dir.
+
+all:
+ g++ -shared -fPIC --no-gnu-unique main.cpp customLayout.cpp customDecoration.cpp -o examplePlugin.so -g -I "/usr/include/pixman-1" -I "/usr/include/libdrm" -I "${HYPRLAND_HEADERS}" -std=c++23
+clean:
+ rm ./examplePlugin.so
diff --git a/example/examplePlugin/customDecoration.cpp b/example/examplePlugin/customDecoration.cpp
new file mode 100644
index 00000000..d336d4e8
--- /dev/null
+++ b/example/examplePlugin/customDecoration.cpp
@@ -0,0 +1,74 @@
+#include "customDecoration.hpp"
+#include "../../src/Window.hpp"
+#include "../../src/Compositor.hpp"
+#include "globals.hpp"
+
+CCustomDecoration::CCustomDecoration(CWindow* pWindow) {
+ m_pWindow = pWindow;
+ m_vLastWindowPos = pWindow->m_vRealPosition.vec();
+ m_vLastWindowSize = pWindow->m_vRealSize.vec();
+}
+
+CCustomDecoration::~CCustomDecoration() {
+ damageEntire();
+}
+
+SWindowDecorationExtents CCustomDecoration::getWindowDecorationExtents() {
+ return m_seExtents;
+}
+
+void CCustomDecoration::draw(CMonitor* pMonitor, float a, const Vector2D& offset) {
+ if (!g_pCompositor->windowValidMapped(m_pWindow))
+ return;
+
+ if (!m_pWindow->m_sSpecialRenderData.decorate)
+ return;
+
+ static auto* const PCOLOR = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:example:border_color")->intValue;
+ static auto* const PROUNDING = &HyprlandAPI::getConfigValue(PHANDLE, "decoration:rounding")->intValue;
+ static auto* const PBORDERSIZE = &HyprlandAPI::getConfigValue(PHANDLE, "general:border_size")->intValue;
+
+ const auto ROUNDING = !m_pWindow->m_sSpecialRenderData.rounding ?
+ 0 :
+ (m_pWindow->m_sAdditionalConfigData.rounding.toUnderlying() == -1 ? *PROUNDING : m_pWindow->m_sAdditionalConfigData.rounding.toUnderlying());
+
+ // draw the border
+ wlr_box fullBox = {(int)(m_vLastWindowPos.x - *PBORDERSIZE), (int)(m_vLastWindowPos.y - *PBORDERSIZE), (int)(m_vLastWindowSize.x + 2.0 * *PBORDERSIZE),
+ (int)(m_vLastWindowSize.y + 2.0 * *PBORDERSIZE)};
+
+ fullBox.x -= pMonitor->vecPosition.x;
+ fullBox.y -= pMonitor->vecPosition.y;
+
+ m_seExtents = {{m_vLastWindowPos.x - fullBox.x - pMonitor->vecPosition.x + 2, m_vLastWindowPos.y - fullBox.y - pMonitor->vecPosition.y + 2},
+ {fullBox.x + fullBox.width + pMonitor->vecPosition.x - m_vLastWindowPos.x - m_vLastWindowSize.x + 2,
+ fullBox.y + fullBox.height + pMonitor->vecPosition.y - m_vLastWindowPos.y - m_vLastWindowSize.y + 2}};
+
+ fullBox.x += offset.x;
+ fullBox.y += offset.y;
+
+ if (fullBox.width < 1 || fullBox.height < 1)
+ return; // don't draw invisible shadows
+
+ g_pHyprOpenGL->scissor((wlr_box*)nullptr);
+
+ scaleBox(&fullBox, pMonitor->scale);
+ g_pHyprOpenGL->renderBorder(&fullBox, CColor(*PCOLOR), *PROUNDING * pMonitor->scale + *PBORDERSIZE * 2, a);
+}
+
+eDecorationType CCustomDecoration::getDecorationType() {
+ return DECORATION_CUSTOM;
+}
+
+void CCustomDecoration::updateWindow(CWindow* pWindow) {
+
+ m_vLastWindowPos = pWindow->m_vRealPosition.vec();
+ m_vLastWindowSize = pWindow->m_vRealSize.vec();
+
+ damageEntire();
+}
+
+void CCustomDecoration::damageEntire() {
+ wlr_box dm = {(int)(m_vLastWindowPos.x - m_seExtents.topLeft.x), (int)(m_vLastWindowPos.y - m_seExtents.topLeft.y),
+ (int)(m_vLastWindowSize.x + m_seExtents.topLeft.x + m_seExtents.bottomRight.x), (int)m_seExtents.topLeft.y};
+ g_pHyprRenderer->damageBox(&dm);
+} \ No newline at end of file
diff --git a/example/examplePlugin/customDecoration.hpp b/example/examplePlugin/customDecoration.hpp
new file mode 100644
index 00000000..a08b48bf
--- /dev/null
+++ b/example/examplePlugin/customDecoration.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#define WLR_USE_UNSTABLE
+
+#include "../../src/render/decorations/IHyprWindowDecoration.hpp"
+
+class CCustomDecoration : public IHyprWindowDecoration {
+ public:
+ CCustomDecoration(CWindow*);
+ virtual ~CCustomDecoration();
+
+ virtual SWindowDecorationExtents getWindowDecorationExtents();
+
+ virtual void draw(CMonitor*, float a, const Vector2D& offset);
+
+ virtual eDecorationType getDecorationType();
+
+ virtual void updateWindow(CWindow*);
+
+ virtual void damageEntire();
+
+ private:
+ SWindowDecorationExtents m_seExtents;
+
+ CWindow* m_pWindow = nullptr;
+
+ Vector2D m_vLastWindowPos;
+ Vector2D m_vLastWindowSize;
+}; \ No newline at end of file
diff --git a/example/examplePlugin/customLayout.cpp b/example/examplePlugin/customLayout.cpp
new file mode 100644
index 00000000..fcc51394
--- /dev/null
+++ b/example/examplePlugin/customLayout.cpp
@@ -0,0 +1,80 @@
+#include "customLayout.hpp"
+#include "../../src/Compositor.hpp"
+#include "globals.hpp"
+
+void CHyprCustomLayout::onWindowCreatedTiling(CWindow* pWindow) {
+ const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID);
+ const auto SIZE = PMONITOR->vecSize;
+
+ // these are used for focus and move calculations, and are *required* to touch for moving focus to work properly.
+ pWindow->m_vPosition = Vector2D{(SIZE.x / 2.0) * (m_vWindowData.size() % 2), (SIZE.y / 2.0) * (int)(m_vWindowData.size() > 1)};
+ pWindow->m_vSize = SIZE / 2.0;
+
+ // this is the actual pos and size of the window (where it's rendered)
+ pWindow->m_vRealPosition = pWindow->m_vPosition + Vector2D{10, 10};
+ pWindow->m_vRealSize = pWindow->m_vSize - Vector2D{20, 20};
+
+ const auto PDATA = &m_vWindowData.emplace_back();
+ PDATA->pWindow = pWindow;
+}
+
+void CHyprCustomLayout::onWindowRemovedTiling(CWindow* pWindow) {
+ std::erase_if(m_vWindowData, [&](const auto& other) { return other.pWindow == pWindow; });
+}
+
+bool CHyprCustomLayout::isWindowTiled(CWindow* pWindow) {
+ return std::find_if(m_vWindowData.begin(), m_vWindowData.end(), [&](const auto& other) { return other.pWindow == pWindow; }) != m_vWindowData.end();
+}
+
+void CHyprCustomLayout::recalculateMonitor(const int& eIdleInhibitMode) {
+ ; // empty
+}
+
+void CHyprCustomLayout::recalculateWindow(CWindow* pWindow) {
+ ; // empty
+}
+
+void CHyprCustomLayout::resizeActiveWindow(const Vector2D& delta, CWindow* pWindow) {
+ ; // empty
+}
+
+void CHyprCustomLayout::fullscreenRequestForWindow(CWindow* pWindow, eFullscreenMode mode, bool on) {
+ ; // empty
+}
+
+std::any CHyprCustomLayout::layoutMessage(SLayoutMessageHeader header, std::string content) {
+ return "";
+}
+
+SWindowRenderLayoutHints CHyprCustomLayout::requestRenderHints(CWindow* pWindow) {
+ return {};
+}
+
+void CHyprCustomLayout::switchWindows(CWindow* pWindowA, CWindow* pWindowB) {
+ ; // empty
+}
+
+void CHyprCustomLayout::alterSplitRatio(CWindow* pWindow, float delta, bool exact) {
+ ; // empty
+}
+
+std::string CHyprCustomLayout::getLayoutName() {
+ return "custom";
+}
+
+void CHyprCustomLayout::replaceWindowDataWith(CWindow* from, CWindow* to) {
+ ; // empty
+}
+
+void CHyprCustomLayout::onEnable() {
+ for (auto& w : g_pCompositor->m_vWindows) {
+ if (w->isHidden() || !w->m_bIsMapped || w->m_bFadingOut || w->m_bIsFloating)
+ continue;
+
+ onWindowCreatedTiling(w.get());
+ }
+}
+
+void CHyprCustomLayout::onDisable() {
+ m_vWindowData.clear();
+} \ No newline at end of file
diff --git a/example/examplePlugin/customLayout.hpp b/example/examplePlugin/customLayout.hpp
new file mode 100644
index 00000000..974882c2
--- /dev/null
+++ b/example/examplePlugin/customLayout.hpp
@@ -0,0 +1,32 @@
+#pragma once
+
+#define WLR_USE_UNSTABLE
+
+#include "../../src/layout/IHyprLayout.hpp"
+
+struct SWindowData {
+ CWindow* pWindow = nullptr;
+};
+
+class CHyprCustomLayout : public IHyprLayout {
+ public:
+ virtual void onWindowCreatedTiling(CWindow*);
+ virtual void onWindowRemovedTiling(CWindow*);
+ virtual bool isWindowTiled(CWindow*);
+ virtual void recalculateMonitor(const int&);
+ virtual void recalculateWindow(CWindow*);
+ virtual void resizeActiveWindow(const Vector2D&, CWindow* pWindow = nullptr);
+ virtual void fullscreenRequestForWindow(CWindow*, eFullscreenMode, bool);
+ virtual std::any layoutMessage(SLayoutMessageHeader, std::string);
+ virtual SWindowRenderLayoutHints requestRenderHints(CWindow*);
+ virtual void switchWindows(CWindow*, CWindow*);
+ virtual void alterSplitRatio(CWindow*, float, bool);
+ virtual std::string getLayoutName();
+ virtual void replaceWindowDataWith(CWindow* from, CWindow* to);
+
+ virtual void onEnable();
+ virtual void onDisable();
+
+ private:
+ std::vector<SWindowData> m_vWindowData;
+}; \ No newline at end of file
diff --git a/example/examplePlugin/globals.hpp b/example/examplePlugin/globals.hpp
new file mode 100644
index 00000000..37e8363b
--- /dev/null
+++ b/example/examplePlugin/globals.hpp
@@ -0,0 +1,5 @@
+#pragma once
+
+#include <src/plugins/PluginAPI.hpp>
+
+inline HANDLE PHANDLE = nullptr; \ No newline at end of file
diff --git a/example/examplePlugin/main.cpp b/example/examplePlugin/main.cpp
new file mode 100644
index 00000000..49364f59
--- /dev/null
+++ b/example/examplePlugin/main.cpp
@@ -0,0 +1,92 @@
+#define WLR_USE_UNSTABLE
+
+#include "globals.hpp"
+
+#include <src/Window.hpp>
+#include <src/Compositor.hpp>
+#include "customLayout.hpp"
+#include "customDecoration.hpp"
+
+#include <unistd.h>
+#include <thread>
+
+// Methods
+inline std::unique_ptr<CHyprCustomLayout> g_pCustomLayout;
+inline CFunctionHook* g_pFocusHook = nullptr;
+inline CFunctionHook* g_pMotionHook = nullptr;
+inline CFunctionHook* g_pMouseDownHook = nullptr;
+typedef void (*origFocusWindow)(void*, CWindow*, wlr_surface*);
+typedef void (*origMotion)(wlr_seat*, uint32_t, double, double);
+typedef void (*origMouseDownNormal)(void*, wlr_pointer_button_event*);
+
+// Do NOT change this function.
+APICALL EXPORT std::string PLUGIN_API_VERSION() {
+ return HYPRLAND_API_VERSION;
+}
+
+static void onActiveWindowChange(void* self, std::any data) {
+ try {
+ auto* const PWINDOW = std::any_cast<CWindow*>(data);
+
+ HyprlandAPI::addNotification(PHANDLE, "[ExamplePlugin] Active window: " + (PWINDOW ? PWINDOW->m_szTitle : "None"), CColor{0.f, 0.5f, 1.f, 1.f}, 5000);
+ } catch (std::bad_any_cast& e) { HyprlandAPI::addNotification(PHANDLE, "[ExamplePlugin] Active window: None", CColor{0.f, 0.5f, 1.f, 1.f}, 5000); }
+}
+
+static void onNewWindow(void* self, std::any data) {
+ auto* const PWINDOW = std::any_cast<CWindow*>(data);
+
+ HyprlandAPI::addWindowDecoration(PHANDLE, PWINDOW, new CCustomDecoration(PWINDOW));
+}
+
+void hkFocusWindow(void* thisptr, CWindow* pWindow, wlr_surface* pSurface) {
+ // HyprlandAPI::addNotification(PHANDLE, getFormat("FocusWindow with %lx %lx", pWindow, pSurface), CColor{0.f, 1.f, 1.f, 1.f}, 5000);
+ (*(origFocusWindow)g_pFocusHook->m_pOriginal)(thisptr, pWindow, pSurface);
+}
+
+void hkNotifyMotion(wlr_seat* wlr_seat, uint32_t time_msec, double sx, double sy) {
+ // HyprlandAPI::addNotification(PHANDLE, getFormat("NotifyMotion with %lf %lf", sx, sy), CColor{0.f, 1.f, 1.f, 1.f}, 5000);
+ (*(origMotion)g_pMotionHook->m_pOriginal)(wlr_seat, time_msec, sx, sy);
+}
+
+void hkProcessMouseDownNormal(void* thisptr, wlr_pointer_button_event* e) {
+ // HyprlandAPI::addNotification(PHANDLE, "Mouse down normal!", CColor{0.8f, 0.2f, 0.5f, 1.0f}, 5000);
+ (*(origMouseDownNormal)g_pMouseDownHook->m_pOriginal)(thisptr, e);
+}
+
+APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) {
+ PHANDLE = handle;
+
+ HyprlandAPI::addNotification(PHANDLE, "Hello World from an example plugin!", CColor{0.f, 1.f, 1.f, 1.f}, 5000);
+
+ HyprlandAPI::registerCallbackDynamic(PHANDLE, "activeWindow", [&](void* self, std::any data) { onActiveWindowChange(self, data); });
+ HyprlandAPI::registerCallbackDynamic(PHANDLE, "openWindow", [&](void* self, std::any data) { onNewWindow(self, data); });
+
+ g_pCustomLayout = std::make_unique<CHyprCustomLayout>();
+
+ HyprlandAPI::addLayout(PHANDLE, "custom", g_pCustomLayout.get());
+
+ HyprlandAPI::addConfigValue(PHANDLE, "plugin:example:border_color", SConfigValue{.intValue = configStringToInt("rgb(44ee44)")});
+
+ HyprlandAPI::addDispatcher(PHANDLE, "example", [](std::string arg) { HyprlandAPI::addNotification(PHANDLE, "Arg passed: " + arg, CColor{0.5f, 0.5f, 0.7f, 1.0f}, 5000); });
+
+ // Hook a public member
+ g_pFocusHook = HyprlandAPI::createFunctionHook(PHANDLE, (void*)&CCompositor::focusWindow, (void*)&hkFocusWindow);
+ // Hook a public non-member
+ g_pMotionHook = HyprlandAPI::createFunctionHook(PHANDLE, (void*)&wlr_seat_pointer_notify_motion, (void*)&hkNotifyMotion);
+ // Hook a private member (!WARNING: the signature may differ in clang. This one is for gcc ONLY.)
+ g_pMouseDownHook = HyprlandAPI::createFunctionHook(
+ PHANDLE, HyprlandAPI::getFunctionAddressFromSignature(PHANDLE, "_ZN13CInputManager22processMouseDownNormalEP24wlr_pointer_button_event"), (void*)&hkProcessMouseDownNormal);
+
+ // Enable our hooks
+ g_pFocusHook->hook();
+ g_pMotionHook->hook();
+ g_pMouseDownHook->hook();
+
+ HyprlandAPI::reloadConfig();
+
+ return {"ExamplePlugin", "An example plugin", "Vaxry", "1.0"};
+}
+
+APICALL EXPORT void PLUGIN_EXIT() {
+ HyprlandAPI::invokeHyprctlCommand("seterror", "disable");
+} \ No newline at end of file