diff --git a/README.md b/README.md index 5034910..89227cc 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ This repo houses official plugins for Hyprland. - csgo-vulkan-fix -> fixes custom resolutions on CS:GO with `-vulkan` - hyprbars -> adds title bars to windows - hyprtrails -> adds smooth trails behind moving windows + - hyprwinwrap -> clone of xwinwrap, allows you to put any app as a wallpaper # Nix diff --git a/hyprwinwrap/Makefile b/hyprwinwrap/Makefile new file mode 100644 index 0000000..20275ee --- /dev/null +++ b/hyprwinwrap/Makefile @@ -0,0 +1,4 @@ +all: + $(CXX) -shared -fPIC --no-gnu-unique main.cpp -o hyprwinwrap.so -g `pkg-config --cflags pixman-1 libdrm hyprland` -std=c++2b -O2 +clean: + rm ./hyprwinwrap.so diff --git a/hyprwinwrap/README.md b/hyprwinwrap/README.md new file mode 100644 index 0000000..db3bd18 --- /dev/null +++ b/hyprwinwrap/README.md @@ -0,0 +1,25 @@ +# hyprwinwrap + +Clone of xwinwrap for hyprland. + +Example config: +```ini +plugin { + hyprwinwrap { + # class is an EXACT match and NOT a regex! + class = kitty-bg + } +} + +``` + +Launch `kitty -c "~/.config/hypr/kittyconfigbg.conf" --class="kitty-bg" "/home/vaxry/.config/hypr/cava.sh"` + +Example script for cava: + +```sh +#!/bin/sh +sleep 1 && cava +``` + +_sleep required because resizing happens a few ms after open, which breaks cava_ \ No newline at end of file diff --git a/hyprwinwrap/default.nix b/hyprwinwrap/default.nix new file mode 100644 index 0000000..4a6779a --- /dev/null +++ b/hyprwinwrap/default.nix @@ -0,0 +1,21 @@ +{ + lib, + stdenv, + hyprland, +}: +stdenv.mkDerivation { + pname = "hyprwinwrap"; + version = "0.1"; + src = ./.; + + inherit (hyprland) nativeBuildInputs; + + buildInputs = [hyprland] ++ hyprland.buildInputs; + + meta = with lib; { + homepage = "https://github.com/hyprwm/hyprland-plugins"; + description = "Hyprland version of xwinwrap"; + license = licenses.bsd3; + platforms = platforms.linux; + }; +} diff --git a/hyprwinwrap/globals.hpp b/hyprwinwrap/globals.hpp new file mode 100644 index 0000000..2257475 --- /dev/null +++ b/hyprwinwrap/globals.hpp @@ -0,0 +1,5 @@ +#pragma once + +#include + +inline HANDLE PHANDLE = nullptr; \ No newline at end of file diff --git a/hyprwinwrap/main.cpp b/hyprwinwrap/main.cpp new file mode 100644 index 0000000..236d6bf --- /dev/null +++ b/hyprwinwrap/main.cpp @@ -0,0 +1,156 @@ +#define WLR_USE_UNSTABLE + +#include +#include + +#include +#include + +#define private public +#include +#include +#include +#include +#undef private + +#include "globals.hpp" + +// Do NOT change this function. +APICALL EXPORT std::string PLUGIN_API_VERSION() { + return HYPRLAND_API_VERSION; +} + +// hooks +inline CFunctionHook* damageHook = nullptr; +inline CFunctionHook* commitHook = nullptr; +typedef void (*origDamage)(void* owner, void* data); +typedef void (*origCommit)(void* owner, void* data); + +std::vector bgWindows; + +// +void onNewWindow(CWindow* pWindow) { + static auto* const PCLASS = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprwinwrap:class")->strValue; + + if (pWindow->m_szInitialClass != *PCLASS) + return; + + const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); + + if (!PMONITOR) + return; + + if (!pWindow->m_bIsFloating) + g_pLayoutManager->getCurrentLayout()->changeWindowFloatingMode(pWindow); + + pWindow->m_vRealSize.setValueAndWarp(PMONITOR->vecSize); + pWindow->m_vRealPosition.setValueAndWarp(PMONITOR->vecPosition); + pWindow->m_vSize = PMONITOR->vecSize; + pWindow->m_vPosition = PMONITOR->vecPosition; + pWindow->m_bPinned = true; + g_pXWaylandManager->setWindowSize(pWindow, pWindow->m_vRealSize.goalv(), true); + + bgWindows.push_back(pWindow); + + pWindow->setHidden(true); // so that hl doesn't render this + + g_pInputManager->refocus(); + + Debug::log(LOG, "[hyprwinwrap] new window moved to bg {}", pWindow); +} + +void onCloseWindow(CWindow* pWindow) { + std::erase(bgWindows, pWindow); + + Debug::log(LOG, "[hyprwinwrap] closed window {}", pWindow); +} + +void onRenderStage(eRenderStage stage) { + if (stage != RENDER_PRE_WINDOWS) + return; + + for (auto& bgw : bgWindows) { + if (bgw->m_iMonitorID != g_pHyprOpenGL->m_RenderData.pMonitor->ID) + continue; + + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + bgw->setHidden(false); + + g_pHyprRenderer->renderWindow(bgw, g_pHyprOpenGL->m_RenderData.pMonitor, &now, false, RENDER_PASS_ALL, false, true); + + bgw->setHidden(true); + } +} + +void onDamage(void* owner, void* data) { + const auto PWINDOW = (CWindow*)owner; + + if (std::find(bgWindows.begin(), bgWindows.end(), PWINDOW) == bgWindows.end()) { + ((origDamage)damageHook->m_pOriginal)(owner, data); + return; + } + + PWINDOW->setHidden(false); + + ((origDamage)damageHook->m_pOriginal)(owner, data); + + PWINDOW->setHidden(true); +} + +void onCommit(void* owner, void* data) { + const auto PWINDOW = (CWindow*)owner; + + if (std::find(bgWindows.begin(), bgWindows.end(), PWINDOW) == bgWindows.end()) { + ((origCommit)commitHook->m_pOriginal)(owner, data); + return; + } + + PWINDOW->setHidden(false); + + ((origCommit)commitHook->m_pOriginal)(owner, data); + + PWINDOW->setHidden(true); +} + +void onConfigReloaded() { + static auto* const PCLASS = &HyprlandAPI::getConfigValue(PHANDLE, "plugin:hyprwinwrap:class")->strValue; + g_pConfigManager->parseKeyword("windowrulev2", "float, class:^(" + *PCLASS + ")$", true); + g_pConfigManager->parseKeyword("windowrulev2", "size 100\% 100\%, class:^(" + *PCLASS + ")$", true); +} + +APICALL EXPORT PLUGIN_DESCRIPTION_INFO PLUGIN_INIT(HANDLE handle) { + PHANDLE = handle; + + HyprlandAPI::registerCallbackDynamic(PHANDLE, "openWindow", [&](void* self, SCallbackInfo& info, std::any data) { onNewWindow(std::any_cast(data)); }); + HyprlandAPI::registerCallbackDynamic(PHANDLE, "closeWindow", [&](void* self, SCallbackInfo& info, std::any data) { onCloseWindow(std::any_cast(data)); }); + HyprlandAPI::registerCallbackDynamic(PHANDLE, "render", [&](void* self, SCallbackInfo& info, std::any data) { onRenderStage(std::any_cast(data)); }); + HyprlandAPI::registerCallbackDynamic(PHANDLE, "configReloaded", [&](void* self, SCallbackInfo& info, std::any data) { onConfigReloaded(); }); + + auto fns = HyprlandAPI::findFunctionsByName(PHANDLE, "listener_commitSubsurface"); + if (fns.size() < 1) + throw std::runtime_error("hyprwinwrap: listener_commitSubsurface not found"); + damageHook = HyprlandAPI::createFunctionHook(PHANDLE, fns[0].address, (void*)&onDamage); + + fns = HyprlandAPI::findFunctionsByName(PHANDLE, "listener_commitWindow"); + if (fns.size() < 1) + throw std::runtime_error("hyprwinwrap: listener_commitWindow not found"); + commitHook = HyprlandAPI::createFunctionHook(PHANDLE, fns[0].address, (void*)&onCommit); + + bool hkResult = damageHook->hook(); + hkResult = hkResult && commitHook->hook(); + + if (!hkResult) + throw std::runtime_error("hyprwinwrap: hooks failed"); + + HyprlandAPI::addConfigValue(PHANDLE, "plugin:hyprwinwrap:class", SConfigValue{.strValue = "kitty-bg"}); + + HyprlandAPI::addNotification(PHANDLE, "[hyprwinwrap] Initialized successfully!", CColor{0.2, 1.0, 0.2, 1.0}, 5000); + + return {"hyprwinwrap", "A clone of xwinwrap for Hyprland", "Vaxry", "1.0"}; +} + +APICALL EXPORT void PLUGIN_EXIT() { + ; +} \ No newline at end of file diff --git a/hyprwinwrap/meson.build b/hyprwinwrap/meson.build new file mode 100644 index 0000000..ea16ec4 --- /dev/null +++ b/hyprwinwrap/meson.build @@ -0,0 +1,25 @@ +project('hyprwinwrap', 'cpp', + version: '0.1', + default_options: ['buildtype=release'], +) + +cpp_compiler = meson.get_compiler('cpp') +if cpp_compiler.has_argument('-std=c++23') + add_global_arguments('-std=c++23', language: 'cpp') +elif cpp_compiler.has_argument('-std=c++2b') + add_global_arguments('-std=c++2b', language: 'cpp') +else + error('Could not configure current C++ compiler (' + cpp_compiler.get_id() + ' ' + cpp_compiler.version() + ') with required C++ standard (C++23)') +endif + +globber = run_command('find', '.', '-name', '*.cpp', check: true) +src = globber.stdout().strip().split('\n') + +shared_module(meson.project_name(), src, + dependencies: [ + dependency('hyprland'), + dependency('pixman-1'), + dependency('libdrm'), + ], + install: true, +)