diff --git a/README.md b/README.md index adfb06e1..49df6891 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,10 @@ Try it out and report bugs / suggestions! ![Preview B] +
+ +![Preview C] +

@@ -139,8 +143,9 @@ Try it out and report bugs / suggestions! [Stars Preview]: https://starchart.cc/vaxerski/Hyprland.svg -[Preview A]: https://i.imgur.com/ZA4Fa8R.png -[Preview B]: https://i.imgur.com/BpXxM8H.png +[Preview A]: https://i.imgur.com/NbrTnZH.png +[Preview B]: https://i.imgur.com/ZA4Fa8R.png +[Preview C]: https://i.imgur.com/BpXxM8H.png [Banner]: https://raw.githubusercontent.com/vaxerski/Hyprland/main/assets/hyprland.png diff --git a/aur/PKGBUILD-git b/aur/PKGBUILD-git new file mode 100644 index 00000000..af0ac400 --- /dev/null +++ b/aur/PKGBUILD-git @@ -0,0 +1,40 @@ +# Maintainer: Sander van Kasteel , ThatOneCalculator + +_pkgname="hyprland" +pkgname="${_pkgname}-git" +pkgver=r461.96cdf8f +pkgrel=5 +pkgdesc="Hyprland is a dynamic tiling Wayland compositor based on wlroots that doesn't sacrifice on its looks." +arch=(any) +url="https://github.com/vaxerski/Hyprland" +license=('BSD') +depends=(libxcb xcb-proto xcb-util xcb-util-keysyms libxfixes libx11 libxcomposite xorg-xinput libxrender pixman wayland-protocols wlroots-git cairo pango) +makedepends=(git cmake ninja gcc gdb) +source=("${_pkgname}::git+https://github.com/vaxerski/Hyprland.git") +sha256sums=('SKIP') +options=(!makeflags !buildflags) + +pkgver() { + cd "$_pkgname" + ( set -o pipefail + git describe --long 2>/dev/null | sed 's/\([^-]*-g\)/r\1/;s/-/./g' || + printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)" + ) +} + +build() { + cd "${srcdir}/${_pkgname}" + make all +} + +package() { + cd "${srcdir}/${_pkgname}" + mkdir -p "${pkgdir}/usr/share/wayland-sessions" + mkdir -p "${pkgdir}/usr/share/hyprland" + install -Dm755 build/Hyprland -t "${pkgdir}/usr/bin" + install -Dm755 hyprctl/hyprctl -t "${pkgdir}/usr/bin" + install -Dm644 assets/*.png -t "${pkgdir}/usr/share/hyprland" + install -Dm644 example/hyprland.desktop -t "${pkgdir}/usr/share/wayland-sessions" + install -Dm644 example/hyprland.conf -t "${pkgdir}/usr/share/hyprland" + install -Dm644 LICENSE -t "${pkgdir}/usr/share/licenses/${_pkgname}" +} diff --git a/src/Compositor.cpp b/src/Compositor.cpp index 50ce621a..bf98d079 100644 --- a/src/Compositor.cpp +++ b/src/Compositor.cpp @@ -67,7 +67,7 @@ CCompositor::CCompositor() { m_sWLRScene = wlr_scene_create(); wlr_scene_attach_output_layout(m_sWLRScene, m_sWLROutputLayout); - m_sWLRXDGShell = wlr_xdg_shell_create(m_sWLDisplay); + m_sWLRXDGShell = wlr_xdg_shell_create(m_sWLDisplay, 2); m_sWLRCursor = wlr_cursor_create(); wlr_cursor_attach_output_layout(m_sWLRCursor, m_sWLROutputLayout); @@ -402,13 +402,13 @@ void CCompositor::focusWindow(CWindow* pWindow, wlr_surface* pSurface) { return; } - if (pWindow->m_bNoFocus) { - Debug::log(LOG, "Ignoring focus to nofocus window!"); + if (!pWindow || !windowValidMapped(pWindow)) { + wlr_seat_keyboard_notify_clear_focus(m_sSeat.seat); return; } - if (!pWindow || !windowValidMapped(pWindow)) { - wlr_seat_keyboard_notify_clear_focus(m_sSeat.seat); + if (pWindow->m_bNoFocus) { + Debug::log(LOG, "Ignoring focus to nofocus window!"); return; } @@ -513,7 +513,7 @@ CWindow* CCompositor::getWindowForPopup(wlr_xdg_popup* popup) { wlr_surface* CCompositor::vectorToLayerSurface(const Vector2D& pos, std::list* layerSurfaces, Vector2D* sCoords) { for (auto& l : *layerSurfaces) { - if (!l->layerSurface->mapped) + if (l->fadingOut || (l->layerSurface && !l->layerSurface->mapped)) continue; const auto SURFACEAT = wlr_layer_surface_v1_surface_at(l->layerSurface, pos.x - l->geometry.x, pos.y - l->geometry.y, &sCoords->x, &sCoords->y); @@ -632,7 +632,7 @@ void CCompositor::moveWindowToTop(CWindow* pWindow) { } } -void CCompositor::cleanupWindows() { +void CCompositor::cleanupFadingOut() { for (auto& w : m_lWindowsFadingOut) { bool valid = windowExists(w); @@ -650,6 +650,26 @@ void CCompositor::cleanupWindows() { return; } } + + for (auto& ls : m_lSurfacesFadingOut) { + if (ls->fadingOut && ls->readyToDelete && !ls->alpha.isBeingAnimated()) { + for (auto& m : m_lMonitors) { + for (auto& lsl : m.m_aLayerSurfaceLists) { + lsl.remove(ls); + } + } + + g_pHyprOpenGL->m_mLayerFramebuffers[ls].release(); + g_pHyprOpenGL->m_mLayerFramebuffers.erase(ls); + + m_lSurfacesFadingOut.remove(ls); + delete ls; + + Debug::log(LOG, "Cleanup: destroyed a layersurface"); + + return; + } + } } CWindow* CCompositor::getWindowInDirection(CWindow* pWindow, char dir) { @@ -863,4 +883,4 @@ SMonitor* CCompositor::getMonitorInDirection(const char& dir) { return longestIntersectMonitor; return nullptr; -} \ No newline at end of file +} diff --git a/src/Compositor.hpp b/src/Compositor.hpp index 4256dead..33fe7ba7 100644 --- a/src/Compositor.hpp +++ b/src/Compositor.hpp @@ -67,6 +67,7 @@ public: std::list m_lWorkspaces; std::list m_lSubsurfaces; std::list m_lWindowsFadingOut; + std::list m_lSurfacesFadingOut; void startCompositor(); void cleanupExit(); @@ -111,7 +112,7 @@ public: bool doesSeatAcceptInput(wlr_surface*); bool isWindowActive(CWindow*); void moveWindowToTop(CWindow*); - void cleanupWindows(); + void cleanupFadingOut(); CWindow* getWindowInDirection(CWindow*, char); void deactivateAllWLRWorkspaces(wlr_ext_workspace_handle_v1* exclude = nullptr); CWindow* getNextWindowOnWorkspace(CWindow*); diff --git a/src/Window.hpp b/src/Window.hpp index 7dcbb850..f68a5686 100644 --- a/src/Window.hpp +++ b/src/Window.hpp @@ -9,6 +9,10 @@ struct SWindowSpecialRenderData { float alpha = 1.f; }; +struct SWindowAdditionalConfigData { + std::string animationStyle = ""; +}; + class CWindow { public: CWindow(); @@ -81,6 +85,7 @@ public: // Special render data, rules, etc SWindowSpecialRenderData m_sSpecialRenderData; + SWindowAdditionalConfigData m_sAdditionalConfigData; // For the list lookup bool operator==(const CWindow& rhs) { diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 7635b257..631264e0 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -12,6 +12,9 @@ CConfigManager::CConfigManager() { setDefaultVars(); + static const char* const ENVHOME = getenv("HOME"); + const std::string CONFIGPATH = ENVHOME + (ISDEBUG ? (std::string) "/.config/hypr/hyprlandd.conf" : (std::string) "/.config/hypr/hyprland.conf"); + configPaths.emplace_back(CONFIGPATH); } void CConfigManager::setDefaultVars() { @@ -94,14 +97,21 @@ void CConfigManager::init() { Debug::log(WARN, "Error at statting config, error %i", errno); } - lastModifyTime = fileStat.st_mtime; + configModifyTimes[CONFIGPATH] = fileStat.st_mtime; isFirstLaunch = false; } void CConfigManager::configSetValueSafe(const std::string& COMMAND, const std::string& VALUE) { if (configValues.find(COMMAND) == configValues.end()) { - parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">: No such field."; + if (COMMAND[0] == '$') { + // register a dynamic var + Debug::log(LOG, "Registered dynamic var \"%s\" -> %s", COMMAND.c_str(), VALUE.c_str()); + configDynamicVars[COMMAND.substr(1)] = VALUE; + } else { + parseError = "Error setting value <" + VALUE + "> for field <" + COMMAND + ">: No such field."; + } + return; } @@ -398,6 +408,7 @@ void CConfigManager::handleWindowRule(const std::string& command, const std::str && RULE.find("pseudo") != 0 && RULE.find("monitor") != 0 && RULE.find("nofocus") != 0 + && RULE.find("animation") != 0 && RULE.find("workspace") != 0) { Debug::log(ERR, "Invalid rule found: %s", RULE.c_str()); parseError = "Invalid rule found: " + RULE; @@ -421,6 +432,60 @@ void CConfigManager::handleDefaultWorkspace(const std::string& command, const st } } +void CConfigManager::handleSource(const std::string& command, const std::string& rawpath) { + static const char* const ENVHOME = getenv("HOME"); + + auto value = rawpath; + + if (value[0] == '~') { + value.replace(0, 1, std::string(ENVHOME)); + } + + if (!std::filesystem::exists(value)) { + Debug::log(ERR, "source= file doesnt exist"); + parseError = "source file " + value + " doesn't exist!"; + return; + } + + configPaths.push_back(value); + + struct stat fileStat; + int err = stat(value.c_str(), &fileStat); + if (err != 0) { + Debug::log(WARN, "Error at ticking config at %s, error %i: %s", value.c_str(), err, strerror(err)); + return; + } + + configModifyTimes[value] = fileStat.st_mtime; + + std::ifstream ifs; + ifs.open(value); + std::string line = ""; + int linenum = 1; + if (ifs.is_open()) { + while (std::getline(ifs, line)) { + // Read line by line. + try { + configCurrentPath = value; + parseLine(line); + } catch (...) { + Debug::log(ERR, "Error reading line from config. Line:"); + Debug::log(NONE, "%s", line.c_str()); + + parseError += "Config error at line " + std::to_string(linenum) + " (" + configCurrentPath + "): Line parsing error."; + } + + if (parseError != "" && parseError.find("Config error at line") != 0) { + parseError = "Config error at line " + std::to_string(linenum) + " (" + configCurrentPath + "): " + parseError; + } + + ++linenum; + } + + ifs.close(); + } +} + std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std::string& VALUE, bool dynamic) { if (dynamic) parseError = ""; @@ -443,6 +508,7 @@ std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std:: else if (COMMAND == "windowrule") handleWindowRule(COMMAND, VALUE); else if (COMMAND == "bezier") handleBezier(COMMAND, VALUE); else if (COMMAND == "animation") handleAnimation(COMMAND, VALUE); + else if (COMMAND == "source") handleSource(COMMAND, VALUE); else configSetValueSafe(currentCategory + (currentCategory == "" ? "" : ":") + COMMAND, VALUE); @@ -455,6 +521,23 @@ std::string CConfigManager::parseKeyword(const std::string& COMMAND, const std:: return parseError; } +void CConfigManager::applyUserDefinedVars(std::string& line, const size_t equalsPlace) { + auto dollarPlace = line.find_first_of('$', equalsPlace); + + while (dollarPlace != std::string::npos) { + + const auto STRAFTERDOLLAR = line.substr(dollarPlace + 1); + for (auto&[var, value] : configDynamicVars) { + if (STRAFTERDOLLAR.find(var) == 0) { + line.replace(dollarPlace, var.length() + 1, value); + break; + } + } + + dollarPlace = line.find_first_of('$', dollarPlace + 1); + } +} + void CConfigManager::parseLine(std::string& line) { // first check if its not a comment const auto COMMENTSTART = line.find_first_of('#'); @@ -493,6 +576,9 @@ void CConfigManager::parseLine(std::string& line) { // check if command const auto EQUALSPLACE = line.find_first_of('='); + // apply vars + applyUserDefinedVars(line, EQUALSPLACE); + if (EQUALSPLACE == std::string::npos) return; @@ -515,10 +601,16 @@ void CConfigManager::loadConfigLoadVars() { g_pKeybindManager->clearKeybinds(); g_pAnimationManager->removeAllBeziers(); m_mAdditionalReservedAreas.clear(); + configDynamicVars.clear(); - const char* const ENVHOME = getenv("HOME"); + // paths + configPaths.clear(); + + static const char* const ENVHOME = getenv("HOME"); const std::string CONFIGPATH = ENVHOME + (ISDEBUG ? (std::string) "/.config/hypr/hyprlandd.conf" : (std::string) "/.config/hypr/hyprland.conf"); + configPaths.push_back(CONFIGPATH); + std::ifstream ifs; ifs.open(CONFIGPATH); @@ -549,16 +641,17 @@ void CConfigManager::loadConfigLoadVars() { while (std::getline(ifs, line)) { // Read line by line. try { + configCurrentPath = "~/.config/hypr/hyprland.conf"; parseLine(line); } catch (...) { Debug::log(ERR, "Error reading line from config. Line:"); Debug::log(NONE, "%s", line.c_str()); - parseError += "Config error at line " + std::to_string(linenum) + ": Line parsing error."; + parseError += "Config error at line " + std::to_string(linenum) + " (" + configCurrentPath + "): Line parsing error."; } if (parseError != "" && parseError.find("Config error at line") != 0) { - parseError = "Config error at line " + std::to_string(linenum) + ": " + parseError; + parseError = "Config error at line " + std::to_string(linenum) + " (" + configCurrentPath + "): " + parseError; } ++linenum; @@ -601,7 +694,7 @@ void CConfigManager::loadConfigLoadVars() { } void CConfigManager::tick() { - const char* const ENVHOME = getenv("HOME"); + static const char* const ENVHOME = getenv("HOME"); const std::string CONFIGPATH = ENVHOME + (ISDEBUG ? (std::string) "/.config/hypr/hyprlandd.conf" : (std::string) "/.config/hypr/hyprland.conf"); @@ -610,18 +703,26 @@ void CConfigManager::tick() { return; } - struct stat fileStat; - int err = stat(CONFIGPATH.c_str(), &fileStat); - if (err != 0) { - Debug::log(WARN, "Error at ticking config at %s, error %i: %s", CONFIGPATH.c_str(), err, strerror(err)); - return; + bool parse = false; + + for (auto& cf : configPaths) { + struct stat fileStat; + int err = stat(cf.c_str(), &fileStat); + if (err != 0) { + Debug::log(WARN, "Error at ticking config at %s, error %i: %s", cf.c_str(), err, strerror(err)); + return; + } + + // check if we need to reload cfg + if (fileStat.st_mtime != configModifyTimes[cf] || m_bForceReload) { + parse = true; + configModifyTimes[cf] = fileStat.st_mtime; + } } - // check if we need to reload cfg - if (fileStat.st_mtime != lastModifyTime || m_bForceReload) { - lastModifyTime = fileStat.st_mtime; + if (parse) { m_bForceReload = false; - + loadConfigLoadVars(); } } diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp index 9398b4f6..959bdd0d 100644 --- a/src/config/ConfigManager.hpp +++ b/src/config/ConfigManager.hpp @@ -76,8 +76,12 @@ public: std::string parseKeyword(const std::string&, const std::string&, bool dynamic = false); private: + std::deque configPaths; // stores all the config paths + std::unordered_map configModifyTimes; // stores modify times + std::unordered_map configDynamicVars; // stores dynamic vars declared by the user std::unordered_map configValues; - time_t lastModifyTime = 0; // for reloading the config if changed + + std::string configCurrentPath; std::string currentCategory = ""; // For storing the category of the current item @@ -94,6 +98,7 @@ private: // internal methods void setDefaultVars(); + void applyUserDefinedVars(std::string&, const size_t); void loadConfigLoadVars(); SConfigValue getConfigValueSafe(std::string); void parseLine(std::string&); @@ -106,6 +111,7 @@ private: void handleDefaultWorkspace(const std::string&, const std::string&); void handleBezier(const std::string&, const std::string&); void handleAnimation(const std::string&, const std::string&); + void handleSource(const std::string&, const std::string&); }; inline std::unique_ptr g_pConfigManager; \ No newline at end of file diff --git a/src/events/Layers.cpp b/src/events/Layers.cpp index 12e9e93b..86e2ca1a 100644 --- a/src/events/Layers.cpp +++ b/src/events/Layers.cpp @@ -58,6 +58,23 @@ void Events::listener_destroyLayerSurface(void* owner, void* data) { Debug::log(LOG, "LayerSurface %x destroyed", layersurface->layerSurface); + if (!layersurface->fadingOut) { + if (layersurface->layerSurface->mapped) { + Debug::log(LOG, "LayerSurface wasn't unmapped, making a snapshot now!"); + + // make a snapshot and start fade + // layersurfaces aren't required to unmap before destroy + g_pHyprOpenGL->makeLayerSnapshot(layersurface); + layersurface->alpha = 0.f; + + layersurface->fadingOut = true; + } else { + Debug::log(LOG, "Removing LayerSurface that wasn't mapped."); + layersurface->alpha.setValueAndWarp(0.f); + layersurface->fadingOut = true; + } + } + if (layersurface->layerSurface->mapped) layersurface->layerSurface->mapped = false; @@ -72,9 +89,6 @@ void Events::listener_destroyLayerSurface(void* owner, void* data) { const auto PMONITOR = g_pCompositor->getMonitorFromID(layersurface->monitorID); - // remove the layersurface as it's not used anymore - PMONITOR->m_aLayerSurfaceLists[layersurface->layer].remove(layersurface); - // rearrange to fix the reserved areas if (PMONITOR) { g_pHyprRenderer->arrangeLayersForMonitor(PMONITOR->ID); @@ -85,7 +99,8 @@ void Events::listener_destroyLayerSurface(void* owner, void* data) { g_pHyprRenderer->damageBox(&geomFixed); } - delete layersurface; + layersurface->readyToDelete = true; + layersurface->layerSurface = nullptr; } void Events::listener_mapLayerSurface(void* owner, void* data) { @@ -121,6 +136,11 @@ void Events::listener_mapLayerSurface(void* owner, void* data) { wlr_box geomFixed = {layersurface->geometry.x + PMONITOR->vecPosition.x, layersurface->geometry.y + PMONITOR->vecPosition.y, layersurface->geometry.width, layersurface->geometry.height}; g_pHyprRenderer->damageBox(&geomFixed); + + layersurface->alpha.setValue(0); + layersurface->alpha = 255.f; + layersurface->readyToDelete = false; + layersurface->fadingOut = false; } void Events::listener_unmapLayerSurface(void* owner, void* data) { @@ -128,6 +148,14 @@ void Events::listener_unmapLayerSurface(void* owner, void* data) { Debug::log(LOG, "LayerSurface %x unmapped", layersurface->layerSurface); + // make a snapshot and start fade + g_pHyprOpenGL->makeLayerSnapshot(layersurface); + layersurface->alpha = 0.f; + + layersurface->fadingOut = true; + + g_pCompositor->m_lSurfacesFadingOut.push_back(layersurface); + if (layersurface->layerSurface->mapped) layersurface->layerSurface->mapped = false; diff --git a/src/events/Monitors.cpp b/src/events/Monitors.cpp index a08bbfda..65049426 100644 --- a/src/events/Monitors.cpp +++ b/src/events/Monitors.cpp @@ -135,7 +135,7 @@ void Events::listener_monitorFrame(void* owner, void* data) { if (PMONITOR->ID == pMostHzMonitor->ID) { g_pCompositor->sanityCheckWorkspaces(); g_pAnimationManager->tick(); - g_pCompositor->cleanupWindows(); + g_pCompositor->cleanupFadingOut(); HyprCtl::tickHyprCtl(); // so that we dont get that race condition multithread bullshit @@ -191,7 +191,7 @@ void Events::listener_monitorFrame(void* owner, void* data) { const auto BLURSIZE = g_pConfigManager->getInt("decoration:blur_size"); const auto BLURPASSES = g_pConfigManager->getInt("decoration:blur_passes"); - const auto BLURRADIUS = BLURSIZE * BLURPASSES * 2; // is this 2? I don't know but 2 works. + const auto BLURRADIUS = BLURSIZE * pow(2, BLURPASSES); // is this 2^pass? I don't know but it works... I think. pixman_region32_copy(&g_pHyprOpenGL->m_rOriginalDamageRegion, &damage); diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index 0bbe1629..a6ae6c5d 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -95,6 +95,9 @@ void Events::listener_mapWindow(void* owner, void* data) { } catch(std::exception& e) { Debug::log(ERR, "Opacity rule \"%s\" failed with: %s", r.szRule.c_str(), e.what()); } + } else if (r.szRule.find("animation") == 0) { + auto STYLE = r.szRule.substr(r.szRule.find_first_of(' ') + 1); + PWINDOW->m_sAdditionalConfigData.animationStyle = STYLE; } } diff --git a/src/helpers/AnimatedVariable.hpp b/src/helpers/AnimatedVariable.hpp index a484958b..0b31b5c5 100644 --- a/src/helpers/AnimatedVariable.hpp +++ b/src/helpers/AnimatedVariable.hpp @@ -18,6 +18,7 @@ enum AVARDAMAGEPOLICY { class CAnimationManager; class CWorkspace; +struct SLayerSurface; class CAnimatedVariable { public: @@ -183,8 +184,12 @@ private: float* m_pSpeed = nullptr; int64_t* m_pEnabled = nullptr; + + // owners void* m_pWindow = nullptr; void* m_pWorkspace = nullptr; + void* m_pLayer = nullptr; + std::string* m_pBezier = nullptr; bool m_bDummy = true; @@ -196,4 +201,5 @@ private: friend class CAnimationManager; friend class CWorkspace; + friend struct SLayerSurface; }; \ No newline at end of file diff --git a/src/helpers/WLClasses.cpp b/src/helpers/WLClasses.cpp new file mode 100644 index 00000000..82e41b4f --- /dev/null +++ b/src/helpers/WLClasses.cpp @@ -0,0 +1,7 @@ +#include "WLClasses.hpp" +#include "../config/ConfigManager.hpp" + +SLayerSurface::SLayerSurface() { + alpha.create(AVARTYPE_FLOAT, &g_pConfigManager->getConfigValuePtr("animations:fadein_speed")->floatValue, &g_pConfigManager->getConfigValuePtr("animations:fadein")->intValue, &g_pConfigManager->getConfigValuePtr("animations:fadein_curve")->strValue, nullptr, AVARDAMAGE_ENTIRE); + alpha.m_pLayer = this; +} \ No newline at end of file diff --git a/src/helpers/WLClasses.hpp b/src/helpers/WLClasses.hpp index 544498f5..9d1cd6df 100644 --- a/src/helpers/WLClasses.hpp +++ b/src/helpers/WLClasses.hpp @@ -5,8 +5,11 @@ #include "../../wlr-layer-shell-unstable-v1-protocol.h" #include "../Window.hpp" #include "SubsurfaceTree.hpp" +#include "AnimatedVariable.hpp" struct SLayerSurface { + SLayerSurface(); + wlr_layer_surface_v1* layerSurface; wl_list link; @@ -22,6 +25,9 @@ struct SLayerSurface { int monitorID = -1; + CAnimatedVariable alpha; + bool fadingOut = false; + bool readyToDelete = false; // For the list lookup bool operator==(const SLayerSurface& rhs) { diff --git a/src/managers/AnimationManager.cpp b/src/managers/AnimationManager.cpp index a881b7d1..20302da6 100644 --- a/src/managers/AnimationManager.cpp +++ b/src/managers/AnimationManager.cpp @@ -41,12 +41,16 @@ void CAnimationManager::tick() { // window stuff const auto PWINDOW = (CWindow*)av->m_pWindow; const auto PWORKSPACE = (CWorkspace*)av->m_pWorkspace; + const auto PLAYER = (SLayerSurface*)av->m_pLayer; + wlr_box WLRBOXPREV = {0,0,0,0}; if (PWINDOW) { WLRBOXPREV = {(int)PWINDOW->m_vRealPosition.vec().x - BORDERSIZE - 1, (int)PWINDOW->m_vRealPosition.vec().y - BORDERSIZE - 1, (int)PWINDOW->m_vRealSize.vec().x + 2 * BORDERSIZE + 2, (int)PWINDOW->m_vRealSize.vec().y + 2 * BORDERSIZE + 2}; } else if (PWORKSPACE) { const auto PMONITOR = g_pCompositor->getMonitorFromID(PWORKSPACE->m_iMonitorID); WLRBOXPREV = {(int)PMONITOR->vecPosition.x, (int)PMONITOR->vecPosition.y, (int)PMONITOR->vecSize.x, (int)PMONITOR->vecSize.y}; + } else if (PLAYER) { + WLRBOXPREV = PLAYER->geometry; } // check if it's disabled, if so, warp @@ -199,8 +203,64 @@ bool CAnimationManager::deltazero(const CColor& a, const CColor& b) { return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a; } +// +// Anims +// +// + +void CAnimationManager::animationPopin(CWindow* pWindow) { + const auto GOALPOS = pWindow->m_vRealPosition.goalv(); + const auto GOALSIZE = pWindow->m_vRealSize.goalv(); + + pWindow->m_vRealPosition.setValue(GOALPOS + GOALSIZE / 2.f); + pWindow->m_vRealSize.setValue(Vector2D(5, 5)); +} + +void CAnimationManager::animationSlide(CWindow* pWindow, std::string force) { + pWindow->m_vRealSize.warp(); // size we preserve in slide + + const auto GOALPOS = pWindow->m_vRealPosition.goalv(); + const auto GOALSIZE = pWindow->m_vRealSize.goalv(); + + const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); + + if (force != "") { + if (force == "bottom") pWindow->m_vRealPosition.setValue(Vector2D(GOALPOS.x, PMONITOR->vecPosition.y + PMONITOR->vecSize.y)); + else if (force == "left") pWindow->m_vRealPosition.setValue(GOALPOS - Vector2D(GOALSIZE.x, 0)); + else if (force == "right") pWindow->m_vRealPosition.setValue(GOALPOS + Vector2D(GOALSIZE.x, 0)); + else pWindow->m_vRealPosition.setValue(Vector2D(GOALPOS.x, PMONITOR->vecPosition.y - GOALSIZE.y)); + + return; + } + + const auto MIDPOINT = GOALPOS + GOALSIZE / 2.f; + + // check sides it touches + const bool DISPLAYLEFT = STICKS(pWindow->m_vPosition.x, PMONITOR->vecPosition.x + PMONITOR->vecReservedTopLeft.x); + const bool DISPLAYRIGHT = STICKS(pWindow->m_vPosition.x + pWindow->m_vSize.x, PMONITOR->vecPosition.x + PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x); + const bool DISPLAYTOP = STICKS(pWindow->m_vPosition.y, PMONITOR->vecPosition.y + PMONITOR->vecReservedTopLeft.y); + const bool DISPLAYBOTTOM = STICKS(pWindow->m_vPosition.y + pWindow->m_vSize.y, PMONITOR->vecPosition.y + PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y); + + if (DISPLAYBOTTOM && DISPLAYTOP) { + if (DISPLAYLEFT && DISPLAYRIGHT) { + pWindow->m_vRealPosition.setValue(GOALPOS + Vector2D(0, GOALSIZE.y)); + } else if (DISPLAYLEFT) { + pWindow->m_vRealPosition.setValue(GOALPOS - Vector2D(GOALSIZE.x, 0)); + } else { + pWindow->m_vRealPosition.setValue(GOALPOS + Vector2D(GOALSIZE.x, 0)); + } + } else if (DISPLAYTOP) { + pWindow->m_vRealPosition.setValue(GOALPOS - Vector2D(0, GOALSIZE.y)); + } else if (DISPLAYBOTTOM) { + pWindow->m_vRealPosition.setValue(GOALPOS + Vector2D(0, GOALSIZE.y)); + } else { + if (MIDPOINT.y > PMONITOR->vecPosition.y + PMONITOR->vecSize.y / 2.f) + pWindow->m_vRealPosition.setValue(Vector2D(GOALPOS.x, PMONITOR->vecPosition.y + PMONITOR->vecSize.y)); + else + pWindow->m_vRealPosition.setValue(Vector2D(GOALPOS.x, PMONITOR->vecPosition.y - GOALSIZE.y)); + } +} -// animation on events void CAnimationManager::onWindowPostCreate(CWindow* pWindow) { auto ANIMSTYLE = g_pConfigManager->getString("animations:windows_style"); transform(ANIMSTYLE.begin(), ANIMSTYLE.end(), ANIMSTYLE.begin(), ::tolower); @@ -209,42 +269,25 @@ void CAnimationManager::onWindowPostCreate(CWindow* pWindow) { if (!pWindow->m_vRealPosition.isBeingAnimated() && !pWindow->m_vRealSize.isBeingAnimated()) return; - const auto GOALPOS = pWindow->m_vRealPosition.goalv(); - const auto GOALSIZE = pWindow->m_vRealSize.goalv(); - - if (ANIMSTYLE == "slide") { - pWindow->m_vRealSize.warp(); // size we preserve in slide - - const auto MIDPOINT = GOALPOS + GOALSIZE / 2.f; - - // check sides it touches - const auto PMONITOR = g_pCompositor->getMonitorFromID(pWindow->m_iMonitorID); - const bool DISPLAYLEFT = STICKS(pWindow->m_vPosition.x, PMONITOR->vecPosition.x + PMONITOR->vecReservedTopLeft.x); - const bool DISPLAYRIGHT = STICKS(pWindow->m_vPosition.x + pWindow->m_vSize.x, PMONITOR->vecPosition.x + PMONITOR->vecSize.x - PMONITOR->vecReservedBottomRight.x); - const bool DISPLAYTOP = STICKS(pWindow->m_vPosition.y, PMONITOR->vecPosition.y + PMONITOR->vecReservedTopLeft.y); - const bool DISPLAYBOTTOM = STICKS(pWindow->m_vPosition.y + pWindow->m_vSize.y, PMONITOR->vecPosition.y + PMONITOR->vecSize.y - PMONITOR->vecReservedBottomRight.y); - - if (DISPLAYBOTTOM && DISPLAYTOP) { - if (DISPLAYLEFT && DISPLAYRIGHT) { - pWindow->m_vRealPosition.setValue(GOALPOS + Vector2D(0, GOALSIZE.y)); - } else if (DISPLAYLEFT) { - pWindow->m_vRealPosition.setValue(GOALPOS - Vector2D(GOALSIZE.x, 0)); + if (pWindow->m_sAdditionalConfigData.animationStyle != "") { + // the window has config'd special anim + if (pWindow->m_sAdditionalConfigData.animationStyle.find("slide") == 0) { + if (pWindow->m_sAdditionalConfigData.animationStyle.find(' ') != std::string::npos) { + // has a direction + animationSlide(pWindow, pWindow->m_sAdditionalConfigData.animationStyle.substr(pWindow->m_sAdditionalConfigData.animationStyle.find(' ') + 1)); } else { - pWindow->m_vRealPosition.setValue(GOALPOS + Vector2D(GOALSIZE.x, 0)); + animationSlide(pWindow); } - } else if (DISPLAYTOP) { - pWindow->m_vRealPosition.setValue(GOALPOS - Vector2D(0, GOALSIZE.y)); - } else if (DISPLAYBOTTOM) { - pWindow->m_vRealPosition.setValue(GOALPOS + Vector2D(0, GOALSIZE.y)); } else { - if (MIDPOINT.y > PMONITOR->vecPosition.y + PMONITOR->vecSize.y / 2.f) - pWindow->m_vRealPosition.setValue(Vector2D(GOALPOS.x, PMONITOR->vecPosition.y + PMONITOR->vecSize.y)); - else - pWindow->m_vRealPosition.setValue(Vector2D(GOALPOS.x, PMONITOR->vecPosition.y - GOALSIZE.y)); + // anim popin, fallback + animationPopin(pWindow); } } else { - // anim popin, fallback - pWindow->m_vRealPosition.setValue(GOALPOS + GOALSIZE / 2.f); - pWindow->m_vRealSize.setValue(Vector2D(5, 5)); + if (ANIMSTYLE == "slide") { + animationSlide(pWindow); + } else { + // anim popin, fallback + animationPopin(pWindow); + } } } \ No newline at end of file diff --git a/src/managers/AnimationManager.hpp b/src/managers/AnimationManager.hpp index f3343ede..ef554171 100644 --- a/src/managers/AnimationManager.hpp +++ b/src/managers/AnimationManager.hpp @@ -29,6 +29,10 @@ private: bool deltazero(const float& a, const float& b); std::unordered_map m_mBezierCurves; + + // Anim stuff + void animationPopin(CWindow*); + void animationSlide(CWindow*, std::string force = ""); }; inline std::unique_ptr g_pAnimationManager; \ No newline at end of file diff --git a/src/render/OpenGL.cpp b/src/render/OpenGL.cpp index 80478de0..59bea30d 100644 --- a/src/render/OpenGL.cpp +++ b/src/render/OpenGL.cpp @@ -377,7 +377,7 @@ CFramebuffer* CHyprOpenGLImpl::blurMainFramebufferWithDamage(float a, wlr_box* p pixman_region32_t damage; pixman_region32_init(&damage); pixman_region32_copy(&damage, originalDamage); - wlr_region_expand(&damage, &damage, BLURPASSES * BLURSIZE * 2); + wlr_region_expand(&damage, &damage, pow(2, BLURPASSES) * BLURSIZE); // helper const auto PMIRRORFB = &m_mMonitorRenderResources[m_RenderData.pMonitor].mirrorFB; @@ -698,12 +698,57 @@ void CHyprOpenGLImpl::makeWindowSnapshot(CWindow* pWindow) { wlr_output_rollback(PMONITOR->output); } +void CHyprOpenGLImpl::makeLayerSnapshot(SLayerSurface* pLayer) { + // we trust the window is valid. + const auto PMONITOR = g_pCompositor->getMonitorFromID(pLayer->monitorID); + wlr_output_attach_render(PMONITOR->output, nullptr); + + // we need to "damage" the entire monitor + // so that we render the entire window + // this is temporary, doesnt mess with the actual wlr damage + pixman_region32_t fakeDamage; + pixman_region32_init(&fakeDamage); + pixman_region32_union_rect(&fakeDamage, &fakeDamage, 0, 0, (int)PMONITOR->vecSize.x, (int)PMONITOR->vecSize.y); + + begin(PMONITOR, &fakeDamage); + + pixman_region32_fini(&fakeDamage); + + const auto PFRAMEBUFFER = &m_mLayerFramebuffers[pLayer]; + + PFRAMEBUFFER->m_tTransform = pLayer->layerSurface->surface->current.transform; + + PFRAMEBUFFER->alloc(PMONITOR->vecSize.x, PMONITOR->vecSize.y); + + PFRAMEBUFFER->bind(); + + clear(CColor(0, 0, 0, 0)); // JIC + + timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + + // draw the layer + g_pHyprRenderer->renderLayer(pLayer, PMONITOR, &now); + +// restore original fb +#ifndef GLES2 + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_iCurrentOutputFb); +#else + glBindFramebuffer(GL_FRAMEBUFFER, m_iCurrentOutputFb); +#endif + glViewport(0, 0, g_pHyprOpenGL->m_RenderData.pMonitor->vecSize.x, g_pHyprOpenGL->m_RenderData.pMonitor->vecSize.y); + + end(); + + wlr_output_rollback(PMONITOR->output); +} + void CHyprOpenGLImpl::renderSnapshot(CWindow** pWindow) { RASSERT(m_RenderData.pMonitor, "Tried to render snapshot rect without begin()!"); const auto PWINDOW = *pWindow; auto it = m_mWindowFramebuffers.begin(); - for (;it != m_mWindowFramebuffers.end(); it++) { + for (; it != m_mWindowFramebuffers.end(); it++) { if (it->first == PWINDOW) { break; } @@ -724,6 +769,32 @@ void CHyprOpenGLImpl::renderSnapshot(CWindow** pWindow) { pixman_region32_fini(&fakeDamage); } +void CHyprOpenGLImpl::renderSnapshot(SLayerSurface** pLayer) { + RASSERT(m_RenderData.pMonitor, "Tried to render snapshot rect without begin()!"); + const auto PLAYER = *pLayer; + + auto it = m_mLayerFramebuffers.begin(); + for (; it != m_mLayerFramebuffers.end(); it++) { + if (it->first == PLAYER) { + break; + } + } + + if (it == m_mLayerFramebuffers.end() || !it->second.m_cTex.m_iTexID) + return; + + const auto PMONITOR = g_pCompositor->getMonitorFromID(PLAYER->monitorID); + + wlr_box windowBox = {0, 0, PMONITOR->vecSize.x, PMONITOR->vecSize.y}; + + pixman_region32_t fakeDamage; + pixman_region32_init_rect(&fakeDamage, 0, 0, PMONITOR->vecSize.x, PMONITOR->vecSize.y); + + renderTextureInternalWithDamage(it->second.m_cTex, &windowBox, PLAYER->alpha.fl(), &fakeDamage, 0); + + pixman_region32_fini(&fakeDamage); +} + void CHyprOpenGLImpl::createBGTextureForMonitor(SMonitor* pMonitor) { RASSERT(m_RenderData.pMonitor, "Tried to createBGTex without begin()!"); diff --git a/src/render/OpenGL.hpp b/src/render/OpenGL.hpp index 99de2a29..34b1a871 100644 --- a/src/render/OpenGL.hpp +++ b/src/render/OpenGL.hpp @@ -60,7 +60,9 @@ public: void renderBorder(wlr_box*, const CColor&, int thick = 1, int round = 0); void makeWindowSnapshot(CWindow*); + void makeLayerSnapshot(SLayerSurface*); void renderSnapshot(CWindow**); + void renderSnapshot(SLayerSurface**); void clear(const CColor&); void clearWithTex(); @@ -78,6 +80,7 @@ public: pixman_region32_t m_rOriginalDamageRegion; // used for storing the pre-expanded region std::unordered_map m_mWindowFramebuffers; + std::unordered_map m_mLayerFramebuffers; std::unordered_map m_mMonitorRenderResources; std::unordered_map m_mMonitorBGTextures; diff --git a/src/render/Renderer.cpp b/src/render/Renderer.cpp index be51336c..22060e83 100644 --- a/src/render/Renderer.cpp +++ b/src/render/Renderer.cpp @@ -70,8 +70,7 @@ void CHyprRenderer::renderWorkspaceWithFullscreenWindow(SMonitor* pMonitor, CWor // and the overlay layers for (auto& ls : pMonitor->m_aLayerSurfaceLists[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]) { - SRenderData renderdata = {pMonitor->output, time, ls->geometry.x, ls->geometry.y}; - wlr_surface_for_each_surface(ls->layerSurface->surface, renderSurface, &renderdata); + renderLayer(ls, pMonitor, time); } renderDragIcon(pMonitor, time); @@ -121,6 +120,17 @@ void CHyprRenderer::renderWindow(CWindow* pWindow, SMonitor* pMonitor, timespec* } } +void CHyprRenderer::renderLayer(SLayerSurface* pLayer, SMonitor* pMonitor, timespec* time) { + if (pLayer->fadingOut) { + g_pHyprOpenGL->renderSnapshot(&pLayer); + return; + } + + SRenderData renderdata = {pMonitor->output, time, pLayer->geometry.x, pLayer->geometry.y}; + renderdata.fadeAlpha = pLayer->alpha.fl(); + wlr_surface_for_each_surface(pLayer->layerSurface->surface, renderSurface, &renderdata); +} + void CHyprRenderer::renderAllClientsForMonitor(const int& ID, timespec* time) { const auto PMONITOR = g_pCompositor->getMonitorFromID(ID); @@ -129,12 +139,10 @@ void CHyprRenderer::renderAllClientsForMonitor(const int& ID, timespec* time) { // Render layer surfaces below windows for monitor for (auto& ls : PMONITOR->m_aLayerSurfaceLists[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND]) { - SRenderData renderdata = {PMONITOR->output, time, ls->geometry.x, ls->geometry.y}; - wlr_surface_for_each_surface(ls->layerSurface->surface, renderSurface, &renderdata); + renderLayer(ls, PMONITOR, time); } for (auto& ls : PMONITOR->m_aLayerSurfaceLists[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM]) { - SRenderData renderdata = {PMONITOR->output, time, ls->geometry.x, ls->geometry.y}; - wlr_surface_for_each_surface(ls->layerSurface->surface, renderSurface, &renderdata); + renderLayer(ls, PMONITOR, time); } // if there is a fullscreen window, render it and then do not render anymore. @@ -178,12 +186,10 @@ void CHyprRenderer::renderAllClientsForMonitor(const int& ID, timespec* time) { // Render surfaces above windows for monitor for (auto& ls : PMONITOR->m_aLayerSurfaceLists[ZWLR_LAYER_SHELL_V1_LAYER_TOP]) { - SRenderData renderdata = {PMONITOR->output, time, ls->geometry.x, ls->geometry.y}; - wlr_surface_for_each_surface(ls->layerSurface->surface, renderSurface, &renderdata); + renderLayer(ls, PMONITOR, time); } for (auto& ls : PMONITOR->m_aLayerSurfaceLists[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY]) { - SRenderData renderdata = {PMONITOR->output, time, ls->geometry.x, ls->geometry.y}; - wlr_surface_for_each_surface(ls->layerSurface->surface, renderSurface, &renderdata); + renderLayer(ls, PMONITOR, time); } renderDragIcon(PMONITOR, time); @@ -316,6 +322,9 @@ void CHyprRenderer::arrangeLayerArray(SMonitor* pMonitor, const std::listvecPosition.x, pMonitor->vecPosition.y, pMonitor->vecSize.x, pMonitor->vecSize.y}; for (auto& ls : layerSurfaces) { + if (ls->fadingOut || ls->readyToDelete) + continue; + const auto PLAYER = ls->layerSurface; const auto PSTATE = &PLAYER->current; if (exclusiveZone != (PSTATE->exclusive_zone > 0)) { diff --git a/src/render/Renderer.hpp b/src/render/Renderer.hpp index b445c176..f879702b 100644 --- a/src/render/Renderer.hpp +++ b/src/render/Renderer.hpp @@ -37,6 +37,7 @@ private: void drawBorderForWindow(CWindow*, SMonitor*, float a = 255.f, const Vector2D& offset = Vector2D(0,0)); void renderWorkspaceWithFullscreenWindow(SMonitor*, CWorkspace*, timespec*); void renderWindow(CWindow*, SMonitor*, timespec*, bool); + void renderLayer(SLayerSurface*, SMonitor*, timespec*); void renderDragIcon(SMonitor*, timespec*);