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*);