diff --git a/README.md b/README.md index 288015ab..7b4c9007 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ Nevertheless, REPORT any you find! Make an issue! - Config reloaded instantly upon saving - Easily expandable and readable codebase - Support for docks/whatever + - Window rules - Monitor rules - Socket-based IPC - Tiling/floating/fullscreen windows @@ -30,7 +31,6 @@ Nevertheless, REPORT any you find! Make an issue! - Rounded corners - Blur - Fadein/out - - Window rules - Fix electron rendering issues - Optimization - Fix weird scroll on XWayland diff --git a/example/hyprland.conf b/example/hyprland.conf index f2479eb8..524ac94b 100644 --- a/example/hyprland.conf +++ b/example/hyprland.conf @@ -18,6 +18,22 @@ general { col.inactive_border=0x66333333 } +animations { + enabled=1 + windows=1 + borders=1 # not yet implemented + fadein=1 # not yet implemented +} + +# example window rules +# for windows named/classed as abc and xyz +windowrule=move 69 420,abc +windowrule=size 420 69,abc +windowrule=tile,xyz +windowrule=float,abc +windowrule=monitor 0,xyz + +# example binds bind=SUPER,Q,exec,kitty bind=SUPER,C,killactive, bind=SUPER,M,exec,pkill Hyprland diff --git a/src/config/ConfigManager.cpp b/src/config/ConfigManager.cpp index 2a481ed2..b29edacc 100644 --- a/src/config/ConfigManager.cpp +++ b/src/config/ConfigManager.cpp @@ -163,6 +163,30 @@ void CConfigManager::handleBind(const std::string& command, const std::string& v g_pKeybindManager->addKeybind(SKeybind{KEY, MOD, HANDLER, COMMAND}); } +void CConfigManager::handleWindowRule(const std::string& command, const std::string& value) { + const auto RULE = value.substr(0, value.find_first_of(",")); + const auto VALUE = value.substr(value.find_first_of(",") + 1); + + // check rule and value + if (RULE == "" || VALUE == "") { + return; + } + + // verify we support a rule + if (RULE != "float" + && RULE != "tile" + && RULE.find("move") != 0 + && RULE.find("size") != 0 + && RULE.find("monitor") != 0) { + Debug::log(ERR, "Invalid rule found: %s", RULE.c_str()); + parseError = "Invalid rule found: " + RULE; + return; + } + + m_dWindowRules.push_back({RULE, VALUE}); + +} + void CConfigManager::handleDefaultWorkspace(const std::string& command, const std::string& value) { const auto DISPLAY = value.substr(0, value.find_first_of(',')); @@ -230,6 +254,9 @@ void CConfigManager::parseLine(std::string& line) { } else if (COMMAND == "workspace") { handleDefaultWorkspace(COMMAND, VALUE); return; + } else if (COMMAND == "windowrule") { + handleWindowRule(COMMAND, VALUE); + return; } configSetValueSafe(currentCategory + (currentCategory == "" ? "" : ":") + COMMAND, VALUE); @@ -241,6 +268,7 @@ void CConfigManager::loadConfigLoadVars() { currentCategory = ""; // reset the category m_dMonitorRules.clear(); + m_dWindowRules.clear(); g_pKeybindManager->clearKeybinds(); const char* const ENVHOME = getenv("HOME"); @@ -357,4 +385,33 @@ SMonitorRule CConfigManager::getMonitorRuleFor(std::string name) { Debug::log(WARN, "No rules configured. Using the default hardcoded one."); return SMonitorRule{.name = "", .resolution = Vector2D(1280, 720), .offset = Vector2D(0, 0), .mfact = 0.5f, .scale = 1}; +} + +std::vector CConfigManager::getMatchingRules(CWindow* pWindow) { + if (!g_pCompositor->windowValidMapped(pWindow)) + return std::vector(); + + std::vector returns; + + std::string title = g_pXWaylandManager->getTitle(pWindow); + std::string appidclass = g_pXWaylandManager->getAppIDClass(pWindow); + + for (auto& rule : m_dWindowRules) { + // check if we have a matching rule + try { + std::regex classCheck(rule.szValue); + + if (!std::regex_search(title, classCheck) && !std::regex_search(appidclass, classCheck)) + continue; + } catch (...) { + Debug::log(ERR, "Regex error at %s", rule.szValue.c_str()); + } + + // applies. Read the rule and behave accordingly + Debug::log(LOG, "Window rule %s -> %s matched %x [%s]", rule.szRule.c_str(), rule.szValue.c_str(), pWindow, pWindow->m_szTitle.c_str()); + + returns.push_back(rule); + } + + return returns; } \ No newline at end of file diff --git a/src/config/ConfigManager.hpp b/src/config/ConfigManager.hpp index c34aadb1..8c38efa5 100644 --- a/src/config/ConfigManager.hpp +++ b/src/config/ConfigManager.hpp @@ -6,6 +6,9 @@ #include "../defines.hpp" #include #include +#include +#include +#include "../Window.hpp" struct SConfigValue { int64_t intValue = -1; @@ -23,6 +26,11 @@ struct SMonitorRule { int defaultWorkspaceID = -1; }; +struct SWindowRule { + std::string szRule; + std::string szValue; +}; + class CConfigManager { public: CConfigManager(); @@ -36,6 +44,8 @@ public: SMonitorRule getMonitorRuleFor(std::string); + std::vector getMatchingRules(CWindow*); + private: std::unordered_map configValues; time_t lastModifyTime = 0; // for reloading the config if changed @@ -47,6 +57,7 @@ private: bool isFirstLaunch = true; // For exec-once std::deque m_dMonitorRules; + std::deque m_dWindowRules; // internal methods void loadConfigLoadVars(); @@ -56,6 +67,7 @@ private: void handleRawExec(const std::string&, const std::string&); void handleMonitor(const std::string&, const std::string&); void handleBind(const std::string&, const std::string&); + void handleWindowRule(const std::string&, const std::string&); void handleDefaultWorkspace(const std::string&, const std::string&); }; diff --git a/src/events/Windows.cpp b/src/events/Windows.cpp index c6f4e352..0ee5e158 100644 --- a/src/events/Windows.cpp +++ b/src/events/Windows.cpp @@ -36,13 +36,73 @@ void Events::listener_mapWindow(wl_listener* listener, void* data) { wl_signal_add(&PWINDOWSURFACE->events.new_subsurface, &PWINDOW->listen_newSubsurfaceWindow); - if (g_pXWaylandManager->shouldBeFloated(PWINDOW)) { - g_pLayoutManager->getCurrentLayout()->onWindowCreatedFloating(PWINDOW); + if (g_pXWaylandManager->shouldBeFloated(PWINDOW)) PWINDOW->m_bIsFloating = true; + + // window rules + const auto WINDOWRULES = g_pConfigManager->getMatchingRules(PWINDOW); + + for (auto& r : WINDOWRULES) { + if (r.szRule.find("monitor") == 0) { + try { + const long int MONITOR = std::stoi(r.szRule.substr(r.szRule.find(" "))); + + Debug::log(LOG, "Rule monitor, applying to window %x", PWINDOW); + + if (MONITOR >= (long int)g_pCompositor->m_lMonitors.size() || MONITOR < (long int)0) + PWINDOW->m_iMonitorID = 0; + else + PWINDOW->m_iMonitorID = MONITOR; + + PWINDOW->m_iWorkspaceID = g_pCompositor->getMonitorFromID(PWINDOW->m_iMonitorID)->activeWorkspace; + } catch (...) { + Debug::log(LOG, "Rule monitor failed, rule: %s -> %s", r.szRule.c_str(), r.szValue.c_str()); + } + } else if (r.szRule.find("float") == 0) { + PWINDOW->m_bIsFloating = true; + } else if (r.szRule.find("tile") == 0) { + PWINDOW->m_bIsFloating = false; + } + } + + if (PWINDOW->m_bIsFloating) { + g_pLayoutManager->getCurrentLayout()->onWindowCreatedFloating(PWINDOW); + + // size and move rules + for (auto& r : WINDOWRULES) { + if (r.szRule.find("size") == 0) { + try { + const auto VALUE = r.szRule.substr(r.szRule.find(" ") + 1); + const auto SIZEX = stoi(VALUE.substr(0, VALUE.find(" "))); + const auto SIZEY = stoi(VALUE.substr(VALUE.find(" ") + 1)); + + Debug::log(LOG, "Rule size, applying to window %x", PWINDOW); + + PWINDOW->m_vEffectiveSize = Vector2D(SIZEX, SIZEY); + g_pXWaylandManager->setWindowSize(PWINDOW, PWINDOW->m_vEffectiveSize); + } catch (...) { + Debug::log(LOG, "Rule size failed, rule: %s -> %s", r.szRule.c_str(), r.szValue.c_str()); + } + } else if (r.szRule.find("move") == 0) { + try { + const auto VALUE = r.szRule.substr(r.szRule.find(" ") + 1); + const auto POSX = stoi(VALUE.substr(0, VALUE.find(" "))); + const auto POSY = stoi(VALUE.substr(VALUE.find(" ") + 1)); + + Debug::log(LOG, "Rule move, applying to window %x", PWINDOW); + + PWINDOW->m_vEffectivePosition = Vector2D(POSX, POSY) + PMONITOR->vecPosition; + } catch (...) { + Debug::log(LOG, "Rule move failed, rule: %s -> %s", r.szRule.c_str(), r.szValue.c_str()); + } + } + } } else g_pLayoutManager->getCurrentLayout()->onWindowCreated(PWINDOW); - + + PWINDOW->m_szTitle = g_pXWaylandManager->getTitle(PWINDOW); + if (!PWINDOW->m_bIsModal) g_pCompositor->focusWindow(PWINDOW); diff --git a/src/layout/DwindleLayout.cpp b/src/layout/DwindleLayout.cpp index 00309330..adcc884c 100644 --- a/src/layout/DwindleLayout.cpp +++ b/src/layout/DwindleLayout.cpp @@ -357,8 +357,13 @@ void CHyprDwindleLayout::onWindowCreatedFloating(CWindow* pWindow) { } } - pWindow->m_vRealPosition = pWindow->m_vEffectivePosition + pWindow->m_vEffectiveSize / 2.f; - pWindow->m_vRealSize = Vector2D(5,5); + if (!pWindow->m_bX11DoesntWantBorders) { + pWindow->m_vRealPosition = pWindow->m_vEffectivePosition + pWindow->m_vEffectiveSize / 2.f; + pWindow->m_vRealSize = Vector2D(5, 5); + } else { + pWindow->m_vRealPosition = pWindow->m_vEffectivePosition; + pWindow->m_vRealSize = pWindow->m_vEffectiveSize; + } g_pXWaylandManager->setWindowSize(pWindow, pWindow->m_vRealSize); g_pCompositor->fixXWaylandWindowsOnWorkspace(PMONITOR->activeWorkspace); diff --git a/src/managers/XWaylandManager.cpp b/src/managers/XWaylandManager.cpp index d17c1e5c..bbd8f9f0 100644 --- a/src/managers/XWaylandManager.cpp +++ b/src/managers/XWaylandManager.cpp @@ -54,11 +54,7 @@ std::string CHyprXWaylandManager::getTitle(CWindow* pWindow) { if (pWindow->m_uSurface.xwayland) { return std::string(pWindow->m_uSurface.xwayland->title); } - } else { - return ""; - } - - if (pWindow->m_uSurface.xdg) { + } else if (pWindow->m_uSurface.xdg) { if (pWindow->m_uSurface.xdg->toplevel) { return std::string(pWindow->m_uSurface.xdg->toplevel->title); } @@ -72,6 +68,26 @@ std::string CHyprXWaylandManager::getTitle(CWindow* pWindow) { return ""; } +std::string CHyprXWaylandManager::getAppIDClass(CWindow* pWindow) { + try { + if (pWindow->m_bIsX11) { + if (pWindow->m_uSurface.xwayland) { + return std::string(pWindow->m_uSurface.xwayland->_class); + } + } else if (pWindow->m_uSurface.xdg) { + if (pWindow->m_uSurface.xdg->toplevel) { + return std::string(pWindow->m_uSurface.xdg->toplevel->app_id); + } + } else { + return ""; + } + } catch (std::logic_error& e) { + Debug::log(ERR, "Error in getAppIDClass: %s", e.what()); + } + + return ""; +} + void CHyprXWaylandManager::sendCloseWindow(CWindow* pWindow) { if (pWindow->m_bIsX11) { wlr_xwayland_surface_close(pWindow->m_uSurface.xwayland); diff --git a/src/managers/XWaylandManager.hpp b/src/managers/XWaylandManager.hpp index be7221d9..00de8139 100644 --- a/src/managers/XWaylandManager.hpp +++ b/src/managers/XWaylandManager.hpp @@ -14,6 +14,7 @@ public: void activateSurface(wlr_surface*, bool); void getGeometryForWindow(CWindow*, wlr_box*); std::string getTitle(CWindow*); + std::string getAppIDClass(CWindow*); void sendCloseWindow(CWindow*); void setWindowSize(CWindow*, const Vector2D&); void setWindowStyleTiled(CWindow*, uint32_t);